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,
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
38 #include "../usb_common.h"
39 #include <asm/types.h>
40 #include <linux/hiddev.h>
42 /* RHEL has an out-of-date hiddev.h */
44 # define HIDIOCSFLAG _IOW('H', 0x0F, int)
46 #ifndef HIDDEV_FLAG_UREF
47 # define HIDDEV_FLAG_UREF 0x1
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) */
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
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 */
83 static void reinitialize_private_structure(UPSINFO
*ups
)
85 USB_DATA
*my_data
= (USB_DATA
*)ups
->driver_internal_data
;
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
;
111 Dmsg1(200, "Attempting to open \"%s\"\n", dev
);
113 /* Open the device port */
114 fd
= open(dev
, O_RDWR
| O_NOCTTY
);
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
);
133 /* Failed to open the device */
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
};
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
161 * If no device locating specified, we go autodetect it
162 * by searching known places.
164 if (ups
->device
[0] == 0)
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;
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.
184 for (i
= 0; i
< 10; i
++) {
185 my_data
->fd
= open_device(ups
->device
, ups
);
186 if (my_data
->fd
!= -1)
192 * If user-specified device could not be opened, fail.
194 if (my_data
->orig_device
[0] != 0)
198 * If we get here we failed to re-open a previously auto-detected
199 * device. We will fall thru and restart autodetection...
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
));
219 ups
->device
[0] = '\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;
232 USB_DATA
*my_data
= (USB_DATA
*)ups
->driver_internal_data
;
233 static bool linkcheck
= false;
238 linkcheck
= true; /* prevent recursion */
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
)) {
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
]);
255 /* Retry every LINK_RETRY_INTERVAL seconds */
256 sleep(LINK_RETRY_INTERVAL
);
257 if (my_data
->fd
>= 0) {
260 reinitialize_private_structure(ups
);
263 if (open_usb_device(ups
) && usb_ups_get_capabilities(ups
) &&
264 usb_ups_read_static_data(ups
)) {
272 generate_event(ups
, CMDCOMMOK
);
273 ups
->clear_commlost();
274 Dmsg0(200, "link check comm OK.\n");
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
;
288 exponent
= info
->unit_exponent
;
290 exponent
= exponent
- 16;
292 if (info
->data_type
== T_INDEX
) { /* get string */
293 if (info
->uref
.value
== 0)
296 sdesc
.index
= info
->uref
.value
;
297 if (ioctl(my_data
->fd
, HIDIOCGSTRING
, &sdesc
) < 0)
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
) {
309 val
.UnitName
= "Volts";
310 exponent
-= 7; /* remove bias */
313 exponent
+= 2; /* remove bias */
314 val
.UnitName
= "Amps";
317 val
.UnitName
= "Hertz";
320 val
.UnitName
= "Seconds";
323 exponent
-= 7; /* remove bias */
324 val
.UnitName
= "Watts";
327 val
.UnitName
= "Degrees K";
330 val
.UnitName
= "AmpSecs";
334 val
.value_type
= V_INTEGER
;
335 val
.iValue
= info
->uref
.value
;
340 val
.dValue
= info
->uref
.value
;
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 */
352 val
.value_type
= V_INTEGER
;
353 val
.iValue
= info
->uref
.value
;
356 val
.dValue
= info
->uref
.value
;
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
));
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
;
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 */
388 if (ioctl(my_data
->fd
, HIDIOCGUSAGE
, &info
->uref
) < 0) /* update UPS value */
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
;
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
];
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
;
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
];
438 * Read UPS events. I.e. state changes.
440 int pusb_ups_check_state(UPSINFO
*ups
)
444 struct hiddev_usage_ref uref
;
445 struct hiddev_event hev
;
446 USB_DATA
*my_data
= (USB_DATA
*)ups
->driver_internal_data
;
451 tv
.tv_sec
= ups
->wait_time
;
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
470 case 0: /* No chars available in TIMER seconds. */
473 if (errno
== EINTR
|| errno
== EAGAIN
) /* assume SIGCHLD */
476 Dmsg1(200, "select error: ERR=%s\n", strerror(errno
));
477 usb_link_check(ups
); /* link is down, wait */
483 if (!my_data
->compat24
) {
484 /* This is >= linux-2.6, so we can read a uref directly */
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 */
495 if (retval
== 0 || retval
< (int)sizeof(uref
))
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
);
506 * We're in linux-2.4 compatibility mode, so we read a
507 * hiddev_event and use it to construct a uref.
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 */
519 if (retval
== 0 || retval
< (int)sizeof(hev
))
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",
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 */
539 uref
.value
= hev
.value
;
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
);
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.
575 * This is called once by the core code and is the first routine
578 int pusb_ups_open(UPSINFO
*ups
)
580 USB_DATA
*my_data
= (USB_DATA
*)ups
->driver_internal_data
;
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.");
591 memset(my_data
, 0, sizeof(USB_DATA
));
592 ups
->driver_internal_data
= my_data
;
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
)) {
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
);
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");
618 int pusb_ups_setup(UPSINFO
*ups
)
621 * Seems that there is nothing to do.
627 * This is the last routine called from apcupsd core code
629 int pusb_ups_close(UPSINFO
*ups
)
633 if (ups
->driver_internal_data
) {
634 free(ups
->driver_internal_data
);
635 ups
->driver_internal_data
= NULL
;
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
));
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)
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)
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
++) {
696 int ci
= known_info
[k
].ci
;
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
];
709 ups
->UPS_Cap
[ci
] = true;
710 info
= (USB_INFO
*)malloc(sizeof(USB_INFO
));
714 Error_abort0("Out of memory.\n");
717 my_data
->info
[ci
] = info
;
718 memset(info
, 0, sizeof(*info
));
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
);
746 rinfo
.report_id
|= HID_REPORT_ID_NEXT
;
750 ups
->UPS_Cap
[CI_STATUS
] = true; /* we have status flag */
756 int pusb_read_int_from_ups(UPSINFO
*ups
, int ci
, int *value
)
760 if (!pusb_get_value(ups
, ci
, &val
))
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
;
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
;
782 if (ioctl(my_data
->fd
, HIDIOCGREPORT
, &rinfo
) < 0) {
783 Dmsg2(000, "HIDIOCGREPORT for function %s failed. ERR=%s\n",
784 name
, strerror(errno
));
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
));
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
));
810 if (ioctl(my_data
->fd
, HIDIOCSREPORT
, &rinfo
) < 0) {
811 Dmsg2(000, "HIDIOCSREPORT for function %s failed. ERR=%s\n",
812 name
, strerror(errno
));
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
;
827 if (ioctl(my_data
->fd
, HIDIOCGREPORT
, &rinfo
) < 0) {
828 Dmsg2(000, "HIDIOCGREPORT for function %s failed. ERR=%s\n",
829 name
, strerror(errno
));
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
));
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
,
848 Dmsg2(000, "function %s ci=%d not available in this UPS.\n", name
, ci
);