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