UPS: apcupsd clean sources
[tomato.git] / release / src / router / apcupsd / src / drivers / usb / linux / linux-usb.c
blob63222b40ea78d427616abeb579d62bc0d1b357cd
1 /*
2 * linux-usb.c
4 * Platform-specific interface to Linux hiddev USB HID driver.
6 * Parts of this code (especially the initialization and walking
7 * the reports) were derived from a test program hid-ups.c by:
8 * Vojtech Pavlik <vojtech@ucw.cz>
9 * Paul Stewart <hiddev@wetlogic.net>
13 * Copyright (C) 2001-2004 Kern Sibbald
14 * Copyright (C) 2004-2005 Adam Kropelin
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of version 2 of the GNU General
18 * Public License as published by the Free Software Foundation.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * General Public License for more details.
25 * You should have received a copy of the GNU General Public
26 * License along with this program; if not, write to the Free
27 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
28 * MA 02111-1307, USA.
32 * The following is a work around for a problem in 2.6 kernel
33 * linux/hiddev.h file that is fixed in 2.6.9
35 #define HID_MAX_USAGES 1024
37 #include "apc.h"
38 #include "../usb_common.h"
39 #include <asm/types.h>
40 #include <linux/hiddev.h>
42 /* RHEL has an out-of-date hiddev.h */
43 #ifndef HIDIOCGFLAG
44 # define HIDIOCSFLAG _IOW('H', 0x0F, int)
45 #endif
46 #ifndef HIDDEV_FLAG_UREF
47 # define HIDDEV_FLAG_UREF 0x1
48 #endif
50 /* Enable this to force Linux 2.4 compatability mode */
51 #define FORCE_COMPAT24 false
54 * When we are traversing the USB reports given by the UPS and we
55 * find an entry corresponding to an entry in the known_info table
56 * above, we make the following USB_INFO entry in the info table
57 * of our private data.
59 typedef struct s_usb_info {
60 unsigned physical; /* physical value wanted */
61 unsigned unit_exponent; /* exponent */
62 unsigned unit; /* units */
63 int data_type; /* data type */
64 int ci; /* which CI does this usage represent? */
65 struct hiddev_usage_ref uref; /* usage reference (read) */
66 struct hiddev_usage_ref wuref; /* usage reference (write) */
67 } USB_INFO;
70 * This "private" structure is returned to us in the driver private
71 * field, and allows us to get to all the info we keep on each UPS.
72 * The info field is malloced for each command we want and the UPS
73 * has.
75 typedef struct s_usb_data {
76 int fd; /* Our UPS fd when open */
77 bool compat24; /* Linux 2.4 compatibility mode */
78 char orig_device[MAXSTRING]; /* Original port specification */
79 USB_INFO *info[CI_MAXCI + 1]; /* Info pointers for each command */
80 } USB_DATA;
83 static void reinitialize_private_structure(UPSINFO *ups)
85 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
86 int k;
88 Dmsg0(200, "Reinitializing private structure.\n");
90 * We are being reinitialized, so clear the Cap
91 * array, and release previously allocated memory.
93 for (k = 0; k <= CI_MAXCI; k++) {
94 ups->UPS_Cap[k] = false;
95 if (my_data->info[k] != NULL) {
96 free(my_data->info[k]);
97 my_data->info[k] = NULL;
103 * Internal routine to attempt device open.
105 static int open_device(const char *dev, UPSINFO *ups)
107 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
108 int flaguref = HIDDEV_FLAG_UREF;
109 int fd, ret, i;
111 Dmsg1(200, "Attempting to open \"%s\"\n", dev);
113 /* Open the device port */
114 fd = open(dev, O_RDWR | O_NOCTTY);
115 if (fd >= 0) {
116 /* Check for the UPS application HID usage */
117 for (i = 0; (ret = ioctl(fd, HIDIOCAPPLICATION, i)) > 0; i++) {
118 if ((ret & 0xffff000) == (UPS_USAGE & 0xffff0000)) {
119 /* Request full uref reporting from read() */
120 if (FORCE_COMPAT24 || ioctl(fd, HIDIOCSFLAG, &flaguref)) {
121 Dmsg0(100, "HIDIOCSFLAG failed; enabling linux-2.4 "
122 "compatibility mode\n");
123 my_data->compat24 = true;
125 /* Successfully opened the device */
126 Dmsg1(200, "Successfully opened \"%s\"\n", dev);
127 return fd;
130 close(fd);
133 /* Failed to open the device */
134 return -1;
138 * Internal routine to open the device and ensure that there is
139 * a UPS application on the line. This routine may be called
140 * many times because the device may be unplugged and plugged
141 * back in -- the joys of USB devices.
143 static int open_usb_device(UPSINFO *ups)
145 char devname[MAXSTRING];
146 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
147 const char *hiddev[] =
148 { "/dev/usb/hiddev", "/dev/usb/hid/hiddev", "/dev/hiddev", NULL };
149 int i, j, k;
152 * Note, we set ups->fd here so the "core" of apcupsd doesn't
153 * think we are a slave, which is what happens when it is -1.
154 * (ADK: Actually this only appears to be true for apctest as
155 * apcupsd proper uses the UPS_slave flag.)
156 * Internally, we use the fd in our own private space
158 ups->fd = 1;
161 * If no device locating specified, we go autodetect it
162 * by searching known places.
164 if (ups->device[0] == 0)
165 goto auto_detect;
168 * Also if specified device includes deprecated '[]' notation,
169 * just use the automatic search.
171 if (strchr(ups->device, '[') &&
172 strchr(ups->device, ']'))
174 my_data->orig_device[0] = 0;
175 goto auto_detect;
179 * If we get here we know the user specified a device or we are
180 * trying to re-open a device that previously was open.
183 /* Retry 10 times */
184 for (i = 0; i < 10; i++) {
185 my_data->fd = open_device(ups->device, ups);
186 if (my_data->fd != -1)
187 return 1;
188 sleep(1);
192 * If user-specified device could not be opened, fail.
194 if (my_data->orig_device[0] != 0)
195 return 0;
198 * If we get here we failed to re-open a previously auto-detected
199 * device. We will fall thru and restart autodetection...
202 auto_detect:
204 for (i = 0; i < 10; i++) { /* try 10 times */
205 for (j = 0; hiddev[j]; j++) { /* loop over known device names */
206 for (k = 0; k < 16; k++) { /* loop over devices */
207 asnprintf(devname, sizeof(devname), "%s%d", hiddev[j], k);
208 my_data->fd = open_device(devname, ups);
209 if (my_data->fd != -1) {
210 /* Successful open, save device name and return */
211 astrncpy(ups->device, devname, sizeof(ups->device));
212 return 1;
216 sleep(1);
219 ups->device[0] = '\0';
220 return 0;
224 * Called if there is an ioctl() or read() error, we close() and
225 * re open() the port since the device was probably unplugged.
227 static int usb_link_check(UPSINFO *ups)
229 bool comm_err = true;
230 int tlog;
231 bool once = true;
232 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
233 static bool linkcheck = false;
235 if (linkcheck)
236 return 0;
238 linkcheck = true; /* prevent recursion */
240 ups->set_commlost();
241 Dmsg0(200, "link_check comm lost\n");
243 /* Don't warn until we try to get it at least 2 times and fail */
244 for (tlog = LINK_RETRY_INTERVAL * 2; comm_err; tlog -= (LINK_RETRY_INTERVAL)) {
245 if (tlog <= 0) {
246 tlog = 10 * 60; /* notify every 10 minutes */
247 log_event(ups, event_msg[CMDCOMMFAILURE].level,
248 event_msg[CMDCOMMFAILURE].msg);
249 if (once) { /* execute script once */
250 execute_command(ups, ups_event[CMDCOMMFAILURE]);
251 once = false;
255 /* Retry every LINK_RETRY_INTERVAL seconds */
256 sleep(LINK_RETRY_INTERVAL);
257 if (my_data->fd >= 0) {
258 close(my_data->fd);
259 my_data->fd = -1;
260 reinitialize_private_structure(ups);
263 if (open_usb_device(ups) && usb_ups_get_capabilities(ups) &&
264 usb_ups_read_static_data(ups)) {
265 comm_err = false;
266 } else {
267 continue;
271 if (!comm_err) {
272 generate_event(ups, CMDCOMMOK);
273 ups->clear_commlost();
274 Dmsg0(200, "link check comm OK.\n");
277 linkcheck = false;
278 return 1;
281 static bool populate_uval(UPSINFO *ups, USB_INFO *info, USB_VALUE *uval)
283 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
284 struct hiddev_string_descriptor sdesc;
285 USB_VALUE val;
286 int exponent;
288 exponent = info->unit_exponent;
289 if (exponent > 7)
290 exponent = exponent - 16;
292 if (info->data_type == T_INDEX) { /* get string */
293 if (info->uref.value == 0)
294 return false;
296 sdesc.index = info->uref.value;
297 if (ioctl(my_data->fd, HIDIOCGSTRING, &sdesc) < 0)
298 return false;
300 astrncpy(val.sValue, sdesc.value, sizeof(val.sValue));
301 val.value_type = V_STRING;
303 Dmsg4(200, "Def val=%d exp=%d sVal=\"%s\" ci=%d\n", info->uref.value,
304 exponent, val.sValue, info->ci);
305 } else if (info->data_type == T_UNITS) {
306 val.value_type = V_DOUBLE;
307 switch (info->unit) {
308 case 0x00F0D121:
309 val.UnitName = "Volts";
310 exponent -= 7; /* remove bias */
311 break;
312 case 0x00100001:
313 exponent += 2; /* remove bias */
314 val.UnitName = "Amps";
315 break;
316 case 0xF001:
317 val.UnitName = "Hertz";
318 break;
319 case 0x1001:
320 val.UnitName = "Seconds";
321 break;
322 case 0xD121:
323 exponent -= 7; /* remove bias */
324 val.UnitName = "Watts";
325 break;
326 case 0x010001:
327 val.UnitName = "Degrees K";
328 break;
329 case 0x0101001:
330 val.UnitName = "AmpSecs";
331 break;
332 default:
333 val.UnitName = "";
334 val.value_type = V_INTEGER;
335 val.iValue = info->uref.value;
336 break;
339 if (exponent == 0)
340 val.dValue = info->uref.value;
341 else
342 val.dValue = ((double)info->uref.value) * pow_ten(exponent);
344 // Store a (possibly truncated) copy of the floating point value in the
345 // integer field as well.
346 val.iValue = (int)val.dValue;
348 Dmsg4(200, "Def val=%d exp=%d dVal=%f ci=%d\n", info->uref.value,
349 exponent, val.dValue, info->ci);
350 } else { /* should be T_NONE */
351 val.UnitName = "";
352 val.value_type = V_INTEGER;
353 val.iValue = info->uref.value;
355 if (exponent == 0)
356 val.dValue = info->uref.value;
357 else
358 val.dValue = ((double)info->uref.value) * pow_ten(exponent);
360 Dmsg4(200, "Def val=%d exp=%d dVal=%f ci=%d\n", info->uref.value,
361 exponent, val.dValue, info->ci);
364 memcpy(uval, &val, sizeof(*uval));
365 return true;
369 * Get a field value
371 int pusb_get_value(UPSINFO *ups, int ci, USB_VALUE *uval)
373 struct hiddev_report_info rinfo;
374 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
375 USB_INFO *info;
377 if (!ups->UPS_Cap[ci] || !my_data->info[ci])
378 return false; /* UPS does not have capability */
380 /* Fetch the new value from the UPS */
382 info = my_data->info[ci]; /* point to our info structure */
383 rinfo.report_type = info->uref.report_type;
384 rinfo.report_id = info->uref.report_id;
385 if (ioctl(my_data->fd, HIDIOCGREPORT, &rinfo) < 0) /* update Report */
386 return false;
388 if (ioctl(my_data->fd, HIDIOCGUSAGE, &info->uref) < 0) /* update UPS value */
389 return false;
391 /* Process the updated value */
392 return populate_uval(ups, info, uval);
396 * Find the USB_INFO structure used for tracking a given usage. Searching
397 * by usage_code alone is insufficient since the same usage may appear in
398 * multiple reports or even multiple times in the same report.
400 static USB_INFO *find_info_by_uref(UPSINFO *ups, struct hiddev_usage_ref *uref)
402 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
403 int i;
405 for (i=0; i<CI_MAXCI; i++) {
406 if (ups->UPS_Cap[i] && my_data->info[i] &&
407 my_data->info[i]->uref.report_id == uref->report_id &&
408 my_data->info[i]->uref.field_index == uref->field_index &&
409 my_data->info[i]->uref.usage_index == uref->usage_index &&
410 my_data->info[i]->uref.usage_code == uref->usage_code) {
411 return my_data->info[i];
415 return NULL;
419 * Same as find_info_by_uref() but only checks the usage code. This is
420 * not entirely reliable, but it's the best we have on linux-2.4.
422 static USB_INFO *find_info_by_ucode(UPSINFO *ups, unsigned int ucode)
424 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
425 int i;
427 for (i=0; i<CI_MAXCI; i++) {
428 if (ups->UPS_Cap[i] && my_data->info[i] &&
429 my_data->info[i]->uref.usage_code == ucode) {
430 return my_data->info[i];
434 return NULL;
438 * Read UPS events. I.e. state changes.
440 int pusb_ups_check_state(UPSINFO *ups)
442 int retval;
443 bool done = false;
444 struct hiddev_usage_ref uref;
445 struct hiddev_event hev;
446 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
447 USB_INFO* info;
448 USB_VALUE uval;
450 struct timeval tv;
451 tv.tv_sec = ups->wait_time;
452 tv.tv_usec = 0;
454 while (!done) {
455 fd_set rfds;
457 FD_ZERO(&rfds);
458 FD_SET(my_data->fd, &rfds);
460 retval = select((my_data->fd) + 1, &rfds, NULL, NULL, &tv);
463 * Note: We are relying on select() adjusting tv to the amount
464 * of time NOT waited. This is a Linux-specific feature but
465 * shouldn't be a problem since the driver is only intended for
466 * for Linux.
469 switch (retval) {
470 case 0: /* No chars available in TIMER seconds. */
471 return false;
472 case -1:
473 if (errno == EINTR || errno == EAGAIN) /* assume SIGCHLD */
474 continue;
476 Dmsg1(200, "select error: ERR=%s\n", strerror(errno));
477 usb_link_check(ups); /* link is down, wait */
478 return false;
479 default:
480 break;
483 if (!my_data->compat24) {
484 /* This is >= linux-2.6, so we can read a uref directly */
485 do {
486 retval = read(my_data->fd, &uref, sizeof(uref));
487 } while (retval == -1 && (errno == EAGAIN || errno == EINTR));
489 if (retval < 0) { /* error */
490 Dmsg1(200, "read error: ERR=%s\n", strerror(errno));
491 usb_link_check(ups); /* notify that link is down, wait */
492 return false;
495 if (retval == 0 || retval < (int)sizeof(uref))
496 return false;
498 /* Ignore events we don't recognize */
499 if ((info = find_info_by_uref(ups, &uref)) == NULL) {
500 Dmsg3(200, "Unrecognized usage (rpt=%d, usg=0x%08x, val=%d)\n",
501 uref.report_id, uref.usage_code, uref.value);
502 continue;
504 } else {
506 * We're in linux-2.4 compatibility mode, so we read a
507 * hiddev_event and use it to construct a uref.
509 do {
510 retval = read(my_data->fd, &hev, sizeof(hev));
511 } while (retval == -1 && (errno == EAGAIN || errno == EINTR));
513 if (retval < 0) { /* error */
514 Dmsg1(200, "read error: ERR=%s\n", strerror(errno));
515 usb_link_check(ups); /* notify that link is down, wait */
516 return false;
519 if (retval == 0 || retval < (int)sizeof(hev))
520 return false;
522 /* Ignore events we don't recognize */
523 if ((info = find_info_by_ucode(ups, hev.hid)) == NULL) {
524 Dmsg2(200, "Unrecognized usage (usg=0x%08x, val=%d)\n",
525 hev.hid, hev.value);
526 continue;
530 * ADK FIXME: The info-> struct we have now is not guaranteed to
531 * actually be the right one, because linux-2.4 does not give us
532 * enough data in the event to make a positive match. We may need
533 * to filter out ambiguous usages here or manually fetch each CI
534 * that matches the given usage.
537 /* Copy the stored uref, replacing its value */
538 uref = info->uref;
539 uref.value = hev.value;
542 write_lock(ups);
544 /* Ignore events whose value is unchanged */
545 if (info->uref.value == uref.value) {
546 Dmsg3(200, "Ignoring unchanged value (rpt=%d, usg=0x%08x, val=%d)\n",
547 uref.report_id, uref.usage_code, uref.value);
548 write_unlock(ups);
549 continue;
552 /* Update tracked value */
553 Dmsg3(200, "Processing changed value (rpt=%d, usg=0x%08x, val=%d)\n",
554 uref.report_id, uref.usage_code, uref.value);
555 info->uref.value = uref.value;
557 /* Populate a uval and report it to the upper layer */
558 populate_uval(ups, info, &uval);
559 if (usb_report_event(ups, info->ci, &uval)) {
561 * The upper layer considers this an important event,
562 * so we will return immediately.
564 done = true;
567 write_unlock(ups);
570 return true;
574 * Open usb port
575 * This is called once by the core code and is the first routine
576 * called.
578 int pusb_ups_open(UPSINFO *ups)
580 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
582 write_lock(ups);
583 if (my_data == NULL) {
584 my_data = (USB_DATA *)malloc(sizeof(USB_DATA));
585 if (my_data == NULL) {
586 log_event(ups, LOG_ERR, "Out of memory.");
587 write_unlock(ups);
588 exit(1);
591 memset(my_data, 0, sizeof(USB_DATA));
592 ups->driver_internal_data = my_data;
593 } else {
594 reinitialize_private_structure(ups);
597 if (my_data->orig_device[0] == 0)
598 astrncpy(my_data->orig_device, ups->device, sizeof(my_data->orig_device));
600 if (!open_usb_device(ups)) {
601 write_unlock(ups);
602 if (ups->device[0]) {
603 Error_abort1("Cannot open UPS device: \"%s\" --\n"
604 "For a link to detailed USB trouble shooting information,\n"
605 "please see <http://www.apcupsd.com/support.html>.\n", ups->device);
606 } else {
607 Error_abort0("Cannot find UPS device --\n"
608 "For a link to detailed USB trouble shooting information,\n"
609 "please see <http://www.apcupsd.com/support.html>.\n");
613 ups->clear_slave();
614 write_unlock(ups);
615 return 1;
618 int pusb_ups_setup(UPSINFO *ups)
621 * Seems that there is nothing to do.
623 return 1;
627 * This is the last routine called from apcupsd core code
629 int pusb_ups_close(UPSINFO *ups)
631 write_lock(ups);
633 if (ups->driver_internal_data) {
634 free(ups->driver_internal_data);
635 ups->driver_internal_data = NULL;
638 write_unlock(ups);
639 return 1;
643 * Setup capabilities structure for UPS
645 int pusb_ups_get_capabilities(UPSINFO *ups, const struct s_known_info *known_info)
647 int rtype[2] = { HID_REPORT_TYPE_FEATURE, HID_REPORT_TYPE_INPUT };
648 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
649 struct hiddev_report_info rinfo;
650 struct hiddev_field_info finfo;
651 struct hiddev_usage_ref uref;
652 unsigned int i, j, k, n;
654 if (ioctl(my_data->fd, HIDIOCINITREPORT, 0) < 0)
655 Error_abort1("Cannot init USB HID report. ERR=%s\n", strerror(errno));
657 write_lock(ups);
660 * Walk through all available reports and determine
661 * what information we can use.
663 for (n = 0; n < sizeof(rtype)/sizeof(*rtype); n++) {
664 rinfo.report_type = rtype[n];
665 rinfo.report_id = HID_REPORT_ID_FIRST;
667 while (ioctl(my_data->fd, HIDIOCGREPORTINFO, &rinfo) >= 0) {
668 for (i = 0; i < rinfo.num_fields; i++) {
669 memset(&finfo, 0, sizeof(finfo));
670 finfo.report_type = rinfo.report_type;
671 finfo.report_id = rinfo.report_id;
672 finfo.field_index = i;
674 if (ioctl(my_data->fd, HIDIOCGFIELDINFO, &finfo) < 0)
675 continue;
677 memset(&uref, 0, sizeof(uref));
678 for (j = 0; j < finfo.maxusage; j++) {
679 uref.report_type = finfo.report_type;
680 uref.report_id = finfo.report_id;
681 uref.field_index = i;
682 uref.usage_index = j;
684 if (ioctl(my_data->fd, HIDIOCGUCODE, &uref) < 0)
685 continue;
687 ioctl(my_data->fd, HIDIOCGUSAGE, &uref);
690 * We've got a UPS usage entry, now walk down our
691 * know_info table and see if we have a match. If so,
692 * allocate a new entry for it.
694 for (k = 0; known_info[k].usage_code; k++) {
695 USB_INFO *info;
696 int ci = known_info[k].ci;
698 if (ci != CI_NONE &&
699 uref.usage_code == known_info[k].usage_code &&
700 (known_info[k].physical == P_ANY ||
701 known_info[k].physical == finfo.physical) &&
702 (known_info[k].logical == P_ANY ||
703 known_info[k].logical == finfo.logical)) {
705 // If we do not have any data saved for this report yet,
706 // allocate an USB_INFO and populate the read uref.
707 info = my_data->info[ci];
708 if (!info) {
709 ups->UPS_Cap[ci] = true;
710 info = (USB_INFO *)malloc(sizeof(USB_INFO));
712 if (!info) {
713 write_unlock(ups);
714 Error_abort0("Out of memory.\n");
717 my_data->info[ci] = info;
718 memset(info, 0, sizeof(*info));
719 info->ci = ci;
720 info->physical = finfo.physical;
721 info->unit_exponent = finfo.unit_exponent;
722 info->unit = finfo.unit;
723 info->data_type = known_info[k].data_type;
724 memcpy(&info->uref, &uref, sizeof(uref));
726 Dmsg3(200, "Got READ ci=%d, usage=0x%x, rpt=%d\n",
727 ci, known_info[k].usage_code, uref.report_id);
730 // If this is a FEATURE report and we haven't set the
731 // write uref yet, place it in the write uref (possibly in
732 // addition to placing it in the read uref above).
733 if (rinfo.report_type == HID_REPORT_TYPE_FEATURE &&
734 info->wuref.report_id == 0) {
735 memcpy(&info->wuref, &uref, sizeof(uref));
737 Dmsg3(200, "Got WRITE ci=%d, usage=0x%x, rpt=%d\n",
738 ci, known_info[k].usage_code, uref.report_id);
741 break;
746 rinfo.report_id |= HID_REPORT_ID_NEXT;
750 ups->UPS_Cap[CI_STATUS] = true; /* we have status flag */
751 write_unlock(ups);
752 return 1;
756 int pusb_read_int_from_ups(UPSINFO *ups, int ci, int *value)
758 USB_VALUE val;
760 if (!pusb_get_value(ups, ci, &val))
761 return false;
763 *value = val.iValue;
764 return true;
767 int pusb_write_int_to_ups(UPSINFO *ups, int ci, int value, const char *name)
769 struct hiddev_report_info rinfo;
770 USB_DATA *my_data = (USB_DATA *)ups->driver_internal_data;
771 USB_INFO *info;
772 int old_value, new_value;
774 // Make sure we have a writable uref for this CI
775 if (ups->UPS_Cap[ci] && my_data->info[ci] &&
776 my_data->info[ci]->wuref.report_id) {
777 info = my_data->info[ci]; /* point to our info structure */
778 rinfo.report_type = info->uref.report_type;
779 rinfo.report_id = info->uref.report_id;
781 /* Get report */
782 if (ioctl(my_data->fd, HIDIOCGREPORT, &rinfo) < 0) {
783 Dmsg2(000, "HIDIOCGREPORT for function %s failed. ERR=%s\n",
784 name, strerror(errno));
785 return false;
788 /* Get UPS value */
789 if (ioctl(my_data->fd, HIDIOCGUSAGE, &info->wuref) < 0) {
790 Dmsg2(000, "HIDIOCGUSAGE for function %s failed. ERR=%s\n",
791 name, strerror(errno));
792 return false;
794 old_value = info->uref.value;
796 info->wuref.value = value;
797 rinfo.report_type = info->wuref.report_type;
798 rinfo.report_id = info->wuref.report_id;
799 Dmsg3(100, "SUSAGE type=%d id=%d index=%d\n", info->wuref.report_type,
800 info->wuref.report_id, info->wuref.field_index);
802 /* Update UPS value */
803 if (ioctl(my_data->fd, HIDIOCSUSAGE, &info->wuref) < 0) {
804 Dmsg2(000, "HIDIOCSUSAGE for function %s failed. ERR=%s\n",
805 name, strerror(errno));
806 return false;
809 /* Update Report */
810 if (ioctl(my_data->fd, HIDIOCSREPORT, &rinfo) < 0) {
811 Dmsg2(000, "HIDIOCSREPORT for function %s failed. ERR=%s\n",
812 name, strerror(errno));
813 return false;
817 * This readback of the report is NOT just for debugging. It
818 * has the effect of flushing the above SET_REPORT to the
819 * device, which is important since we need to make sure it
820 * happens before subsequent reports are sent.
823 rinfo.report_type = info->uref.report_type;
824 rinfo.report_id = info->uref.report_id;
826 /* Get report */
827 if (ioctl(my_data->fd, HIDIOCGREPORT, &rinfo) < 0) {
828 Dmsg2(000, "HIDIOCGREPORT for function %s failed. ERR=%s\n",
829 name, strerror(errno));
830 return false;
833 /* Get UPS value */
834 if (ioctl(my_data->fd, HIDIOCGUSAGE, &info->uref) < 0) {
835 Dmsg2(000, "HIDIOCGUSAGE for function %s failed. ERR=%s\n",
836 name, strerror(errno));
837 return false;
840 new_value = info->uref.value;
841 Dmsg3(100, "function %s ci=%d value=%d OK.\n", name, ci, value);
842 Dmsg4(100, "%s before=%d set=%d after=%d\n", name, old_value, value,
843 new_value);
845 return true;
848 Dmsg2(000, "function %s ci=%d not available in this UPS.\n", name, ci);
849 return false;