UPS: apcupsd clean sources
[tomato.git] / release / src / router / apcupsd / src / drivers / usb / bsd / bsd-usb.c
blobdb121e9a964cd495a7f05bbffe1b43c1686dd4dc
1 /*
2 * hidutils.c
4 * Platform-specific USB module for *BSD ugen USB driver.
6 * Based on linux-usb.c by Kerb Sibbald.
7 */
9 /*
10 * Copyright (C) 2004-2005 Adam Kropelin
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of version 2 of the GNU General
14 * Public License as published by the Free Software Foundation.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
21 * You should have received a copy of the GNU General Public
22 * License along with this program; if not, write to the Free
23 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
24 * MA 02111-1307, USA.
27 #include "apc.h"
28 #include "hidutils.h"
29 #include "../usb_common.h"
30 #include <dev/usb/usb.h>
31 #include <dev/usb/usbhid.h>
33 /* Compatibility cruft for FreeBSD <= 4.7 */
34 #ifndef USB_MAX_DEVNAMES
35 #define USB_MAX_DEVNAMES MAXDEVNAMES
36 #endif
37 #ifndef USB_MAX_DEVNAMELEN
38 #define USB_MAX_DEVNAMELEN MAXDEVNAMELEN
39 #endif
42 * When we are traversing the USB reports given by the UPS and we find
43 * an entry corresponding to an entry in the known_info table above,
44 * we make the following USB_INFO entry in the info table of our
45 * private data.
47 typedef struct s_usb_info {
48 unsigned usage_code; /* usage code wanted */
49 unsigned unit_exponent; /* exponent */
50 unsigned unit; /* units */
51 int data_type; /* data type */
52 hid_item_t item; /* HID item (read) */
53 hid_item_t witem; /* HID item (write) */
54 int report_len; /* Length of containing report */
55 int ci; /* which CI does this usage represent? */
56 int value; /* Previous value of this item */
57 } USB_INFO;
60 * This "private" structure is returned to us in the driver private
61 * field, and allows us to get to all the info we keep on each UPS.
62 * The info field is malloced for each command we want and the UPS
63 * has.
65 typedef struct s_usb_data {
66 int fd; /* Our UPS control pipe fd when open */
67 int intfd; /* Interrupt pipe fd */
68 char orig_device[MAXSTRING]; /* Original port specification */
69 short vendor; /* UPS vendor id */
70 report_desc_t rdesc; /* Device's report descrptor */
71 USB_INFO *info[CI_MAXCI + 1]; /* Info pointers for each command */
72 } USB_DATA;
75 static void reinitialize_private_structure(UPSINFO *ups)
77 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
78 int k;
80 Dmsg0(200, "Reinitializing private structure.\n");
82 * We are being reinitialized, so clear the Cap
83 * array, and release previously allocated memory.
85 for (k = 0; k <= CI_MAXCI; k++) {
86 ups->UPS_Cap[k] = false;
87 if (my_data->info[k] != NULL) {
88 free(my_data->info[k]);
89 my_data->info[k] = NULL;
95 * Initializes the USB device by fetching its report descriptor
96 * and making sure we can drive the device.
98 static int init_device(UPSINFO *ups, const char *devname)
100 int fd, rc, rdesclen;
101 struct usb_device_info devinfo;
102 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
103 unsigned char *rdesc;
104 char intdevname[USB_MAX_DEVNAMELEN + 5 + 3 + 1];
106 fd = open(devname, O_RDWR | O_NOCTTY);
107 if (fd == -1)
108 return 0;
110 memset(&devinfo, 0, sizeof(devinfo));
111 rc = ioctl(fd, USB_GET_DEVICEINFO, &devinfo);
112 if (rc) {
113 close(fd);
114 Dmsg0(100, "Unable to get device info.\n");
115 return 0;
118 /* Only interested if APC or MGE is the vendor */
119 if (devinfo.udi_vendorNo != VENDOR_APC &&
120 devinfo.udi_vendorNo != VENDOR_MGE) {
121 close(fd);
122 Dmsg1(100, "Unsupported vendor (%04x).\n", devinfo.udi_vendorNo);
123 return 0;
125 my_data->vendor = devinfo.udi_vendorNo;
127 /* Fetch the report descritor */
128 rdesc = hidu_fetch_report_descriptor(fd, &rdesclen);
129 if (!rdesc) {
130 close(fd);
131 Dmsg0(100, "Unable to fetch report descriptor.\n");
132 return 0;
135 /* Initialize hid parser with this descriptor */
136 my_data->rdesc = hid_use_report_desc(rdesc, rdesclen);
137 if (!my_data->rdesc) {
138 free(rdesc);
139 close(fd);
140 Dmsg0(100, "Unable to init parser with report descriptor.\n");
141 return 0;
143 free(rdesc);
145 /* Does this device have an UPS application collection? */
146 if (!hidu_locate_item(
147 my_data->rdesc,
148 UPS_USAGE, /* Match usage code */
149 -1, /* Don't care about application */
150 -1, /* Don't care about physical usage */
151 -1, /* Don't care about logical */
152 HID_KIND_COLLECTION, /* Match collection type */
153 NULL)) {
154 hid_dispose_report_desc(my_data->rdesc);
155 close(fd);
156 Dmsg0(100, "Device does not have an UPS application collection.\n");
157 return 0;
160 my_data->fd = fd;
162 /* Open the interrupt pipe */
163 astrncpy(intdevname, devname, sizeof(intdevname));
165 #ifdef HAVE_FREEBSD_OS
166 /* ugen0 -> ugen0.1 */
167 astrncat(intdevname, ".1", sizeof(intdevname));
168 #else
169 /* ugen0.00 -> ugen0.01 */
170 intdevname[strlen(intdevname) - 1] = '1';
171 #endif
173 fd = open(intdevname, O_RDONLY | O_NOCTTY);
174 if (fd == -1) {
175 Dmsg2(100, "Unable to open interrupt pipe %s: %s\n", intdevname,
176 strerror(errno));
177 hid_dispose_report_desc(my_data->rdesc);
178 close(my_data->fd);
179 my_data->fd = -1;
180 return 0;
182 my_data->intfd = fd;
184 return 1;
188 * Internal routine to open the device and ensure that there is a UPS
189 * application on the line. This routine may be called many times
190 * because the device may be unplugged and plugged back in -- the
191 * joys of USB devices.
193 static int open_usb_device(UPSINFO *ups)
195 int i, j, k, fd, rc;
196 char busname[] = "/dev/usbN";
197 char devname[USB_MAX_DEVNAMELEN + 5 + 1];
198 struct usb_device_info devinfo;
199 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
202 * Note, we set ups->fd here so the "core" of apcupsd doesn't
203 * think we are a slave, which is what happens when it is -1.
204 * (ADK: Actually this only appears to be true for apctest as
205 * apcupsd proper uses the UPS_slave flag.)
206 * Internally, we use the fd in our own private space
208 ups->fd = 1;
211 * If no device location specified, we go autodetect it
212 * by searching known places.
214 if (ups->device[0] == 0)
215 goto auto_detect;
217 if (my_data->orig_device[0] == 0)
218 astrncpy(my_data->orig_device, ups->device, sizeof(my_data->orig_device));
221 * No range support yet... Name the device specifically or we will
222 * search them all.
225 for (i = 0; i < 10; i++) {
226 if (init_device(ups, ups->device))
227 return 1;
229 sleep(1);
233 * If the above device specified by the user fails,
234 * fall through here and look in predefined places
235 * for the device.
238 auto_detect:
241 * We could just start trying to open the /dev/ugenN devices,
242 * one after another, but BSD gives us a decent way to enumerate
243 * them. We might as well be polite and use it.
246 /* Max of 10 USB busses */
247 for (i = 0; i < 10; i++) {
248 busname[8] = '0' + i;
249 fd = open(busname, O_RDWR | O_NOCTTY);
250 if (fd == -1)
251 continue;
253 Dmsg1(200, "Found bus %s.\n", busname);
255 /* Max 127 devices per bus */
256 for (j = 0; j < 127; j++) {
257 memset(&devinfo, 0, sizeof(devinfo));
258 devinfo.udi_addr = j;
259 rc = ioctl(fd, USB_DEVICEINFO, &devinfo);
260 if (rc)
261 continue;
263 /* See if this device is bound to ugen driver */
264 for (k = 0; k < USB_MAX_DEVNAMES; k++)
265 if (strncmp(devinfo.udi_devnames[k], "ugen", 4) == 0)
266 break;
268 if (k < USB_MAX_DEVNAMES) {
269 astrncpy(devname, "/dev/", sizeof(devname));
270 astrncat(devname, devinfo.udi_devnames[k], sizeof(devname));
271 #if defined(HAVE_OPENBSD_OS) || defined(HAVE_NETBSD_OS)
272 astrncat(devname, ".00", sizeof(devname));
273 #endif
274 Dmsg1(200, "Trying device %s.\n", devname);
275 if (init_device(ups, devname)) {
276 astrncpy(ups->device, devname, sizeof(ups->device));
277 return 1;
282 close(fd);
285 ups->device[0] = 0;
286 return 0;
290 * Called if there is an ioctl() or read() error, we close() and
291 * re open() the port since the device was probably unplugged.
293 static int usb_link_check(UPSINFO *ups)
295 bool comm_err = true;
296 int tlog;
297 bool once = true;
298 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
299 static bool linkcheck = false;
301 if (linkcheck)
302 return 0;
304 linkcheck = true; /* prevent recursion */
306 ups->set_commlost();
307 Dmsg0(200, "link_check comm lost\n");
309 /* Don't warn until we try to get it at least 2 times and fail */
310 for (tlog = LINK_RETRY_INTERVAL * 2; comm_err; tlog -= (LINK_RETRY_INTERVAL)) {
312 if (tlog <= 0) {
313 tlog = 10 * 60; /* notify every 10 minutes */
314 log_event(ups, event_msg[CMDCOMMFAILURE].level,
315 event_msg[CMDCOMMFAILURE].msg);
316 if (once) { /* execute script once */
317 execute_command(ups, ups_event[CMDCOMMFAILURE]);
318 once = false;
322 /* Retry every LINK_RETRY_INTERVAL seconds */
323 sleep(LINK_RETRY_INTERVAL);
325 if (my_data->fd >= 0) {
326 close(my_data->fd);
327 my_data->fd = -1;
328 close(my_data->intfd);
329 my_data->intfd = -1;
330 hid_dispose_report_desc(my_data->rdesc);
331 reinitialize_private_structure(ups);
334 if (open_usb_device(ups) && usb_ups_get_capabilities(ups) &&
335 usb_ups_read_static_data(ups)) {
336 comm_err = false;
337 } else {
338 continue;
342 if (!comm_err) {
343 generate_event(ups, CMDCOMMOK);
344 ups->clear_commlost();
345 Dmsg0(200, "link check comm OK.\n");
348 linkcheck = false;
349 return 1;
352 int pusb_ups_get_capabilities(UPSINFO *ups, const struct s_known_info *known_info)
354 int i, input, feature, ci, phys, logi;
355 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
356 hid_item_t input_item, feature_item, item;
357 USB_INFO *info;
359 write_lock(ups);
361 for (i = 0; known_info[i].usage_code; i++) {
362 ci = known_info[i].ci;
363 phys = known_info[i].physical;
364 logi = known_info[i].logical;
366 if (ci != CI_NONE && !my_data->info[ci]) {
367 /* Try to find an INPUT report containing this usage */
368 input = hidu_locate_item(
369 my_data->rdesc,
370 known_info[i].usage_code, /* Match usage code */
371 -1, /* Don't care about application */
372 (phys == P_ANY) ? -1 : phys, /* Match physical usage */
373 (logi == P_ANY) ? -1 : logi, /* Match logical usage */
374 HID_KIND_INPUT, /* Match feature type */
375 &input_item);
377 /* Try to find a FEATURE report containing this usage */
378 feature = hidu_locate_item(
379 my_data->rdesc,
380 known_info[i].usage_code, /* Match usage code */
381 -1, /* Don't care about application */
382 (phys == P_ANY) ? -1 : phys, /* Match physical usage */
383 (logi == P_ANY) ? -1 : logi, /* Match logical usage */
384 HID_KIND_FEATURE, /* Match feature type */
385 &feature_item);
388 * Choose which report to use. We prefer FEATURE since some UPSes
389 * have broken INPUT reports, but we will fall back on INPUT if
390 * FEATURE is not available.
392 if (feature)
393 item = feature_item;
394 else if (input)
395 item = input_item;
396 else
397 continue; // No valid report, bail
399 ups->UPS_Cap[ci] = true;
400 ups->UPS_Cmd[ci] = known_info[i].usage_code;
402 info = (USB_INFO *)malloc(sizeof(USB_INFO));
403 if (!info) {
404 write_unlock(ups);
405 Error_abort0("Out of memory.\n");
408 // Populate READ report data
409 my_data->info[ci] = info;
410 memset(info, 0, sizeof(*info));
411 info->ci = ci;
412 info->usage_code = item.usage;
413 info->unit_exponent = item.unit_exponent;
414 info->unit = item.unit;
415 info->data_type = known_info[i].data_type;
416 info->item = item;
417 info->report_len = hid_report_size( /* +1 for report id */
418 my_data->rdesc, item.kind, item.report_ID) + 1;
419 Dmsg6(200, "Got READ ci=%d, rpt=%d (len=%d), usage=0x%x (len=%d), kind=0x%02x\n",
420 ci, item.report_ID, info->report_len,
421 known_info[i].usage_code, item.report_size, item.kind);
423 // If we have a FEATURE report, use that as the writable report
424 if (feature) {
425 info->witem = item;
426 Dmsg6(200, "Got WRITE ci=%d, rpt=%d (len=%d), usage=0x%x (len=%d), kind=0x%02x\n",
427 ci, item.report_ID, info->report_len,
428 known_info[i].usage_code, item.report_size, item.kind);
433 ups->UPS_Cap[CI_STATUS] = true; /* we always have status flag */
434 write_unlock(ups);
435 return 1;
438 static bool populate_uval(UPSINFO *ups, USB_INFO *info, unsigned char *data, USB_VALUE *uval)
440 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
441 const char *str;
442 int exponent;
443 USB_VALUE val;
445 /* data+1 skips the report tag byte */
446 info->value = hid_get_data(data+1, &info->item);
448 exponent = info->unit_exponent;
449 if (exponent > 7)
450 exponent = exponent - 16;
452 if (info->data_type == T_INDEX) { /* get string */
453 if (info->value == 0)
454 return false;
456 str = hidu_get_string(my_data->fd, info->value);
457 if (!str)
458 return false;
460 astrncpy(val.sValue, str, sizeof(val.sValue));
461 val.value_type = V_STRING;
463 Dmsg4(200, "Def val=%d exp=%d sVal=\"%s\" ci=%d\n", info->value,
464 exponent, val.sValue, info->ci);
465 } else if (info->data_type == T_UNITS) {
466 val.value_type = V_DOUBLE;
468 switch (info->unit) {
469 case 0x00F0D121:
470 val.UnitName = "Volts";
471 exponent -= 7; /* remove bias */
472 break;
473 case 0x00100001:
474 exponent += 2; /* remove bias */
475 val.UnitName = "Amps";
476 break;
477 case 0xF001:
478 val.UnitName = "Hertz";
479 break;
480 case 0x1001:
481 val.UnitName = "Seconds";
482 break;
483 case 0xD121:
484 exponent -= 7; /* remove bias */
485 val.UnitName = "Watts";
486 break;
487 case 0x010001:
488 val.UnitName = "Degrees K";
489 break;
490 case 0x0101001:
491 val.UnitName = "AmpSecs";
492 if (exponent == 0)
493 val.dValue = info->value;
494 else
495 val.dValue = ((double)info->value) * pow_ten(exponent);
496 break;
497 default:
498 val.UnitName = "";
499 val.value_type = V_INTEGER;
500 val.iValue = info->value;
501 break;
504 if (exponent == 0)
505 val.dValue = info->value;
506 else
507 val.dValue = ((double)info->value) * pow_ten(exponent);
509 // Store a (possibly truncated) copy of the floating point value in the
510 // integer field as well.
511 val.iValue = (int)val.dValue;
513 Dmsg4(200, "Def val=%d exp=%d dVal=%f ci=%d\n", info->value,
514 exponent, val.dValue, info->ci);
515 } else { /* should be T_NONE */
517 val.UnitName = "";
518 val.value_type = V_INTEGER;
519 val.iValue = info->value;
521 if (exponent == 0)
522 val.dValue = info->value;
523 else
524 val.dValue = ((double)info->value) * pow_ten(exponent);
526 Dmsg4(200, "Def val=%d exp=%d dVal=%f ci=%d\n", info->value,
527 exponent, val.dValue, info->ci);
530 memcpy(uval, &val, sizeof(*uval));
531 return true;
535 * Get a field value
537 int pusb_get_value(UPSINFO *ups, int ci, USB_VALUE *uval)
539 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
540 USB_INFO *info = my_data->info[ci];
541 unsigned char data[20];
542 int len;
545 * Note we need to check info since CI_STATUS is always true
546 * even when the UPS doesn't directly support that CI.
548 if (!UPS_HAS_CAP(ci) || !info)
549 return false; /* UPS does not have capability */
552 * Clear the destination buffer. In the case of a short transfer (see
553 * below) this will increase the likelihood of extracting the correct
554 * value in spite of the missing data.
556 memset(data, 0, sizeof(data));
558 /* Fetch the proper report */
559 len = hidu_get_report(my_data->fd, &info->item, data, info->report_len);
560 if (len == -1)
561 return false;
564 * Some UPSes seem to have broken firmware that sends a different number
565 * of bytes (usually fewer) than the report descriptor specifies. On
566 * UHCI controllers under *BSD, this can lead to random lockups. To
567 * reduce the likelihood of a lockup, we adjust our expected length to
568 * match the actual as soon as a mismatch is detected, so future
569 * transfers will have the proper lengths from the outset. NOTE that
570 * the data returned may not be parsed properly (since the parsing is
571 * necessarily based on the report descriptor) but given that HID
572 * reports are in little endian byte order and we cleared the buffer
573 * above, chances are good that we will actually extract the right
574 * value in spite of the UPS's brokenness.
576 if (info->report_len != len) {
577 Dmsg4(100, "Report length mismatch, fixing "
578 "(id=%d, ci=%d, expected=%d, actual=%d)\n",
579 info->item.report_ID, ci, info->report_len, len);
580 info->report_len = len;
583 /* Populate a uval struct using the raw report data */
584 return populate_uval(ups, info, data, uval);
588 * Read UPS events. I.e. state changes.
590 int pusb_ups_check_state(UPSINFO *ups)
592 int i, ci;
593 int retval, value;
594 unsigned char buf[100];
595 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
596 struct timespec now, exit;
597 struct timeval tv;
598 fd_set rfds;
599 USB_VALUE uval;
600 bool done = false;
602 /* Figure out when we need to exit by */
603 clock_gettime(CLOCK_REALTIME, &exit);
604 exit.tv_sec += ups->wait_time;
606 while (!done) {
608 /* Figure out how long until we have to exit */
609 clock_gettime(CLOCK_REALTIME, &now);
611 if (now.tv_sec > exit.tv_sec ||
612 (now.tv_sec == exit.tv_sec &&
613 now.tv_nsec / 1000 >= exit.tv_nsec / 1000)) {
614 /* Done already? How time flies... */
615 return 0;
618 tv.tv_sec = exit.tv_sec - now.tv_sec;
619 tv.tv_usec = (exit.tv_nsec - now.tv_nsec) / 1000;
620 if (tv.tv_usec < 0) {
621 tv.tv_sec--; /* Normalize */
622 tv.tv_usec += 1000000;
625 FD_ZERO(&rfds);
626 FD_SET(my_data->intfd, &rfds);
628 retval = select((my_data->intfd) + 1, &rfds, NULL, NULL, &tv);
630 switch (retval) {
631 case 0: /* No chars available in TIMER seconds. */
632 return 0;
633 case -1:
634 if (errno == EINTR || errno == EAGAIN) /* assume SIGCHLD */
635 continue;
636 Dmsg1(200, "select error: ERR=%s\n", strerror(errno));
637 usb_link_check(ups); /* link is down, wait */
638 return 0;
639 default:
640 break;
643 do {
644 retval = read(my_data->intfd, buf, sizeof(buf));
645 } while (retval == -1 && (errno == EAGAIN || errno == EINTR));
647 if (retval < 0) { /* error */
648 Dmsg1(200, "read error: ERR=%s\n", strerror(errno));
649 usb_link_check(ups); /* notify that link is down, wait */
650 return 0;
653 if (debug_level >= 300) {
654 logf("Interrupt data: ");
655 for (i = 0; i < retval; i++)
656 logf("%02x, ", buf[i]);
657 logf("\n");
660 write_lock(ups);
663 * Iterate over all CIs, firing off events for any that are
664 * affected by this report.
666 for (ci=0; ci<CI_MAXCI; ci++) {
667 if (ups->UPS_Cap[ci] && my_data->info[ci] &&
668 my_data->info[ci]->item.report_ID == buf[0]) {
671 * Check if we received fewer bytes of data from the UPS than we
672 * should have. If so, ignore the report since we can't process it
673 * reliably. If we go ahead and try to process it we may get
674 * sporradic bad readings. UPSes we've seen this issue on so far
675 * include:
677 * "Back-UPS CS 650 FW:817.v7 .I USB FW:v7"
678 * "Back-UPS CS 500 FW:808.q8.I USB FW:q8"
680 if (my_data->info[ci]->report_len != retval) {
681 Dmsg4(100, "Report length mismatch, ignoring "
682 "(id=%d, ci=%d, expected=%d, actual=%d)\n",
683 my_data->info[ci]->item.report_ID, ci,
684 my_data->info[ci]->report_len, retval);
685 break; /* don't continue since other CIs will be just as wrong */
688 /* Ignore this event if the value has not changed */
689 value = hid_get_data(buf+1, &my_data->info[ci]->item);
690 if (my_data->info[ci]->value == value) {
691 Dmsg3(200, "Ignoring unchanged value (ci=%d, rpt=%d, val=%d)\n",
692 ci, buf[0], value);
693 continue;
696 Dmsg3(200, "Processing changed value (ci=%d, rpt=%d, val=%d)\n",
697 ci, buf[0], value);
699 /* Populate a uval and report it to the upper layer */
700 populate_uval(ups, my_data->info[ci], buf, &uval);
701 if (usb_report_event(ups, ci, &uval)) {
703 * The upper layer considers this an important event,
704 * so we will return after processing any remaining
705 * CIs for this report.
707 done = true;
712 write_unlock(ups);
715 return true;
719 * Open usb port
721 * This is called once by the core code and is the first
722 * routine called.
724 int pusb_ups_open(UPSINFO *ups)
726 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
728 write_lock(ups);
729 if (my_data == NULL) {
730 my_data = (USB_DATA *)malloc(sizeof(USB_DATA));
731 if (my_data == NULL) {
732 log_event(ups, LOG_ERR, "Out of memory.");
733 write_unlock(ups);
734 exit(1);
737 memset(my_data, 0, sizeof(USB_DATA));
738 ups->driver_internal_data = my_data;
739 } else {
740 reinitialize_private_structure(ups);
743 if (my_data->orig_device[0] == 0)
744 astrncpy(my_data->orig_device, ups->device, sizeof(my_data->orig_device));
746 if (!open_usb_device(ups)) {
747 write_unlock(ups);
748 if (ups->device[0]) {
749 Error_abort1("Cannot open UPS device: \"%s\" --\n"
750 "For a link to detailed USB trouble shooting information,\n"
751 "please see <http://www.apcupsd.com/support.html>.\n", ups->device);
752 } else {
753 Error_abort0("Cannot find UPS device --\n"
754 "For a link to detailed USB trouble shooting information,\n"
755 "please see <http://www.apcupsd.com/support.html>.\n");
759 ups->clear_slave();
760 write_unlock(ups);
761 return 1;
765 * This is the last routine called from apcupsd core code
767 int pusb_ups_close(UPSINFO *ups)
769 /* Should we be politely closing fds here or anything? */
770 write_lock(ups);
772 if (ups->driver_internal_data) {
773 free(ups->driver_internal_data);
774 ups->driver_internal_data = NULL;
777 write_unlock(ups);
778 return 1;
781 int pusb_ups_setup(UPSINFO *ups)
783 /* Nothing to do */
784 return 1;
787 int pusb_read_int_from_ups(UPSINFO *ups, int ci, int *value)
789 USB_VALUE val;
791 if (!pusb_get_value(ups, ci, &val))
792 return false;
794 *value = val.iValue;
795 return true;
798 int pusb_write_int_to_ups(UPSINFO *ups, int ci, int value, const char *name)
800 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
801 USB_INFO *info;
802 int old_value, new_value;
803 unsigned char rpt[20];
805 if (ups->UPS_Cap[ci] && my_data->info[ci] && my_data->info[ci]->witem.report_ID) {
806 info = my_data->info[ci]; /* point to our info structure */
808 if (hidu_get_report(my_data->fd, &info->item, rpt, info->report_len) < 1) {
809 Dmsg1(000, "get_report for kill power function %s failed.\n", name);
810 return false;
813 old_value = hid_get_data(rpt + 1, &info->item);
815 hid_set_data(rpt + 1, &info->witem, value);
817 if (!hidu_set_report(my_data->fd, &info->witem, rpt, info->report_len)) {
818 Dmsg1(000, "set_report for kill power function %s failed.\n", name);
819 return false;
822 if (hidu_get_report(my_data->fd, &info->item, rpt, info->report_len) < 1) {
823 Dmsg1(000, "get_report for kill power function %s failed.\n", name);
824 return false;
827 new_value = hid_get_data(rpt + 1, &info->item);
829 Dmsg3(100, "function %s ci=%d value=%d OK.\n", name, ci, value);
830 Dmsg4(100, "%s before=%d set=%d after=%d\n", name, old_value, value, new_value);
831 return true;
834 Dmsg2(000, "function %s ci=%d not available in this UPS.\n", name, ci);
835 return false;