1 /*
2 * linux-usb.c
3 *
4 * Platform-specific interface to Linux hiddev USB HID driver.
5 *
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>
10 */
11
12 /*
13 * Copyright (C) 2001-2004 Kern Sibbald
14 * Copyright (C) 2004-2005 Adam Kropelin
15 *
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.
19 *
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.
24 *
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., 51 Franklin Street, Fifth Floor, Boston,
28 * MA 02110-1335, USA.
29 */
30
31 /*
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
34 */
35 #define HID_MAX_USAGES 1024
36
37 #include "apc.h"
38 #include "linux-usb.h"
39 #include <glob.h>
40 #include <linux/usbdevice_fs.h>
41
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
49
50 /* Enable this to force Linux 2.4 compatability mode */
51 #define FORCE_COMPAT24 false
52
Factory(UPSINFO * ups)53 UpsDriver *UsbUpsDriver::Factory(UPSINFO *ups)
54 {
55 return new LinuxUsbUpsDriver(ups);
56 }
57
LinuxUsbUpsDriver(UPSINFO * ups)58 LinuxUsbUpsDriver::LinuxUsbUpsDriver(UPSINFO *ups) :
59 UsbUpsDriver(ups),
60 _fd(-1),
61 _compat24(false),
62 _linkcheck(false)
63 {
64 memset(_orig_device, 0, sizeof(_orig_device));
65 memset(_info, 0, sizeof(_info));
66 }
67
reinitialize_private_structure()68 void LinuxUsbUpsDriver::reinitialize_private_structure()
69 {
70 int k;
71
72 Dmsg(200, "Reinitializing private structure.\n");
73 /*
74 * We are being reinitialized, so clear the Cap
75 * array, and release previously allocated memory.
76 */
77 for (k = 0; k <= CI_MAXCI; k++) {
78 _ups->UPS_Cap[k] = false;
79 if (_info[k] != NULL) {
80 free(_info[k]);
81 _info[k] = NULL;
82 }
83 }
84 }
85
86 /*
87 * Internal routine to attempt device open.
88 */
open_device(const char * dev)89 int LinuxUsbUpsDriver::open_device(const char *dev)
90 {
91 int flaguref = HIDDEV_FLAG_UREF;
92 int fd, ret, i;
93
94 Dmsg(200, "Attempting to open \"%s\"\n", dev);
95
96 /* Open the device port */
97 fd = open(dev, O_RDWR | O_NOCTTY);
98 if (fd >= 0) {
99 /* Check for the UPS application HID usage */
100 for (i = 0; (ret = ioctl(fd, HIDIOCAPPLICATION, i)) > 0; i++) {
101 if ((ret & 0xffff000) == (UPS_USAGE & 0xffff0000)) {
102 /* Request full uref reporting from read() */
103 if (FORCE_COMPAT24 || ioctl(fd, HIDIOCSFLAG, &flaguref)) {
104 Dmsg(100, "HIDIOCSFLAG failed; enabling linux-2.4 "
105 "compatibility mode\n");
106 _compat24 = true;
107 }
108 /* Successfully opened the device */
109 Dmsg(200, "Successfully opened \"%s\"\n", dev);
110 return fd;
111 }
112 }
113 close(fd);
114 }
115
116 /* Failed to open the device */
117 return -1;
118 }
119
120 /*
121 * Routine to request that kernel driver attach to any APC USB UPSes
122 */
bind_upses()123 void LinuxUsbUpsDriver::bind_upses()
124 {
125 #ifdef USBDEVFS_CONNECT
126 // Find all USB devices in usbfs
127 glob_t g;
128 if (glob("/proc/bus/usb/[0-9][0-9][0-9]/[0-9][0-9][0-9]", 0, NULL, &g))
129 return;
130
131 // Iterate over all USB devices...
132 for (size_t i = 0; i < g.gl_pathc; ++i)
133 {
134 // Open the device in usbfs
135 int fd = open(g.gl_pathv[i], O_RDWR);
136 if (fd == -1)
137 continue;
138
139 struct usb_device_descriptor
140 {
141 uint8_t bLength;
142 uint8_t bDescriptorType;
143 uint16_t bcdUSB;
144 uint8_t bDeviceClass;
145 uint8_t bDeviceSubClass;
146 uint8_t bDeviceProtocol;
147 uint8_t bMaxPacketSize0;
148 uint16_t idVendor;
149 uint16_t idProduct;
150 uint16_t bcdDevice;
151 uint8_t iManufacturer;
152 uint8_t iProduct;
153 uint8_t iSerialNumber;
154 uint8_t bNumConfigurations;
155 } __attribute__ ((packed)) dd;
156
157 // Fetch device descriptor, then verify device VID/PID are supported and
158 // no kernel driver is currently attached
159 struct usbdevfs_getdriver getdrv = {0};
160 if (read(fd, &dd, sizeof(dd)) == sizeof(dd) &&
161 MatchVidPid(dd.idVendor, dd.idProduct) &&
162 ioctl(fd, USBDEVFS_GETDRIVER, &getdrv))
163 {
164 // APC device with no kernel driver attached
165 // Issue command to attach kernel driver
166 struct usbdevfs_ioctl command = {0};
167 command.ioctl_code = USBDEVFS_CONNECT;
168 int rc = ioctl(fd, USBDEVFS_IOCTL, &command);
169 // Hard to tell if this succeeded because the return value of the
170 // ioctl was changed. In linux 2.4.37, 0 means success but in 3.4 it
171 // means failure. So...we're screwed. The only thing we know for sure
172 // is <0 is definitely failure.
173 if (rc >= 0)
174 {
175 Dmsg(100, "Reattached kernel driver to %s\n", g.gl_pathv[i]);
176 }
177 else
178 {
179 Dmsg(0, "Failed to attach kernel driver to %s (%d)\n",
180 g.gl_pathv[i], rc);
181 }
182 }
183
184 close(fd);
185 }
186
187 globfree(&g);
188 #endif // USBDEVFS_CONNECT
189 }
190
191 /*
192 * Internal routine to open the device and ensure that there is
193 * a UPS application on the line. This routine may be called
194 * many times because the device may be unplugged and plugged
195 * back in -- the joys of USB devices.
196 */
open_usb_device()197 bool LinuxUsbUpsDriver::open_usb_device()
198 {
199 char devname[MAXSTRING];
200 const char *hiddev[] =
201 { "/dev/usb/hiddev", "/dev/usb/hid/hiddev", "/dev/hiddev", NULL };
202 int i, j, k;
203
204 /*
205 * Ensure any APC UPSes are utilizing the usbhid/hiddev kernel driver.
206 * This is necessary if they were detached from the kernel driver in order
207 * to use libusb (the apcctrl 'generic' USB driver).
208 */
209 bind_upses();
210
211 /*
212 * Note, we set _ups->fd here so the "core" of apcctrl doesn't
213 * think we are a slave, which is what happens when it is -1.
214 * (ADK: Actually this only appears to be true for apctest as
215 * apcctrl proper uses the UPS_slave flag.)
216 * Internally, we use the fd in our own private space
217 */
218 _ups->fd = 1;
219
220 /*
221 * If no device locating specified, we go autodetect it
222 * by searching known places.
223 */
224 if (_ups->device[0] == 0)
225 goto auto_detect;
226
227 /*
228 * Also if specified device includes deprecated '[]' notation,
229 * just use the automatic search.
230 */
231 if (strchr(_ups->device, '[') &&
232 strchr(_ups->device, ']'))
233 {
234 _orig_device[0] = 0;
235 goto auto_detect;
236 }
237
238 /*
239 * If we get here we know the user specified a device or we are
240 * trying to re-open a device that previously was open.
241 */
242
243 /* Retry 10 times */
244 for (i = 0; i < 10; i++) {
245 _fd = open_device(_ups->device);
246 if (_fd != -1)
247 return true;
248 sleep(1);
249 }
250
251 /*
252 * If user-specified device could not be opened, fail.
253 */
254 if (_orig_device[0] != 0)
255 return false;
256
257 /*
258 * If we get here we failed to re-open a previously auto-detected
259 * device. We will fall thru and restart autodetection...
260 */
261
262 auto_detect:
263
264 for (i = 0; i < 10; i++) { /* try 10 times */
265 for (j = 0; hiddev[j]; j++) { /* loop over known device names */
266 for (k = 0; k < 16; k++) { /* loop over devices */
267 asnprintf(devname, sizeof(devname), "%s%d", hiddev[j], k);
268 _fd = open_device(devname);
269 if (_fd != -1) {
270 /* Successful open, save device name and return */
271 strlcpy(_ups->device, devname, sizeof(_ups->device));
272 return true;
273 }
274 }
275 }
276 sleep(1);
277 }
278
279 _ups->device[0] = '\0';
280 return false;
281 }
282
283 /*
284 * Called if there is an ioctl() or read() error, we close() and
285 * re open() the port since the device was probably unplugged.
286 */
usb_link_check()287 bool LinuxUsbUpsDriver::usb_link_check()
288 {
289 bool comm_err = true;
290 int tlog;
291 bool once = true;
292
293 if (_linkcheck)
294 return false;
295
296 _linkcheck = true; /* prevent recursion */
297
298 _ups->set_commlost();
299 Dmsg(200, "link_check comm lost\n");
300
301 /* Don't warn until we try to get it at least 2 times and fail */
302 for (tlog = LINK_RETRY_INTERVAL * 2; comm_err; tlog -= (LINK_RETRY_INTERVAL)) {
303 if (tlog <= 0) {
304 tlog = 10 * 60; /* notify every 10 minutes */
305 log_event(_ups, event_msg[CMDCOMMFAILURE].level,
306 event_msg[CMDCOMMFAILURE].msg);
307 if (once) { /* execute script once */
308 execute_command(_ups, ups_event[CMDCOMMFAILURE]);
309 once = false;
310 }
311 }
312
313 /* Retry every LINK_RETRY_INTERVAL seconds */
314 sleep(LINK_RETRY_INTERVAL);
315 if (_fd >= 0) {
316 close(_fd);
317 _fd = -1;
318 reinitialize_private_structure();
319 }
320
321 if (open_usb_device() && get_capabilities() &&
322 read_static_data()) {
323 comm_err = false;
324 } else {
325 continue;
326 }
327 }
328
329 if (!comm_err) {
330 generate_event(_ups, CMDCOMMOK);
331 _ups->clear_commlost();
332 Dmsg(200, "link check comm OK.\n");
333 }
334
335 _linkcheck = false;
336 return true;
337 }
338
populate_uval(USB_INFO * info,USB_VALUE * uval)339 bool LinuxUsbUpsDriver::populate_uval(USB_INFO *info, USB_VALUE *uval)
340 {
341 struct hiddev_string_descriptor sdesc;
342 USB_VALUE val;
343 int exponent;
344
345 exponent = info->unit_exponent;
346 if (exponent > 7)
347 exponent = exponent - 16;
348
349 if (info->data_type == T_INDEX) { /* get string */
350 if (info->uref.value == 0)
351 return false;
352
353 sdesc.index = info->uref.value;
354 if (ioctl(_fd, HIDIOCGSTRING, &sdesc) < 0)
355 return false;
356
357 strlcpy(val.sValue, sdesc.value, sizeof(val.sValue));
358 val.value_type = V_STRING;
359
360 Dmsg(200, "Def val=%d exp=%d sVal=\"%s\" ci=%d\n", info->uref.value,
361 exponent, val.sValue, info->ci);
362 } else if (info->data_type == T_UNITS) {
363 val.value_type = V_DOUBLE;
364 switch (info->unit) {
365 case 0x00F0D121:
366 val.UnitName = "Volts";
367 exponent -= 7; /* remove bias */
368 break;
369 case 0x00100001:
370 exponent += 2; /* remove bias */
371 val.UnitName = "Amps";
372 break;
373 case 0xF001:
374 val.UnitName = "Hertz";
375 break;
376 case 0x1001:
377 val.UnitName = "Seconds";
378 break;
379 case 0xD121:
380 exponent -= 7; /* remove bias */
381 val.UnitName = "Watts";
382 break;
383 case 0x010001:
384 val.UnitName = "Degrees K";
385 break;
386 case 0x0101001:
387 val.UnitName = "AmpSecs";
388 break;
389 default:
390 val.UnitName = "";
391 val.value_type = V_INTEGER;
392 val.iValue = info->uref.value;
393 break;
394 }
395
396 if (exponent == 0)
397 val.dValue = info->uref.value;
398 else
399 val.dValue = ((double)info->uref.value) * pow_ten(exponent);
400
401 // Store a (possibly truncated) copy of the floating point value in the
402 // integer field as well.
403 val.iValue = (int)val.dValue;
404
405 Dmsg(200, "Def val=%d exp=%d dVal=%f ci=%d\n", info->uref.value,
406 exponent, val.dValue, info->ci);
407 } else { /* should be T_NONE */
408 val.UnitName = "";
409 val.value_type = V_INTEGER;
410 val.iValue = info->uref.value;
411
412 if (exponent == 0)
413 val.dValue = info->uref.value;
414 else
415 val.dValue = ((double)info->uref.value) * pow_ten(exponent);
416
417 Dmsg(200, "Def val=%d exp=%d dVal=%f ci=%d\n", info->uref.value,
418 exponent, val.dValue, info->ci);
419 }
420
421 memcpy(uval, &val, sizeof(*uval));
422 return true;
423 }
424
425 /*
426 * Get a field value
427 */
pusb_get_value(int ci,USB_VALUE * uval)428 bool LinuxUsbUpsDriver::pusb_get_value(int ci, USB_VALUE *uval)
429 {
430 struct hiddev_report_info rinfo;
431 USB_INFO *info;
432
433 if (!_ups->UPS_Cap[ci] || !_info[ci])
434 return false; /* UPS does not have capability */
435
436 /* Fetch the new value from the UPS */
437
438 info = _info[ci]; /* point to our info structure */
439 rinfo.report_type = info->uref.report_type;
440 rinfo.report_id = info->uref.report_id;
441 if (ioctl(_fd, HIDIOCGREPORT, &rinfo) < 0) /* update Report */
442 return false;
443
444 if (ioctl(_fd, HIDIOCGUSAGE, &info->uref) < 0) /* update UPS value */
445 return false;
446
447 /* Process the updated value */
448 return populate_uval(info, uval);
449 }
450
451 /*
452 * Find the USB_INFO structure used for tracking a given usage. Searching
453 * by usage_code alone is insufficient since the same usage may appear in
454 * multiple reports or even multiple times in the same report.
455 */
find_info_by_uref(struct hiddev_usage_ref * uref)456 LinuxUsbUpsDriver::USB_INFO *LinuxUsbUpsDriver::find_info_by_uref(
457 struct hiddev_usage_ref *uref)
458 {
459 int i;
460
461 for (i=0; i<CI_MAXCI; i++) {
462 if (_ups->UPS_Cap[i] && _info[i] &&
463 _info[i]->uref.report_id == uref->report_id &&
464 _info[i]->uref.field_index == uref->field_index &&
465 _info[i]->uref.usage_index == uref->usage_index &&
466 _info[i]->uref.usage_code == uref->usage_code) {
467 return _info[i];
468 }
469 }
470
471 return NULL;
472 }
473
474 /*
475 * Same as find_info_by_uref() but only checks the usage code. This is
476 * not entirely reliable, but it's the best we have on linux-2.4.
477 */
find_info_by_ucode(unsigned int ucode)478 LinuxUsbUpsDriver::USB_INFO *LinuxUsbUpsDriver::find_info_by_ucode(
479 unsigned int ucode)
480 {
481 int i;
482
483 for (i=0; i<CI_MAXCI; i++) {
484 if (_ups->UPS_Cap[i] && _info[i] &&
485 _info[i]->uref.usage_code == ucode) {
486 return _info[i];
487 }
488 }
489
490 return NULL;
491 }
492
493 /*
494 * Read UPS events. I.e. state changes.
495 */
check_state()496 bool LinuxUsbUpsDriver::check_state()
497 {
498 int retval;
499 bool done = false;
500 struct hiddev_usage_ref uref;
501 struct hiddev_event hev;
502 USB_INFO* info;
503 USB_VALUE uval;
504
505 struct timeval tv;
506 tv.tv_sec = _ups->wait_time;
507 tv.tv_usec = 0;
508
509 while (!done) {
510 fd_set rfds;
511
512 FD_ZERO(&rfds);
513 FD_SET(_fd, &rfds);
514
515 retval = select(_fd + 1, &rfds, NULL, NULL, &tv);
516
517 /*
518 * Note: We are relying on select() adjusting tv to the amount
519 * of time NOT waited. This is a Linux-specific feature but
520 * shouldn't be a problem since the driver is only intended for
521 * for Linux.
522 */
523
524 switch (retval) {
525 case 0: /* No chars available in TIMER seconds. */
526 return false;
527 case -1:
528 if (errno == EINTR || errno == EAGAIN) /* assume SIGCHLD */
529 continue;
530
531 Dmsg(200, "select error: ERR=%s\n", strerror(errno));
532 usb_link_check(); /* link is down, wait */
533 return false;
534 default:
535 break;
536 }
537
538 if (!_compat24) {
539 /* This is >= linux-2.6, so we can read a uref directly */
540 do {
541 retval = read(_fd, &uref, sizeof(uref));
542 } while (retval == -1 && (errno == EAGAIN || errno == EINTR));
543
544 if (retval < 0) { /* error */
545 Dmsg(200, "read error: ERR=%s\n", strerror(errno));
546 usb_link_check(); /* notify that link is down, wait */
547 return false;
548 }
549
550 if (retval == 0 || retval < (int)sizeof(uref))
551 return false;
552
553 /* Ignore events we don't recognize */
554 if ((info = find_info_by_uref(&uref)) == NULL) {
555 Dmsg(200, "Unrecognized usage (rpt=%d, usg=0x%08x, val=%d)\n",
556 uref.report_id, uref.usage_code, uref.value);
557 continue;
558 }
559 } else {
560 /*
561 * We're in linux-2.4 compatibility mode, so we read a
562 * hiddev_event and use it to construct a uref.
563 */
564 do {
565 retval = read(_fd, &hev, sizeof(hev));
566 } while (retval == -1 && (errno == EAGAIN || errno == EINTR));
567
568 if (retval < 0) { /* error */
569 Dmsg(200, "read error: ERR=%s\n", strerror(errno));
570 usb_link_check(); /* notify that link is down, wait */
571 return false;
572 }
573
574 if (retval == 0 || retval < (int)sizeof(hev))
575 return false;
576
577 /* Ignore events we don't recognize */
578 if ((info = find_info_by_ucode(hev.hid)) == NULL) {
579 Dmsg(200, "Unrecognized usage (usg=0x%08x, val=%d)\n",
580 hev.hid, hev.value);
581 continue;
582 }
583
584 /*
585 * ADK FIXME: The info-> struct we have now is not guaranteed to
586 * actually be the right one, because linux-2.4 does not give us
587 * enough data in the event to make a positive match. We may need
588 * to filter out ambiguous usages here or manually fetch each CI
589 * that matches the given usage.
590 */
591
592 /* Copy the stored uref, replacing its value */
593 uref = info->uref;
594 uref.value = hev.value;
595 }
596
597 write_lock(_ups);
598
599 /* Ignore events whose value is unchanged */
600 if (info->uref.value == uref.value) {
601 Dmsg(200, "Ignoring unchanged value (rpt=%d, usg=0x%08x, val=%d)\n",
602 uref.report_id, uref.usage_code, uref.value);
603 write_unlock(_ups);
604 continue;
605 }
606
607 /* Update tracked value */
608 Dmsg(200, "Processing changed value (rpt=%d, usg=0x%08x, val=%d)\n",
609 uref.report_id, uref.usage_code, uref.value);
610 info->uref.value = uref.value;
611
612 /* Populate a uval and report it to the upper layer */
613 populate_uval(info, &uval);
614 if (usb_report_event(info->ci, &uval)) {
615 /*
616 * The upper layer considers this an important event,
617 * so we will return immediately.
618 */
619 done = true;
620 }
621
622 write_unlock(_ups);
623 }
624
625 return true;
626 }
627
628 /*
629 * Open usb port
630 * This is called once by the core code and is the first routine
631 * called.
632 */
Open()633 bool LinuxUsbUpsDriver::Open()
634 {
635 write_lock(_ups);
636
637 if (_orig_device[0] == 0)
638 strlcpy(_orig_device, _ups->device, sizeof(_orig_device));
639
640 bool rc = open_usb_device();
641
642 _ups->clear_slave();
643 write_unlock(_ups);
644 return rc;
645 }
646
647 /*
648 * This is the last routine called from apcctrl core code
649 */
Close()650 bool LinuxUsbUpsDriver::Close()
651 {
652 return 1;
653 }
654
655 /*
656 * Setup capabilities structure for UPS
657 */
pusb_ups_get_capabilities()658 bool LinuxUsbUpsDriver::pusb_ups_get_capabilities()
659 {
660 int rtype[2] = { HID_REPORT_TYPE_FEATURE, HID_REPORT_TYPE_INPUT };
661 struct hiddev_report_info rinfo;
662 struct hiddev_field_info finfo;
663 struct hiddev_usage_ref uref;
664 unsigned int i, j, k, n;
665
666 if (ioctl(_fd, HIDIOCINITREPORT, 0) < 0)
667 Error_abort("Cannot init USB HID report. ERR=%s\n", strerror(errno));
668
669 write_lock(_ups);
670
671 /*
672 * Walk through all available reports and determine
673 * what information we can use.
674 */
675 for (n = 0; n < sizeof(rtype)/sizeof(*rtype); n++) {
676 rinfo.report_type = rtype[n];
677 rinfo.report_id = HID_REPORT_ID_FIRST;
678
679 while (ioctl(_fd, HIDIOCGREPORTINFO, &rinfo) >= 0) {
680 for (i = 0; i < rinfo.num_fields; i++) {
681 memset(&finfo, 0, sizeof(finfo));
682 finfo.report_type = rinfo.report_type;
683 finfo.report_id = rinfo.report_id;
684 finfo.field_index = i;
685
686 if (ioctl(_fd, HIDIOCGFIELDINFO, &finfo) < 0)
687 continue;
688
689 memset(&uref, 0, sizeof(uref));
690 for (j = 0; j < finfo.maxusage; j++) {
691 uref.report_type = finfo.report_type;
692 uref.report_id = finfo.report_id;
693 uref.field_index = i;
694 uref.usage_index = j;
695
696 if (ioctl(_fd, HIDIOCGUCODE, &uref) < 0)
697 continue;
698
699 ioctl(_fd, HIDIOCGUSAGE, &uref);
700
701 /*
702 * We've got a UPS usage entry, now walk down our
703 * know_info table and see if we have a match. If so,
704 * allocate a new entry for it.
705 */
706 for (k = 0; _known_info[k].usage_code; k++) {
707 USB_INFO *info;
708 int ci = _known_info[k].ci;
709
710 if (ci != CI_NONE &&
711 uref.usage_code == _known_info[k].usage_code &&
712 (_known_info[k].physical == P_ANY ||
713 _known_info[k].physical == finfo.physical) &&
714 (_known_info[k].logical == P_ANY ||
715 _known_info[k].logical == finfo.logical)) {
716
717 // If we do not have any data saved for this report yet,
718 // allocate an USB_INFO and populate the read uref.
719 info = _info[ci];
720 if (!info) {
721 _ups->UPS_Cap[ci] = true;
722 info = (USB_INFO *)malloc(sizeof(USB_INFO));
723
724 if (!info) {
725 write_unlock(_ups);
726 Error_abort("Out of memory.\n");
727 }
728
729 _info[ci] = info;
730 memset(info, 0, sizeof(*info));
731 info->ci = ci;
732 info->physical = finfo.physical;
733 info->unit_exponent = finfo.unit_exponent;
734 info->unit = finfo.unit;
735 info->data_type = _known_info[k].data_type;
736 memcpy(&info->uref, &uref, sizeof(uref));
737
738 Dmsg(200, "Got READ ci=%d, usage=0x%x, rpt=%d\n",
739 ci, _known_info[k].usage_code, uref.report_id);
740 }
741
742 // If this is a FEATURE report and we haven't set the
743 // write uref yet, place it in the write uref (possibly in
744 // addition to placing it in the read uref above).
745 if (rinfo.report_type == HID_REPORT_TYPE_FEATURE &&
746 info->wuref.report_id == 0) {
747 memcpy(&info->wuref, &uref, sizeof(uref));
748
749 Dmsg(200, "Got WRITE ci=%d, usage=0x%x, rpt=%d\n",
750 ci, _known_info[k].usage_code, uref.report_id);
751 }
752
753 break;
754 }
755 }
756 }
757 }
758 rinfo.report_id |= HID_REPORT_ID_NEXT;
759 }
760 }
761
762 _ups->UPS_Cap[CI_STATUS] = true; /* we have status flag */
763 write_unlock(_ups);
764 return 1;
765 }
766
767
read_int_from_ups(int ci,int * value)768 int LinuxUsbUpsDriver::read_int_from_ups(int ci, int *value)
769 {
770 USB_VALUE val;
771
772 if (!pusb_get_value(ci, &val))
773 return false;
774
775 *value = val.iValue;
776 return true;
777 }
778
write_int_to_ups(int ci,int value,const char * name)779 int LinuxUsbUpsDriver::write_int_to_ups(int ci, int value, const char *name)
780 {
781 struct hiddev_report_info rinfo;
782 USB_INFO *info;
783 int old_value, new_value;
784
785 // Make sure we have a writable uref for this CI
786 if (_ups->UPS_Cap[ci] && _info[ci] &&
787 _info[ci]->wuref.report_id) {
788 info = _info[ci]; /* point to our info structure */
789 rinfo.report_type = info->uref.report_type;
790 rinfo.report_id = info->uref.report_id;
791
792 /* Get report */
793 if (ioctl(_fd, HIDIOCGREPORT, &rinfo) < 0) {
794 Dmsg(000, "HIDIOCGREPORT for function %s failed. ERR=%s\n",
795 name, strerror(errno));
796 return false;
797 }
798
799 /* Get UPS value */
800 if (ioctl(_fd, HIDIOCGUSAGE, &info->wuref) < 0) {
801 Dmsg(000, "HIDIOCGUSAGE for function %s failed. ERR=%s\n",
802 name, strerror(errno));
803 return false;
804 }
805 old_value = info->uref.value;
806
807 info->wuref.value = value;
808 rinfo.report_type = info->wuref.report_type;
809 rinfo.report_id = info->wuref.report_id;
810 Dmsg(100, "SUSAGE type=%d id=%d index=%d\n", info->wuref.report_type,
811 info->wuref.report_id, info->wuref.field_index);
812
813 /* Update UPS value */
814 if (ioctl(_fd, HIDIOCSUSAGE, &info->wuref) < 0) {
815 Dmsg(000, "HIDIOCSUSAGE for function %s failed. ERR=%s\n",
816 name, strerror(errno));
817 return false;
818 }
819
820 /* Update Report */
821 if (ioctl(_fd, HIDIOCSREPORT, &rinfo) < 0) {
822 Dmsg(000, "HIDIOCSREPORT for function %s failed. ERR=%s\n",
823 name, strerror(errno));
824 return false;
825 }
826
827 /*
828 * This readback of the report is NOT just for debugging. It
829 * has the effect of flushing the above SET_REPORT to the
830 * device, which is important since we need to make sure it
831 * happens before subsequent reports are sent.
832 */
833
834 rinfo.report_type = info->uref.report_type;
835 rinfo.report_id = info->uref.report_id;
836
837 /* Get report */
838 if (ioctl(_fd, HIDIOCGREPORT, &rinfo) < 0) {
839 Dmsg(000, "HIDIOCGREPORT for function %s failed. ERR=%s\n",
840 name, strerror(errno));
841 return false;
842 }
843
844 /* Get UPS value */
845 if (ioctl(_fd, HIDIOCGUSAGE, &info->uref) < 0) {
846 Dmsg(000, "HIDIOCGUSAGE for function %s failed. ERR=%s\n",
847 name, strerror(errno));
848 return false;
849 }
850
851 new_value = info->uref.value;
852 Dmsg(100, "function %s ci=%d value=%d OK.\n", name, ci, value);
853 Dmsg(100, "%s before=%d set=%d after=%d\n", name, old_value, value,
854 new_value);
855
856 return true;
857 }
858
859 Dmsg(000, "function %s ci=%d not available in this UPS.\n", name, ci);
860 return false;
861 }
862