On Wed, 21 Nov 2007 00:43:57 +0300, Krzysztof Sasiak <krzsas at o2.pl>
wrote:
> Hi,
>
> I'm having a hard time configuring a Mustek Powermust 600VA ups to
> work via USB with nut. I read somewhere that nut works OK via the
> rs232 cable, but unfortunately I don't have a COM port in my computer.
> The kernel detects the ups as an Xbox pad :) and loads the xpad
> module. I tried running /lib/nut/megatec with different /dev/ttySx but
> it displays megatec protocol UPS was not detected.
>
> This is what I get from lsusb -v:
>
> Bus 001 Device 002: ID 06da:0003 Phoenixtec Power Co., Ltd
[snip]>
> How do I create an appropriate /dev/ttyS and is there a way to make
> linux work with this ups?
>
Please, try this off-tree patch:
http://probu.nl/~p.v.valderen/lakeview.v2.patch
or an updated version below with resolved conflicts with the
current trunk.
The driver name is 'lakeview_usb'.
Index: drivers/lakeview_usb.c
==================================================================---
drivers/lakeview_usb.c (revision 0)
+++ drivers/lakeview_usb.c (revision 0)
@@ -0,0 +1,427 @@
+/* lakeview_usb.h - driver for UPS with lakeview chipset, such as
+ 'Sweex Manageable UPS 1000VA' (ca. 2006)
+
+ May also work on 'Kebo UPS-650D', not tested as of 05/23/2007
+
+ Copyright (C) 2007 Peter van Valderen <p.v.valderen at probu.nl>
+ Dirk Teurlings <dirk at upexia.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ #include "nut_usb.h"
+*/
+
+#include "main.h"
+#include "lakeview_usb.h"
+
+#include <usb.h>
+
+usb_dev_handle *upsdev = NULL;
+
+extern int exit_flag;
+static unsigned int comm_failures = 0;
+
+int query_ups (unsigned char *reply) {
+ unsigned char buf[4];
+ int ret;
+
+ /*
+ * This packet is a status request to the UPS
+ */
+ buf[0]=0x01;
+ buf[1]=0x00;
+ buf[2]=0x00;
+ buf[3]=0x30;
+
+ return execute_and_retrieve_query(buf, reply);
+}
+
+int execute_and_retrieve_query(unsigned char *query, unsigned char *reply) {
+ int ret;
+
+ ret = usb_control_msg(upsdev, STATUS_REQUESTTYPE, REQUEST_VALUE,
+ MESSAGE_VALUE, INDEX_VALUE, query, sizeof(query), 1000);
+
+ if (ret < 0) {
+ usb_comm_fail("Error sending control message to USB device\n");
+ return ret;
+ }
+
+ ret = usb_interrupt_read(upsdev, REPLY_REQUESTTYPE, reply,
sizeof(REPLY_PACKETSIZE), 1000);
+
+ upsdebugx(5, "usb_interrupt_read return code: %d", ret);
+ if (ret < 0) {
+ usb_comm_fail("Receive error (Request command): COMMAND: %x\n",
query);
+ return -1;
+ }
+
+ return ret;
+}
+
+static void usb_open_error(const char *port)
+{
+ printf("Unable to find Lakeview UPS device on USB bus \n\n");
+
+ printf("Things to try:\n\n");
+ printf(" - Connect UPS device to USB bus\n\n");
+ printf(" - Run this driver as another user (upsdrvctl -u or
'user=...' in ups.conf).\n");
+ printf(" See upsdrvctl(8) and ups.conf(5).\n\n");
+
+ fatalx(EXIT_FAILURE, "Fatal error: unusable configuration");
+}
+
+void usb_comm_fail(const char *fmt, ...)
+{
+ int ret;
+ char why[SMALLBUF];
+ va_list ap;
+
+ /* this means we're probably here because select was interrupted */
+ if (exit_flag != 0)
+ return; /* ignored, since we're about to exit
anyway */
+
+ comm_failures++;
+
+ if ((comm_failures == USB_ERR_LIMIT) ||
+ ((comm_failures % USB_ERR_RATE) == 0))
+ {
+ upslogx(LOG_WARNING, "Warning: excessive comm failures,
"
+ "limiting error reporting");
+ }
+
+ /* once it's past the limit, only log once every USB_ERR_LIMIT
calls */
+ if ((comm_failures > USB_ERR_LIMIT) &&
+ ((comm_failures % USB_ERR_LIMIT) != 0))
+ return;
+
+ /* generic message if the caller hasn't elaborated */
+ if (!fmt)
+ {
+ upslogx(LOG_WARNING, "Communications with UPS lost"
+ " - check cabling");
+ return;
+ }
+
+ va_start(ap, fmt);
+ ret = vsnprintf(why, sizeof(why), fmt, ap);
+ va_end(ap);
+
+ if ((ret < 1) || (ret >= (int) sizeof(why)))
+ upslogx(LOG_WARNING, "usb_comm_fail: vsnprintf needed
"
+ "more than %d bytes", sizeof(why));
+
+ upslogx(LOG_WARNING, "Communications with UPS lost: %s",
why);
+}
+
+void upsdrv_comm_good()
+{
+ if (comm_failures == 0)
+ return;
+
+ upslogx(LOG_NOTICE, "Communications with UPS re-established");
+ comm_failures = 0;
+}
+
+static usb_dev_handle *open_lakeview_usb()
+{
+ struct usb_bus *busses = usb_get_busses();
+ struct usb_bus *bus;
+
+ for (bus = busses; bus; bus = bus->next)
+ {
+ struct usb_device *dev;
+
+ for (dev = bus->devices; dev; dev = dev->next)
{
+ /* XXX Check for Lakeview USB compatible devices */
+ if (dev->descriptor.bDeviceClass ==
USB_CLASS_PER_INTERFACE &&
+ (dev->descriptor.idVendor == 0x0925 &&
+ dev->descriptor.idProduct == 0x1234))
+ return usb_open(dev);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Connect to the UPS
+ */
+
+usb_dev_handle *open_ups(const char *port) {
+ static int libusb_init = 0;
+ int dev_claimed = 0;
+ usb_dev_handle *dev_h = NULL;
+ int retry;
+
+ if (!libusb_init)
+ {
+ /* Initialize Libusb */
+ usb_init();
+ libusb_init = 1;
+ }
+
+ for (retry = 0; dev_h == NULL && retry < 32; retry++)
+ {
+ struct timespec t = {5, 0};
+ usb_find_busses();
+ usb_find_devices();
+
+ dev_h = open_lakeview_usb();
+ if (!dev_h) {
+ upslogx(LOG_WARNING, "Can't open Lakeview USB
device, retrying ...");
+ if (nanosleep(&t, NULL) < 0 && errno ==
EINTR)
+ break;
+ }
+ }
+
+ if (!dev_h)
+ {
+ upslogx(LOG_ERR, "Can't open Lakeview USB
device");
+ goto errout;
+ }
+
+#if LIBUSB_HAS_DETACH_KRNL_DRV
+ /* this method requires at least libusb 0.1.8:
+ * it force device claiming by unbinding
+ * attached driver... From libhid */
+ retry = 3;
+ while (usb_set_configuration(dev_h, 1) != 0 && retry-- > 0)
{
+// while ((dev_claimed = usb_claim_interface(dev_h, 0)) != 0 &&
retry-- > 0) {
+ upsdebugx(2, "Can't set Lakeview USB configuration,
trying %d more time(s)...", retry);
+
+ upsdebugx(2, "detaching kernel driver from USB
device...");
+ if (usb_detach_kernel_driver_np(dev_h, 0) < 0) {
+ upsdebugx(2, "failed to detach kernel driver from
USB device...");
+ }
+
+ upsdebugx(2, "trying again to set USB
configuration...");
+ }
+
+ if (retry < 3) {
+ upsdebugx(2, "USB configuration successfully set");
+ }
+#else
+ if (usb_set_configuration(dev_h, 1) < 0)
+ {
+ upslogx(LOG_ERR, "Can't set Lakeview USB
configuration");
+ goto errout;
+ }
+#endif
+
+ if (usb_claim_interface(dev_h, 0) < 0)
+ {
+ upslogx(LOG_ERR, "Can't claim Lakeview USB interface");
+ goto errout;
+ }
+ else
+ dev_claimed = 1;
+
+ if (usb_set_altinterface(dev_h, 0) < 0)
+ {
+ upslogx(LOG_ERR, "Can't set Lakeview USB alternate
interface");
+ goto errout;
+ }
+
+ if (usb_clear_halt(dev_h, 0x81) < 0)
+ {
+ upslogx(LOG_ERR, "Can't reset Lakeview USB
endpoint");
+ goto errout;
+ }
+
+ return dev_h;
+
+errout:
+ if (dev_h && dev_claimed)
+ usb_release_interface(dev_h, 0);
+ if (dev_h)
+ usb_close(dev_h);
+
+
+
+ usb_open_error(port);
+ return 0;
+}
+
+int close_ups(usb_dev_handle *dev_h, const char *port)
+{
+ if (dev_h)
+ {
+ usb_release_interface(dev_h, 0);
+ return usb_close(dev_h);
+ }
+
+ return 0;
+}
+
+
+/*
+ * Initialise the UPS
+ */
+
+void upsdrv_initups(void)
+{
+ unsigned char reply[REPLY_PACKETSIZE];
+ int i;
+
+ /* open the USB connection to the UPS */
+ upsdev = open_ups("USB");
+
+ /*
+ * Read rubbish data a few times; the UPS doesn't seem to respond
properly
+ * the first few times after connecting
+ */
+
+ for (i=0;i<5;i++) {
+ query_ups(reply);
+ sleep(1);
+ }
+}
+
+void upsdrv_cleanup(void)
+{
+ upslogx(LOG_ERR, "CLOSING\n");
+ close_ups(upsdev, "USB");
+}
+
+void upsdrv_reconnect(void)
+{
+
+ upslogx(LOG_WARNING, "RECONNECT USB DEVICE\n");
+ close_ups(upsdev, "USB");
+
+ upsdev = NULL;
+ sleep(3);
+ upsdrv_initups();
+}
+
+void upsdrv_initinfo(void)
+{
+ dstate_setinfo("driver.version.internal", "%s",
DRV_VERSION);
+
+ dstate_setinfo("ups.mfr", "Lakeview Research
compatible");
+ dstate_setinfo("ups.model","Unknown");
+}
+
+void upsdrv_updateinfo(void)
+{
+ unsigned char reply[REPLY_PACKETSIZE];
+ int ret, online, battery_normal;
+
+ unsigned char test;
+
+ ret = query_ups(reply);
+
+ if (ret < 0) {
+ upslog_with_errno(LOG_INFO, "Query to UPS failed");
+ dstate_datastale();
+
+ /* reconnect the UPS */
+ upsdebugx(2, "Query failed, reconnecting UPS...");
+ upsdrv_reconnect();
+
+ return;
+ }
+
+ /*
+ * 3rd bit of 4th byte indicates whether the UPS is on line (1)
+ * or on battery (0)
+ */
+ online = (reply[3]&4)>>2;
+
+ /*
+ * 2nd bit of 4th byte indicates battery status; normal (1)
+ * or low (0)
+ */
+ battery_normal = (reply[3]&2)>>1;
+
+ status_init();
+
+ if (online) {
+ status_set("OL");
+ }
+ else {
+ status_set("OB");
+ }
+
+ if (!battery_normal) {
+ status_set("LB");
+ }
+
+ status_commit();
+ dstate_dataok();
+}
+
+/*
+ * The shutdown feature is a bit strange on this UPS IMHO, it
+ * switches the polarity of the 'Shutdown UPS' signal, at which
+ * point it will automatically power down once it loses power.
+ *
+ * It will still, however, be possible to poll the UPS and
+ * reverse the polarity _again_, at which point it will
+ * start back up once power comes back.
+ *
+ * Maybe this is the normal way, it just seems a bit strange.
+ *
+ * Please note, this function doesn't power the UPS off if
+ * line power is connected.
+ */
+void upsdrv_shutdown(void)
+{
+ unsigned char reply[REPLY_PACKETSIZE];
+ unsigned char buf[4];
+ int ret;
+
+ /*
+ * This packet shuts down the UPS, that is, if it is
+ * not currently on line power
+ */
+
+ buf[0]=0x02;
+ buf[1]=0x00;
+ buf[2]=0x00;
+ buf[3]=0x00;
+
+ execute_and_retrieve_query(buf, reply);
+
+ sleep(1); /* have to, the previous command seems to be
+ * ignored if the second command comes right
+ * behind it
+ */
+
+ /*
+ * This should make the UPS turn itself back on once the
+ * power comes back on; which is probably what we want
+ */
+ buf[0]=0x02;
+ buf[1]=0x01;
+ buf[2]=0x00;
+ buf[3]=0x00;
+
+ execute_and_retrieve_query(buf, reply);
+}
+
+void upsdrv_help(void)
+{
+}
+
+void upsdrv_makevartable(void)
+{
+}
+
+void upsdrv_banner(void)
+{
+ printf("Network UPS Tools - Lakeview Research compatible USB UPS
driver %s (%s)\n\n",
+ DRV_VERSION, UPS_VERSION);
+}
Index: drivers/lakeview_usb.h
==================================================================---
drivers/lakeview_usb.h (revision 0)
+++ drivers/lakeview_usb.h (revision 0)
@@ -0,0 +1,37 @@
+/* lakeview_usb.h - driver for UPS with lakeview chipset, such as
+ 'Sweex Manageable UPS 1000VA' (ca. 2006)
+
+ May also work on 'Kebo UPS-650D', not tested as of 05/23/2007
+
+ Copyright (C) 2007 Peter van Valderen <p.v.valderen at probu.nl>
+ Dirk Teurlings <dirk at upexia.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/* driver version */
+#define DRV_VERSION "0.02"
+
+/* driver definitions */
+#define STATUS_REQUESTTYPE 0x21
+#define REPLY_REQUESTTYPE 0x81
+#define REPLY_PACKETSIZE 6
+#define REQUEST_VALUE 0x09
+#define MESSAGE_VALUE 0x200
+#define INDEX_VALUE 0
+
+/* limit the amount of spew that goes in the syslog when we lose the UPS (from
nut_usb.h) */
+#define USB_ERR_LIMIT 10 /* start limiting after 10 in a row */
+#define USB_ERR_RATE 10 /* then only print every 10th error */
Index: drivers/Makefile.am
==================================================================---
drivers/Makefile.am (revision 1154)
+++ drivers/Makefile.am (working copy)
@@ -21,7 +21,7 @@
mge-shut mge-utalk newmge-shut nitram oneac optiups powercom rhino \
safenet skel solis tripplite tripplitesu upscode2 victronups powerpanel
SNMP_DRIVERLIST = snmp-ups
-USB_LIBUSB_DRIVERLIST = usbhid-ups bcmxcp_usb tripplite_usb megatec_usb
+USB_LIBUSB_DRIVERLIST = usbhid-ups bcmxcp_usb lakeview_usb tripplite_usb
megatec_usb
USB_HIDDEV_DRIVERLIST = energizerups
USB_DRIVERLIST = $(USB_LIBUSB_DRIVERLIST) $(USB_HIDDEV_DRIVERLIST)
HAL_DRIVERLIST = hald-addon-usbhid-ups hald-addon-bcmxcp_usb
hald-addon-tripplite_usb hald-addon-megatec_usb
@@ -121,6 +121,9 @@
energizerups_SOURCES = energizerups.c
energizerups_LDADD = $(LDADD_DRIVERS)
+lakeview_usb_SOURCES = lakeview_usb.c
+lakeview_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LDFLAGS)
+
tripplite_usb_SOURCES = tripplite_usb.c libusb.c
tripplite_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LDFLAGS) -lm
@@ -173,8 +176,8 @@
masterguard.h megatec.h metasys.h mge-hid.h mgemib.h mge-shut.h \
mge-utalk.h netvisionmib.h usbhid-ups.h nitram.h nut_usb.h \
oneac.h optiups.h powercom.h pwmib.h safenet.h serial.h \
- snmp-ups.h solis.h tripplite.h tripplite-hid.h tripplitesu.h \
- upscode2.h victronups.h powerpanel.h upshandler.h
+ snmp-ups.h solis.h lakeview_usb.h tripplite.h tripplite-hid.h \
+ tripplitesu.h upscode2.h victronups.h powerpanel.h upshandler.h
# Define a dummy library so that Automake builds rules for the
# corresponding object files. This library is not actually built,
--
Alexander