1 /* $NetBSD: parse.c,v 1.11 2000/09/24 02:19:54 augustss 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 <assert.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/time.h>
33
34 #include "usbhid.h"
35 #include "usbvar.h"
36 #include "usbcompat.h"
37
38 #define MAXUSAGE 100
39 struct hid_data {
40 unsigned char *start;
41 unsigned char *end;
42 unsigned char *p;
43 hid_item_t cur;
44 unsigned int usages[MAXUSAGE];
45 int nusage;
46 int minset;
47 int logminsize;
48 int multi;
49 int multimax;
50 int kindset;
51 int reportid;
52
53 /*
54 * The start of collection item has no report ID set, so save
55 * it until we know the ID.
56 */
57 hid_item_t savedcoll;
58 unsigned char hassavedcoll;
59 /*
60 * Absolute data position (bits) for input/output/feature.
61 * Assumes that hid_input, hid_output and hid_feature have
62 * values 0, 1 and 2.
63 */
64 unsigned int kindpos[3];
65 };
66
min(int x,int y)67 static int min(int x, int y) { return x < y ? x : y; }
68
69 static int hid_get_item_raw(hid_data_t s, hid_item_t *h);
70
71 static void
hid_clear_local(hid_item_t * c)72 hid_clear_local(hid_item_t *c)
73 {
74 c->usage = 0;
75 c->usage_minimum = 0;
76 c->usage_maximum = 0;
77 c->designator_index = 0;
78 c->designator_minimum = 0;
79 c->designator_maximum = 0;
80 c->string_index = 0;
81 c->string_minimum = 0;
82 c->string_maximum = 0;
83 c->set_delimiter = 0;
84 }
85
86 hid_data_t
hid_start_parse(report_desc_t d,int kindset,int id)87 hid_start_parse(report_desc_t d, int kindset, int id)
88 {
89 struct hid_data *s;
90
91 s = (struct hid_data *)malloc(sizeof *s);
92 memset(s, 0, sizeof *s);
93 s->start = s->p = d->data;
94 s->end = d->data + d->size;
95 s->kindset = kindset;
96 s->reportid = id;
97 s->hassavedcoll = 0;
98 return (s);
99 }
100
101 void
hid_end_parse(hid_data_t s)102 hid_end_parse(hid_data_t s)
103 {
104 while (s->cur.next) {
105 hid_item_t *hi = s->cur.next->next;
106 free(s->cur.next);
107 s->cur.next = hi;
108 }
109 free(s);
110 }
111
112 int
hid_get_item(hid_data_t s,hid_item_t * h)113 hid_get_item(hid_data_t s, hid_item_t *h)
114 {
115 int r;
116
117 for (;;) {
118 r = hid_get_item_raw(s, h);
119 if (r <= 0)
120 break;
121 if (h->report_ID == s->reportid || s->reportid == -1)
122 break;
123 }
124 return (r);
125 }
126
127 #define REPORT_SAVED_COLL \
128 do { \
129 if (s->hassavedcoll) { \
130 *h = s->savedcoll; \
131 h->report_ID = c->report_ID; \
132 s->hassavedcoll = 0; \
133 return (1); \
134 } \
135 } while(/*LINTED*/ 0)
136
137 static int
hid_get_item_raw(hid_data_t s,hid_item_t * h)138 hid_get_item_raw(hid_data_t s, hid_item_t *h)
139 {
140 hid_item_t *c;
141 unsigned int bTag = 0, bType = 0, bSize;
142 unsigned char *data;
143 unsigned int dval;
144 unsigned char *p;
145 hid_item_t *hi;
146 hid_item_t nc;
147 unsigned int i;
148 hid_kind_t retkind;
149
150 c = &s->cur;
151
152 top:
153 if (s->multimax) {
154 REPORT_SAVED_COLL;
155 if (c->logical_minimum >= c->logical_maximum) {
156 if (s->logminsize == 1)
157 c->logical_minimum =(signed char)c->logical_minimum;
158 else if (s->logminsize == 2)
159 c->logical_minimum =(signed short)c->logical_minimum;
160 }
161 if (s->multi < s->multimax) {
162 c->usage = s->usages[min(s->multi, s->nusage-1)];
163 s->multi++;
164 *h = *c;
165 /*
166 * 'multimax' is only non-zero if the current
167 * item kind is input/output/feature
168 */
169 h->pos = s->kindpos[c->kind];
170 s->kindpos[c->kind] += c->report_size;
171 h->next = 0;
172 return (1);
173 } else {
174 c->report_count = s->multimax;
175 s->multimax = 0;
176 s->nusage = 0;
177 hid_clear_local(c);
178 }
179 }
180 for (;;) {
181 p = s->p;
182 if (p >= s->end)
183 return (0);
184
185 bSize = *p++;
186 if (bSize == 0xfe) {
187 /* long item */
188 bSize = *p++;
189 bSize |= *p++ << 8;
190 bTag = *p++;
191 data = p;
192 p += bSize;
193 } else {
194 /* short item */
195 bTag = bSize >> 4;
196 bType = (bSize >> 2) & 3;
197 bSize &= 3;
198 if (bSize == 3) bSize = 4;
199 data = p;
200 p += bSize;
201 }
202 s->p = p;
203 /*
204 * The spec is unclear if the data is signed or unsigned.
205 */
206 switch(bSize) {
207 case 0:
208 dval = 0;
209 break;
210 case 1:
211 dval = *data++;
212 break;
213 case 2:
214 dval = *data++;
215 dval |= *data++ << 8;
216 break;
217 case 4:
218 dval = *data++;
219 dval |= *data++ << 8;
220 dval |= *data++ << 16;
221 dval |= *data++ << 24;
222 break;
223 default:
224 return (-1);
225 }
226
227 switch (bType) {
228 case 0: /* Main */
229 switch (bTag) {
230 case 8: /* Input */
231 retkind = hid_input;
232 ret:
233 if (!(s->kindset & (1 << retkind))) {
234 /* Drop the items of this kind */
235 s->nusage = 0;
236 continue;
237 }
238 c->kind = retkind;
239 c->flags = dval;
240 if (c->flags & HIO_VARIABLE) {
241 s->multimax = c->report_count;
242 s->multi = 0;
243 c->report_count = 1;
244 if (s->minset) {
245 for (i = c->usage_minimum;
246 i <= c->usage_maximum;
247 i++) {
248 s->usages[s->nusage] = i;
249 if (s->nusage < MAXUSAGE-1)
250 s->nusage++;
251 }
252 c->usage_minimum = 0;
253 c->usage_maximum = 0;
254 s->minset = 0;
255 }
256 goto top;
257 } else {
258 if (s->minset)
259 c->usage = c->usage_minimum;
260 *h = *c;
261 h->next = 0;
262 h->pos = s->kindpos[c->kind];
263 s->kindpos[c->kind] +=
264 c->report_size * c->report_count;
265 hid_clear_local(c);
266 s->minset = 0;
267 return (1);
268 }
269 case 9: /* Output */
270 retkind = hid_output;
271 goto ret;
272 case 10: /* Collection */
273 c->kind = hid_collection;
274 c->collection = dval;
275 c->collevel++;
276 nc = *c;
277 hid_clear_local(c);
278 /*c->report_ID = NO_REPORT_ID;*/
279 s->nusage = 0;
280 if (s->hassavedcoll) {
281 *h = s->savedcoll;
282 h->report_ID = nc.report_ID;
283 s->savedcoll = nc;
284 return (1);
285 } else {
286 s->hassavedcoll = 1;
287 s->savedcoll = nc;
288 }
289 break;
290 case 11: /* Feature */
291 retkind = hid_feature;
292 goto ret;
293 case 12: /* End collection */
294 REPORT_SAVED_COLL;
295 c->kind = hid_endcollection;
296 c->collevel--;
297 *h = *c;
298 /*hid_clear_local(c);*/
299 s->nusage = 0;
300 return (1);
301 default:
302 return (-2);
303 }
304 break;
305
306 case 1: /* Global */
307 switch (bTag) {
308 case 0:
309 c->_usage_page = dval << 16;
310 break;
311 case 1:
312 c->logical_minimum = dval;
313 s->logminsize = bSize;
314 break;
315 case 2:
316 c->logical_maximum = dval;
317 break;
318 case 3:
319 c->physical_minimum = dval;
320 break;
321 case 4:
322 c->physical_maximum = dval;
323 break;
324 case 5:
325 c->unit_exponent = dval;
326 break;
327 case 6:
328 c->unit = dval;
329 break;
330 case 7:
331 c->report_size = dval;
332 break;
333 case 8:
334 c->report_ID = dval;
335 s->kindpos[hid_input] =
336 s->kindpos[hid_output] =
337 s->kindpos[hid_feature] = 0;
338 break;
339 case 9:
340 c->report_count = dval;
341 break;
342 case 10: /* Push */
343 hi = (hid_item_t *)malloc(sizeof *hi);
344 *hi = s->cur;
345 c->next = hi;
346 break;
347 case 11: /* Pop */
348 hi = c->next;
349 s->cur = *hi;
350 free(hi);
351 break;
352 default:
353 return (-3);
354 }
355 break;
356 case 2: /* Local */
357 switch (bTag) {
358 case 0:
359 c->usage = c->_usage_page | dval;
360 if (s->nusage < MAXUSAGE)
361 s->usages[s->nusage++] = c->usage;
362 /* else XXX */
363 break;
364 case 1:
365 s->minset = 1;
366 c->usage_minimum = c->_usage_page | dval;
367 break;
368 case 2:
369 c->usage_maximum = c->_usage_page | dval;
370 break;
371 case 3:
372 c->designator_index = dval;
373 break;
374 case 4:
375 c->designator_minimum = dval;
376 break;
377 case 5:
378 c->designator_maximum = dval;
379 break;
380 case 7:
381 c->string_index = dval;
382 break;
383 case 8:
384 c->string_minimum = dval;
385 break;
386 case 9:
387 c->string_maximum = dval;
388 break;
389 case 10:
390 c->set_delimiter = dval;
391 break;
392 default:
393 return (-4);
394 }
395 break;
396 default:
397 return (-5);
398 }
399 }
400 }
401
402 int
hid_report_size(report_desc_t r,enum hid_kind k,int id)403 hid_report_size(report_desc_t r, enum hid_kind k, int id)
404 {
405 struct hid_data *d;
406 hid_item_t h;
407 int size;
408
409 memset(&h, 0, sizeof h);
410 size = 0;
411 for (d = hid_start_parse(r, 1<<k, id); hid_get_item(d, &h); ) {
412 if (h.report_ID == id && h.kind == k) {
413 size = d->kindpos[k];
414 }
415 }
416 hid_end_parse(d);
417 return ((size + 7) / 8);
418 }
419
420 int
hid_locate(report_desc_t desc,unsigned int u,enum hid_kind k,hid_item_t * h,int id)421 hid_locate(report_desc_t desc, unsigned int u, enum hid_kind k,
422 hid_item_t *h, int id)
423 {
424 hid_data_t d;
425
426 for (d = hid_start_parse(desc, 1<<k, id); hid_get_item(d, h); ) {
427 if (h->kind == k && !(h->flags & HIO_CONST) && h->usage == u) {
428 hid_end_parse(d);
429 return (1);
430 }
431 }
432 hid_end_parse(d);
433 h->report_size = 0;
434 return (0);
435 }
436