parse.c revision 1.7 1 /* $NetBSD: parse.c,v 1.7 2010/08/13 19:56:34 jakllsch Exp $ */
2
3 /*
4 * Copyright (c) 1999, 2001 Lennart Augustsson <augustss (at) 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.7 2010/08/13 19:56:34 jakllsch 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/usb/usbhid.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
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
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
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
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
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
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 dval = /*(int16_t)*/dval;
242 break;
243 case 4:
244 dval = *data++;
245 dval |= *data++ << 8;
246 dval |= *data++ << 16;
247 dval |= *data++ << 24;
248 break;
249 default:
250 return (-1);
251 }
252
253 switch (bType) {
254 case 0: /* Main */
255 switch (bTag) {
256 case 8: /* Input */
257 retkind = hid_input;
258 ret:
259 if (!(s->kindset & (1 << retkind))) {
260 /* Drop the items of this kind */
261 s->nusage = 0;
262 continue;
263 }
264 c->kind = retkind;
265 c->flags = dval;
266 if (c->flags & HIO_VARIABLE) {
267 s->multimax = c->report_count;
268 s->multi = 0;
269 c->report_count = 1;
270 if (s->minset) {
271 for (i = c->usage_minimum;
272 i <= c->usage_maximum;
273 i++) {
274 s->usages[s->nusage] = i;
275 if (s->nusage < MAXUSAGE-1)
276 s->nusage++;
277 }
278 c->usage_minimum = 0;
279 c->usage_maximum = 0;
280 s->minset = 0;
281 }
282 goto top;
283 } else {
284 if (s->minset)
285 c->usage = c->usage_minimum;
286 *h = *c;
287 h->next = 0;
288 h->pos = s->kindpos[c->kind];
289 s->kindpos[c->kind] +=
290 c->report_size * c->report_count;
291 hid_clear_local(c);
292 s->minset = 0;
293 return (1);
294 }
295 case 9: /* Output */
296 retkind = hid_output;
297 goto ret;
298 case 10: /* Collection */
299 c->kind = hid_collection;
300 c->collection = dval;
301 c->collevel++;
302 nc = *c;
303 hid_clear_local(c);
304 /*c->report_ID = NO_REPORT_ID;*/
305 s->nusage = 0;
306 if (s->hassavedcoll) {
307 *h = s->savedcoll;
308 h->report_ID = nc.report_ID;
309 s->savedcoll = nc;
310 return (1);
311 } else {
312 s->hassavedcoll = 1;
313 s->savedcoll = nc;
314 }
315 break;
316 case 11: /* Feature */
317 retkind = hid_feature;
318 goto ret;
319 case 12: /* End collection */
320 REPORT_SAVED_COLL;
321 c->kind = hid_endcollection;
322 c->collevel--;
323 *h = *c;
324 /*hid_clear_local(c);*/
325 s->nusage = 0;
326 return (1);
327 default:
328 return (-2);
329 }
330 break;
331
332 case 1: /* Global */
333 switch (bTag) {
334 case 0:
335 c->_usage_page = dval << 16;
336 break;
337 case 1:
338 c->logical_minimum = dval;
339 s->logminsize = bSize;
340 break;
341 case 2:
342 c->logical_maximum = dval;
343 break;
344 case 3:
345 c->physical_minimum = dval;
346 s->phyminsize = bSize;
347 break;
348 case 4:
349 c->physical_maximum = dval;
350 break;
351 case 5:
352 if ( dval > 7 && dval < 0x10)
353 c->unit_exponent = -16 + dval;
354 else
355 c->unit_exponent = dval;
356 break;
357 case 6:
358 c->unit = dval;
359 break;
360 case 7:
361 c->report_size = dval;
362 break;
363 case 8:
364 c->report_ID = dval;
365 s->kindpos[hid_input] =
366 s->kindpos[hid_output] =
367 s->kindpos[hid_feature] = 0;
368 break;
369 case 9:
370 c->report_count = dval;
371 break;
372 case 10: /* Push */
373 hi = malloc(sizeof *hi);
374 *hi = s->cur;
375 c->next = hi;
376 break;
377 case 11: /* Pop */
378 hi = c->next;
379 s->cur = *hi;
380 free(hi);
381 break;
382 default:
383 return (-3);
384 }
385 break;
386 case 2: /* Local */
387 switch (bTag) {
388 case 0:
389 c->usage = c->_usage_page | dval;
390 if (s->nusage < MAXUSAGE)
391 s->usages[s->nusage++] = c->usage;
392 /* else XXX */
393 break;
394 case 1:
395 s->minset = 1;
396 c->usage_minimum = c->_usage_page | dval;
397 break;
398 case 2:
399 c->usage_maximum = c->_usage_page | dval;
400 break;
401 case 3:
402 c->designator_index = dval;
403 break;
404 case 4:
405 c->designator_minimum = dval;
406 break;
407 case 5:
408 c->designator_maximum = dval;
409 break;
410 case 7:
411 c->string_index = dval;
412 break;
413 case 8:
414 c->string_minimum = dval;
415 break;
416 case 9:
417 c->string_maximum = dval;
418 break;
419 case 10:
420 c->set_delimiter = dval;
421 break;
422 default:
423 return (-4);
424 }
425 break;
426 default:
427 return (-5);
428 }
429 }
430 }
431
432 int
433 hid_report_size(report_desc_t r, enum hid_kind k, int id)
434 {
435 struct hid_data *d;
436 hid_item_t h;
437 int size;
438
439 _DIAGASSERT(r != NULL);
440
441 memset(&h, 0, sizeof h);
442 size = 0;
443 for (d = hid_start_parse(r, 1<<k, id); hid_get_item(d, &h); ) {
444 if (h.report_ID == id && h.kind == k) {
445 size = d->kindpos[k];
446 }
447 }
448 hid_end_parse(d);
449 return ((size + 7) / 8);
450 }
451
452 int
453 hid_locate(report_desc_t desc, unsigned int u, enum hid_kind k,
454 hid_item_t *h, int id)
455 {
456 hid_data_t d;
457
458 _DIAGASSERT(desc != NULL);
459 _DIAGASSERT(h != NULL);
460
461 for (d = hid_start_parse(desc, 1<<k, id); hid_get_item(d, h); ) {
462 if (h->kind == k && !(h->flags & HIO_CONST) && h->usage == u) {
463 hid_end_parse(d);
464 return (1);
465 }
466 }
467 hid_end_parse(d);
468 h->report_size = 0;
469 return (0);
470 }
471