1 /*
2  * HidUps.cpp
3  *
4  * Utility functions for interfacing with the libusbhid userspace
5  * HID parsing library.
6  */
7 
8 /*
9  * Copyright (C) 2004-2014 Adam Kropelin
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of version 2 of the GNU General
13  * Public License as published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public
21  * License along with this program; if not, write to the Free
22  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
23  * MA 02110-1335, USA.
24  */
25 
26 #include "apc.h"
27 #include "HidUps.h"
28 #include "usb_common.h"
29 #include "astring.h"
30 
31 #define MAX_SANE_DESCRIPTOR_LEN 4096
32 
HidUps()33 HidUps::HidUps() :
34    _fd(NULL),
35    _rdesc(NULL)
36 {
37 }
38 
~HidUps()39 HidUps::~HidUps()
40 {
41    Close();
42 }
43 
Open(const char * serno)44 bool HidUps::Open(const char *serno)
45 {
46    /* Set libusb debug level */
47    usb_set_debug(debug_level/100);
48 
49    /* Initialize libusb */
50    Dmsg(200, "Initializing libusb\n");
51    usb_init();
52 
53    /* Enumerate usb busses and devices */
54    int i = usb_find_busses();
55    Dmsg(200, "Found %d USB busses\n", i);
56    i = usb_find_devices();
57    Dmsg(200, "Found %d USB devices\n", i);
58 
59    /* Iterate over all devices */
60    struct usb_bus *bus = usb_get_busses();
61    while (bus)
62    {
63       struct usb_device *dev = bus->devices;
64       while (dev)
65       {
66          Dmsg(200, "%s:%s - %04x:%04x\n",
67             bus->dirname, dev->filename,
68             dev->descriptor.idVendor, dev->descriptor.idProduct);
69 
70          if (init_device(dev, serno)) {
71             /* Successfully found and initialized an UPS */
72             return true;
73          }
74 
75          dev = dev->next;
76       }
77 
78       bus = bus->next;
79    }
80 
81    /* Failed to find an UPS */
82    return false;
83 }
84 
Close()85 void HidUps::Close()
86 {
87    if (_rdesc)
88    {
89       hid_dispose_report_desc(_rdesc);
90       _rdesc = NULL;
91    }
92 
93    if (_fd)
94    {
95       usb_release_interface(_fd, 0);
96       usb_reset(_fd);
97       usb_close(_fd);
98       _fd = NULL;
99    }
100 }
101 
init_device(struct usb_device * dev,const char * serno)102 bool HidUps::init_device(
103    struct usb_device *dev,
104    const char *serno)
105 {
106    int rc;
107    unsigned char* rdesc;
108    int rdesclen;
109 
110    /* Check if this is a supported device before we mess with it */
111    if (!MatchVidPid(dev->descriptor.idVendor, dev->descriptor.idProduct)) {
112       Dmsg(100, "Not an APC device.\n");
113       return false;
114    }
115 
116    /* Open the device with libusb */
117    _fd = usb_open(dev);
118    if (!_fd) {
119       Dmsg(100, "Unable to open device.\n");
120       return false;
121    }
122 
123 #ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
124    /*
125     * Attempt to detach the kernel driver so we can drive
126     * this device from userspace. Don't worry if this fails;
127     * that just means the driver was already detached.
128     */
129    usb_detach_kernel_driver_np(_fd, 0);
130 #endif
131 
132    /* Check device serial number, if user specified one */
133    if (serno && serno[0] != '\0')
134    {
135       /* Fetch serial number from device */
136       const char *tmpser;
137       if (dev->descriptor.iSerialNumber == 0 ||
138           (tmpser = GetString(dev->descriptor.iSerialNumber)) == NULL)
139       {
140          usb_close(_fd);
141          _fd = NULL;
142          Dmsg(100, "Device does not report serial number.\n");
143          return false;
144       }
145 
146       /* Remove leading/trailing whitespace */
147       astring serial(tmpser);
148       serial.trim();
149 
150       /* Check against user specification, ignoring case */
151       Dmsg(100, "device='%s', user='%s'\n", serial.str(), serno);
152       if (strcasecmp(serial, serno))
153       {
154          usb_close(_fd);
155          _fd = NULL;
156          return false;
157       }
158    }
159 
160    /* Choose config #1 */
161    rc = usb_set_configuration(_fd, 1);
162    if (rc) {
163       usb_close(_fd);
164       _fd = NULL;
165       Dmsg(100, "Unable to set configuration (%d) %s.\n", rc, usb_strerror());
166       return false;
167    }
168 
169    /* Claim the interface */
170    rc = usb_claim_interface(_fd, 0);
171    if (rc) {
172       usb_close(_fd);
173       _fd = NULL;
174       Dmsg(100, "Unable to claim interface (%d) %s.\n", rc, usb_strerror());
175       return false;
176    }
177 
178    /* Fetch the report descritor */
179    rdesc = FetchReportDescr(&rdesclen);
180    if (!rdesc) {
181       Dmsg(100, "Unable to fetch report descriptor.\n");
182       goto error_out;
183    }
184 
185    /* Initialize hid parser with this descriptor */
186    _rdesc = hid_use_report_desc(rdesc, rdesclen);
187    free(rdesc);
188    if (!_rdesc) {
189       Dmsg(100, "Unable to init parser with report descriptor.\n");
190       goto error_out;
191    }
192 
193    /* Does this device have an UPS application collection? */
194    if (!LocateItem(
195          UPS_USAGE,             /* Match usage code */
196          -1,                    /* Don't care about application */
197          -1,                    /* Don't care about physical usage */
198          -1,                    /* Don't care about logical */
199          HID_KIND_COLLECTION,   /* Match collection type */
200          NULL)) {
201       hid_dispose_report_desc(_rdesc);
202       Dmsg(100, "Device does not have an UPS application collection.\n");
203       goto error_out;
204    }
205 
206    return true;
207 
208 error_out:
209    usb_release_interface(_fd, 0);
210    usb_close(_fd);
211    _fd = NULL;
212    return false;
213 }
214 
215 /*
216  * Fetch a string descriptor from the device given an _fd for the
217  * device's control endpoint and the string index. Returns a pointer
218  * to a static buffer containing the NUL-terminated string or NULL
219  * on failure.
220  */
GetString(int index)221 const char *HidUps::GetString(int index)
222 {
223    int rc;
224    static char string[128];
225 
226    rc = usb_get_string_simple(_fd, index, string, sizeof(string));
227    if (rc <= 0) {
228       Dmsg(100, "Error fetching string descriptor: (%d) %s\n", rc, strerror(-rc));
229       return NULL;
230    }
231 
232    Dmsg(200, "Got string of length=%d\n", rc);
233    return string;
234 }
235 
236 /* Fetch a descriptor from an interface (as opposed to from the device) */
GetIntfDescr(unsigned char type,unsigned char index,void * buf,int size)237 int HidUps::GetIntfDescr(
238    unsigned char type, unsigned char index, void *buf, int size)
239 {
240    memset(buf, 0, size);
241 
242    return usb_control_msg(_fd, USB_ENDPOINT_IN | USB_RECIP_INTERFACE,
243                          USB_REQ_GET_DESCRIPTOR,
244                          (type << 8) + index, 0, (char*)buf, size, 1000);
245 }
246 
247 /*
248  * Fetch the report descriptor from the device given an _fd for the
249  * device's control endpoint. Descriptor length is written to the
250  * rlen out paramter and a pointer to a malloc'ed buffer containing
251  * the descriptor is returned. Returns NULL on failure.
252  */
FetchReportDescr(int * rlen)253 unsigned char *HidUps::FetchReportDescr(int *rlen)
254 {
255    unsigned char *ptr;
256    int rdesclen;
257 
258    ptr = (unsigned char*)malloc(MAX_SANE_DESCRIPTOR_LEN);
259    rdesclen = GetIntfDescr(USB_DT_REPORT, 0, ptr, MAX_SANE_DESCRIPTOR_LEN);
260    if (rdesclen <= 0) {
261       Dmsg(100, "Unable to get REPORT descriptor (%d).\n", rdesclen);
262       free(ptr);
263       return NULL;
264    }
265 
266    Dmsg(300, "Report descriptor:\n");
267    hex_dump(300, ptr, rdesclen);
268 
269    *rlen = rdesclen;
270    return ptr;
271 }
272 
273 /* Push a value onto the collection stack */
274 #define PUSH_COLLECTION(c, v)             \
275 do                                        \
276 {                                         \
277     if (c##_idx<MAX_COLLECTION_NESTING-1) \
278     {                                     \
279         c##_idx++;                        \
280         c##_stack[c##_idx] = v;           \
281     }                                     \
282 } while(0)
283 
284 /* Remove a value from the collection stack */
285 #define POP_COLLECTION(c) \
286 do                        \
287 {                         \
288     if (c##_idx >= 0)     \
289         c##_idx--;        \
290 } while(0)
291 
292 /* Get the topmost item on the stack */
293 #define TOP_COLLECTION(c) \
294     ((c##_idx == -1) ? -1 : c##_stack[c##_idx])
295 
296 /* Collection types */
297 #define HIDCOL_PHYSICAL     0
298 #define HIDCOL_APPLICATION  1
299 #define HIDCOL_LOGICAL      2
300 #define MAX_COLLECTION_NESTING 10
301 
302 /* For pretty printing... */
303 #define KIND_TO_CHAR(x)                         \
304         ((x) == hid_input) ? 'I' :              \
305         ((x) == hid_output) ? 'O' :             \
306         ((x) == hid_feature) ? 'F' :            \
307         ((x) == hid_collection) ? 'C' :         \
308         ((x) == hid_endcollection) ? 'E' : '?'
309 
310 #define COLLECTION_TO_CHAR(x)   \
311         ((x) == 0) ? 'P' :      /* Physical */       \
312         ((x) == 1) ? 'A' :      /* Application */    \
313         ((x) == 2) ? 'L' :      /* Logical */        \
314         ((x) == 3) ? 'R' :      /* Report */         \
315         ((x) == 4) ? 'N' :      /* Named Array */    \
316         ((x) == 5) ? 'S' :      /* Usage Switch */   \
317         ((x) == 6) ? 'M' : '?'  /* Usage Modifier */
318 
319 
320 /*
321  * Locate an item matching the given parameters. If found, the
322  * item is copied to the supplied buffer. Returns true on success,
323  * false on failure. Any of usage, app, phys, logical, and kind
324  * may be set to -1 for "don't care".
325  */
LocateItem(int usage,int app,int phys,int logical,int kind,hid_item_t * outitem)326 int HidUps::LocateItem(int usage, int app, int phys,
327    int logical, int kind, hid_item_t *outitem)
328 {
329    int rc;
330    hid_data_t cookie;
331    hid_item_t item;
332 
333    int phys_stack[MAX_COLLECTION_NESTING];
334    int app_stack[MAX_COLLECTION_NESTING];
335    int logical_stack[MAX_COLLECTION_NESTING];
336    int phys_idx = -1, app_idx = -1, logical_idx = -1;
337 
338    cookie = hid_start_parse(_rdesc, HID_KIND_ALL, -1);
339    if (!cookie) {
340       Dmsg(100, "Unable to start hid parser\n");
341       return 0;
342    }
343 
344    while ((rc = hid_get_item(cookie, &item)) > 0) {
345       if (item.kind == hid_collection) {
346          if (item.collection == HIDCOL_PHYSICAL)
347             PUSH_COLLECTION(phys, item.usage);
348          else if (item.collection == HIDCOL_LOGICAL)
349             PUSH_COLLECTION(logical, item.usage);
350          else if (item.collection == HIDCOL_APPLICATION)
351             PUSH_COLLECTION(app, item.usage);
352       }
353 
354       if (usage != -1 && (unsigned int)usage != item.usage)
355          goto next;
356       if (app != -1 && app != TOP_COLLECTION(app))
357          goto next;
358       if (phys != -1 && phys != TOP_COLLECTION(phys))
359          goto next;
360       if (logical != -1 && logical != TOP_COLLECTION(logical))
361          goto next;
362       if (kind != -1 && ((1 << item.kind) & kind) == 0)
363          goto next;
364 
365       if (outitem)
366          memcpy(outitem, &item, sizeof(item));
367 
368       hid_end_parse(cookie);
369       return 1;
370 
371     next:
372       if (item.kind == hid_endcollection) {
373          if (item.collection == HIDCOL_PHYSICAL)
374             POP_COLLECTION(phys);
375          else if (item.collection == HIDCOL_LOGICAL)
376             POP_COLLECTION(logical);
377          else if (item.collection == HIDCOL_APPLICATION)
378             POP_COLLECTION(app);
379       }
380    }
381 
382    hid_end_parse(cookie);
383    return 0;
384 }
385 
386 #define USB_REQ_GET_REPORT 0x01
387 #define USB_REQ_SET_REPORT 0x09
388 
389 /*
390  * Fetch a report from a device given an _fd for the device's control
391  * endpoint, the populated item structure describing the report, a
392  * data buffer in which to store the result, and the report length.
393  * Returns actual report length (in bytes) on success and -1 on failure.
394  */
GetReport(hid_item_t * item,unsigned char * data,int len)395 int HidUps::GetReport(hid_item_t *item, unsigned char *data, int len)
396 {
397    int actlen;
398 
399    Dmsg(200, "get_report: id=0x%02x, kind=%d, length=%d pos=%d\n",
400       item->report_ID, item->kind, len, item->pos);
401 
402    actlen = usb_control_msg(_fd,
403       USB_ENDPOINT_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
404       USB_REQ_GET_REPORT, ((item->kind + 1) << 8) | item->report_ID,
405       0, (char*)data, len, 1000);
406 
407    if (actlen <= 0) {
408       Dmsg(100, "Error getting report: (%d) %s\n", actlen, strerror(-actlen));
409       return -1;
410    }
411 
412    hex_dump(300, data, actlen);
413 
414    return actlen;
415 }
416 
417 /*
418  * Send a report to the device given an _fd for the device's control
419  * endpoint, the populated item structure, the data to send, and the
420  * report length. Returns true on success, false on failure.
421  */
SetReport(hid_item_t * item,unsigned char * data,int len)422 int HidUps::SetReport(hid_item_t *item, unsigned char *data, int len)
423 {
424    int actlen;
425 
426    Dmsg(200, "set_report: id=0x%02x, kind=%d, length=%d pos=%d\n",
427       item->report_ID, item->kind, len, item->pos);
428    hex_dump(300, data, len);
429 
430    actlen = usb_control_msg(_fd,
431       USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
432       USB_REQ_SET_REPORT, ((item->kind + 1) << 8) | item->report_ID,
433       0, (char*)data, len, 1000);
434    if (actlen != len) {
435       Dmsg(100, "Error setting report: (%d) %s\n", actlen, strerror(-actlen));
436       return 0;
437    }
438 
439    return 1;
440 }
441