1 /*
2 * hidutils.c
3 *
4 * Utility functions for interfacing with the libusbhid userspace
5 * HID parsing library.
6 */
7
8 /*
9 * Copyright (C) 2004-2005 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
28 #include "hidutils.h"
29 #include <dev/usb/usb.h>
30 #include <dev/usb/usbhid.h>
31
32 #define MAX_SANE_DESCRIPTOR_LEN 4096
33
34 /*
35 * Fetch the report descriptor from the device given an fd for the
36 * device's control endpoint. Descriptor length is written to the
37 * rlen out paramter and a pointer to a malloc'ed buffer containing
38 * the descriptor is returned. Returns NULL on failure.
39 */
hidu_fetch_report_descriptor(int fd,int * rlen)40 unsigned char *hidu_fetch_report_descriptor(int fd, int *rlen)
41 {
42 int rc, i, rdesclen;
43 struct usb_config_desc cdesc;
44 struct usb_full_desc fdesc;
45 struct usb_ctl_request req;
46 int len;
47 unsigned char *ptr;
48
49 /*
50 * In order to fetch the report descriptor we need to first
51 * determine that descriptor's length. Unlike the other std
52 * descriptors, report descriptors are not prefixed with their
53 * length. We must instead look in the HID descriptor. This is
54 * made especially painful because for some reason we cannot
55 * request the HID descriptor directly. The length bytes come
56 * back munged when we do that (bad, FreeBSD, bad!). So instead
57 * we ask for the set of all top level descriptors and dig the
58 * HID descriptor out of there. Then we can *finally* go about
59 * asking for the report descriptor itself.
60 */
61
62 /*
63 * First, get the CONFIG descriptor alone so we can look up
64 * the length of the full descriptor set.
65 */
66 cdesc.ucd_config_index = USB_CURRENT_CONFIG_INDEX;
67 rc = ioctl(fd, USB_GET_CONFIG_DESC, &cdesc);
68 if (rc) {
69 Dmsg(100, "Unable to get USB CONFIG descriptor.\n");
70 return NULL;
71 }
72
73 len = UGETW(cdesc.ucd_desc.wTotalLength);
74 if (!len || len > MAX_SANE_DESCRIPTOR_LEN) {
75 Dmsg(100, "Unreasonable length %d.\n", len);
76 return NULL;
77 }
78
79 /*
80 * Now get the full set of descriptors (does not include
81 * report descriptor).
82 */
83 fdesc.ufd_size = len;
84 fdesc.ufd_data = (u_char*)malloc(len);
85 fdesc.ufd_config_index = USB_CURRENT_CONFIG_INDEX;
86 rc = ioctl(fd, USB_GET_FULL_DESC, &fdesc);
87 if (rc) {
88 Dmsg(100, "Unable to get full descriptors.\n");
89 free(fdesc.ufd_data);
90 return NULL;
91 }
92
93 Dmsg(300, "Full descriptors:\n");
94 hex_dump(300, fdesc.ufd_data, len);
95
96 /* Search for the HID descriptor */
97 for (ptr = fdesc.ufd_data, i = 0; i < len; i += ptr[0], ptr += ptr[0])
98 if (ptr[1] == UDESC_HID)
99 break;
100
101 if (i >= len) {
102 Dmsg(100, "Unable to locate HID descriptor.\n");
103 free(fdesc.ufd_data);
104 return NULL;
105 }
106
107 /* We expect the first additional descriptor type to be report */
108 if (ptr[6] != UDESC_REPORT) {
109 Dmsg(100, "First extra descriptor not report.\n");
110 free(fdesc.ufd_data);
111 return NULL;
112 }
113
114 /* Finally! The report descriptor's length! */
115 rdesclen = ptr[8] << 8 | ptr[7];
116 Dmsg(200, "Report desc len=0x%04x (%d)\n", rdesclen, rdesclen);
117
118 /* That's all we needed from the buffer */
119 free(fdesc.ufd_data);
120
121 if (!rdesclen || rdesclen > MAX_SANE_DESCRIPTOR_LEN) {
122 Dmsg(100, "Unreasonable rdesclen %d.\n", rdesclen);
123 return NULL;
124 }
125
126 /*
127 * Now fetch the report descriptor itself. We use a raw USB request
128 * for this because the report descriptor is a class specific item.
129 */
130 req.ucr_flags = 0;
131 req.ucr_actlen = 0;
132 req.ucr_addr = 0;
133 req.ucr_data = malloc(rdesclen);
134 req.ucr_request.bmRequestType = UT_READ_INTERFACE;
135 req.ucr_request.bRequest = UR_GET_DESCRIPTOR;
136 USETW(req.ucr_request.wValue, UDESC_REPORT << 8);
137 USETW(req.ucr_request.wIndex, 0);
138 USETW(req.ucr_request.wLength, rdesclen);
139 rc = ioctl(fd, USB_DO_REQUEST, &req);
140 if (rc) {
141 Dmsg(100, "Unable to read report descriptor.\n");
142 free(req.ucr_data);
143 return NULL;
144 }
145
146 Dmsg(300, "Report descriptor:\n");
147 hex_dump(300, req.ucr_data, rdesclen);
148
149 *rlen = rdesclen;
150 return (unsigned char *)req.ucr_data;
151 }
152
153 /* Push a value onto the collection stack */
154 #define PUSH_COLLECTION(c, v) \
155 do \
156 { \
157 if (c##_idx<MAX_COLLECTION_NESTING-1) \
158 { \
159 c##_idx++; \
160 c##_stack[c##_idx] = v; \
161 } \
162 } while(0)
163
164 /* Remove a value from the collection stack */
165 #define POP_COLLECTION(c) \
166 do \
167 { \
168 if (c##_idx >= 0) \
169 c##_idx--; \
170 } while(0)
171
172 /* Get the topmost item on the stack */
173 #define TOP_COLLECTION(c) \
174 ((c##_idx == -1) ? -1 : c##_stack[c##_idx])
175
176 /* Collection types */
177 #define HIDCOL_PHYSICAL 0
178 #define HIDCOL_APPLICATION 1
179 #define HIDCOL_LOGICAL 2
180 #define MAX_COLLECTION_NESTING 10
181
182 /* For pretty printing... */
183 #define KIND_TO_CHAR(x) \
184 ((x) == hid_input) ? 'I' : \
185 ((x) == hid_output) ? 'O' : \
186 ((x) == hid_feature) ? 'F' : \
187 ((x) == hid_collection) ? 'C' : \
188 ((x) == hid_endcollection) ? 'E' : '?'
189
190 #define COLLECTION_TO_CHAR(x) \
191 ((x) == 0) ? 'P' : /* Physical */ \
192 ((x) == 1) ? 'A' : /* Application */ \
193 ((x) == 2) ? 'L' : /* Logical */ \
194 ((x) == 3) ? 'R' : /* Report */ \
195 ((x) == 4) ? 'N' : /* Named Array */ \
196 ((x) == 5) ? 'S' : /* Usage Switch */ \
197 ((x) == 6) ? 'M' : '?' /* Usage Modifier */ \
198
199
200 /*
201 * Locate an item matching the given parameters. If found, the
202 * item is copied to the supplied buffer. Returns true on success,
203 * false on failure. Any of usage, app, phys, logical, and kind
204 * may be set to -1 for "don't care".
205 */
hidu_locate_item(report_desc_t rdesc,int usage,int app,int phys,int logical,int kind,hid_item_t * outitem)206 int hidu_locate_item(report_desc_t rdesc, int usage, int app, int phys,
207 int logical, int kind, hid_item_t *outitem)
208 {
209 int rc;
210 hid_data_t cookie;
211 hid_item_t item;
212
213 int phys_stack[MAX_COLLECTION_NESTING];
214 int app_stack[MAX_COLLECTION_NESTING];
215 int logical_stack[MAX_COLLECTION_NESTING];
216 int phys_idx = -1, app_idx = -1, logical_idx = -1;
217
218 cookie = hid_start_parse(rdesc, HID_KIND_ALL, -1);
219 if (!cookie) {
220 Dmsg(100, "Unable to start hid parser\n");
221 return 0;
222 }
223
224 while ((rc = hid_get_item(cookie, &item)) > 0) {
225 if (item.kind == hid_collection) {
226 if (item.collection == HIDCOL_PHYSICAL)
227 PUSH_COLLECTION(phys, item.usage);
228 else if (item.collection == HIDCOL_LOGICAL)
229 PUSH_COLLECTION(logical, item.usage);
230 else if (item.collection == HIDCOL_APPLICATION)
231 PUSH_COLLECTION(app, item.usage);
232 }
233
234 if (usage != -1 && (unsigned int)usage != item.usage)
235 goto next;
236 if (app != -1 && app != TOP_COLLECTION(app))
237 goto next;
238 if (phys != -1 && phys != TOP_COLLECTION(phys))
239 goto next;
240 if (logical != -1 && logical != TOP_COLLECTION(logical))
241 goto next;
242 if (kind != -1 && ((1 << item.kind) & kind) == 0)
243 goto next;
244
245 if (outitem)
246 memcpy(outitem, &item, sizeof(item));
247
248 hid_end_parse(cookie);
249 return 1;
250
251 next:
252 if (item.kind == hid_endcollection) {
253 if (item.collection == HIDCOL_PHYSICAL)
254 POP_COLLECTION(phys);
255 else if (item.collection == HIDCOL_LOGICAL)
256 POP_COLLECTION(logical);
257 else if (item.collection == HIDCOL_APPLICATION)
258 POP_COLLECTION(app);
259 }
260 }
261
262 hid_end_parse(cookie);
263 return 0;
264 }
265
266 /*
267 * Fetch a report from a device given an fd for the device's control
268 * endpoint, the populated item structure describing the report, a
269 * data buffer in which to store the result, and the report length.
270 * Returns actual report length (in bytes) on success and -1 on failure.
271 */
hidu_get_report(int fd,hid_item_t * item,unsigned char * data,int len)272 int hidu_get_report(int fd, hid_item_t *item, unsigned char *data, int len)
273 {
274 int rc;
275 struct usb_ctl_request req;
276
277 Dmsg(200, "get_report: id=0x%02x, kind=%d, length=%d pos=%d\n",
278 item->report_ID, item->kind, len, item->pos);
279
280 req.ucr_flags = USBD_SHORT_XFER_OK;
281 req.ucr_actlen = 0;
282 req.ucr_addr = 0;
283 req.ucr_data = data;
284 req.ucr_request.bmRequestType = UT_READ_CLASS_INTERFACE;
285 req.ucr_request.bRequest = UR_GET_REPORT;
286 USETW(req.ucr_request.wValue, ((item->kind + 1) << 8) | item->report_ID);
287 USETW(req.ucr_request.wIndex, 0);
288 USETW(req.ucr_request.wLength, len);
289
290 Dmsg(200, "get_report: wValue=0x%04x, wLength=%d\n",
291 UGETW(req.ucr_request.wValue), UGETW(req.ucr_request.wLength));
292
293 rc = ioctl(fd, USB_DO_REQUEST, &req);
294 if (rc) {
295 Dmsg(100, "Error getting report: %s\n", strerror(errno));
296 return -1;
297 }
298
299 hex_dump(300, data, req.ucr_actlen);
300
301 return req.ucr_actlen;
302 }
303
304 /*
305 * Send a report to the device given an fd for the device's control
306 * endpoint, the populated item structure, the data to send, and the
307 * report length. Returns true on success, false on failure.
308 */
hidu_set_report(int fd,hid_item_t * item,unsigned char * data,int len)309 int hidu_set_report(int fd, hid_item_t *item, unsigned char *data, int len)
310 {
311 int rc;
312 struct usb_ctl_request req;
313
314 Dmsg(200, "set_report: id=0x%02x, kind=%d, length=%d pos=%d\n",
315 item->report_ID, item->kind, len, item->pos);
316 hex_dump(300, data, len);
317
318 req.ucr_flags = 0;
319 req.ucr_actlen = 0;
320 req.ucr_addr = 0;
321 req.ucr_data = data;
322 req.ucr_request.bmRequestType = UT_WRITE_CLASS_INTERFACE;
323 req.ucr_request.bRequest = UR_SET_REPORT;
324 USETW(req.ucr_request.wValue, ((item->kind + 1) << 8) | item->report_ID);
325 USETW(req.ucr_request.wIndex, 0);
326 USETW(req.ucr_request.wLength, len);
327
328 Dmsg(200, "set_report: wValue=0x%04x, wLength=%d\n",
329 UGETW(req.ucr_request.wValue), UGETW(req.ucr_request.wLength));
330
331 rc = ioctl(fd, USB_DO_REQUEST, &req);
332 if (rc) {
333 Dmsg(100, "Error setting report: (%d) %s\n", errno, strerror(errno));
334 return 0;
335 }
336
337 return 1;
338 }
339
340 /*
341 * Fetch a string descriptor from the device given an fd for the
342 * device's control endpoint and the string index. Returns a pointer
343 * to a static buffer containing the NUL-terminated string or NULL
344 * on failure.
345 */
hidu_get_string(int fd,int index)346 const char *hidu_get_string(int fd, int index)
347 {
348 int rc, i;
349 struct usb_string_desc sd;
350 static char string[128];
351
352 sd.usd_string_index = index;
353 sd.usd_language_id = 0;
354 rc = ioctl(fd, USB_GET_STRING_DESC, &sd);
355 if (rc) {
356 Dmsg(100, "Error fetching string descriptor: %s\n", strerror(errno));
357 return NULL;
358 }
359
360 Dmsg(200, "Got string of length=%d\n", sd.usd_desc.bLength);
361
362 /*
363 * Convert from wide chars to bytes...just assume it's ASCII.
364 * Length is in bytes although structure is arranged as words
365 * and there always seems to be a byte of garbage on the end.
366 * (Not sure if the garbage is an APC bug, a kernel bug, or a
367 * bug in my understanding.)
368 */
369 for (i = 0; i < sd.usd_desc.bLength / 2 - 1 && i < (int)sizeof(string) - 1; i++)
370 string[i] = UGETW(sd.usd_desc.bString[i]);
371
372 string[i] = '\0';
373 return string;
374 }
375