1 /* $OpenBSD: hid.c,v 1.7 2024/05/10 10:49:10 mglocker Exp $ */
2 /* $NetBSD: hid.c,v 1.23 2002/07/11 21:14:25 augustss Exp $ */
3 /* $FreeBSD: src/sys/dev/usb/hid.c,v 1.11 1999/11/17 22:33:39 n_hibma Exp $ */
4
5 /*
6 * Copyright (c) 1998 The NetBSD Foundation, Inc.
7 * All rights reserved.
8 *
9 * This code is derived from software contributed to The NetBSD Foundation
10 * by Lennart Augustsson (lennart@augustsson.net) at
11 * Carlstedt Research & Technology.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/malloc.h>
38
39 #include <dev/hid/hid.h>
40
41 #ifdef USBHID_DEBUG
42 #define DPRINTF(x...) do { printf(x); } while (0)
43 #else
44 #define DPRINTF(x...)
45 #endif
46
47 #define MAXUSAGE 64
48 #define MAXPUSH 4
49 #define MAXID 16
50
51 struct hid_pos_data {
52 int32_t rid;
53 uint32_t pos;
54 };
55
56 struct hid_data {
57 const uint8_t *start;
58 const uint8_t *end;
59 const uint8_t *p;
60 struct hid_item cur[MAXPUSH];
61 struct hid_pos_data last_pos[MAXID];
62 int32_t usages_min[MAXUSAGE];
63 int32_t usages_max[MAXUSAGE];
64 int32_t usage_last; /* last seen usage */
65 uint32_t loc_size; /* last seen size */
66 uint32_t loc_count; /* last seen count */
67 enum hid_kind kind;
68 uint8_t pushlevel; /* current pushlevel */
69 uint8_t ncount; /* end usage item count */
70 uint8_t icount; /* current usage item count */
71 uint8_t nusage; /* end "usages_min/max" index */
72 uint8_t iusage; /* current "usages_min/max" index */
73 uint8_t ousage; /* current "usages_min/max" offset */
74 uint8_t susage; /* usage set flags */
75 };
76
77 static void
hid_clear_local(struct hid_item * c)78 hid_clear_local(struct hid_item *c)
79 {
80 c->loc.count = 0;
81 c->loc.size = 0;
82 c->usage = 0;
83 c->usage_minimum = 0;
84 c->usage_maximum = 0;
85 c->designator_index = 0;
86 c->designator_minimum = 0;
87 c->designator_maximum = 0;
88 c->string_index = 0;
89 c->string_minimum = 0;
90 c->string_maximum = 0;
91 c->set_delimiter = 0;
92 }
93
94 static void
hid_switch_rid(struct hid_data * s,struct hid_item * c,int32_t nextid)95 hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t nextid)
96 {
97 uint8_t i;
98
99 if (c->report_ID == nextid)
100 return;
101
102 /* save current position for current rID */
103 if (c->report_ID == 0) {
104 i = 0;
105 } else {
106 for (i = 1; i != MAXID; i++) {
107 if (s->last_pos[i].rid == c->report_ID)
108 break;
109 if (s->last_pos[i].rid == 0)
110 break;
111 }
112 }
113 if (i != MAXID) {
114 s->last_pos[i].rid = c->report_ID;
115 s->last_pos[i].pos = c->loc.pos;
116 }
117
118 /* store next report ID */
119 c->report_ID = nextid;
120
121 /* lookup last position for next rID */
122 if (nextid == 0) {
123 i = 0;
124 } else {
125 for (i = 1; i != MAXID; i++) {
126 if (s->last_pos[i].rid == nextid)
127 break;
128 if (s->last_pos[i].rid == 0)
129 break;
130 }
131 }
132 if (i != MAXID) {
133 s->last_pos[i].rid = nextid;
134 c->loc.pos = s->last_pos[i].pos;
135 } else {
136 DPRINTF("Out of RID entries, position is set to zero!\n");
137 c->loc.pos = 0;
138 }
139 }
140
141 struct hid_data *
hid_start_parse(const void * d,int len,enum hid_kind kind)142 hid_start_parse(const void *d, int len, enum hid_kind kind)
143 {
144 struct hid_data *s;
145
146 s = malloc(sizeof(*s), M_TEMP, M_WAITOK | M_ZERO);
147
148 s->start = s->p = d;
149 s->end = ((const uint8_t *)d) + len;
150 s->kind = kind;
151 return (s);
152 }
153
154 void
hid_end_parse(struct hid_data * s)155 hid_end_parse(struct hid_data *s)
156 {
157 if (s == NULL)
158 return;
159
160 free(s, M_TEMP, 0);
161 }
162
163 static uint8_t
hid_get_byte(struct hid_data * s,const uint16_t wSize)164 hid_get_byte(struct hid_data *s, const uint16_t wSize)
165 {
166 const uint8_t *ptr;
167 uint8_t retval;
168
169 ptr = s->p;
170
171 /* check if end is reached */
172 if (ptr == s->end)
173 return (0);
174
175 /* read out a byte */
176 retval = *ptr;
177
178 /* check if data pointer can be advanced by "wSize" bytes */
179 if ((s->end - ptr) < wSize)
180 ptr = s->end;
181 else
182 ptr += wSize;
183
184 /* update pointer */
185 s->p = ptr;
186
187 return (retval);
188 }
189
190 int
hid_get_item(struct hid_data * s,struct hid_item * h)191 hid_get_item(struct hid_data *s, struct hid_item *h)
192 {
193 struct hid_item *c;
194 unsigned int bTag, bType, bSize;
195 uint32_t oldpos;
196 int32_t mask;
197 int32_t dval;
198
199 if (s == NULL)
200 return (0);
201
202 if (s->pushlevel >= MAXPUSH)
203 return (0);
204
205 c = &s->cur[s->pushlevel];
206
207 top:
208 /* check if there is an array of items */
209 DPRINTF("%s: icount=%d ncount=%d\n", __func__,
210 s->icount, s->ncount);
211 if (s->icount < s->ncount) {
212 /* get current usage */
213 if (s->iusage < s->nusage) {
214 dval = s->usages_min[s->iusage] + s->ousage;
215 c->usage = dval;
216 s->usage_last = dval;
217 if (dval == s->usages_max[s->iusage]) {
218 s->iusage ++;
219 s->ousage = 0;
220 } else {
221 s->ousage ++;
222 }
223 } else {
224 DPRINTF("Using last usage\n");
225 dval = s->usage_last;
226 }
227 s->icount ++;
228 /*
229 * Only copy HID item, increment position and return
230 * if correct kind!
231 */
232 if (s->kind == hid_all || s->kind == c->kind) {
233 *h = *c;
234 DPRINTF("%u,%u,%u\n", h->loc.pos,
235 h->loc.size, h->loc.count);
236 c->loc.pos += c->loc.size * c->loc.count;
237 return (1);
238 }
239 }
240
241 /* reset state variables */
242 s->icount = 0;
243 s->ncount = 0;
244 s->iusage = 0;
245 s->nusage = 0;
246 s->susage = 0;
247 s->ousage = 0;
248 hid_clear_local(c);
249
250 /* get next item */
251 while (s->p != s->end) {
252
253 bSize = hid_get_byte(s, 1);
254 if (bSize == 0xfe) {
255 /* long item */
256 bSize = hid_get_byte(s, 1);
257 bSize |= hid_get_byte(s, 1) << 8;
258 bTag = hid_get_byte(s, 1);
259 bType = 0xff; /* XXX what should it be */
260 } else {
261 /* short item */
262 bTag = bSize >> 4;
263 bType = (bSize >> 2) & 3;
264 bSize &= 3;
265 if (bSize == 3)
266 bSize = 4;
267 }
268 switch (bSize) {
269 case 0:
270 dval = 0;
271 mask = 0;
272 break;
273 case 1:
274 dval = hid_get_byte(s, 1);
275 mask = 0xFF;
276 break;
277 case 2:
278 dval = hid_get_byte(s, 1);
279 dval |= hid_get_byte(s, 1) << 8;
280 mask = 0xFFFF;
281 break;
282 case 4:
283 dval = hid_get_byte(s, 1);
284 dval |= hid_get_byte(s, 1) << 8;
285 dval |= hid_get_byte(s, 1) << 16;
286 dval |= hid_get_byte(s, 1) << 24;
287 mask = 0xFFFFFFFF;
288 break;
289 default:
290 dval = hid_get_byte(s, bSize);
291 DPRINTF("bad length %u (data=0x%02x)\n",
292 bSize, dval);
293 continue;
294 }
295
296 DPRINTF("%s: bType=%d bTag=%d dval=%d\n", __func__,
297 bType, bTag, dval);
298 switch (bType) {
299 case 0: /* Main */
300 switch (bTag) {
301 case 8: /* Input */
302 c->kind = hid_input;
303 c->flags = dval;
304 ret:
305 c->loc.count = s->loc_count;
306 c->loc.size = s->loc_size;
307
308 if (c->flags & HIO_VARIABLE) {
309 /* range check usage count */
310 if (c->loc.count > 255) {
311 DPRINTF("Number of "
312 "items truncated to 255\n");
313 s->ncount = 255;
314 } else
315 s->ncount = c->loc.count;
316
317 /*
318 * The "top" loop will return
319 * one and one item:
320 */
321 c->loc.count = 1;
322 } else {
323 s->ncount = 1;
324 }
325 goto top;
326
327 case 9: /* Output */
328 c->kind = hid_output;
329 c->flags = dval;
330 goto ret;
331 case 10: /* Collection */
332 c->kind = hid_collection;
333 c->collection = dval;
334 c->collevel++;
335 c->usage = s->usage_last;
336 *h = *c;
337 return (1);
338 case 11: /* Feature */
339 c->kind = hid_feature;
340 c->flags = dval;
341 goto ret;
342 case 12: /* End collection */
343 c->kind = hid_endcollection;
344 if (c->collevel == 0) {
345 DPRINTF("invalid end collection\n");
346 return (0);
347 }
348 c->collevel--;
349 *h = *c;
350 return (1);
351 default:
352 DPRINTF("Main bTag=%d\n", bTag);
353 break;
354 }
355 break;
356 case 1: /* Global */
357 switch (bTag) {
358 case 0:
359 c->_usage_page = dval << 16;
360 break;
361 case 1:
362 c->logical_minimum = dval;
363 break;
364 case 2:
365 c->logical_maximum = dval;
366 break;
367 case 3:
368 c->physical_minimum = dval;
369 break;
370 case 4:
371 c->physical_maximum = dval;
372 break;
373 case 5:
374 c->unit_exponent = dval;
375 break;
376 case 6:
377 c->unit = dval;
378 break;
379 case 7:
380 /* mask because value is unsigned */
381 s->loc_size = dval & mask;
382 break;
383 case 8:
384 hid_switch_rid(s, c, dval & mask);
385 break;
386 case 9:
387 /* mask because value is unsigned */
388 s->loc_count = dval & mask;
389 break;
390 case 10: /* Push */
391 if (s->pushlevel < MAXPUSH - 1) {
392 s->pushlevel++;
393 s->cur[s->pushlevel] = *c;
394 /* store size and count */
395 c->loc.size = s->loc_size;
396 c->loc.count = s->loc_count;
397 /* update current item pointer */
398 c = &s->cur[s->pushlevel];
399 } else {
400 DPRINTF("Cannot push "
401 "item @ %d\n", s->pushlevel);
402 }
403 break;
404 case 11: /* Pop */
405 if (s->pushlevel > 0) {
406 s->pushlevel--;
407 /* preserve position */
408 oldpos = c->loc.pos;
409 c = &s->cur[s->pushlevel];
410 /* restore size and count */
411 s->loc_size = c->loc.size;
412 s->loc_count = c->loc.count;
413 /* set default item location */
414 c->loc.pos = oldpos;
415 c->loc.size = 0;
416 c->loc.count = 0;
417 } else {
418 DPRINTF("Cannot pop "
419 "item @ %d\n", s->pushlevel);
420 }
421 break;
422 default:
423 DPRINTF("Global bTag=%d\n", bTag);
424 break;
425 }
426 break;
427 case 2: /* Local */
428 switch (bTag) {
429 case 0:
430 if (bSize != 4)
431 dval = (dval & mask) | c->_usage_page;
432
433 /* set last usage, in case of a collection */
434 s->usage_last = dval;
435
436 if (s->nusage < MAXUSAGE) {
437 s->usages_min[s->nusage] = dval;
438 s->usages_max[s->nusage] = dval;
439 s->nusage ++;
440 } else {
441 DPRINTF("max usage reached\n");
442 }
443
444 /* clear any pending usage sets */
445 s->susage = 0;
446 break;
447 case 1:
448 s->susage |= 1;
449
450 if (bSize != 4)
451 dval = (dval & mask) | c->_usage_page;
452 c->usage_minimum = dval;
453
454 goto check_set;
455 case 2:
456 s->susage |= 2;
457
458 if (bSize != 4)
459 dval = (dval & mask) | c->_usage_page;
460 c->usage_maximum = dval;
461
462 check_set:
463 if (s->susage != 3)
464 break;
465
466 /* sanity check */
467 if ((s->nusage < MAXUSAGE) &&
468 (c->usage_minimum <= c->usage_maximum)) {
469 /* add usage range */
470 s->usages_min[s->nusage] =
471 c->usage_minimum;
472 s->usages_max[s->nusage] =
473 c->usage_maximum;
474 s->nusage ++;
475 } else {
476 DPRINTF("Usage set dropped\n");
477 }
478 s->susage = 0;
479 break;
480 case 3:
481 c->designator_index = dval;
482 break;
483 case 4:
484 c->designator_minimum = dval;
485 break;
486 case 5:
487 c->designator_maximum = dval;
488 break;
489 case 7:
490 c->string_index = dval;
491 break;
492 case 8:
493 c->string_minimum = dval;
494 break;
495 case 9:
496 c->string_maximum = dval;
497 break;
498 case 10:
499 c->set_delimiter = dval;
500 break;
501 default:
502 DPRINTF("Local bTag=%d\n", bTag);
503 break;
504 }
505 break;
506 default:
507 DPRINTF("default bType=%d\n", bType);
508 break;
509 }
510 }
511 return (0);
512 }
513
514 int
hid_report_size(const void * buf,int len,enum hid_kind k,u_int8_t id)515 hid_report_size(const void *buf, int len, enum hid_kind k, u_int8_t id)
516 {
517 struct hid_data *d;
518 struct hid_item h;
519 int lo, hi;
520
521 h.report_ID = 0;
522 lo = hi = -1;
523 DPRINTF("hid_report_size: kind=%d id=%d\n", k, id);
524 for (d = hid_start_parse(buf, len, k); hid_get_item(d, &h); ) {
525 DPRINTF("hid_report_size: item kind=%d id=%d pos=%d "
526 "size=%d count=%d\n",
527 h.kind, h.report_ID, h.loc.pos, h.loc.size,
528 h.loc.count);
529 if (h.report_ID == id && h.kind == k) {
530 if (lo < 0) {
531 lo = h.loc.pos;
532 #ifdef DIAGNOSTIC
533 if (lo != 0) {
534 printf("hid_report_size: lo != 0\n");
535 }
536 #endif
537 }
538 hi = h.loc.pos + h.loc.size * h.loc.count;
539 DPRINTF("hid_report_size: lo=%d hi=%d\n", lo, hi);
540
541 }
542 }
543 hid_end_parse(d);
544 return ((hi - lo + 7) / 8);
545 }
546
547 int
hid_locate(const void * desc,int size,int32_t u,uint8_t id,enum hid_kind k,struct hid_location * loc,uint32_t * flags)548 hid_locate(const void *desc, int size, int32_t u, uint8_t id, enum hid_kind k,
549 struct hid_location *loc, uint32_t *flags)
550 {
551 struct hid_data *d;
552 struct hid_item h;
553
554 h.report_ID = 0;
555 DPRINTF("hid_locate: enter usage=0x%x kind=%d id=%d\n", u, k, id);
556 for (d = hid_start_parse(desc, size, k); hid_get_item(d, &h); ) {
557 DPRINTF("hid_locate: usage=0x%x kind=%d id=%d flags=0x%x\n",
558 h.usage, h.kind, h.report_ID, h.flags);
559 if (h.kind == k && !(h.flags & HIO_CONST) &&
560 h.usage == u && h.report_ID == id) {
561 if (loc != NULL)
562 *loc = h.loc;
563 if (flags != NULL)
564 *flags = h.flags;
565 hid_end_parse(d);
566 return (1);
567 }
568 }
569 hid_end_parse(d);
570 if (loc != NULL)
571 loc->size = 0;
572 if (flags != NULL)
573 *flags = 0;
574 return (0);
575 }
576
577 uint32_t
hid_get_data_sub(const uint8_t * buf,int len,struct hid_location * loc,int is_signed)578 hid_get_data_sub(const uint8_t *buf, int len, struct hid_location *loc,
579 int is_signed)
580 {
581 uint32_t hpos = loc->pos;
582 uint32_t hsize = loc->size;
583 uint32_t data;
584 uint32_t rpos;
585 uint8_t n;
586
587 DPRINTF("hid_get_data_sub: loc %d/%d\n", hpos, hsize);
588
589 /* Range check and limit */
590 if (hsize == 0)
591 return (0);
592 if (hsize > 32)
593 hsize = 32;
594
595 /* Get data in a safe way */
596 data = 0;
597 rpos = (hpos / 8);
598 n = (hsize + 7) / 8;
599 rpos += n;
600 while (n--) {
601 rpos--;
602 if (rpos < len)
603 data |= buf[rpos] << (8 * n);
604 }
605
606 /* Correctly shift down data */
607 data = (data >> (hpos % 8));
608 n = 32 - hsize;
609
610 /* Mask and sign extend in one */
611 if (is_signed != 0)
612 data = (int32_t)((int32_t)data << n) >> n;
613 else
614 data = (uint32_t)((uint32_t)data << n) >> n;
615
616 DPRINTF("hid_get_data_sub: loc %d/%d = %lu\n",
617 loc->pos, loc->size, (long)data);
618 return (data);
619 }
620
621 int32_t
hid_get_data(const uint8_t * buf,int len,struct hid_location * loc)622 hid_get_data(const uint8_t *buf, int len, struct hid_location *loc)
623 {
624 return (hid_get_data_sub(buf, len, loc, 1));
625 }
626
627 uint32_t
hid_get_udata(const uint8_t * buf,int len,struct hid_location * loc)628 hid_get_udata(const uint8_t *buf, int len, struct hid_location *loc)
629 {
630 return (hid_get_data_sub(buf, len, loc, 0));
631 }
632
633 int
hid_is_collection(const void * desc,int size,uint8_t id,int32_t usage)634 hid_is_collection(const void *desc, int size, uint8_t id, int32_t usage)
635 {
636 struct hid_data *hd;
637 struct hid_item hi;
638 uint32_t coll_usage = ~0;
639
640 hd = hid_start_parse(desc, size, hid_all);
641
642 DPRINTF("%s: id=%d usage=0x%x\n", __func__, id, usage);
643 while (hid_get_item(hd, &hi)) {
644 DPRINTF("%s: kind=%d id=%d usage=0x%x(0x%x)\n", __func__,
645 hi.kind, hi.report_ID, hi.usage, coll_usage);
646 if (hi.kind == hid_collection &&
647 hi.collection == HCOLL_APPLICATION)
648 coll_usage = hi.usage;
649 if (hi.kind == hid_endcollection &&
650 coll_usage == usage && hi.report_ID == id) {
651 DPRINTF("%s: found\n", __func__);
652 hid_end_parse(hd);
653 return (1);
654 }
655 }
656 DPRINTF("%s: not found\n", __func__);
657 hid_end_parse(hd);
658 return (0);
659 }
660
661 struct hid_data *
hid_get_collection_data(const void * desc,int size,int32_t usage,uint32_t collection)662 hid_get_collection_data(const void *desc, int size, int32_t usage,
663 uint32_t collection)
664 {
665 struct hid_data *hd;
666 struct hid_item hi;
667
668 hd = hid_start_parse(desc, size, hid_all);
669
670 DPRINTF("%s: usage=0x%x\n", __func__, usage);
671 while (hid_get_item(hd, &hi)) {
672 DPRINTF("%s: kind=%d id=%d usage=0x%x(0x%x)\n", __func__,
673 hi.kind, hi.report_ID, hi.usage, usage);
674 if (hi.kind == hid_collection &&
675 hi.collection == collection && hi.usage == usage) {
676 DPRINTF("%s: found\n", __func__);
677 return hd;
678 }
679 }
680 DPRINTF("%s: not found\n", __func__);
681 hid_end_parse(hd);
682 return NULL;
683 }
684
685 int
hid_get_id_of_collection(const void * desc,int size,int32_t usage,uint32_t collection)686 hid_get_id_of_collection(const void *desc, int size, int32_t usage,
687 uint32_t collection)
688 {
689 struct hid_data *hd;
690 struct hid_item hi;
691
692 hd = hid_start_parse(desc, size, hid_all);
693
694 DPRINTF("%s: usage=0x%x\n", __func__, usage);
695 while (hid_get_item(hd, &hi)) {
696 DPRINTF("%s: kind=%d id=%d usage=0x%x(0x%x)\n", __func__,
697 hi.kind, hi.report_ID, hi.usage, usage);
698 if (hi.kind == hid_collection &&
699 hi.collection == collection && hi.usage == usage) {
700 DPRINTF("%s: found\n", __func__);
701 hid_end_parse(hd);
702 return hi.report_ID;
703 }
704 }
705 DPRINTF("%s: not found\n", __func__);
706 hid_end_parse(hd);
707 return -1;
708 }
709