xref: /dragonfly/lib/libu4bhid/parse.c (revision 19380330)
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 <bus/u4b/usb.h>
35 #include <bus/u4b/usbhid.h>
36 
37 #include "usbhid.h"
38 #include "usbvar.h"
39 
40 #define	MAXUSAGE 100
41 #define	MAXPUSH 4
42 #define	MAXID 64
43 #define	ITEMTYPES 3
44 
45 struct hid_pos_data {
46 	int32_t rid;
47 	uint32_t pos[ITEMTYPES];
48 };
49 
50 struct hid_data {
51 	const uint8_t *start;
52 	const uint8_t *end;
53 	const uint8_t *p;
54 	struct hid_item cur[MAXPUSH];
55 	struct hid_pos_data last_pos[MAXID];
56 	uint32_t pos[ITEMTYPES];
57 	int32_t usages_min[MAXUSAGE];
58 	int32_t usages_max[MAXUSAGE];
59 	int32_t usage_last;	/* last seen usage */
60 	uint32_t loc_size;	/* last seen size */
61 	uint32_t loc_count;	/* last seen count */
62 	uint8_t	kindset;	/* we have 5 kinds so 8 bits are enough */
63 	uint8_t	pushlevel;	/* current pushlevel */
64 	uint8_t	ncount;		/* end usage item count */
65 	uint8_t icount;		/* current usage item count */
66 	uint8_t	nusage;		/* end "usages_min/max" index */
67 	uint8_t	iusage;		/* current "usages_min/max" index */
68 	uint8_t ousage;		/* current "usages_min/max" offset */
69 	uint8_t	susage;		/* usage set flags */
70 };
71 
72 /*------------------------------------------------------------------------*
73  *	hid_clear_local
74  *------------------------------------------------------------------------*/
75 static void
76 hid_clear_local(hid_item_t *c)
77 {
78 
79 	c->usage = 0;
80 	c->usage_minimum = 0;
81 	c->usage_maximum = 0;
82 	c->designator_index = 0;
83 	c->designator_minimum = 0;
84 	c->designator_maximum = 0;
85 	c->string_index = 0;
86 	c->string_minimum = 0;
87 	c->string_maximum = 0;
88 	c->set_delimiter = 0;
89 }
90 
91 static void
92 hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t next_rID)
93 {
94 	uint8_t i, j;
95 
96 	/* check for same report ID - optimise */
97 
98 	if (c->report_ID == next_rID)
99 		return;
100 
101 	/* save current position for current rID */
102 
103 	if (c->report_ID == 0) {
104 		i = 0;
105 	} else {
106 		for (i = 1; i != MAXID; i++) {
107 			if (s->last_pos[i].rid == c->report_ID)
108 				break;
109 			if (s->last_pos[i].rid == 0)
110 				break;
111 		}
112 	}
113 	if (i != MAXID) {
114 		s->last_pos[i].rid = c->report_ID;
115 		for (j = 0; j < ITEMTYPES; j++)
116 			s->last_pos[i].pos[j] = s->pos[j];
117 	}
118 
119 	/* store next report ID */
120 
121 	c->report_ID = next_rID;
122 
123 	/* lookup last position for next rID */
124 
125 	if (next_rID == 0) {
126 		i = 0;
127 	} else {
128 		for (i = 1; i != MAXID; i++) {
129 			if (s->last_pos[i].rid == next_rID)
130 				break;
131 			if (s->last_pos[i].rid == 0)
132 				break;
133 		}
134 	}
135 	if (i != MAXID) {
136 		s->last_pos[i].rid = next_rID;
137 		for (j = 0; j < ITEMTYPES; j++)
138 			s->pos[j] = s->last_pos[i].pos[j];
139 	} else {
140 		for (j = 0; j < ITEMTYPES; j++)
141 			s->pos[j] = 0;	/* Out of RID entries. */
142 	}
143 }
144 
145 /*------------------------------------------------------------------------*
146  *	hid_start_parse
147  *------------------------------------------------------------------------*/
148 hid_data_t
149 hid_start_parse(report_desc_t d, int kindset, int id __unused)
150 {
151 	struct hid_data *s;
152 
153 	s = malloc(sizeof *s);
154 	memset(s, 0, sizeof *s);
155 	s->start = s->p = d->data;
156 	s->end = d->data + d->size;
157 	s->kindset = kindset;
158 	return (s);
159 }
160 
161 /*------------------------------------------------------------------------*
162  *	hid_end_parse
163  *------------------------------------------------------------------------*/
164 void
165 hid_end_parse(hid_data_t s)
166 {
167 
168 	if (s == NULL)
169 		return;
170 
171 	free(s);
172 }
173 
174 /*------------------------------------------------------------------------*
175  *	get byte from HID descriptor
176  *------------------------------------------------------------------------*/
177 static uint8_t
178 hid_get_byte(struct hid_data *s, const uint16_t wSize)
179 {
180 	const uint8_t *ptr;
181 	uint8_t retval;
182 
183 	ptr = s->p;
184 
185 	/* check if end is reached */
186 	if (ptr == s->end)
187 		return (0);
188 
189 	/* read out a byte */
190 	retval = *ptr;
191 
192 	/* check if data pointer can be advanced by "wSize" bytes */
193 	if ((s->end - ptr) < wSize)
194 		ptr = s->end;
195 	else
196 		ptr += wSize;
197 
198 	/* update pointer */
199 	s->p = ptr;
200 
201 	return (retval);
202 }
203 
204 /*------------------------------------------------------------------------*
205  *	hid_get_item
206  *------------------------------------------------------------------------*/
207 int
208 hid_get_item(hid_data_t s, hid_item_t *h)
209 {
210 	hid_item_t *c;
211 	unsigned int bTag, bType, bSize;
212 	int32_t mask;
213 	int32_t dval;
214 
215 	if (s == NULL)
216 		return (0);
217 
218 	c = &s->cur[s->pushlevel];
219 
220  top:
221 	/* check if there is an array of items */
222 	if (s->icount < s->ncount) {
223 		/* get current usage */
224 		if (s->iusage < s->nusage) {
225 			dval = s->usages_min[s->iusage] + s->ousage;
226 			c->usage = dval;
227 			s->usage_last = dval;
228 			if (dval == s->usages_max[s->iusage]) {
229 				s->iusage ++;
230 				s->ousage = 0;
231 			} else {
232 				s->ousage ++;
233 			}
234 		} else {
235 			/* Using last usage */
236 			dval = s->usage_last;
237 		}
238 		s->icount ++;
239 		/*
240 		 * Only copy HID item, increment position and return
241 		 * if correct kindset!
242 		 */
243 		if (s->kindset & (1 << c->kind)) {
244 			*h = *c;
245 			h->pos = s->pos[c->kind];
246 			s->pos[c->kind] += c->report_size * c->report_count;
247 			return (1);
248 		}
249 	}
250 
251 	/* reset state variables */
252 	s->icount = 0;
253 	s->ncount = 0;
254 	s->iusage = 0;
255 	s->nusage = 0;
256 	s->susage = 0;
257 	s->ousage = 0;
258 	hid_clear_local(c);
259 
260 	/* get next item */
261 	while (s->p != s->end) {
262 
263 		bSize = hid_get_byte(s, 1);
264 		if (bSize == 0xfe) {
265 			/* long item */
266 			bSize = hid_get_byte(s, 1);
267 			bSize |= hid_get_byte(s, 1) << 8;
268 			bTag = hid_get_byte(s, 1);
269 			bType = 0xff;	/* XXX what should it be */
270 		} else {
271 			/* short item */
272 			bTag = bSize >> 4;
273 			bType = (bSize >> 2) & 3;
274 			bSize &= 3;
275 			if (bSize == 3)
276 				bSize = 4;
277 		}
278 
279 		switch(bSize) {
280 		case 0:
281 			dval = 0;
282 			mask = 0;
283 			break;
284 		case 1:
285 			dval = (int8_t)hid_get_byte(s, 1);
286 			mask = 0xFF;
287 			break;
288 		case 2:
289 			dval = hid_get_byte(s, 1);
290 			dval |= hid_get_byte(s, 1) << 8;
291 			dval = (int16_t)dval;
292 			mask = 0xFFFF;
293 			break;
294 		case 4:
295 			dval = hid_get_byte(s, 1);
296 			dval |= hid_get_byte(s, 1) << 8;
297 			dval |= hid_get_byte(s, 1) << 16;
298 			dval |= hid_get_byte(s, 1) << 24;
299 			mask = 0xFFFFFFFF;
300 			break;
301 		default:
302 			dval = hid_get_byte(s, bSize);
303 			continue;
304 		}
305 
306 		switch (bType) {
307 		case 0:		/* Main */
308 			switch (bTag) {
309 			case 8:	/* Input */
310 				c->kind = hid_input;
311 				c->flags = dval;
312 		ret:
313 				c->report_count = s->loc_count;
314 				c->report_size = s->loc_size;
315 
316 				if (c->flags & HIO_VARIABLE) {
317 					/* range check usage count */
318 					if (c->report_count > 255) {
319 						s->ncount = 255;
320 					} else
321 						s->ncount = c->report_count;
322 
323 					/*
324 					 * The "top" loop will return
325 					 * one and one item:
326 					 */
327 					c->report_count = 1;
328 					c->usage_minimum = 0;
329 					c->usage_maximum = 0;
330 				} else {
331 					s->ncount = 1;
332 				}
333 				goto top;
334 
335 			case 9:	/* Output */
336 				c->kind = hid_output;
337 				c->flags = dval;
338 				goto ret;
339 			case 10:	/* Collection */
340 				c->kind = hid_collection;
341 				c->collection = dval;
342 				c->collevel++;
343 				c->usage = s->usage_last;
344 				*h = *c;
345 				return (1);
346 			case 11:	/* Feature */
347 				c->kind = hid_feature;
348 				c->flags = dval;
349 				goto ret;
350 			case 12:	/* End collection */
351 				c->kind = hid_endcollection;
352 				if (c->collevel == 0) {
353 					/* Invalid end collection. */
354 					return (0);
355 				}
356 				c->collevel--;
357 				*h = *c;
358 				return (1);
359 			default:
360 				break;
361 			}
362 			break;
363 
364 		case 1:		/* Global */
365 			switch (bTag) {
366 			case 0:
367 				c->_usage_page = dval << 16;
368 				break;
369 			case 1:
370 				c->logical_minimum = dval;
371 				break;
372 			case 2:
373 				c->logical_maximum = dval;
374 				break;
375 			case 3:
376 				c->physical_minimum = dval;
377 				break;
378 			case 4:
379 				c->physical_maximum = dval;
380 				break;
381 			case 5:
382 				c->unit_exponent = dval;
383 				break;
384 			case 6:
385 				c->unit = dval;
386 				break;
387 			case 7:
388 				/* mask because value is unsigned */
389 				s->loc_size = dval & mask;
390 				break;
391 			case 8:
392 				hid_switch_rid(s, c, dval);
393 				break;
394 			case 9:
395 				/* mask because value is unsigned */
396 				s->loc_count = dval & mask;
397 				break;
398 			case 10:	/* Push */
399 				s->pushlevel ++;
400 				if (s->pushlevel < MAXPUSH) {
401 					s->cur[s->pushlevel] = *c;
402 					/* store size and count */
403 					c->report_size = s->loc_size;
404 					c->report_count = s->loc_count;
405 					/* update current item pointer */
406 					c = &s->cur[s->pushlevel];
407 				}
408 				break;
409 			case 11:	/* Pop */
410 				s->pushlevel --;
411 				if (s->pushlevel < MAXPUSH) {
412 					c = &s->cur[s->pushlevel];
413 					/* restore size and count */
414 					s->loc_size = c->report_size;
415 					s->loc_count = c->report_count;
416 					c->report_size = 0;
417 					c->report_count = 0;
418 				}
419 				break;
420 			default:
421 				break;
422 			}
423 			break;
424 		case 2:		/* Local */
425 			switch (bTag) {
426 			case 0:
427 				if (bSize != 4)
428 					dval = (dval & mask) | c->_usage_page;
429 
430 				/* set last usage, in case of a collection */
431 				s->usage_last = dval;
432 
433 				if (s->nusage < MAXUSAGE) {
434 					s->usages_min[s->nusage] = dval;
435 					s->usages_max[s->nusage] = dval;
436 					s->nusage ++;
437 				}
438 				/* else XXX */
439 
440 				/* clear any pending usage sets */
441 				s->susage = 0;
442 				break;
443 			case 1:
444 				s->susage |= 1;
445 
446 				if (bSize != 4)
447 					dval = (dval & mask) | c->_usage_page;
448 				c->usage_minimum = dval;
449 
450 				goto check_set;
451 			case 2:
452 				s->susage |= 2;
453 
454 				if (bSize != 4)
455 					dval = (dval & mask) | c->_usage_page;
456 				c->usage_maximum = dval;
457 
458 			check_set:
459 				if (s->susage != 3)
460 					break;
461 
462 				/* sanity check */
463 				if ((s->nusage < MAXUSAGE) &&
464 				    (c->usage_minimum <= c->usage_maximum)) {
465 					/* add usage range */
466 					s->usages_min[s->nusage] =
467 					    c->usage_minimum;
468 					s->usages_max[s->nusage] =
469 					    c->usage_maximum;
470 					s->nusage ++;
471 				}
472 				/* else XXX */
473 
474 				s->susage = 0;
475 				break;
476 			case 3:
477 				c->designator_index = dval;
478 				break;
479 			case 4:
480 				c->designator_minimum = dval;
481 				break;
482 			case 5:
483 				c->designator_maximum = dval;
484 				break;
485 			case 7:
486 				c->string_index = dval;
487 				break;
488 			case 8:
489 				c->string_minimum = dval;
490 				break;
491 			case 9:
492 				c->string_maximum = dval;
493 				break;
494 			case 10:
495 				c->set_delimiter = dval;
496 				break;
497 			default:
498 				break;
499 			}
500 			break;
501 		default:
502 			break;
503 		}
504 	}
505 	return (0);
506 }
507 
508 int
509 hid_report_size(report_desc_t r, enum hid_kind k, int id)
510 {
511 	struct hid_data *d;
512 	struct hid_item h;
513 	uint32_t temp;
514 	uint32_t hpos;
515 	uint32_t lpos;
516 	int report_id = 0;
517 
518 	hpos = 0;
519 	lpos = 0xFFFFFFFF;
520 
521 	memset(&h, 0, sizeof h);
522 	for (d = hid_start_parse(r, 1 << k, id); hid_get_item(d, &h); ) {
523 		if ((h.report_ID == id || id < 0) && h.kind == k) {
524 			/* compute minimum */
525 			if (lpos > h.pos)
526 				lpos = h.pos;
527 			/* compute end position */
528 			temp = h.pos + (h.report_size * h.report_count);
529 			/* compute maximum */
530 			if (hpos < temp)
531 				hpos = temp;
532 			if (h.report_ID != 0)
533 				report_id = 1;
534 		}
535 	}
536 	hid_end_parse(d);
537 
538 	/* safety check - can happen in case of currupt descriptors */
539 	if (lpos > hpos)
540 		temp = 0;
541 	else
542 		temp = hpos - lpos;
543 
544 	/* return length in bytes rounded up */
545 	return ((temp + 7) / 8 + report_id);
546 }
547 
548 int
549 hid_locate(report_desc_t desc, unsigned int u, enum hid_kind k,
550 	   hid_item_t *h, int id)
551 {
552 	struct hid_data *d;
553 
554 	for (d = hid_start_parse(desc, 1 << k, id); hid_get_item(d, h); ) {
555 		if (h->kind == k && !(h->flags & HIO_CONST) && h->usage == u) {
556 			hid_end_parse(d);
557 			return (1);
558 		}
559 	}
560 	hid_end_parse(d);
561 	h->report_size = 0;
562 	return (0);
563 }
564