File: | dev/hid/hid.c |
Warning: | line 290, column 4 Value stored to 'dval' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: hid.c,v 1.6 2023/08/12 20:47:06 miod 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 MAXUSAGE64 64 |
48 | #define MAXPUSH4 4 |
49 | #define MAXID16 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[MAXPUSH4]; |
61 | struct hid_pos_data last_pos[MAXID16]; |
62 | int32_t usages_min[MAXUSAGE64]; |
63 | int32_t usages_max[MAXUSAGE64]; |
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 | 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 |
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 != MAXID16; 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 != MAXID16) { |
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 != MAXID16; 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 != MAXID16) { |
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 * |
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_TEMP127, M_WAITOK0x0001 | M_ZERO0x0008); |
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 |
155 | hid_end_parse(struct hid_data *s) |
156 | { |
157 | if (s == NULL((void *)0)) |
158 | return; |
159 | |
160 | free(s, M_TEMP127, 0); |
161 | } |
162 | |
163 | static uint8_t |
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 |
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((void *)0)) |
200 | return (0); |
201 | |
202 | if (s->pushlevel >= MAXPUSH4) |
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); |
Value stored to 'dval' is never read | |
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_VARIABLE0x002) { |
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 < MAXPUSH4 - 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 < MAXUSAGE64) { |
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 < MAXUSAGE64) && |
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 |
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 DIAGNOSTIC1 |
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 |
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_CONST0x001) && |
560 | h.usage == u && h.report_ID == id) { |
561 | if (loc != NULL((void *)0)) |
562 | *loc = h.loc; |
563 | if (flags != NULL((void *)0)) |
564 | *flags = h.flags; |
565 | hid_end_parse(d); |
566 | return (1); |
567 | } |
568 | } |
569 | hid_end_parse(d); |
570 | if (loc != NULL((void *)0)) |
571 | loc->size = 0; |
572 | if (flags != NULL((void *)0)) |
573 | *flags = 0; |
574 | return (0); |
575 | } |
576 | |
577 | uint32_t |
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 |
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 |
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 |
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_APPLICATION1) |
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 * |
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((void *)0); |
683 | } |
684 | |
685 | int |
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: id=%d usage=0x%x\n", __func__, id, 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 | } |