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