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