[Nut-upsuser] Mustek Powermust 600VA

Alexander I. Gordeev lasaine at lvk.cs.msu.su
Tue Nov 20 23:27:31 UTC 2007


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



More information about the Nut-upsuser mailing list