Line data Source code
1 : /* $OpenBSD: hid.c,v 1.2 2016/01/20 01:11:50 jcs 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
78 0 : hid_clear_local(struct hid_item *c)
79 : {
80 0 : c->loc.count = 0;
81 0 : c->loc.size = 0;
82 0 : c->usage = 0;
83 0 : c->usage_minimum = 0;
84 0 : c->usage_maximum = 0;
85 0 : c->designator_index = 0;
86 0 : c->designator_minimum = 0;
87 0 : c->designator_maximum = 0;
88 0 : c->string_index = 0;
89 0 : c->string_minimum = 0;
90 0 : c->string_maximum = 0;
91 0 : c->set_delimiter = 0;
92 0 : }
93 :
94 : static void
95 0 : hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t nextid)
96 : {
97 : uint8_t i;
98 :
99 0 : if (c->report_ID == nextid)
100 0 : return;
101 :
102 : /* save current position for current rID */
103 0 : if (c->report_ID == 0) {
104 : i = 0;
105 0 : } else {
106 0 : for (i = 1; i != MAXID; i++) {
107 0 : if (s->last_pos[i].rid == c->report_ID)
108 : break;
109 0 : if (s->last_pos[i].rid == 0)
110 : break;
111 : }
112 : }
113 0 : if (i != MAXID) {
114 0 : s->last_pos[i].rid = c->report_ID;
115 0 : s->last_pos[i].pos = c->loc.pos;
116 0 : }
117 :
118 : /* store next report ID */
119 0 : c->report_ID = nextid;
120 :
121 : /* lookup last position for next rID */
122 0 : if (nextid == 0) {
123 : i = 0;
124 0 : } else {
125 0 : for (i = 1; i != MAXID; i++) {
126 0 : if (s->last_pos[i].rid == nextid)
127 : break;
128 0 : if (s->last_pos[i].rid == 0)
129 : break;
130 : }
131 : }
132 0 : if (i != MAXID) {
133 0 : s->last_pos[i].rid = nextid;
134 0 : c->loc.pos = s->last_pos[i].pos;
135 0 : } else {
136 : DPRINTF("Out of RID entries, position is set to zero!\n");
137 0 : c->loc.pos = 0;
138 : }
139 0 : }
140 :
141 : struct hid_data *
142 0 : hid_start_parse(const void *d, int len, enum hid_kind kind)
143 : {
144 : struct hid_data *s;
145 :
146 0 : s = malloc(sizeof(*s), M_TEMP, M_WAITOK | M_ZERO);
147 :
148 0 : s->start = s->p = d;
149 0 : s->end = ((const uint8_t *)d) + len;
150 0 : s->kind = kind;
151 0 : return (s);
152 : }
153 :
154 : void
155 0 : hid_end_parse(struct hid_data *s)
156 : {
157 0 : if (s == NULL)
158 : return;
159 :
160 0 : free(s, M_TEMP, 0);
161 0 : }
162 :
163 : static uint8_t
164 0 : hid_get_byte(struct hid_data *s, const uint16_t wSize)
165 : {
166 : const uint8_t *ptr;
167 : uint8_t retval;
168 :
169 0 : ptr = s->p;
170 :
171 : /* check if end is reached */
172 0 : if (ptr == s->end)
173 0 : return (0);
174 :
175 : /* read out a byte */
176 0 : retval = *ptr;
177 :
178 : /* check if data pointer can be advanced by "wSize" bytes */
179 0 : if ((s->end - ptr) < wSize)
180 0 : ptr = s->end;
181 : else
182 0 : ptr += wSize;
183 :
184 : /* update pointer */
185 0 : s->p = ptr;
186 :
187 0 : return (retval);
188 0 : }
189 :
190 : int
191 0 : 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 0 : if (s == NULL)
200 0 : return (0);
201 :
202 0 : c = &s->cur[s->pushlevel];
203 :
204 : top:
205 : /* check if there is an array of items */
206 : DPRINTF("%s: icount=%d ncount=%d\n", __func__,
207 : s->icount, s->ncount);
208 0 : if (s->icount < s->ncount) {
209 : /* get current usage */
210 0 : if (s->iusage < s->nusage) {
211 0 : dval = s->usages_min[s->iusage] + s->ousage;
212 0 : c->usage = dval;
213 0 : s->usage_last = dval;
214 0 : if (dval == s->usages_max[s->iusage]) {
215 0 : s->iusage ++;
216 0 : s->ousage = 0;
217 0 : } else {
218 0 : s->ousage ++;
219 : }
220 : } else {
221 : DPRINTF("Using last usage\n");
222 0 : dval = s->usage_last;
223 : }
224 0 : s->icount ++;
225 : /*
226 : * Only copy HID item, increment position and return
227 : * if correct kind!
228 : */
229 0 : if (s->kind == c->kind) {
230 0 : *h = *c;
231 : DPRINTF("%u,%u,%u\n", h->loc.pos,
232 : h->loc.size, h->loc.count);
233 0 : c->loc.pos += c->loc.size * c->loc.count;
234 0 : return (1);
235 : }
236 : }
237 :
238 : /* reset state variables */
239 0 : s->icount = 0;
240 0 : s->ncount = 0;
241 0 : s->iusage = 0;
242 0 : s->nusage = 0;
243 0 : s->susage = 0;
244 0 : s->ousage = 0;
245 0 : hid_clear_local(c);
246 :
247 : /* get next item */
248 0 : while (s->p != s->end) {
249 :
250 0 : bSize = hid_get_byte(s, 1);
251 0 : if (bSize == 0xfe) {
252 : /* long item */
253 0 : bSize = hid_get_byte(s, 1);
254 0 : bSize |= hid_get_byte(s, 1) << 8;
255 0 : bTag = hid_get_byte(s, 1);
256 : bType = 0xff; /* XXX what should it be */
257 0 : } else {
258 : /* short item */
259 0 : bTag = bSize >> 4;
260 0 : bType = (bSize >> 2) & 3;
261 0 : bSize &= 3;
262 0 : if (bSize == 3)
263 : bSize = 4;
264 : }
265 0 : switch (bSize) {
266 : case 0:
267 : dval = 0;
268 : mask = 0;
269 0 : break;
270 : case 1:
271 0 : dval = hid_get_byte(s, 1);
272 : mask = 0xFF;
273 0 : break;
274 : case 2:
275 0 : dval = hid_get_byte(s, 1);
276 0 : dval |= hid_get_byte(s, 1) << 8;
277 : mask = 0xFFFF;
278 0 : break;
279 : case 4:
280 0 : dval = hid_get_byte(s, 1);
281 0 : dval |= hid_get_byte(s, 1) << 8;
282 0 : dval |= hid_get_byte(s, 1) << 16;
283 0 : dval |= hid_get_byte(s, 1) << 24;
284 : mask = 0xFFFFFFFF;
285 0 : break;
286 : default:
287 0 : dval = hid_get_byte(s, bSize);
288 : DPRINTF("bad length %u (data=0x%02x)\n",
289 : bSize, dval);
290 0 : continue;
291 : }
292 :
293 : DPRINTF("%s: bType=%d bTag=%d dval=%d\n", __func__,
294 : bType, bTag, dval);
295 0 : switch (bType) {
296 : case 0: /* Main */
297 0 : switch (bTag) {
298 : case 8: /* Input */
299 0 : c->kind = hid_input;
300 0 : c->flags = dval;
301 : ret:
302 0 : c->loc.count = s->loc_count;
303 0 : c->loc.size = s->loc_size;
304 :
305 0 : if (c->flags & HIO_VARIABLE) {
306 : /* range check usage count */
307 0 : if (c->loc.count > 255) {
308 : DPRINTF("Number of "
309 : "items truncated to 255\n");
310 0 : s->ncount = 255;
311 0 : } else
312 0 : s->ncount = c->loc.count;
313 :
314 : /*
315 : * The "top" loop will return
316 : * one and one item:
317 : */
318 0 : c->loc.count = 1;
319 0 : } else {
320 0 : s->ncount = 1;
321 : }
322 0 : goto top;
323 :
324 : case 9: /* Output */
325 0 : c->kind = hid_output;
326 0 : c->flags = dval;
327 0 : goto ret;
328 : case 10: /* Collection */
329 0 : c->kind = hid_collection;
330 0 : c->collection = dval;
331 0 : c->collevel++;
332 0 : c->usage = s->usage_last;
333 0 : *h = *c;
334 0 : return (1);
335 : case 11: /* Feature */
336 0 : c->kind = hid_feature;
337 0 : c->flags = dval;
338 0 : goto ret;
339 : case 12: /* End collection */
340 0 : c->kind = hid_endcollection;
341 0 : if (c->collevel == 0) {
342 : DPRINTF("invalid end collection\n");
343 0 : return (0);
344 : }
345 0 : c->collevel--;
346 0 : *h = *c;
347 0 : return (1);
348 : default:
349 : DPRINTF("Main bTag=%d\n", bTag);
350 : break;
351 : }
352 : break;
353 : case 1: /* Global */
354 0 : switch (bTag) {
355 : case 0:
356 0 : c->_usage_page = dval << 16;
357 0 : break;
358 : case 1:
359 0 : c->logical_minimum = dval;
360 0 : break;
361 : case 2:
362 0 : c->logical_maximum = dval;
363 0 : break;
364 : case 3:
365 0 : c->physical_minimum = dval;
366 0 : break;
367 : case 4:
368 0 : c->physical_maximum = dval;
369 0 : break;
370 : case 5:
371 0 : c->unit_exponent = dval;
372 0 : break;
373 : case 6:
374 0 : c->unit = dval;
375 0 : break;
376 : case 7:
377 : /* mask because value is unsigned */
378 0 : s->loc_size = dval & mask;
379 0 : break;
380 : case 8:
381 0 : hid_switch_rid(s, c, dval & mask);
382 0 : break;
383 : case 9:
384 : /* mask because value is unsigned */
385 0 : s->loc_count = dval & mask;
386 0 : break;
387 : case 10: /* Push */
388 0 : s->pushlevel ++;
389 0 : if (s->pushlevel < MAXPUSH) {
390 0 : s->cur[s->pushlevel] = *c;
391 : /* store size and count */
392 0 : c->loc.size = s->loc_size;
393 0 : c->loc.count = s->loc_count;
394 : /* update current item pointer */
395 0 : c = &s->cur[s->pushlevel];
396 0 : } else {
397 : DPRINTF("Cannot push "
398 : "item @ %d\n", s->pushlevel);
399 : }
400 : break;
401 : case 11: /* Pop */
402 0 : s->pushlevel --;
403 0 : if (s->pushlevel < MAXPUSH) {
404 : /* preserve position */
405 0 : oldpos = c->loc.pos;
406 0 : c = &s->cur[s->pushlevel];
407 : /* restore size and count */
408 0 : s->loc_size = c->loc.size;
409 0 : s->loc_count = c->loc.count;
410 : /* set default item location */
411 0 : c->loc.pos = oldpos;
412 0 : c->loc.size = 0;
413 0 : c->loc.count = 0;
414 0 : } else {
415 : DPRINTF("Cannot pop "
416 : "item @ %d\n", s->pushlevel);
417 : }
418 : break;
419 : default:
420 : DPRINTF("Global bTag=%d\n", bTag);
421 : break;
422 : }
423 : break;
424 : case 2: /* Local */
425 0 : switch (bTag) {
426 : case 0:
427 0 : if (bSize != 4)
428 0 : dval = (dval & mask) | c->_usage_page;
429 :
430 : /* set last usage, in case of a collection */
431 0 : s->usage_last = dval;
432 :
433 0 : if (s->nusage < MAXUSAGE) {
434 0 : s->usages_min[s->nusage] = dval;
435 0 : s->usages_max[s->nusage] = dval;
436 0 : s->nusage ++;
437 0 : } else {
438 : DPRINTF("max usage reached\n");
439 : }
440 :
441 : /* clear any pending usage sets */
442 0 : s->susage = 0;
443 0 : break;
444 : case 1:
445 0 : s->susage |= 1;
446 :
447 0 : if (bSize != 4)
448 0 : dval = (dval & mask) | c->_usage_page;
449 0 : c->usage_minimum = dval;
450 :
451 0 : goto check_set;
452 : case 2:
453 0 : s->susage |= 2;
454 :
455 0 : if (bSize != 4)
456 0 : dval = (dval & mask) | c->_usage_page;
457 0 : c->usage_maximum = dval;
458 :
459 : check_set:
460 0 : if (s->susage != 3)
461 : break;
462 :
463 : /* sanity check */
464 0 : if ((s->nusage < MAXUSAGE) &&
465 0 : (c->usage_minimum <= c->usage_maximum)) {
466 : /* add usage range */
467 0 : s->usages_min[s->nusage] =
468 : c->usage_minimum;
469 0 : s->usages_max[s->nusage] =
470 0 : c->usage_maximum;
471 0 : s->nusage ++;
472 0 : } else {
473 : DPRINTF("Usage set dropped\n");
474 : }
475 0 : s->susage = 0;
476 0 : break;
477 : case 3:
478 0 : c->designator_index = dval;
479 0 : break;
480 : case 4:
481 0 : c->designator_minimum = dval;
482 0 : break;
483 : case 5:
484 0 : c->designator_maximum = dval;
485 0 : break;
486 : case 7:
487 0 : c->string_index = dval;
488 0 : break;
489 : case 8:
490 0 : c->string_minimum = dval;
491 0 : break;
492 : case 9:
493 0 : c->string_maximum = dval;
494 0 : break;
495 : case 10:
496 0 : c->set_delimiter = dval;
497 0 : break;
498 : default:
499 : DPRINTF("Local bTag=%d\n", bTag);
500 : break;
501 : }
502 : break;
503 : default:
504 : DPRINTF("default bType=%d\n", bType);
505 : break;
506 : }
507 : }
508 0 : return (0);
509 0 : }
510 :
511 : int
512 0 : hid_report_size(const void *buf, int len, enum hid_kind k, u_int8_t id)
513 : {
514 : struct hid_data *d;
515 0 : struct hid_item h;
516 : int lo, hi;
517 :
518 0 : h.report_ID = 0;
519 : lo = hi = -1;
520 : DPRINTF("hid_report_size: kind=%d id=%d\n", k, id);
521 0 : for (d = hid_start_parse(buf, len, k); hid_get_item(d, &h); ) {
522 : DPRINTF("hid_report_size: item kind=%d id=%d pos=%d "
523 : "size=%d count=%d\n",
524 : h.kind, h.report_ID, h.loc.pos, h.loc.size,
525 : h.loc.count);
526 0 : if (h.report_ID == id && h.kind == k) {
527 0 : if (lo < 0) {
528 0 : lo = h.loc.pos;
529 : #ifdef DIAGNOSTIC
530 0 : if (lo != 0) {
531 0 : printf("hid_report_size: lo != 0\n");
532 0 : }
533 : #endif
534 : }
535 0 : hi = h.loc.pos + h.loc.size * h.loc.count;
536 : DPRINTF("hid_report_size: lo=%d hi=%d\n", lo, hi);
537 :
538 0 : }
539 : }
540 0 : hid_end_parse(d);
541 0 : return ((hi - lo + 7) / 8);
542 0 : }
543 :
544 : int
545 0 : hid_locate(const void *desc, int size, int32_t u, uint8_t id, enum hid_kind k,
546 : struct hid_location *loc, uint32_t *flags)
547 : {
548 : struct hid_data *d;
549 0 : struct hid_item h;
550 :
551 0 : h.report_ID = 0;
552 : DPRINTF("hid_locate: enter usage=0x%x kind=%d id=%d\n", u, k, id);
553 0 : for (d = hid_start_parse(desc, size, k); hid_get_item(d, &h); ) {
554 : DPRINTF("hid_locate: usage=0x%x kind=%d id=%d flags=0x%x\n",
555 : h.usage, h.kind, h.report_ID, h.flags);
556 0 : if (h.kind == k && !(h.flags & HIO_CONST) &&
557 0 : h.usage == u && h.report_ID == id) {
558 0 : if (loc != NULL)
559 0 : *loc = h.loc;
560 0 : if (flags != NULL)
561 0 : *flags = h.flags;
562 0 : hid_end_parse(d);
563 0 : return (1);
564 : }
565 : }
566 0 : hid_end_parse(d);
567 0 : if (loc != NULL)
568 0 : loc->size = 0;
569 0 : if (flags != NULL)
570 0 : *flags = 0;
571 0 : return (0);
572 0 : }
573 :
574 : uint32_t
575 0 : hid_get_data_sub(const uint8_t *buf, int len, struct hid_location *loc,
576 : int is_signed)
577 : {
578 0 : uint32_t hpos = loc->pos;
579 0 : uint32_t hsize = loc->size;
580 : uint32_t data;
581 : uint32_t rpos;
582 : uint8_t n;
583 :
584 : DPRINTF("hid_get_data_sub: loc %d/%d\n", hpos, hsize);
585 :
586 : /* Range check and limit */
587 0 : if (hsize == 0)
588 0 : return (0);
589 0 : if (hsize > 32)
590 0 : hsize = 32;
591 :
592 : /* Get data in a safe way */
593 : data = 0;
594 0 : rpos = (hpos / 8);
595 0 : n = (hsize + 7) / 8;
596 0 : rpos += n;
597 0 : while (n--) {
598 0 : rpos--;
599 0 : if (rpos < len)
600 0 : data |= buf[rpos] << (8 * n);
601 : }
602 :
603 : /* Correctly shift down data */
604 0 : data = (data >> (hpos % 8));
605 0 : n = 32 - hsize;
606 :
607 : /* Mask and sign extend in one */
608 0 : if (is_signed != 0)
609 0 : data = (int32_t)((int32_t)data << n) >> n;
610 : else
611 0 : data = (uint32_t)((uint32_t)data << n) >> n;
612 :
613 : DPRINTF("hid_get_data_sub: loc %d/%d = %lu\n",
614 : loc->pos, loc->size, (long)data);
615 0 : return (data);
616 0 : }
617 :
618 : int32_t
619 0 : hid_get_data(const uint8_t *buf, int len, struct hid_location *loc)
620 : {
621 0 : return (hid_get_data_sub(buf, len, loc, 1));
622 : }
623 :
624 : uint32_t
625 0 : hid_get_udata(const uint8_t *buf, int len, struct hid_location *loc)
626 : {
627 0 : return (hid_get_data_sub(buf, len, loc, 0));
628 : }
629 :
630 : int
631 0 : hid_is_collection(const void *desc, int size, uint8_t id, int32_t usage)
632 : {
633 : struct hid_data *hd;
634 0 : struct hid_item hi;
635 : uint32_t coll_usage = ~0;
636 :
637 0 : hd = hid_start_parse(desc, size, hid_none);
638 :
639 : DPRINTF("%s: id=%d usage=0x%x\n", __func__, id, usage);
640 0 : while (hid_get_item(hd, &hi)) {
641 : DPRINTF("%s: kind=%d id=%d usage=0x%x(0x%x)\n", __func__,
642 : hi.kind, hi.report_ID, hi.usage, coll_usage);
643 0 : if (hi.kind == hid_collection &&
644 0 : hi.collection == HCOLL_APPLICATION)
645 0 : coll_usage = hi.usage;
646 0 : if (hi.kind == hid_endcollection &&
647 0 : coll_usage == usage && hi.report_ID == id) {
648 : DPRINTF("%s: found\n", __func__);
649 0 : hid_end_parse(hd);
650 0 : return (1);
651 : }
652 : }
653 : DPRINTF("%s: not found\n", __func__);
654 0 : hid_end_parse(hd);
655 0 : return (0);
656 0 : }
|