parse.c revision 1.3 1 /* $NetBSD: parse.c,v 1.3 2003/03/08 07:45:58 lukem 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.3 2003/03/08 07:45:58 lukem 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 multi;
54 int multimax;
55 int kindset;
56 int reportid;
57
58 /*
59 * The start of collection item has no report ID set, so save
60 * it until we know the ID.
61 */
62 hid_item_t savedcoll;
63 u_char hassavedcoll;
64 /*
65 * Absolute data position (bits) for input/output/feature.
66 * Assumes that hid_input, hid_output and hid_feature have
67 * values 0, 1 and 2.
68 */
69 unsigned int kindpos[3];
70 };
71
72 static int min(int x, int y) { return x < y ? x : y; }
73
74 static int hid_get_item_raw(hid_data_t s, hid_item_t *h);
75
76 static void
77 hid_clear_local(hid_item_t *c)
78 {
79
80 _DIAGASSERT(c != NULL);
81
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 c->report_size = 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 (s->multi < s->multimax) {
179 c->usage = s->usages[min(s->multi, s->nusage-1)];
180 s->multi++;
181 *h = *c;
182 /*
183 * 'multimax' is only non-zero if the current
184 * item kind is input/output/feature
185 */
186 h->pos = s->kindpos[c->kind];
187 s->kindpos[c->kind] += c->report_size;
188 h->next = 0;
189 return (1);
190 } else {
191 c->report_count = s->multimax;
192 s->multimax = 0;
193 s->nusage = 0;
194 hid_clear_local(c);
195 }
196 }
197 for (;;) {
198 p = s->p;
199 if (p >= s->end)
200 return (0);
201
202 bSize = *p++;
203 if (bSize == 0xfe) {
204 /* long item */
205 bSize = *p++;
206 bSize |= *p++ << 8;
207 bTag = *p++;
208 data = p;
209 p += bSize;
210 } else {
211 /* short item */
212 bTag = bSize >> 4;
213 bType = (bSize >> 2) & 3;
214 bSize &= 3;
215 if (bSize == 3) bSize = 4;
216 data = p;
217 p += bSize;
218 }
219 s->p = p;
220 /*
221 * The spec is unclear if the data is signed or unsigned.
222 */
223 switch(bSize) {
224 case 0:
225 dval = 0;
226 break;
227 case 1:
228 dval = /*(int8_t)*/*data++;
229 break;
230 case 2:
231 dval = *data++;
232 dval |= *data++ << 8;
233 dval = /*(int16_t)*/dval;
234 break;
235 case 4:
236 dval = *data++;
237 dval |= *data++ << 8;
238 dval |= *data++ << 16;
239 dval |= *data++ << 24;
240 break;
241 default:
242 return (-1);
243 }
244
245 switch (bType) {
246 case 0: /* Main */
247 switch (bTag) {
248 case 8: /* Input */
249 retkind = hid_input;
250 ret:
251 if (!(s->kindset & (1 << retkind))) {
252 /* Drop the items of this kind */
253 s->nusage = 0;
254 continue;
255 }
256 c->kind = retkind;
257 c->flags = dval;
258 if (c->flags & HIO_VARIABLE) {
259 s->multimax = c->report_count;
260 s->multi = 0;
261 c->report_count = 1;
262 if (s->minset) {
263 for (i = c->usage_minimum;
264 i <= c->usage_maximum;
265 i++) {
266 s->usages[s->nusage] = i;
267 if (s->nusage < MAXUSAGE-1)
268 s->nusage++;
269 }
270 c->usage_minimum = 0;
271 c->usage_maximum = 0;
272 s->minset = 0;
273 }
274 goto top;
275 } else {
276 if (s->minset)
277 c->usage = c->usage_minimum;
278 *h = *c;
279 h->next = 0;
280 h->pos = s->kindpos[c->kind];
281 s->kindpos[c->kind] +=
282 c->report_size * c->report_count;
283 hid_clear_local(c);
284 s->minset = 0;
285 return (1);
286 }
287 case 9: /* Output */
288 retkind = hid_output;
289 goto ret;
290 case 10: /* Collection */
291 c->kind = hid_collection;
292 c->collection = dval;
293 c->collevel++;
294 nc = *c;
295 hid_clear_local(c);
296 /*c->report_ID = NO_REPORT_ID;*/
297 s->nusage = 0;
298 if (s->hassavedcoll) {
299 *h = s->savedcoll;
300 h->report_ID = nc.report_ID;
301 s->savedcoll = nc;
302 return (1);
303 } else {
304 s->hassavedcoll = 1;
305 s->savedcoll = nc;
306 }
307 break;
308 case 11: /* Feature */
309 retkind = hid_feature;
310 goto ret;
311 case 12: /* End collection */
312 REPORT_SAVED_COLL;
313 c->kind = hid_endcollection;
314 c->collevel--;
315 *h = *c;
316 /*hid_clear_local(c);*/
317 s->nusage = 0;
318 return (1);
319 default:
320 return (-2);
321 }
322 break;
323
324 case 1: /* Global */
325 switch (bTag) {
326 case 0:
327 c->_usage_page = dval << 16;
328 break;
329 case 1:
330 c->logical_minimum = dval;
331 s->logminsize = bSize;
332 break;
333 case 2:
334 c->logical_maximum = dval;
335 break;
336 case 3:
337 c->physical_maximum = dval;
338 break;
339 case 4:
340 c->physical_maximum = dval;
341 break;
342 case 5:
343 c->unit_exponent = dval;
344 break;
345 case 6:
346 c->unit = dval;
347 break;
348 case 7:
349 c->report_size = dval;
350 break;
351 case 8:
352 c->report_ID = dval;
353 s->kindpos[hid_input] =
354 s->kindpos[hid_output] =
355 s->kindpos[hid_feature] = 0;
356 break;
357 case 9:
358 c->report_count = dval;
359 break;
360 case 10: /* Push */
361 hi = malloc(sizeof *hi);
362 *hi = s->cur;
363 c->next = hi;
364 break;
365 case 11: /* Pop */
366 hi = c->next;
367 s->cur = *hi;
368 free(hi);
369 break;
370 default:
371 return (-3);
372 }
373 break;
374 case 2: /* Local */
375 switch (bTag) {
376 case 0:
377 c->usage = c->_usage_page | dval;
378 if (s->nusage < MAXUSAGE)
379 s->usages[s->nusage++] = c->usage;
380 /* else XXX */
381 break;
382 case 1:
383 s->minset = 1;
384 c->usage_minimum = c->_usage_page | dval;
385 break;
386 case 2:
387 c->usage_maximum = c->_usage_page | dval;
388 break;
389 case 3:
390 c->designator_index = dval;
391 break;
392 case 4:
393 c->designator_minimum = dval;
394 break;
395 case 5:
396 c->designator_maximum = dval;
397 break;
398 case 7:
399 c->string_index = dval;
400 break;
401 case 8:
402 c->string_minimum = dval;
403 break;
404 case 9:
405 c->string_maximum = dval;
406 break;
407 case 10:
408 c->set_delimiter = dval;
409 break;
410 default:
411 return (-4);
412 }
413 break;
414 default:
415 return (-5);
416 }
417 }
418 }
419
420 int
421 hid_report_size(report_desc_t r, enum hid_kind k, int id)
422 {
423 struct hid_data *d;
424 hid_item_t h;
425 int size;
426
427 _DIAGASSERT(r != NULL);
428
429 memset(&h, 0, sizeof h);
430 size = 0;
431 for (d = hid_start_parse(r, 1<<k, id); hid_get_item(d, &h); ) {
432 if (h.report_ID == id && h.kind == k) {
433 size = d->kindpos[k];
434 }
435 }
436 hid_end_parse(d);
437 return ((size + 7) / 8);
438 }
439
440 int
441 hid_locate(report_desc_t desc, unsigned int u, enum hid_kind k,
442 hid_item_t *h, int id)
443 {
444 hid_data_t d;
445
446 _DIAGASSERT(desc != NULL);
447 _DIAGASSERT(h != NULL);
448
449 for (d = hid_start_parse(desc, 1<<k, id); hid_get_item(d, h); ) {
450 if (h->kind == k && !(h->flags & HIO_CONST) && h->usage == u) {
451 hid_end_parse(d);
452 return (1);
453 }
454 }
455 hid_end_parse(d);
456 h->report_size = 0;
457 return (0);
458 }
459