hid.c revision 1.1 1 /* $NetBSD: hid.c,v 1.1 2017/12/10 17:03:07 bouyer 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 2017/12/10 17:03:07 bouyer 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 const u_char *p;
126 struct hid_item *hi;
127 int i;
128 enum hid_kind retkind;
129
130 top:
131 DPRINTFN(5,("hid_get_item: multi=%d multimax=%d\n",
132 s->multi, s->multimax));
133 if (s->multimax != 0) {
134 if (s->multi < s->multimax) {
135 c->usage = s->usages[min(s->multi, s->nu-1)];
136 s->multi++;
137 *h = *c;
138 c->loc.pos += c->loc.size;
139 h->next = NULL;
140 DPRINTFN(5,("return multi\n"));
141 return 1;
142 } else {
143 c->loc.count = s->multimax;
144 s->multimax = 0;
145 s->nu = 0;
146 hid_clear_local(c);
147 }
148 }
149 for (;;) {
150 p = s->p;
151 if (p >= s->end)
152 return 0;
153
154 bSize = *p++;
155 if (bSize == 0xfe) {
156 /* long item */
157 bSize = *p++;
158 bSize |= *p++ << 8;
159 bTag = *p++;
160 data = p;
161 p += bSize;
162 bType = 0xff; /* XXX what should it be */
163 } else {
164 /* short item */
165 bTag = bSize >> 4;
166 bType = (bSize >> 2) & 3;
167 bSize &= 3;
168 if (bSize == 3) bSize = 4;
169 data = p;
170 p += bSize;
171 }
172 s->p = p;
173 switch(bSize) {
174 case 0:
175 dval = 0;
176 break;
177 case 1:
178 dval = (int8_t)*data++;
179 break;
180 case 2:
181 dval = *data++;
182 dval |= *data++ << 8;
183 dval = (int16_t)dval;
184 break;
185 case 4:
186 dval = *data++;
187 dval |= *data++ << 8;
188 dval |= *data++ << 16;
189 dval |= *data++ << 24;
190 dval = (int32_t)dval;
191 break;
192 default:
193 aprint_normal("BAD LENGTH %d\n", bSize);
194 continue;
195 }
196
197 DPRINTFN(5,("hid_get_item: bType=%d bTag=%d dval=%d\n",
198 bType, bTag, dval));
199 switch (bType) {
200 case 0: /* Main */
201 switch (bTag) {
202 case 8: /* Input */
203 retkind = hid_input;
204 ret:
205 if (s->kind != retkind) {
206 s->minset = 0;
207 s->nu = 0;
208 hid_clear_local(c);
209 continue;
210 }
211 c->kind = retkind;
212 c->flags = dval;
213 if (c->flags & HIO_VARIABLE) {
214 s->multimax = c->loc.count;
215 s->multi = 0;
216 c->loc.count = 1;
217 if (s->minset) {
218 for (i = c->usage_minimum;
219 i <= c->usage_maximum;
220 i++) {
221 s->usages[s->nu] = i;
222 if (s->nu < MAXUSAGE-1)
223 s->nu++;
224 }
225 s->minset = 0;
226 }
227 goto top;
228 } else {
229 if (s->minset)
230 c->usage = c->usage_minimum;
231 *h = *c;
232 h->next = NULL;
233 c->loc.pos +=
234 c->loc.size * c->loc.count;
235 s->minset = 0;
236 s->nu = 0;
237 hid_clear_local(c);
238 return 1;
239 }
240 case 9: /* Output */
241 retkind = hid_output;
242 goto ret;
243 case 10: /* Collection */
244 c->kind = hid_collection;
245 c->collection = dval;
246 c->collevel++;
247 *h = *c;
248 hid_clear_local(c);
249 s->nu = 0;
250 return 1;
251 case 11: /* Feature */
252 retkind = hid_feature;
253 goto ret;
254 case 12: /* End collection */
255 c->kind = hid_endcollection;
256 c->collevel--;
257 *h = *c;
258 s->nu = 0;
259 return 1;
260 default:
261 aprint_normal("Main bTag=%d\n", bTag);
262 break;
263 }
264 break;
265 case 1: /* Global */
266 switch (bTag) {
267 case 0:
268 c->_usage_page = dval << 16;
269 break;
270 case 1:
271 c->logical_minimum = dval;
272 break;
273 case 2:
274 c->logical_maximum = dval;
275 break;
276 case 3:
277 c->physical_minimum = dval;
278 break;
279 case 4:
280 c->physical_maximum = dval;
281 break;
282 case 5:
283 c->unit_exponent = dval;
284 break;
285 case 6:
286 c->unit = dval;
287 break;
288 case 7:
289 c->loc.size = dval;
290 break;
291 case 8:
292 c->report_ID = dval;
293 c->loc.pos = 0;
294 break;
295 case 9:
296 c->loc.count = dval;
297 break;
298 case 10: /* Push */
299 hi = kmem_alloc(sizeof(*hi), KM_SLEEP);
300 *hi = *c;
301 c->next = hi;
302 break;
303 case 11: /* Pop */
304 hi = c->next;
305 if (hi == NULL)
306 break;
307 oldpos = c->loc.pos;
308 *c = *hi;
309 c->loc.pos = oldpos;
310 kmem_free(hi, sizeof(*hi));
311 break;
312 default:
313 aprint_normal("Global bTag=%d\n", bTag);
314 break;
315 }
316 break;
317 case 2: /* Local */
318 switch (bTag) {
319 case 0:
320 if (bSize == 1)
321 dval = c->_usage_page | (dval&0xff);
322 else if (bSize == 2)
323 dval = c->_usage_page | (dval&0xffff);
324 c->usage = dval;
325 if (s->nu < MAXUSAGE)
326 s->usages[s->nu++] = dval;
327 /* else XXX */
328 break;
329 case 1:
330 s->minset = 1;
331 if (bSize == 1)
332 dval = c->_usage_page | (dval&0xff);
333 else if (bSize == 2)
334 dval = c->_usage_page | (dval&0xffff);
335 c->usage_minimum = dval;
336 break;
337 case 2:
338 if (bSize == 1)
339 dval = c->_usage_page | (dval&0xff);
340 else if (bSize == 2)
341 dval = c->_usage_page | (dval&0xffff);
342 c->usage_maximum = dval;
343 break;
344 case 3:
345 c->designator_index = dval;
346 break;
347 case 4:
348 c->designator_minimum = dval;
349 break;
350 case 5:
351 c->designator_maximum = dval;
352 break;
353 case 7:
354 c->string_index = dval;
355 break;
356 case 8:
357 c->string_minimum = dval;
358 break;
359 case 9:
360 c->string_maximum = dval;
361 break;
362 case 10:
363 c->set_delimiter = dval;
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