1 /* $NetBSD: parse.c,v 1.11 2000/09/24 02:19:54 augustss Exp $ */
2
3 /*-
4 * SPDX-License-Identifier: BSD-2-Clause-NetBSD
5 *
6 * Copyright (c) 1999, 2001 Lennart Augustsson <[email protected]>
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 #include <assert.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/time.h>
38
39 #include <dev/usb/usb.h>
40 #include <dev/usb/usbhid.h>
41
42 #include "usbhid.h"
43 #include "usbvar.h"
44
45 #define MAXUSAGE 100
46 #define MAXPUSH 4
47 #define MAXID 64
48 #define ITEMTYPES 3
49
50 struct hid_pos_data {
51 int32_t rid;
52 uint32_t pos[ITEMTYPES];
53 };
54
55 struct hid_data {
56 const uint8_t *start;
57 const uint8_t *end;
58 const uint8_t *p;
59 struct hid_item cur[MAXPUSH];
60 struct hid_pos_data last_pos[MAXID];
61 uint32_t pos[ITEMTYPES];
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 uint8_t kindset; /* we have 5 kinds so 8 bits are enough */
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 int32_t reportid; /* requested report ID */
76 };
77
78 /*------------------------------------------------------------------------*
79 * hid_clear_local
80 *------------------------------------------------------------------------*/
81 static void
hid_clear_local(hid_item_t * c)82 hid_clear_local(hid_item_t *c)
83 {
84
85 c->usage = 0;
86 c->usage_minimum = 0;
87 c->usage_maximum = 0;
88 c->designator_index = 0;
89 c->designator_minimum = 0;
90 c->designator_maximum = 0;
91 c->string_index = 0;
92 c->string_minimum = 0;
93 c->string_maximum = 0;
94 c->set_delimiter = 0;
95 }
96
97 static void
hid_switch_rid(struct hid_data * s,struct hid_item * c,int32_t next_rID)98 hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t next_rID)
99 {
100 uint8_t i, j;
101
102 /* check for same report ID - optimise */
103
104 if (c->report_ID == next_rID)
105 return;
106
107 /* save current position for current rID */
108
109 if (c->report_ID == 0) {
110 i = 0;
111 } else {
112 for (i = 1; i != MAXID; i++) {
113 if (s->last_pos[i].rid == c->report_ID)
114 break;
115 if (s->last_pos[i].rid == 0)
116 break;
117 }
118 }
119 if (i != MAXID) {
120 s->last_pos[i].rid = c->report_ID;
121 for (j = 0; j < ITEMTYPES; j++)
122 s->last_pos[i].pos[j] = s->pos[j];
123 }
124
125 /* store next report ID */
126
127 c->report_ID = next_rID;
128
129 /* lookup last position for next rID */
130
131 if (next_rID == 0) {
132 i = 0;
133 } else {
134 for (i = 1; i != MAXID; i++) {
135 if (s->last_pos[i].rid == next_rID)
136 break;
137 if (s->last_pos[i].rid == 0)
138 break;
139 }
140 }
141 if (i != MAXID) {
142 s->last_pos[i].rid = next_rID;
143 for (j = 0; j < ITEMTYPES; j++)
144 s->pos[j] = s->last_pos[i].pos[j];
145 } else {
146 for (j = 0; j < ITEMTYPES; j++)
147 s->pos[j] = 0; /* Out of RID entries. */
148 }
149 }
150
151 /*------------------------------------------------------------------------*
152 * hid_start_parse
153 *------------------------------------------------------------------------*/
154 hid_data_t
hid_start_parse(report_desc_t d,int kindset,int id)155 hid_start_parse(report_desc_t d, int kindset, int id)
156 {
157 struct hid_data *s;
158
159 s = malloc(sizeof *s);
160 memset(s, 0, sizeof *s);
161 s->start = s->p = d->data;
162 s->end = d->data + d->size;
163 s->kindset = kindset;
164 s->reportid = id;
165 return (s);
166 }
167
168 /*------------------------------------------------------------------------*
169 * hid_end_parse
170 *------------------------------------------------------------------------*/
171 void
hid_end_parse(hid_data_t s)172 hid_end_parse(hid_data_t s)
173 {
174
175 if (s == NULL)
176 return;
177
178 free(s);
179 }
180
181 /*------------------------------------------------------------------------*
182 * get byte from HID descriptor
183 *------------------------------------------------------------------------*/
184 static uint8_t
hid_get_byte(struct hid_data * s,const uint16_t wSize)185 hid_get_byte(struct hid_data *s, const uint16_t wSize)
186 {
187 const uint8_t *ptr;
188 uint8_t retval;
189
190 ptr = s->p;
191
192 /* check if end is reached */
193 if (ptr == s->end)
194 return (0);
195
196 /* read out a byte */
197 retval = *ptr;
198
199 /* check if data pointer can be advanced by "wSize" bytes */
200 if ((s->end - ptr) < wSize)
201 ptr = s->end;
202 else
203 ptr += wSize;
204
205 /* update pointer */
206 s->p = ptr;
207
208 return (retval);
209 }
210
211 /*------------------------------------------------------------------------*
212 * hid_get_item
213 *------------------------------------------------------------------------*/
214 static int
hid_get_item_raw(hid_data_t s,hid_item_t * h)215 hid_get_item_raw(hid_data_t s, hid_item_t *h)
216 {
217 hid_item_t *c;
218 unsigned int bTag, bType, bSize;
219 int32_t mask;
220 int32_t dval;
221
222 if (s == NULL)
223 return (0);
224
225 c = &s->cur[s->pushlevel];
226
227 top:
228 /* check if there is an array of items */
229 if (s->icount < s->ncount) {
230 /* get current usage */
231 if (s->iusage < s->nusage) {
232 dval = s->usages_min[s->iusage] + s->ousage;
233 c->usage = dval;
234 s->usage_last = dval;
235 if (dval == s->usages_max[s->iusage]) {
236 s->iusage ++;
237 s->ousage = 0;
238 } else {
239 s->ousage ++;
240 }
241 } else {
242 /* Using last usage */
243 dval = s->usage_last;
244 }
245 s->icount ++;
246 /*
247 * Only copy HID item, increment position and return
248 * if correct kindset!
249 */
250 if (s->kindset & (1 << c->kind)) {
251 *h = *c;
252 h->pos = s->pos[c->kind];
253 s->pos[c->kind] += c->report_size * c->report_count;
254 return (1);
255 }
256 }
257
258 /* reset state variables */
259 s->icount = 0;
260 s->ncount = 0;
261 s->iusage = 0;
262 s->nusage = 0;
263 s->susage = 0;
264 s->ousage = 0;
265 hid_clear_local(c);
266
267 /* get next item */
268 while (s->p != s->end) {
269
270 bSize = hid_get_byte(s, 1);
271 if (bSize == 0xfe) {
272 /* long item */
273 bSize = hid_get_byte(s, 1);
274 bSize |= hid_get_byte(s, 1) << 8;
275 bTag = hid_get_byte(s, 1);
276 bType = 0xff; /* XXX what should it be */
277 } else {
278 /* short item */
279 bTag = bSize >> 4;
280 bType = (bSize >> 2) & 3;
281 bSize &= 3;
282 if (bSize == 3)
283 bSize = 4;
284 }
285
286 switch(bSize) {
287 case 0:
288 dval = 0;
289 mask = 0;
290 break;
291 case 1:
292 dval = (int8_t)hid_get_byte(s, 1);
293 mask = 0xFF;
294 break;
295 case 2:
296 dval = hid_get_byte(s, 1);
297 dval |= hid_get_byte(s, 1) << 8;
298 dval = (int16_t)dval;
299 mask = 0xFFFF;
300 break;
301 case 4:
302 dval = hid_get_byte(s, 1);
303 dval |= hid_get_byte(s, 1) << 8;
304 dval |= hid_get_byte(s, 1) << 16;
305 dval |= hid_get_byte(s, 1) << 24;
306 mask = 0xFFFFFFFF;
307 break;
308 default:
309 dval = hid_get_byte(s, bSize);
310 continue;
311 }
312
313 switch (bType) {
314 case 0: /* Main */
315 switch (bTag) {
316 case 8: /* Input */
317 c->kind = hid_input;
318 c->flags = dval;
319 ret:
320 c->report_count = s->loc_count;
321 c->report_size = s->loc_size;
322
323 if (c->flags & HIO_VARIABLE) {
324 /* range check usage count */
325 if (c->report_count > 255) {
326 s->ncount = 255;
327 } else
328 s->ncount = c->report_count;
329
330 /*
331 * The "top" loop will return
332 * one and one item:
333 */
334 c->report_count = 1;
335 c->usage_minimum = 0;
336 c->usage_maximum = 0;
337 } else {
338 s->ncount = 1;
339 }
340 goto top;
341
342 case 9: /* Output */
343 c->kind = hid_output;
344 c->flags = dval;
345 goto ret;
346 case 10: /* Collection */
347 c->kind = hid_collection;
348 c->collection = dval;
349 c->collevel++;
350 c->usage = s->usage_last;
351 *h = *c;
352 return (1);
353 case 11: /* Feature */
354 c->kind = hid_feature;
355 c->flags = dval;
356 goto ret;
357 case 12: /* End collection */
358 c->kind = hid_endcollection;
359 if (c->collevel == 0) {
360 /* Invalid end collection. */
361 return (0);
362 }
363 c->collevel--;
364 *h = *c;
365 return (1);
366 default:
367 break;
368 }
369 break;
370
371 case 1: /* Global */
372 switch (bTag) {
373 case 0:
374 c->_usage_page = dval << 16;
375 break;
376 case 1:
377 c->logical_minimum = dval;
378 break;
379 case 2:
380 c->logical_maximum = dval;
381 break;
382 case 3:
383 c->physical_minimum = dval;
384 break;
385 case 4:
386 c->physical_maximum = dval;
387 break;
388 case 5:
389 c->unit_exponent = dval;
390 break;
391 case 6:
392 c->unit = dval;
393 break;
394 case 7:
395 /* mask because value is unsigned */
396 s->loc_size = dval & mask;
397 break;
398 case 8:
399 hid_switch_rid(s, c, dval & mask);
400 break;
401 case 9:
402 /* mask because value is unsigned */
403 s->loc_count = dval & mask;
404 break;
405 case 10: /* Push */
406 /* stop parsing, if invalid push level */
407 if ((s->pushlevel + 1) >= MAXPUSH)
408 return (0);
409 s->pushlevel ++;
410 s->cur[s->pushlevel] = *c;
411 /* store size and count */
412 c->report_size = s->loc_size;
413 c->report_count = s->loc_count;
414 /* update current item pointer */
415 c = &s->cur[s->pushlevel];
416 break;
417 case 11: /* Pop */
418 /* stop parsing, if invalid push level */
419 if (s->pushlevel == 0)
420 return (0);
421 s->pushlevel --;
422 c = &s->cur[s->pushlevel];
423 /* restore size and count */
424 s->loc_size = c->report_size;
425 s->loc_count = c->report_count;
426 c->report_size = 0;
427 c->report_count = 0;
428 break;
429 default:
430 break;
431 }
432 break;
433 case 2: /* Local */
434 switch (bTag) {
435 case 0:
436 if (bSize != 4)
437 dval = (dval & mask) | c->_usage_page;
438
439 /* set last usage, in case of a collection */
440 s->usage_last = dval;
441
442 if (s->nusage < MAXUSAGE) {
443 s->usages_min[s->nusage] = dval;
444 s->usages_max[s->nusage] = dval;
445 s->nusage ++;
446 }
447 /* else XXX */
448
449 /* clear any pending usage sets */
450 s->susage = 0;
451 break;
452 case 1:
453 s->susage |= 1;
454
455 if (bSize != 4)
456 dval = (dval & mask) | c->_usage_page;
457 c->usage_minimum = dval;
458
459 goto check_set;
460 case 2:
461 s->susage |= 2;
462
463 if (bSize != 4)
464 dval = (dval & mask) | c->_usage_page;
465 c->usage_maximum = dval;
466
467 check_set:
468 if (s->susage != 3)
469 break;
470
471 /* sanity check */
472 if ((s->nusage < MAXUSAGE) &&
473 (c->usage_minimum <= c->usage_maximum)) {
474 /* add usage range */
475 s->usages_min[s->nusage] =
476 c->usage_minimum;
477 s->usages_max[s->nusage] =
478 c->usage_maximum;
479 s->nusage ++;
480 }
481 /* else XXX */
482
483 s->susage = 0;
484 break;
485 case 3:
486 c->designator_index = dval;
487 break;
488 case 4:
489 c->designator_minimum = dval;
490 break;
491 case 5:
492 c->designator_maximum = dval;
493 break;
494 case 7:
495 c->string_index = dval;
496 break;
497 case 8:
498 c->string_minimum = dval;
499 break;
500 case 9:
501 c->string_maximum = dval;
502 break;
503 case 10:
504 c->set_delimiter = dval;
505 break;
506 default:
507 break;
508 }
509 break;
510 default:
511 break;
512 }
513 }
514 return (0);
515 }
516
517 int
hid_get_item(hid_data_t s,hid_item_t * h)518 hid_get_item(hid_data_t s, hid_item_t *h)
519 {
520 int r;
521
522 for (;;) {
523 r = hid_get_item_raw(s, h);
524 if (r <= 0 || s->reportid == -1 || h->report_ID == s->reportid)
525 break;
526 }
527 return (r);
528 }
529
530 int
hid_report_size(report_desc_t r,enum hid_kind k,int id)531 hid_report_size(report_desc_t r, enum hid_kind k, int id)
532 {
533 struct hid_data *d;
534 struct hid_item h;
535 uint32_t temp;
536 uint32_t hpos;
537 uint32_t lpos;
538 int report_id = 0;
539
540 hpos = 0;
541 lpos = 0xFFFFFFFF;
542
543 memset(&h, 0, sizeof h);
544 for (d = hid_start_parse(r, 1 << k, id); hid_get_item(d, &h); ) {
545 if (h.kind == k) {
546 /* compute minimum */
547 if (lpos > h.pos)
548 lpos = h.pos;
549 /* compute end position */
550 temp = h.pos + (h.report_size * h.report_count);
551 /* compute maximum */
552 if (hpos < temp)
553 hpos = temp;
554 if (h.report_ID != 0)
555 report_id = 1;
556 }
557 }
558 hid_end_parse(d);
559
560 /* safety check - can happen in case of currupt descriptors */
561 if (lpos > hpos)
562 temp = 0;
563 else
564 temp = hpos - lpos;
565
566 /* return length in bytes rounded up */
567 return ((temp + 7) / 8 + report_id);
568 }
569
570 int
hid_locate(report_desc_t desc,unsigned int u,enum hid_kind k,hid_item_t * h,int id)571 hid_locate(report_desc_t desc, unsigned int u, enum hid_kind k,
572 hid_item_t *h, int id)
573 {
574 struct hid_data *d;
575
576 for (d = hid_start_parse(desc, 1 << k, id); hid_get_item(d, h); ) {
577 if (h->kind == k && !(h->flags & HIO_CONST) && h->usage == u) {
578 hid_end_parse(d);
579 return (1);
580 }
581 }
582 hid_end_parse(d);
583 h->report_size = 0;
584 return (0);
585 }
586