On Wed, 21 Nov 2007 00:43:57 +0300, Krzysztof Sasiak <[EMAIL PROTECTED]> 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 <[EMAIL PROTECTED]> + Dirk Teurlings <[EMAIL PROTECTED]> + + 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 <[EMAIL PROTECTED]> + Dirk Teurlings <[EMAIL PROTECTED]> + + 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 _______________________________________________ Nut-upsuser mailing list [email protected] http://lists.alioth.debian.org/mailman/listinfo/nut-upsuser

