hid.c revision 1.1.2.2 1 /* $NetBSD: hid.c,v 1.1.2.2 2018/11/26 01:52:31 pgoyette Exp $ */
2 /* $FreeBSD: src/sys/dev/usb/hid.c,v 1.11 1999/11/17 22:33:39 n_hibma Exp $ */
3
4 /*
5 * Copyright (c) 1998 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Lennart Augustsson (lennart (at) augustsson.net) at
10 * Carlstedt Research & Technology.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: hid.c,v 1.1.2.2 2018/11/26 01:52:31 pgoyette Exp $");
36
37 #ifdef _KERNEL_OPT
38 #include "opt_usb.h"
39 #endif
40
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/kernel.h>
44 #include <sys/kmem.h>
45
46 #include <dev/usb/usb.h>
47 #include <dev/usb/usbhid.h>
48
49 #include <dev/hid/hid.h>
50
51 #ifdef UHIDEV_DEBUG
52 #define DPRINTF(x) if (uhidevdebug) printf x
53 #define DPRINTFN(n,x) if (uhidevdebug>(n)) printf x
54 extern int uhidevdebug;
55 #else
56 #define DPRINTF(x)
57 #define DPRINTFN(n,x)
58 #endif
59
60 Static void hid_clear_local(struct hid_item *);
61
62 #define MAXUSAGE 256
63 struct hid_data {
64 const u_char *start;
65 const u_char *end;
66 const u_char *p;
67 struct hid_item cur;
68 int32_t usages[MAXUSAGE];
69 int nu;
70 int minset;
71 int multi;
72 int multimax;
73 enum hid_kind kind;
74 };
75
76 Static void
77 hid_clear_local(struct hid_item *c)
78 {
79
80 DPRINTFN(5,("hid_clear_local\n"));
81 c->usage = 0;
82 c->usage_minimum = 0;
83 c->usage_maximum = 0;
84 c->designator_index = 0;
85 c->designator_minimum = 0;
86 c->designator_maximum = 0;
87 c->string_index = 0;
88 c->string_minimum = 0;
89 c->string_maximum = 0;
90 c->set_delimiter = 0;
91 }
92
93 struct hid_data *
94 hid_start_parse(const void *d, int len, enum hid_kind kind)
95 {
96 struct hid_data *s;
97
98 s = kmem_zalloc(sizeof(*s), KM_SLEEP);
99 s->start = s->p = d;
100 s->end = (const char *)d + len;
101 s->kind = kind;
102 return s;
103 }
104
105 void
106 hid_end_parse(struct hid_data *s)
107 {
108
109 while (s->cur.next != NULL) {
110 struct hid_item *hi = s->cur.next->next;
111 kmem_free(s->cur.next, sizeof(*s->cur.next));
112 s->cur.next = hi;
113 }
114 kmem_free(s, sizeof(*s));
115 }
116
117 int
118 hid_get_item(struct hid_data *s, struct hid_item *h)
119 {
120 struct hid_item *c = &s->cur;
121 unsigned int bTag, bType, bSize;
122 uint32_t oldpos;
123 const u_char *data;
124 int32_t dval;
125 uint32_t uval;
126 const u_char *p;
127 struct hid_item *hi;
128 int i;
129 enum hid_kind retkind;
130
131 top:
132 DPRINTFN(5,("hid_get_item: multi=%d multimax=%d\n",
133 s->multi, s->multimax));
134 if (s->multimax != 0) {
135 if (s->multi < s->multimax) {
136 c->usage = s->usages[uimin(s->multi, s->nu-1)];
137 s->multi++;
138 *h = *c;
139 c->loc.pos += c->loc.size;
140 h->next = NULL;
141 DPRINTFN(5,("return multi\n"));
142 return 1;
143 } else {
144 c->loc.count = s->multimax;
145 s->multimax = 0;
146 s->nu = 0;
147 hid_clear_local(c);
148 }
149 }
150 for (;;) {
151 p = s->p;
152 if (p >= s->end)
153 return 0;
154
155 bSize = *p++;
156 if (bSize == 0xfe) {
157 /* long item */
158 bSize = *p++;
159 bSize |= *p++ << 8;
160 bTag = *p++;
161 data = p;
162 p += bSize;
163 bType = 0xff; /* XXX what should it be */
164 } else {
165 /* short item */
166 bTag = bSize >> 4;
167 bType = (bSize >> 2) & 3;
168 bSize &= 3;
169 if (bSize == 3) bSize = 4;
170 data = p;
171 p += bSize;
172 }
173 s->p = p;
174 switch(bSize) {
175 case 0:
176 dval = 0;
177 uval = dval;
178 break;
179 case 1:
180 dval = *data++;
181 uval = dval;
182 dval = (int8_t)dval;
183 break;
184 case 2:
185 dval = *data++;
186 dval |= *data++ << 8;
187 uval = dval;
188 dval = (int16_t)dval;
189 break;
190 case 4:
191 dval = *data++;
192 dval |= *data++ << 8;
193 dval |= *data++ << 16;
194 dval |= *data++ << 24;
195 uval = dval;
196 dval = (int32_t)dval;
197 break;
198 default:
199 aprint_normal("BAD LENGTH %d\n", bSize);
200 continue;
201 }
202
203 DPRINTFN(5,("hid_get_item: bType=%d bTag=%d dval=%d uval=%u\n",
204 bType, bTag, dval, uval));
205 switch (bType) {
206 case 0: /* Main */
207 switch (bTag) {
208 case 8: /* Input */
209 retkind = hid_input;
210 ret:
211 if (s->kind != retkind) {
212 s->minset = 0;
213 s->nu = 0;
214 hid_clear_local(c);
215 continue;
216 }
217 c->kind = retkind;
218 c->flags = uval;
219 if (c->flags & HIO_VARIABLE) {
220 s->multimax = c->loc.count;
221 s->multi = 0;
222 c->loc.count = 1;
223 if (s->minset) {
224 for (i = c->usage_minimum;
225 i <= c->usage_maximum;
226 i++) {
227 s->usages[s->nu] = i;
228 if (s->nu < MAXUSAGE-1)
229 s->nu++;
230 }
231 s->minset = 0;
232 }
233 goto top;
234 } else {
235 if (s->minset)
236 c->usage = c->usage_minimum;
237 *h = *c;
238 h->next = NULL;
239 c->loc.pos +=
240 c->loc.size * c->loc.count;
241 s->minset = 0;
242 s->nu = 0;
243 hid_clear_local(c);
244 return 1;
245 }
246 case 9: /* Output */
247 retkind = hid_output;
248 goto ret;
249 case 10: /* Collection */
250 c->kind = hid_collection;
251 c->collection = uval;
252 c->collevel++;
253 *h = *c;
254 hid_clear_local(c);
255 s->nu = 0;
256 return 1;
257 case 11: /* Feature */
258 retkind = hid_feature;
259 goto ret;
260 case 12: /* End collection */
261 c->kind = hid_endcollection;
262 c->collevel--;
263 *h = *c;
264 s->nu = 0;
265 return 1;
266 default:
267 aprint_normal("Main bTag=%d\n", bTag);
268 break;
269 }
270 break;
271 case 1: /* Global */
272 switch (bTag) {
273 case 0:
274 c->_usage_page = uval << 16;
275 break;
276 case 1:
277 c->logical_minimum = dval;
278 break;
279 case 2:
280 c->logical_maximum = dval;
281 break;
282 case 3:
283 c->physical_minimum = dval;
284 break;
285 case 4:
286 c->physical_maximum = dval;
287 break;
288 case 5:
289 c->unit_exponent = uval;
290 break;
291 case 6:
292 c->unit = uval;
293 break;
294 case 7:
295 c->loc.size = uval;
296 break;
297 case 8:
298 c->report_ID = uval;
299 c->loc.pos = 0;
300 break;
301 case 9:
302 c->loc.count = uval;
303 break;
304 case 10: /* Push */
305 hi = kmem_alloc(sizeof(*hi), KM_SLEEP);
306 *hi = *c;
307 c->next = hi;
308 break;
309 case 11: /* Pop */
310 hi = c->next;
311 if (hi == NULL)
312 break;
313 oldpos = c->loc.pos;
314 *c = *hi;
315 c->loc.pos = oldpos;
316 kmem_free(hi, sizeof(*hi));
317 break;
318 default:
319 aprint_normal("Global bTag=%d\n", bTag);
320 break;
321 }
322 break;
323 case 2: /* Local */
324 switch (bTag) {
325 case 0:
326 if (bSize < 4)
327 uval = c->_usage_page | uval;
328 c->usage = uval;
329 if (s->nu < MAXUSAGE)
330 s->usages[s->nu++] = uval;
331 /* else XXX */
332 break;
333 case 1:
334 s->minset = 1;
335 if (bSize < 4)
336 uval = c->_usage_page | uval;
337 c->usage_minimum = uval;
338 break;
339 case 2:
340 if (bSize < 4)
341 uval = c->_usage_page | uval;
342 c->usage_maximum = uval;
343 break;
344 case 3:
345 c->designator_index = uval;
346 break;
347 case 4:
348 c->designator_minimum = uval;
349 break;
350 case 5:
351 c->designator_maximum = uval;
352 break;
353 case 7:
354 c->string_index = uval;
355 break;
356 case 8:
357 c->string_minimum = uval;
358 break;
359 case 9:
360 c->string_maximum = uval;
361 break;
362 case 10:
363 c->set_delimiter = uval;
364 break;
365 default:
366 aprint_normal("Local bTag=%d\n", bTag);
367 break;
368 }
369 break;
370 default:
371 aprint_normal("default bType=%d\n", bType);
372 break;
373 }
374 }
375 }
376
377 int
378 hid_report_size(const void *buf, int len, enum hid_kind k, uint8_t id)
379 {
380 struct hid_data *d;
381 struct hid_item h;
382 int lo, hi;
383
384 h.report_ID = 0;
385 lo = hi = -1;
386 DPRINTFN(2,("hid_report_size: kind=%d id=%d\n", k, id));
387 for (d = hid_start_parse(buf, len, k); hid_get_item(d, &h); ) {
388 DPRINTFN(2,("hid_report_size: item kind=%d id=%d pos=%d "
389 "size=%d count=%d\n",
390 h.kind, h.report_ID, h.loc.pos, h.loc.size,
391 h.loc.count));
392 if (h.report_ID == id && h.kind == k) {
393 if (lo < 0) {
394 lo = h.loc.pos;
395 #ifdef DIAGNOSTIC
396 if (lo != 0) {
397 aprint_normal("hid_report_size:"
398 " lo != 0\n");
399 }
400 #endif
401 }
402 hi = h.loc.pos + h.loc.size * h.loc.count;
403 DPRINTFN(2,("hid_report_size: lo=%d hi=%d\n", lo, hi));
404 }
405 }
406 hid_end_parse(d);
407 return (hi - lo + 7) / 8;
408 }
409
410 int
411 hid_locate(const void *desc, int size, uint32_t u, uint8_t id, enum hid_kind k,
412 struct hid_location *loc, uint32_t *flags)
413 {
414 struct hid_data *d;
415 struct hid_item h;
416
417 h.report_ID = 0;
418 DPRINTFN(5,("hid_locate: enter usage=0x%x kind=%d id=%d\n", u, k, id));
419 for (d = hid_start_parse(desc, size, k); hid_get_item(d, &h); ) {
420 DPRINTFN(5,("hid_locate: usage=0x%x kind=%d id=%d flags=0x%x\n",
421 h.usage, h.kind, h.report_ID, h.flags));
422 if (h.kind == k && !(h.flags & HIO_CONST) &&
423 h.usage == u && h.report_ID == id) {
424 if (loc != NULL)
425 *loc = h.loc;
426 if (flags != NULL)
427 *flags = h.flags;
428 hid_end_parse(d);
429 return 1;
430 }
431 }
432 hid_end_parse(d);
433 if (loc != NULL)
434 loc->size = 0;
435 return 0;
436 }
437
438 long
439 hid_get_data(const u_char *buf, const struct hid_location *loc)
440 {
441 u_int hsize = loc->size;
442 u_long data;
443
444 if (hsize == 0)
445 return 0;
446
447 data = hid_get_udata(buf, loc);
448 if (data < (1UL << (hsize - 1)) || hsize == sizeof(data) * NBBY)
449 return data;
450 return data - (1UL << hsize);
451 }
452
453 u_long
454 hid_get_udata(const u_char *buf, const struct hid_location *loc)
455 {
456 u_int hpos = loc->pos;
457 u_int hsize = loc->size;
458 u_int i, num, off;
459 u_long data;
460
461 if (hsize == 0)
462 return 0;
463
464 data = 0;
465 off = hpos / 8;
466 num = (hpos + hsize + 7) / 8 - off;
467
468 for (i = 0; i < num; i++)
469 data |= (unsigned long)buf[off + i] << (i * 8);
470
471 data >>= hpos % 8;
472 if (hsize < sizeof(data) * NBBY)
473 data &= (1UL << hsize) - 1;
474
475 DPRINTFN(10,("hid_get_udata: loc %d/%d = %lu\n", hpos, hsize, data));
476 return data;
477 }
478
479 /*
480 * hid_is_collection(desc, size, id, usage)
481 *
482 * This function is broken in the following way.
483 *
484 * It is used to discover if the given 'id' is part of 'usage' collection
485 * in the descriptor in order to match report id against device type.
486 *
487 * The semantics of hid_start_parse() means though, that only a single
488 * kind of report is considered. The current HID code that uses this for
489 * matching is actually only looking for input reports, so this works
490 * for now.
491 *
492 * This function could try all report kinds (input, output and feature)
493 * consecutively if necessary, but it may be better to integrate the
494 * libusbhid code which can consider multiple report kinds simultaneously
495 *
496 * Needs some thought.
497 */
498 int
499 hid_is_collection(const void *desc, int size, uint8_t id, uint32_t usage)
500 {
501 struct hid_data *hd;
502 struct hid_item hi;
503 uint32_t coll_usage = ~0;
504
505 hd = hid_start_parse(desc, size, hid_input);
506 if (hd == NULL)
507 return 0;
508
509 DPRINTFN(2,("hid_is_collection: id=%d usage=0x%x\n", id, usage));
510 while (hid_get_item(hd, &hi)) {
511 DPRINTFN(2,("hid_is_collection: kind=%d id=%d usage=0x%x"
512 "(0x%x)\n",
513 hi.kind, hi.report_ID, hi.usage, coll_usage));
514
515 if (hi.kind == hid_collection &&
516 hi.collection == HCOLL_APPLICATION)
517 coll_usage = hi.usage;
518
519 if (hi.kind == hid_endcollection)
520 coll_usage = ~0;
521
522 if (hi.kind == hid_input &&
523 coll_usage == usage &&
524 hi.report_ID == id) {
525 DPRINTFN(2,("hid_is_collection: found\n"));
526 hid_end_parse(hd);
527 return 1;
528 }
529 }
530 DPRINTFN(2,("hid_is_collection: not found\n"));
531 hid_end_parse(hd);
532 return 0;
533 }
534