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$");
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				/* stop parsing, if invalid push level */
407				if ((s->pushlevel + 1) >= MAXPUSH)
408					return (0);
409				s->pushlevel ++;
410				s->cur[s->pushlevel] = *c;
411				/* store size and count */
412				c->report_size = s->loc_size;
413				c->report_count = s->loc_count;
414				/* update current item pointer */
415				c = &s->cur[s->pushlevel];
416				break;
417			case 11:	/* Pop */
418				/* stop parsing, if invalid push level */
419				if (s->pushlevel == 0)
420					return (0);
421				s->pushlevel --;
422				c = &s->cur[s->pushlevel];
423				/* restore size and count */
424				s->loc_size = c->report_size;
425				s->loc_count = c->report_count;
426				c->report_size = 0;
427				c->report_count = 0;
428				break;
429			default:
430				break;
431			}
432			break;
433		case 2:		/* Local */
434			switch (bTag) {
435			case 0:
436				if (bSize != 4)
437					dval = (dval & mask) | c->_usage_page;
438
439				/* set last usage, in case of a collection */
440				s->usage_last = dval;
441
442				if (s->nusage < MAXUSAGE) {
443					s->usages_min[s->nusage] = dval;
444					s->usages_max[s->nusage] = dval;
445					s->nusage ++;
446				}
447				/* else XXX */
448
449				/* clear any pending usage sets */
450				s->susage = 0;
451				break;
452			case 1:
453				s->susage |= 1;
454
455				if (bSize != 4)
456					dval = (dval & mask) | c->_usage_page;
457				c->usage_minimum = dval;
458
459				goto check_set;
460			case 2:
461				s->susage |= 2;
462
463				if (bSize != 4)
464					dval = (dval & mask) | c->_usage_page;
465				c->usage_maximum = dval;
466
467			check_set:
468				if (s->susage != 3)
469					break;
470
471				/* sanity check */
472				if ((s->nusage < MAXUSAGE) &&
473				    (c->usage_minimum <= c->usage_maximum)) {
474					/* add usage range */
475					s->usages_min[s->nusage] =
476					    c->usage_minimum;
477					s->usages_max[s->nusage] =
478					    c->usage_maximum;
479					s->nusage ++;
480				}
481				/* else XXX */
482
483				s->susage = 0;
484				break;
485			case 3:
486				c->designator_index = dval;
487				break;
488			case 4:
489				c->designator_minimum = dval;
490				break;
491			case 5:
492				c->designator_maximum = dval;
493				break;
494			case 7:
495				c->string_index = dval;
496				break;
497			case 8:
498				c->string_minimum = dval;
499				break;
500			case 9:
501				c->string_maximum = dval;
502				break;
503			case 10:
504				c->set_delimiter = dval;
505				break;
506			default:
507				break;
508			}
509			break;
510		default:
511			break;
512		}
513	}
514	return (0);
515}
516
517int
518hid_get_item(hid_data_t s, hid_item_t *h)
519{
520	int r;
521
522	for (;;) {
523		r = hid_get_item_raw(s, h);
524		if (r <= 0 || s->reportid == -1 || h->report_ID == s->reportid)
525			break;
526	}
527	return (r);
528}
529
530int
531hid_report_size(report_desc_t r, enum hid_kind k, int id)
532{
533	struct hid_data *d;
534	struct hid_item h;
535	uint32_t temp;
536	uint32_t hpos;
537	uint32_t lpos;
538	int report_id = 0;
539
540	hpos = 0;
541	lpos = 0xFFFFFFFF;
542
543	memset(&h, 0, sizeof h);
544	for (d = hid_start_parse(r, 1 << k, id); hid_get_item(d, &h); ) {
545		if (h.kind == k) {
546			/* compute minimum */
547			if (lpos > h.pos)
548				lpos = h.pos;
549			/* compute end position */
550			temp = h.pos + (h.report_size * h.report_count);
551			/* compute maximum */
552			if (hpos < temp)
553				hpos = temp;
554			if (h.report_ID != 0)
555				report_id = 1;
556		}
557	}
558	hid_end_parse(d);
559
560	/* safety check - can happen in case of currupt descriptors */
561	if (lpos > hpos)
562		temp = 0;
563	else
564		temp = hpos - lpos;
565
566	/* return length in bytes rounded up */
567	return ((temp + 7) / 8 + report_id);
568}
569
570int
571hid_locate(report_desc_t desc, unsigned int u, enum hid_kind k,
572	   hid_item_t *h, int id)
573{
574	struct hid_data *d;
575
576	for (d = hid_start_parse(desc, 1 << k, id); hid_get_item(d, h); ) {
577		if (h->kind == k && !(h->flags & HIO_CONST) && h->usage == u) {
578			hid_end_parse(d);
579			return (1);
580		}
581	}
582	hid_end_parse(d);
583	h->report_size = 0;
584	return (0);
585}
586