/* nut_usb.c - common usb functions for Network UPS Tools drivers Copyright (C) 2005 Wolfgang Ocker Based upon serial.c: Copyright (C) 2003 Russell Kroll 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 "common.h" #include "timehead.h" #include #include #include #include #include #include "nut_usb.h" extern int exit_flag; static unsigned int comm_failures = 0; static void nutusb_open_error(const char *port) { printf("Unable to find POWERWARE 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"); } static usb_dev_handle *open_powerware_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 POWERWARE 3105 or 3110 ... other models??? */ if (dev->descriptor.bDeviceClass == USB_CLASS_PER_INTERFACE && (dev->descriptor.idVendor == 0x0592 || dev->descriptor.idVendor == 0x06da) && dev->descriptor.idProduct == 0x0002) return usb_open(dev); } } return 0; } usb_dev_handle *nutusb_open(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_powerware_usb(); if (!dev_h) { upslogx(LOG_WARNING, "Can't open POWERWARE USB device, retrying ..."); if (nanosleep(&t, NULL) < 0 && errno == EINTR) break; } } if (!dev_h) { upslogx(LOG_ERR, "Can't open POWERWARE USB device"); goto errout; } if (usb_set_configuration(dev_h, 1) < 0) { upslogx(LOG_ERR, "Can't set POWERWARE USB configuration"); goto errout; } if (usb_claim_interface(dev_h, 0) < 0) { upslogx(LOG_ERR, "Can't claim POWERWARE USB interface"); goto errout; } else dev_claimed = 1; if (usb_set_altinterface(dev_h, 0) < 0) { upslogx(LOG_ERR, "Can't set POWERWARE USB alternate interface"); goto errout; } if (usb_clear_halt(dev_h, 0x81) < 0) { upslogx(LOG_ERR, "Can't reset POWERWARE 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); nutusb_open_error(port); return 0; } int nutusb_close(usb_dev_handle *dev_h, const char *port) { if (dev_h) { usb_release_interface(dev_h, 0); return usb_close(dev_h); } return 0; } void nutusb_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 nutusb_comm_good(void) { if (comm_failures == 0) return; upslogx(LOG_NOTICE, "Communications with UPS re-established"); comm_failures = 0; } int usb_set_descriptor(usb_dev_handle *udev, unsigned char type, unsigned char index, void *buf, int size) { return usb_control_msg(udev, USB_ENDPOINT_OUT, USB_REQ_SET_DESCRIPTOR, (type << 8) + index, 0, buf, size, 1000); }