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