1 /* $NetBSD: parse.c,v 1.11 2020/04/04 21:26:16 fox Exp $ */
2
3 /*
4 * Copyright (c) 1999, 2001 Lennart Augustsson <augustss@NetBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __RCSID("$NetBSD: parse.c,v 1.11 2020/04/04 21:26:16 fox Exp $");
31
32 #include <assert.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/time.h>
36
37 #include <dev/usb/usb.h>
38 #include <dev/hid/hid.h>
39
40 #include "usbhid.h"
41 #include "usbvar.h"
42
43 #define MAXUSAGE 100
44 struct hid_data {
45 u_char *start;
46 u_char *end;
47 u_char *p;
48 hid_item_t cur;
49 unsigned int usages[MAXUSAGE];
50 int nusage;
51 int minset;
52 int logminsize;
53 int phyminsize;
54 int multi;
55 int multimax;
56 int kindset;
57 int reportid;
58
59 /*
60 * The start of collection item has no report ID set, so save
61 * it until we know the ID.
62 */
63 hid_item_t savedcoll;
64 u_char hassavedcoll;
65 /*
66 * Absolute data position (bits) for input/output/feature.
67 * Assumes that hid_input, hid_output and hid_feature have
68 * values 0, 1 and 2.
69 */
70 unsigned int kindpos[3];
71 };
72
min(int x,int y)73 static int min(int x, int y) { return x < y ? x : y; }
74
75 static int hid_get_item_raw(hid_data_t s, hid_item_t *h);
76
77 static void
hid_clear_local(hid_item_t * c)78 hid_clear_local(hid_item_t *c)
79 {
80
81 _DIAGASSERT(c != NULL);
82
83 c->usage = 0;
84 c->usage_minimum = 0;
85 c->usage_maximum = 0;
86 c->designator_index = 0;
87 c->designator_minimum = 0;
88 c->designator_maximum = 0;
89 c->string_index = 0;
90 c->string_minimum = 0;
91 c->string_maximum = 0;
92 c->set_delimiter = 0;
93 }
94
95 hid_data_t
hid_start_parse(report_desc_t d,int kindset,int id)96 hid_start_parse(report_desc_t d, int kindset, int id)
97 {
98 struct hid_data *s;
99
100 _DIAGASSERT(d != NULL);
101
102 s = malloc(sizeof *s);
103 memset(s, 0, sizeof *s);
104 s->start = s->p = d->data;
105 s->end = d->data + d->size;
106 s->kindset = kindset;
107 s->reportid = id;
108 s->hassavedcoll = 0;
109 return (s);
110 }
111
112 void
hid_end_parse(hid_data_t s)113 hid_end_parse(hid_data_t s)
114 {
115
116 _DIAGASSERT(s != NULL);
117
118 while (s->cur.next) {
119 hid_item_t *hi = s->cur.next->next;
120 free(s->cur.next);
121 s->cur.next = hi;
122 }
123 free(s);
124 }
125
126 int
hid_get_item(hid_data_t s,hid_item_t * h)127 hid_get_item(hid_data_t s, hid_item_t *h)
128 {
129 int r;
130
131 for (;;) {
132 r = hid_get_item_raw(s, h);
133 if (r <= 0)
134 break;
135 if (h->report_ID == s->reportid || s->reportid == -1)
136 break;
137 }
138 return (r);
139 }
140
141 #define REPORT_SAVED_COLL \
142 do { \
143 if (s->hassavedcoll) { \
144 *h = s->savedcoll; \
145 h->report_ID = c->report_ID; \
146 s->hassavedcoll = 0; \
147 return (1); \
148 } \
149 } while(/*LINTED*/ 0)
150
151 static int
hid_get_item_raw(hid_data_t s,hid_item_t * h)152 hid_get_item_raw(hid_data_t s, hid_item_t *h)
153 {
154 hid_item_t *c;
155 unsigned int bTag = 0, bType = 0, bSize;
156 unsigned char *data;
157 int dval;
158 unsigned char *p;
159 hid_item_t *hi;
160 hid_item_t nc;
161 int i;
162 hid_kind_t retkind;
163
164 _DIAGASSERT(s != NULL);
165 _DIAGASSERT(h != NULL);
166
167 c = &s->cur;
168
169 top:
170 if (s->multimax) {
171 REPORT_SAVED_COLL;
172 if (c->logical_minimum >= c->logical_maximum) {
173 if (s->logminsize == 1)
174 c->logical_minimum =(int8_t)c->logical_minimum;
175 else if (s->logminsize == 2)
176 c->logical_minimum =(int16_t)c->logical_minimum;
177 }
178 if (c->physical_minimum >= c->physical_maximum) {
179 if (s->phyminsize == 1)
180 c->physical_minimum =
181 (int8_t)c->physical_minimum;
182 else if (s->phyminsize == 2)
183 c->physical_minimum =
184 (int16_t)c->physical_minimum;
185 }
186 if (s->multi < s->multimax) {
187 c->usage = s->usages[min(s->multi, s->nusage-1)];
188 s->multi++;
189 *h = *c;
190 /*
191 * 'multimax' is only non-zero if the current
192 * item kind is input/output/feature
193 */
194 h->pos = s->kindpos[c->kind];
195 s->kindpos[c->kind] += c->report_size;
196 h->next = 0;
197 return (1);
198 } else {
199 c->report_count = s->multimax;
200 s->multimax = 0;
201 s->nusage = 0;
202 hid_clear_local(c);
203 }
204 }
205 for (;;) {
206 p = s->p;
207 if (p >= s->end)
208 return (0);
209
210 bSize = *p++;
211 if (bSize == 0xfe) {
212 /* long item */
213 bSize = *p++;
214 bSize |= *p++ << 8;
215 bTag = *p++;
216 data = p;
217 p += bSize;
218 } else {
219 /* short item */
220 bTag = bSize >> 4;
221 bType = (bSize >> 2) & 3;
222 bSize &= 3;
223 if (bSize == 3) bSize = 4;
224 data = p;
225 p += bSize;
226 }
227 s->p = p;
228 /*
229 * The spec is unclear if the data is signed or unsigned.
230 */
231 switch(bSize) {
232 case 0:
233 dval = 0;
234 break;
235 case 1:
236 dval = /*(int8_t)*/*data++;
237 break;
238 case 2:
239 dval = *data++;
240 dval |= *data++ << 8;
241 break;
242 case 4:
243 dval = *data++;
244 dval |= *data++ << 8;
245 dval |= *data++ << 16;
246 dval |= ((uint32_t)*data++) << 24;
247 break;
248 default:
249 return (-1);
250 }
251
252 switch (bType) {
253 case 0: /* Main */
254 switch (bTag) {
255 case 8: /* Input */
256 retkind = hid_input;
257 ret:
258 if (!(s->kindset & (1 << retkind))) {
259 /* Drop the items of this kind */
260 s->nusage = 0;
261 continue;
262 }
263 c->kind = retkind;
264 c->flags = dval;
265 if (c->flags & HIO_VARIABLE) {
266 s->multimax = c->report_count;
267 s->multi = 0;
268 c->report_count = 1;
269 if (s->minset) {
270 for (i = c->usage_minimum;
271 i <= c->usage_maximum;
272 i++) {
273 s->usages[s->nusage] = i;
274 if (s->nusage < MAXUSAGE-1)
275 s->nusage++;
276 }
277 c->usage_minimum = 0;
278 c->usage_maximum = 0;
279 s->minset = 0;
280 }
281 goto top;
282 } else {
283 if (s->minset)
284 c->usage = c->usage_minimum;
285 *h = *c;
286 h->next = 0;
287 h->pos = s->kindpos[c->kind];
288 s->kindpos[c->kind] +=
289 c->report_size * c->report_count;
290 hid_clear_local(c);
291 s->minset = 0;
292 return (1);
293 }
294 case 9: /* Output */
295 retkind = hid_output;
296 goto ret;
297 case 10: /* Collection */
298 c->kind = hid_collection;
299 c->collection = dval;
300 c->collevel++;
301 nc = *c;
302 hid_clear_local(c);
303 /*c->report_ID = NO_REPORT_ID;*/
304 s->nusage = 0;
305 if (s->hassavedcoll) {
306 *h = s->savedcoll;
307 h->report_ID = nc.report_ID;
308 s->savedcoll = nc;
309 return (1);
310 } else {
311 s->hassavedcoll = 1;
312 s->savedcoll = nc;
313 }
314 break;
315 case 11: /* Feature */
316 retkind = hid_feature;
317 goto ret;
318 case 12: /* End collection */
319 REPORT_SAVED_COLL;
320 c->kind = hid_endcollection;
321 c->collevel--;
322 *h = *c;
323 /*hid_clear_local(c);*/
324 s->nusage = 0;
325 return (1);
326 default:
327 return (-2);
328 }
329 break;
330
331 case 1: /* Global */
332 switch (bTag) {
333 case 0:
334 c->_usage_page = dval << 16;
335 break;
336 case 1:
337 c->logical_minimum = dval;
338 s->logminsize = bSize;
339 break;
340 case 2:
341 c->logical_maximum = dval;
342 break;
343 case 3:
344 c->physical_minimum = dval;
345 s->phyminsize = bSize;
346 break;
347 case 4:
348 c->physical_maximum = dval;
349 break;
350 case 5:
351 if ( dval > 7 && dval < 0x10)
352 c->unit_exponent = -16 + dval;
353 else
354 c->unit_exponent = dval;
355 break;
356 case 6:
357 c->unit = dval;
358 break;
359 case 7:
360 c->report_size = dval;
361 break;
362 case 8:
363 c->report_ID = dval;
364 s->kindpos[hid_input] =
365 s->kindpos[hid_output] =
366 s->kindpos[hid_feature] = 0;
367 break;
368 case 9:
369 c->report_count = dval;
370 break;
371 case 10: /* Push */
372 hi = malloc(sizeof *hi);
373 *hi = s->cur;
374 c->next = hi;
375 break;
376 case 11: /* Pop */
377 hi = c->next;
378 if (hi == NULL)
379 break;
380 s->cur = *hi;
381 free(hi);
382 break;
383 default:
384 return (-3);
385 }
386 break;
387 case 2: /* Local */
388 switch (bTag) {
389 case 0:
390 c->usage = c->_usage_page | dval;
391 if (s->nusage < MAXUSAGE)
392 s->usages[s->nusage++] = c->usage;
393 /* else XXX */
394 break;
395 case 1:
396 s->minset = 1;
397 c->usage_minimum = c->_usage_page | dval;
398 break;
399 case 2:
400 c->usage_maximum = c->_usage_page | dval;
401 break;
402 case 3:
403 c->designator_index = dval;
404 break;
405 case 4:
406 c->designator_minimum = dval;
407 break;
408 case 5:
409 c->designator_maximum = dval;
410 break;
411 case 7:
412 c->string_index = dval;
413 break;
414 case 8:
415 c->string_minimum = dval;
416 break;
417 case 9:
418 c->string_maximum = dval;
419 break;
420 case 10:
421 c->set_delimiter = dval;
422 break;
423 default:
424 return (-4);
425 }
426 break;
427 default:
428 return (-5);
429 }
430 }
431 }
432
433 int
hid_report_size(report_desc_t r,enum hid_kind k,int id)434 hid_report_size(report_desc_t r, enum hid_kind k, int id)
435 {
436 struct hid_data *d;
437 hid_item_t h;
438 int size;
439
440 _DIAGASSERT(r != NULL);
441
442 memset(&h, 0, sizeof h);
443 size = 0;
444 for (d = hid_start_parse(r, 1<<k, id); hid_get_item(d, &h); ) {
445 if (h.report_ID == id && h.kind == k) {
446 size = d->kindpos[k];
447 }
448 }
449 hid_end_parse(d);
450 return ((size + 7) / 8);
451 }
452
453 int
hid_locate(report_desc_t desc,unsigned int u,enum hid_kind k,hid_item_t * h,int id)454 hid_locate(report_desc_t desc, unsigned int u, enum hid_kind k,
455 hid_item_t *h, int id)
456 {
457 hid_data_t d;
458
459 _DIAGASSERT(desc != NULL);
460 _DIAGASSERT(h != NULL);
461
462 for (d = hid_start_parse(desc, 1<<k, id); hid_get_item(d, h); ) {
463 if (h->kind == k && !(h->flags & HIO_CONST) && h->usage == u) {
464 hid_end_parse(d);
465 return (1);
466 }
467 }
468 hid_end_parse(d);
469 h->report_size = 0;
470 return (0);
471 }
472