UPS: apcupsd clean sources
[tomato.git] / release / src / router / apcupsd / src / drivers / usb / generic / generic-usb.c
blobfd3135cd33f965aa0ecb6286b0e1de42088278fb
1 /*
2 * generic-usb.c
4 * Generic USB module for libusb.
5 */
7 /*
8 * Copyright (C) 2005 Adam Kropelin
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of version 2 of the GNU General
12 * Public License as published by the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
19 * You should have received a copy of the GNU General Public
20 * License along with this program; if not, write to the Free
21 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
22 * MA 02111-1307, USA.
25 #include "apc.h"
26 #include "../usb_common.h"
27 #include "hidutils.h"
28 #include "libusb.h"
29 #include "astring.h"
32 * When we are traversing the USB reports given by the UPS and we find
33 * an entry corresponding to an entry in the known_info table above,
34 * we make the following USB_INFO entry in the info table of our
35 * private data.
37 typedef struct s_usb_info {
38 unsigned usage_code; /* usage code wanted */
39 unsigned unit_exponent; /* exponent */
40 unsigned unit; /* units */
41 int data_type; /* data type */
42 hid_item_t item; /* HID item (for read) */
43 hid_item_t witem; /* HID item (for write) */
44 int report_len; /* Length of containing report */
45 int ci; /* which CI does this usage represent? */
46 int value; /* Previous value of this item */
47 } USB_INFO;
50 * This "private" structure is returned to us in the driver private
51 * field, and allows us to get to all the info we keep on each UPS.
52 * The info field is malloced for each command we want and the UPS
53 * has.
55 typedef struct s_usb_data {
56 usb_dev_handle *fd; /* Our UPS control pipe fd when open */
57 report_desc_t rdesc; /* Device's report descrptor */
58 USB_INFO *info[CI_MAXCI + 1]; /* Info pointers for each command */
59 } USB_DATA;
61 int pusb_ups_get_capabilities(UPSINFO *ups, const struct s_known_info *known_info)
63 int i, input, feature, ci, phys, logi;
64 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
65 hid_item_t input_item, feature_item, item;
66 USB_INFO *info;
68 write_lock(ups);
70 for (i = 0; known_info[i].usage_code; i++) {
71 ci = known_info[i].ci;
72 phys = known_info[i].physical;
73 logi = known_info[i].logical;
75 if (ci != CI_NONE && !my_data->info[ci]) {
77 /* Try to find an INPUT report containing this usage */
78 input = hidu_locate_item(
79 my_data->rdesc,
80 known_info[i].usage_code, /* Match usage code */
81 -1, /* Don't care about application */
82 (phys == P_ANY) ? -1 : phys, /* Match physical usage */
83 (logi == P_ANY) ? -1 : logi, /* Match logical usage */
84 HID_KIND_INPUT, /* Match feature type */
85 &input_item);
87 /* Try to find a FEATURE report containing this usage */
88 feature = hidu_locate_item(
89 my_data->rdesc,
90 known_info[i].usage_code, /* Match usage code */
91 -1, /* Don't care about application */
92 (phys == P_ANY) ? -1 : phys, /* Match physical usage */
93 (logi == P_ANY) ? -1 : logi, /* Match logical usage */
94 HID_KIND_FEATURE, /* Match feature type */
95 &feature_item);
98 * Choose which report to use. We prefer FEATURE since some UPSes
99 * have broken INPUT reports, but we will fall back on INPUT if
100 * FEATURE is not available.
102 if (feature)
103 item = feature_item;
104 else if (input)
105 item = input_item;
106 else
107 continue; // No valid report, bail
109 ups->UPS_Cap[ci] = true;
110 ups->UPS_Cmd[ci] = known_info[i].usage_code;
112 info = (USB_INFO *)malloc(sizeof(USB_INFO));
113 if (!info) {
114 write_unlock(ups);
115 Error_abort0("Out of memory.\n");
118 // Populate READ report data
119 my_data->info[ci] = info;
120 memset(info, 0, sizeof(*info));
121 info->ci = ci;
122 info->usage_code = item.usage;
123 info->unit_exponent = item.unit_exponent;
124 info->unit = item.unit;
125 info->data_type = known_info[i].data_type;
126 info->item = item;
127 info->report_len = hid_report_size( /* +1 for report id */
128 my_data->rdesc, item.kind, item.report_ID) + 1;
129 Dmsg6(200, "Got READ ci=%d, rpt=%d (len=%d), usage=0x%x (len=%d), kind=0x%02x\n",
130 ci, item.report_ID, info->report_len,
131 known_info[i].usage_code, item.report_size, item.kind);
133 // If we have a FEATURE report, use that as the writable report
134 if (feature) {
135 info->witem = item;
136 Dmsg6(200, "Got WRITE ci=%d, rpt=%d (len=%d), usage=0x%x (len=%d), kind=0x%02x\n",
137 ci, item.report_ID, info->report_len,
138 known_info[i].usage_code, item.report_size, item.kind);
143 ups->UPS_Cap[CI_STATUS] = true; /* we always have status flag */
144 write_unlock(ups);
145 return 1;
148 static bool populate_uval(UPSINFO *ups, USB_INFO *info, unsigned char *data, USB_VALUE *uval)
150 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
151 const char *str;
152 int exponent;
153 USB_VALUE val;
155 /* data+1 skips the report tag byte */
156 info->value = hid_get_data(data+1, &info->item);
158 exponent = info->unit_exponent;
159 if (exponent > 7)
160 exponent = exponent - 16;
162 if (info->data_type == T_INDEX) { /* get string */
163 if (info->value == 0)
164 return false;
166 str = hidu_get_string(my_data->fd, info->value);
167 if (!str)
168 return false;
170 astrncpy(val.sValue, str, sizeof(val.sValue));
171 val.value_type = V_STRING;
173 Dmsg4(200, "Def val=%d exp=%d sVal=\"%s\" ci=%d\n", info->value,
174 exponent, val.sValue, info->ci);
175 } else if (info->data_type == T_UNITS) {
176 val.value_type = V_DOUBLE;
178 switch (info->unit) {
179 case 0x00F0D121:
180 val.UnitName = "Volts";
181 exponent -= 7; /* remove bias */
182 break;
183 case 0x00100001:
184 exponent += 2; /* remove bias */
185 val.UnitName = "Amps";
186 break;
187 case 0xF001:
188 val.UnitName = "Hertz";
189 break;
190 case 0x1001:
191 val.UnitName = "Seconds";
192 break;
193 case 0xD121:
194 exponent -= 7; /* remove bias */
195 val.UnitName = "Watts";
196 break;
197 case 0x010001:
198 val.UnitName = "Degrees K";
199 break;
200 case 0x0101001:
201 val.UnitName = "AmpSecs";
202 if (exponent == 0)
203 val.dValue = info->value;
204 else
205 val.dValue = ((double)info->value) * pow_ten(exponent);
206 break;
207 default:
208 val.UnitName = "";
209 val.value_type = V_INTEGER;
210 val.iValue = info->value;
211 break;
214 if (exponent == 0)
215 val.dValue = info->value;
216 else
217 val.dValue = ((double)info->value) * pow_ten(exponent);
219 // Store a (possibly truncated) copy of the floating point value in the
220 // integer field as well.
221 val.iValue = (int)val.dValue;
223 Dmsg4(200, "Def val=%d exp=%d dVal=%f ci=%d\n", info->value,
224 exponent, val.dValue, info->ci);
225 } else { /* should be T_NONE */
227 val.UnitName = "";
228 val.value_type = V_INTEGER;
229 val.iValue = info->value;
231 if (exponent == 0)
232 val.dValue = info->value;
233 else
234 val.dValue = ((double)info->value) * pow_ten(exponent);
236 Dmsg4(200, "Def val=%d exp=%d dVal=%f ci=%d\n", info->value,
237 exponent, val.dValue, info->ci);
240 memcpy(uval, &val, sizeof(*uval));
241 return true;
245 * Get a field value
247 int pusb_get_value(UPSINFO *ups, int ci, USB_VALUE *uval)
249 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
250 USB_INFO *info = my_data->info[ci];
251 unsigned char data[20];
252 int len;
255 * Note we need to check info since CI_STATUS is always true
256 * even when the UPS doesn't directly support that CI.
258 if (!UPS_HAS_CAP(ci) || !info)
259 return false; /* UPS does not have capability */
262 * Clear the destination buffer. In the case of a short transfer (see
263 * below) this will increase the likelihood of extracting the correct
264 * value in spite of the missing data.
266 memset(data, 0, sizeof(data));
268 /* Fetch the proper report */
269 len = hidu_get_report(my_data->fd, &info->item, data, info->report_len);
270 if (len == -1)
271 return false;
274 * Some UPSes seem to have broken firmware that sends a different number
275 * of bytes (usually fewer) than the report descriptor specifies. On
276 * UHCI controllers under *BSD, this can lead to random lockups. To
277 * reduce the likelihood of a lockup, we adjust our expected length to
278 * match the actual as soon as a mismatch is detected, so future
279 * transfers will have the proper lengths from the outset. NOTE that
280 * the data returned may not be parsed properly (since the parsing is
281 * necessarily based on the report descriptor) but given that HID
282 * reports are in little endian byte order and we cleared the buffer
283 * above, chances are good that we will actually extract the right
284 * value in spite of the UPS's brokenness.
286 if (info->report_len != len) {
287 Dmsg4(100, "Report length mismatch, fixing "
288 "(id=%d, ci=%d, expected=%d, actual=%d)\n",
289 info->item.report_ID, ci, info->report_len, len);
290 info->report_len = len;
293 /* Populate a uval struct using the raw report data */
294 return populate_uval(ups, info, data, uval);
297 static void reinitialize_private_structure(UPSINFO *ups)
299 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
300 int k;
302 Dmsg0(200, "Reinitializing private structure.\n");
304 * We are being reinitialized, so clear the Cap
305 * array, and release previously allocated memory.
307 for (k = 0; k <= CI_MAXCI; k++) {
308 ups->UPS_Cap[k] = false;
309 if (my_data->info[k] != NULL) {
310 free(my_data->info[k]);
311 my_data->info[k] = NULL;
316 int init_device(UPSINFO *ups, struct usb_device *dev)
318 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
319 usb_dev_handle *fd;
320 int rc;
321 unsigned char* rdesc;
322 int rdesclen;
324 /* Open the device with libusb */
325 fd = usb_open(dev);
326 if (!fd) {
327 Dmsg0(100, "Unable to open device.\n");
328 return 0;
331 #ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
333 * Attempt to detach the kernel driver so we can drive
334 * this device from userspace. Don't worry if this fails;
335 * that just means the driver was already detached.
337 rc = usb_detach_kernel_driver_np(fd, 0);
338 #endif
340 /* Check device serial number, if user specified one */
341 if (ups->device[0] != '\0')
343 /* Fetch serial number from device */
344 const char *tmpser;
345 if (dev->descriptor.iSerialNumber == 0 ||
346 (tmpser = hidu_get_string(fd, dev->descriptor.iSerialNumber)) == NULL)
348 usb_close(fd);
349 Dmsg0(100, "Device does not report serial number.\n");
350 return 0;
353 /* Remove leading/trailing whitespace */
354 astring serial(tmpser);
355 serial.trim();
357 /* Check against user specification, ignoring case */
358 Dmsg2(100, "device='%s', user='%s'\n", serial.str(), ups->device);
359 if (strcasecmp(serial, ups->device))
361 usb_close(fd);
362 return 0;
366 /* Choose config #1 */
367 rc = usb_set_configuration(fd, 1);
368 if (rc) {
369 usb_close(fd);
370 Dmsg2(100, "Unable to set configuration (%d) %s.\n", rc, usb_strerror());
371 return 0;
374 /* Claim the interface */
375 rc = usb_claim_interface(fd, 0);
376 if (rc) {
377 usb_close(fd);
378 Dmsg2(100, "Unable to claim interface (%d) %s.\n", rc, usb_strerror());
379 return 0;
382 /* Fetch the report descritor */
383 rdesc = hidu_fetch_report_descriptor(fd, &rdesclen);
384 if (!rdesc) {
385 usb_close(fd);
386 Dmsg0(100, "Unable to fetch report descriptor.\n");
387 return 0;
390 /* Initialize hid parser with this descriptor */
391 my_data->rdesc = hid_use_report_desc(rdesc, rdesclen);
392 free(rdesc);
393 if (!my_data->rdesc) {
394 usb_close(fd);
395 Dmsg0(100, "Unable to init parser with report descriptor.\n");
396 return 0;
399 /* Does this device have an UPS application collection? */
400 if (!hidu_locate_item(
401 my_data->rdesc,
402 UPS_USAGE, /* Match usage code */
403 -1, /* Don't care about application */
404 -1, /* Don't care about physical usage */
405 -1, /* Don't care about logical */
406 HID_KIND_COLLECTION, /* Match collection type */
407 NULL)) {
408 hid_dispose_report_desc(my_data->rdesc);
409 usb_close(fd);
410 Dmsg0(100, "Device does not have an UPS application collection.\n");
411 return 0;
414 my_data->fd = fd;
415 return 1;
418 int open_usb_device(UPSINFO *ups)
420 int i;
421 struct usb_bus* bus;
422 struct usb_device* dev;
424 /* Initialize libusb */
425 Dmsg0(200, "Initializing libusb\n");
426 usb_init();
428 /* Enumerate usb busses and devices */
429 i = usb_find_busses();
430 Dmsg1(200, "Found %d USB busses\n", i);
431 i = usb_find_devices();
432 Dmsg1(200, "Found %d USB devices\n", i);
434 /* Iterate over all devices, checking for idVendor=APC */
435 bus = usb_get_busses();
436 while (bus)
438 dev = bus->devices;
439 while (dev)
441 Dmsg4(200, "%s:%s - %04x:%04x\n",
442 bus->dirname, dev->filename,
443 dev->descriptor.idVendor, dev->descriptor.idProduct);
445 if (dev->descriptor.idVendor == VENDOR_APC) {
446 Dmsg2(200, "Trying device %s:%s\n", bus->dirname, dev->filename);
447 if (init_device(ups, dev)) {
448 /* Successfully found and initialized an UPS */
449 return 1;
453 dev = dev->next;
456 bus = bus->next;
459 /* Failed to find an UPS */
460 return 0;
464 * Called if there is an ioctl() or read() error, we close() and
465 * re open() the port since the device was probably unplugged.
467 static int usb_link_check(UPSINFO *ups)
469 bool comm_err = true;
470 int tlog;
471 bool once = true;
472 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
473 static bool linkcheck = false;
475 if (linkcheck)
476 return 0;
478 linkcheck = true; /* prevent recursion */
480 ups->set_commlost();
481 Dmsg0(200, "link_check comm lost\n");
483 /* Don't warn until we try to get it at least 2 times and fail */
484 for (tlog = LINK_RETRY_INTERVAL * 2; comm_err; tlog -= (LINK_RETRY_INTERVAL)) {
486 if (tlog <= 0) {
487 tlog = 10 * 60; /* notify every 10 minutes */
488 log_event(ups, event_msg[CMDCOMMFAILURE].level,
489 event_msg[CMDCOMMFAILURE].msg);
490 if (once) { /* execute script once */
491 execute_command(ups, ups_event[CMDCOMMFAILURE]);
492 once = false;
496 /* Retry every LINK_RETRY_INTERVAL seconds */
497 sleep(LINK_RETRY_INTERVAL);
499 if (my_data->fd) {
500 usb_reset(my_data->fd);
501 usb_close(my_data->fd);
502 my_data->fd = NULL;
503 hid_dispose_report_desc(my_data->rdesc);
504 reinitialize_private_structure(ups);
507 if (open_usb_device(ups) && usb_ups_get_capabilities(ups) &&
508 usb_ups_read_static_data(ups)) {
509 comm_err = false;
510 } else {
511 continue;
515 if (!comm_err) {
516 generate_event(ups, CMDCOMMOK);
517 ups->clear_commlost();
518 Dmsg0(200, "link check comm OK.\n");
521 linkcheck = false;
522 return 1;
526 * libusb-win32, pthreads, and compat.h all have different ideas
527 * of what ETIMEDOUT is on mingw. We need to make sure we match
528 * libusb-win32's error.h in that case, so override ETIMEDOUT.
530 #ifdef HAVE_MINGW
531 # define LIBUSB_ETIMEDOUT 116
532 #else
533 # define LIBUSB_ETIMEDOUT ETIMEDOUT
534 #endif
536 int pusb_ups_check_state(UPSINFO *ups)
538 int i, ci;
539 int retval, value;
540 unsigned char buf[20];
541 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
542 struct timeval now, exit;
543 int timeout;
544 USB_VALUE uval;
545 bool done = false;
547 /* Figure out when we need to exit by */
548 gettimeofday(&exit, NULL);
549 exit.tv_sec += ups->wait_time;
551 while (!done) {
553 /* Figure out how long until we have to exit */
554 gettimeofday(&now, NULL);
555 timeout = TV_DIFF_MS(now, exit);
556 if (timeout <= 0) {
557 /* Done already? How time flies... */
558 return 0;
561 Dmsg1(200, "Timeout=%d\n", timeout);
562 retval = usb_interrupt_read(my_data->fd, USB_ENDPOINT_IN|1, (char*)buf, sizeof(buf), timeout);
564 if (retval == 0 || retval == -LIBUSB_ETIMEDOUT) {
565 /* No events available in ups->wait_time seconds. */
566 return 0;
567 } else if (retval == -EINTR || retval == -EAGAIN) {
568 /* assume SIGCHLD */
569 continue;
570 } else if (retval < 0) {
571 /* Hard error */
572 Dmsg2(200, "usb_interrupt_read error: (%d) %s\n", retval, strerror(-retval));
573 usb_link_check(ups); /* link is down, wait */
574 return 0;
577 if (debug_level >= 300) {
578 logf("Interrupt data: ");
579 for (i = 0; i < retval; i++)
580 logf("%02x, ", buf[i]);
581 logf("\n");
584 write_lock(ups);
587 * Iterate over all CIs, firing off events for any that are
588 * affected by this report.
590 for (ci=0; ci<CI_MAXCI; ci++) {
591 if (ups->UPS_Cap[ci] && my_data->info[ci] &&
592 my_data->info[ci]->item.report_ID == buf[0]) {
595 * Check if we received fewer bytes of data from the UPS than we
596 * should have. If so, ignore the report since we can't process it
597 * reliably. If we go ahead and try to process it we may get
598 * sporradic bad readings. UPSes we've seen this issue on so far
599 * include:
601 * "Back-UPS CS 650 FW:817.v7 .I USB FW:v7"
602 * "Back-UPS CS 500 FW:808.q8.I USB FW:q8"
604 if (my_data->info[ci]->report_len != retval) {
605 Dmsg4(100, "Report length mismatch, ignoring "
606 "(id=%d, ci=%d, expected=%d, actual=%d)\n",
607 my_data->info[ci]->item.report_ID, ci,
608 my_data->info[ci]->report_len, retval);
609 break; /* don't continue since other CIs will be just as wrong */
612 /* Ignore this event if the value has not changed */
613 value = hid_get_data(buf+1, &my_data->info[ci]->item);
614 if (my_data->info[ci]->value == value) {
615 Dmsg3(200, "Ignoring unchanged value (ci=%d, rpt=%d, val=%d)\n",
616 ci, buf[0], value);
617 continue;
620 Dmsg3(200, "Processing changed value (ci=%d, rpt=%d, val=%d)\n",
621 ci, buf[0], value);
623 /* Populate a uval and report it to the upper layer */
624 populate_uval(ups, my_data->info[ci], buf, &uval);
625 if (usb_report_event(ups, ci, &uval)) {
627 * The upper layer considers this an important event,
628 * so we will return after processing any remaining
629 * CIs for this report.
631 done = true;
636 write_unlock(ups);
639 return true;
643 * Open usb port
645 * This is called once by the core code and is the first
646 * routine called.
648 int pusb_ups_open(UPSINFO *ups)
650 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
652 /* Set libusb debug level */
653 usb_set_debug(debug_level/100);
655 write_lock(ups);
656 if (my_data == NULL) {
657 my_data = (USB_DATA *)malloc(sizeof(USB_DATA));
658 if (my_data == NULL) {
659 log_event(ups, LOG_ERR, "Out of memory.");
660 write_unlock(ups);
661 exit(1);
664 memset(my_data, 0, sizeof(USB_DATA));
665 ups->driver_internal_data = my_data;
666 } else {
667 reinitialize_private_structure(ups);
670 if (!open_usb_device(ups)) {
671 write_unlock(ups);
672 Error_abort0("Cannot find UPS device --\n"
673 "For a link to detailed USB trouble shooting information,\n"
674 "please see <http://www.apcupsd.com/support.html>.\n");
678 * Note, we set ups->fd here so the "core" of apcupsd doesn't
679 * think we are a slave, which is what happens when it is -1.
680 * (ADK: Actually this only appears to be true for apctest as
681 * apcupsd proper uses the UPS_slave flag.)
682 * Internally, we use the fd in our own private space
684 ups->fd = 1;
686 ups->clear_slave();
687 write_unlock(ups);
688 return 1;
691 int pusb_ups_close(UPSINFO *ups)
693 /* Should we be politely closing fds here or anything? */
694 write_lock(ups);
696 if (ups->driver_internal_data) {
697 free(ups->driver_internal_data);
698 ups->driver_internal_data = NULL;
701 write_unlock(ups);
702 return 1;
705 int pusb_ups_setup(UPSINFO *ups)
707 /* Nothing to do */
708 return 1;
711 int pusb_read_int_from_ups(UPSINFO *ups, int ci, int *value)
713 USB_VALUE val;
715 if (!pusb_get_value(ups, ci, &val))
716 return false;
718 *value = val.iValue;
719 return true;
722 int pusb_write_int_to_ups(UPSINFO *ups, int ci, int value, const char *name)
724 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
725 USB_INFO *info;
726 int old_value, new_value;
727 unsigned char rpt[20];
729 if (ups->UPS_Cap[ci] && my_data->info[ci] && my_data->info[ci]->witem.report_ID) {
730 info = my_data->info[ci]; /* point to our info structure */
732 if (hidu_get_report(my_data->fd, &info->item, rpt, info->report_len) < 1) {
733 Dmsg1(000, "get_report for kill power function %s failed.\n", name);
734 return false;
737 old_value = hid_get_data(rpt + 1, &info->item);
739 hid_set_data(rpt + 1, &info->witem, value);
741 if (!hidu_set_report(my_data->fd, &info->witem, rpt, info->report_len)) {
742 Dmsg1(000, "set_report for kill power function %s failed.\n", name);
743 return false;
746 if (hidu_get_report(my_data->fd, &info->item, rpt, info->report_len) < 1) {
747 Dmsg1(000, "get_report for kill power function %s failed.\n", name);
748 return false;
751 new_value = hid_get_data(rpt + 1, &info->item);
753 Dmsg3(100, "function %s ci=%d value=%d OK.\n", name, ci, value);
754 Dmsg4(100, "%s before=%d set=%d after=%d\n", name, old_value, value, new_value);
755 return true;
758 Dmsg2(000, "function %s ci=%d not available in this UPS.\n", name, ci);
759 return false;