xref: /freebsd/lib/libusbhid/parse.c (revision b425e3194780686c46c4dd3d01f71029a97c0284)
1b425e319SNick Hibma /*	$NetBSD: parse.c,v 1.11 2000/09/24 02:19:54 augustss Exp $	*/
29e2046dfSNick Hibma 
39e2046dfSNick Hibma /*
49e2046dfSNick Hibma  * Copyright (c) 1999 Lennart Augustsson <augustss@netbsd.org>
59e2046dfSNick Hibma  * All rights reserved.
69e2046dfSNick Hibma  *
79e2046dfSNick Hibma  * Redistribution and use in source and binary forms, with or without
89e2046dfSNick Hibma  * modification, are permitted provided that the following conditions
99e2046dfSNick Hibma  * are met:
109e2046dfSNick Hibma  * 1. Redistributions of source code must retain the above copyright
119e2046dfSNick Hibma  *    notice, this list of conditions and the following disclaimer.
129e2046dfSNick Hibma  * 2. Redistributions in binary form must reproduce the above copyright
139e2046dfSNick Hibma  *    notice, this list of conditions and the following disclaimer in the
149e2046dfSNick Hibma  *    documentation and/or other materials provided with the distribution.
159e2046dfSNick Hibma  *
169e2046dfSNick Hibma  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
179e2046dfSNick Hibma  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
189e2046dfSNick Hibma  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
199e2046dfSNick Hibma  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
209e2046dfSNick Hibma  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
219e2046dfSNick Hibma  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
229e2046dfSNick Hibma  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
239e2046dfSNick Hibma  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
249e2046dfSNick Hibma  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
259e2046dfSNick Hibma  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
269e2046dfSNick Hibma  * SUCH DAMAGE.
279e2046dfSNick Hibma  *
289e2046dfSNick Hibma  * $FreeBSD$
299e2046dfSNick Hibma  *
309e2046dfSNick Hibma  */
319e2046dfSNick Hibma 
329e2046dfSNick Hibma #include <assert.h>
339e2046dfSNick Hibma #include <stdlib.h>
349e2046dfSNick Hibma #include <string.h>
359e2046dfSNick Hibma #include <sys/time.h>
369e2046dfSNick Hibma 
379e2046dfSNick Hibma #include <dev/usb/usb.h>
389e2046dfSNick Hibma #include <dev/usb/usbhid.h>
399e2046dfSNick Hibma 
409e2046dfSNick Hibma #include "libusb.h"
419e2046dfSNick Hibma #include "usbvar.h"
429e2046dfSNick Hibma 
439e2046dfSNick Hibma #define MAXUSAGE 100
449e2046dfSNick Hibma struct hid_data {
459e2046dfSNick Hibma 	u_char *start;
469e2046dfSNick Hibma 	u_char *end;
479e2046dfSNick Hibma 	u_char *p;
489e2046dfSNick Hibma 	hid_item_t cur;
499e2046dfSNick Hibma 	unsigned int usages[MAXUSAGE];
509e2046dfSNick Hibma 	int nusage;
519e2046dfSNick Hibma 	int minset;
529e2046dfSNick Hibma 	int multi;
539e2046dfSNick Hibma 	int multimax;
549e2046dfSNick Hibma 	int kindset;
55b425e319SNick Hibma 
56b425e319SNick Hibma 	/* Absolute data position (bits) for input/output/feature.
57b425e319SNick Hibma            Assumes that hid_input, hid_output and hid_feature have
58b425e319SNick Hibma            values 0, 1 and 2. */
59b425e319SNick Hibma         unsigned int kindpos[3];
609e2046dfSNick Hibma };
619e2046dfSNick Hibma 
629e2046dfSNick Hibma static int min(int x, int y) { return x < y ? x : y; }
639e2046dfSNick Hibma 
649e2046dfSNick Hibma static void
659e2046dfSNick Hibma hid_clear_local(hid_item_t *c)
669e2046dfSNick Hibma {
679e2046dfSNick Hibma 	c->usage = 0;
689e2046dfSNick Hibma 	c->usage_minimum = 0;
699e2046dfSNick Hibma 	c->usage_maximum = 0;
709e2046dfSNick Hibma 	c->designator_index = 0;
719e2046dfSNick Hibma 	c->designator_minimum = 0;
729e2046dfSNick Hibma 	c->designator_maximum = 0;
739e2046dfSNick Hibma 	c->string_index = 0;
749e2046dfSNick Hibma 	c->string_minimum = 0;
759e2046dfSNick Hibma 	c->string_maximum = 0;
769e2046dfSNick Hibma 	c->set_delimiter = 0;
779e2046dfSNick Hibma }
789e2046dfSNick Hibma 
799e2046dfSNick Hibma hid_data_t
809e2046dfSNick Hibma hid_start_parse(report_desc_t d, int kindset)
819e2046dfSNick Hibma {
829e2046dfSNick Hibma 	struct hid_data *s;
839e2046dfSNick Hibma 
849e2046dfSNick Hibma 	s = malloc(sizeof *s);
859e2046dfSNick Hibma 	memset(s, 0, sizeof *s);
869e2046dfSNick Hibma 	s->start = s->p = d->data;
879e2046dfSNick Hibma 	s->end = d->data + d->size;
889e2046dfSNick Hibma 	s->kindset = kindset;
899e2046dfSNick Hibma 	return (s);
909e2046dfSNick Hibma }
919e2046dfSNick Hibma 
929e2046dfSNick Hibma void
939e2046dfSNick Hibma hid_end_parse(hid_data_t s)
949e2046dfSNick Hibma {
959e2046dfSNick Hibma 	while (s->cur.next) {
969e2046dfSNick Hibma 		hid_item_t *hi = s->cur.next->next;
979e2046dfSNick Hibma 		free(s->cur.next);
989e2046dfSNick Hibma 		s->cur.next = hi;
999e2046dfSNick Hibma 	}
1009e2046dfSNick Hibma 	free(s);
1019e2046dfSNick Hibma }
1029e2046dfSNick Hibma 
1039e2046dfSNick Hibma int
1049e2046dfSNick Hibma hid_get_item(hid_data_t s, hid_item_t *h)
1059e2046dfSNick Hibma {
1069e2046dfSNick Hibma 	hid_item_t *c;
107b425e319SNick Hibma 	unsigned int bTag = 0, bType = 0, bSize;
1089e2046dfSNick Hibma 	unsigned char *data;
1099e2046dfSNick Hibma 	int dval;
1109e2046dfSNick Hibma 	unsigned char *p;
1119e2046dfSNick Hibma 	hid_item_t *hi;
1129e2046dfSNick Hibma 	int i;
113b425e319SNick Hibma 	hid_kind_t retkind;
1149e2046dfSNick Hibma 
1159e2046dfSNick Hibma 	c = &s->cur;
1169e2046dfSNick Hibma 
1179e2046dfSNick Hibma  top:
1189e2046dfSNick Hibma 	if (s->multimax) {
1199e2046dfSNick Hibma 		if (s->multi < s->multimax) {
1209e2046dfSNick Hibma 			c->usage = s->usages[min(s->multi, s->nusage-1)];
1219e2046dfSNick Hibma 			s->multi++;
1229e2046dfSNick Hibma 			*h = *c;
123b425e319SNick Hibma 
124b425e319SNick Hibma 			/* 'multimax' is only non-zero if the current
125b425e319SNick Hibma                            item kind is input/output/feature */
126b425e319SNick Hibma 			h->pos = s->kindpos[c->kind];
127b425e319SNick Hibma 			s->kindpos[c->kind] += c->report_size;
1289e2046dfSNick Hibma 			h->next = 0;
1299e2046dfSNick Hibma 			return (1);
1309e2046dfSNick Hibma 		} else {
1319e2046dfSNick Hibma 			c->report_count = s->multimax;
1329e2046dfSNick Hibma 			s->multimax = 0;
1339e2046dfSNick Hibma 			s->nusage = 0;
1349e2046dfSNick Hibma 			hid_clear_local(c);
1359e2046dfSNick Hibma 		}
1369e2046dfSNick Hibma 	}
1379e2046dfSNick Hibma 	for (;;) {
1389e2046dfSNick Hibma 		p = s->p;
1399e2046dfSNick Hibma 		if (p >= s->end)
1409e2046dfSNick Hibma 			return (0);
1419e2046dfSNick Hibma 
1429e2046dfSNick Hibma 		bSize = *p++;
1439e2046dfSNick Hibma 		if (bSize == 0xfe) {
1449e2046dfSNick Hibma 			/* long item */
1459e2046dfSNick Hibma 			bSize = *p++;
1469e2046dfSNick Hibma 			bSize |= *p++ << 8;
1479e2046dfSNick Hibma 			bTag = *p++;
1489e2046dfSNick Hibma 			data = p;
1499e2046dfSNick Hibma 			p += bSize;
1509e2046dfSNick Hibma 		} else {
1519e2046dfSNick Hibma 			/* short item */
1529e2046dfSNick Hibma 			bTag = bSize >> 4;
1539e2046dfSNick Hibma 			bType = (bSize >> 2) & 3;
1549e2046dfSNick Hibma 			bSize &= 3;
1559e2046dfSNick Hibma 			if (bSize == 3) bSize = 4;
1569e2046dfSNick Hibma 			data = p;
1579e2046dfSNick Hibma 			p += bSize;
1589e2046dfSNick Hibma 		}
1599e2046dfSNick Hibma 		s->p = p;
1609e2046dfSNick Hibma 		/*
1619e2046dfSNick Hibma 		 * The spec is unclear if the data is signed or unsigned.
1629e2046dfSNick Hibma 		 */
1639e2046dfSNick Hibma 		switch(bSize) {
1649e2046dfSNick Hibma 		case 0:
1659e2046dfSNick Hibma 			dval = 0;
1669e2046dfSNick Hibma 			break;
1679e2046dfSNick Hibma 		case 1:
1689e2046dfSNick Hibma 			dval = (int8_t)*data++;
1699e2046dfSNick Hibma 			break;
1709e2046dfSNick Hibma 		case 2:
1719e2046dfSNick Hibma 			dval = *data++;
1729e2046dfSNick Hibma 			dval |= *data++ << 8;
1739e2046dfSNick Hibma 			dval = (int16_t)dval;
1749e2046dfSNick Hibma 			break;
1759e2046dfSNick Hibma 		case 4:
1769e2046dfSNick Hibma 			dval = *data++;
1779e2046dfSNick Hibma 			dval |= *data++ << 8;
1789e2046dfSNick Hibma 			dval |= *data++ << 16;
1799e2046dfSNick Hibma 			dval |= *data++ << 24;
1809e2046dfSNick Hibma 			break;
1819e2046dfSNick Hibma 		default:
1829e2046dfSNick Hibma 			return (-1);
1839e2046dfSNick Hibma 		}
1849e2046dfSNick Hibma 
1859e2046dfSNick Hibma 		switch (bType) {
1869e2046dfSNick Hibma 		case 0:			/* Main */
1879e2046dfSNick Hibma 			switch (bTag) {
1889e2046dfSNick Hibma 			case 8:		/* Input */
189b425e319SNick Hibma 				retkind = hid_input;
1909e2046dfSNick Hibma 			ret:
191b425e319SNick Hibma 				if (!(s->kindset & (1 << retkind))) {
192b425e319SNick Hibma 					/* Drop the items of this kind */
193b425e319SNick Hibma 					s->nusage = 0;
194b425e319SNick Hibma 					continue;
195b425e319SNick Hibma 				}
196b425e319SNick Hibma 				c->kind = retkind;
197b425e319SNick Hibma 				c->flags = dval;
1989e2046dfSNick Hibma 				if (c->flags & HIO_VARIABLE) {
1999e2046dfSNick Hibma 					s->multimax = c->report_count;
2009e2046dfSNick Hibma 					s->multi = 0;
2019e2046dfSNick Hibma 					c->report_count = 1;
2029e2046dfSNick Hibma 					if (s->minset) {
2039e2046dfSNick Hibma 						for (i = c->usage_minimum;
2049e2046dfSNick Hibma 						     i <= c->usage_maximum;
2059e2046dfSNick Hibma 						     i++) {
2069e2046dfSNick Hibma 							s->usages[s->nusage] = i;
2079e2046dfSNick Hibma 							if (s->nusage < MAXUSAGE-1)
2089e2046dfSNick Hibma 								s->nusage++;
2099e2046dfSNick Hibma 						}
2109e2046dfSNick Hibma 						s->minset = 0;
2119e2046dfSNick Hibma 					}
2129e2046dfSNick Hibma 					goto top;
2139e2046dfSNick Hibma 				} else {
2149e2046dfSNick Hibma 					if (s->minset)
2159e2046dfSNick Hibma 						c->usage = c->usage_minimum;
2169e2046dfSNick Hibma 					*h = *c;
2179e2046dfSNick Hibma 					h->next = 0;
218b425e319SNick Hibma 					h->pos = s->kindpos[c->kind];
219b425e319SNick Hibma 					s->kindpos[c->kind] += c->report_size * c->report_count;
2209e2046dfSNick Hibma 					hid_clear_local(c);
2219e2046dfSNick Hibma 					s->minset = 0;
2229e2046dfSNick Hibma 					return (1);
2239e2046dfSNick Hibma 				}
2249e2046dfSNick Hibma 			case 9:		/* Output */
225b425e319SNick Hibma 				retkind = hid_output;
2269e2046dfSNick Hibma 				goto ret;
2279e2046dfSNick Hibma 			case 10:	/* Collection */
2289e2046dfSNick Hibma 				c->kind = hid_collection;
2299e2046dfSNick Hibma 				c->collection = dval;
2309e2046dfSNick Hibma 				c->collevel++;
2319e2046dfSNick Hibma 				*h = *c;
2329e2046dfSNick Hibma 				hid_clear_local(c);
2339e2046dfSNick Hibma 				c->report_ID = NO_REPORT_ID;
2349e2046dfSNick Hibma 				s->nusage = 0;
2359e2046dfSNick Hibma 				return (1);
2369e2046dfSNick Hibma 			case 11:	/* Feature */
237b425e319SNick Hibma 				retkind = hid_feature;
2389e2046dfSNick Hibma 				goto ret;
2399e2046dfSNick Hibma 			case 12:	/* End collection */
2409e2046dfSNick Hibma 				c->kind = hid_endcollection;
2419e2046dfSNick Hibma 				c->collevel--;
2429e2046dfSNick Hibma 				*h = *c;
2439e2046dfSNick Hibma 				/*hid_clear_local(c);*/
2449e2046dfSNick Hibma 				s->nusage = 0;
2459e2046dfSNick Hibma 				return (1);
2469e2046dfSNick Hibma 			default:
2479e2046dfSNick Hibma 				return (-2);
2489e2046dfSNick Hibma 			}
2499e2046dfSNick Hibma 
2509e2046dfSNick Hibma 		case 1:		/* Global */
2519e2046dfSNick Hibma 			switch (bTag) {
2529e2046dfSNick Hibma 			case 0:
2539e2046dfSNick Hibma 				c->_usage_page = dval << 16;
2549e2046dfSNick Hibma 				break;
2559e2046dfSNick Hibma 			case 1:
2569e2046dfSNick Hibma 				c->logical_minimum = dval;
2579e2046dfSNick Hibma 				break;
2589e2046dfSNick Hibma 			case 2:
2599e2046dfSNick Hibma 				c->logical_maximum = dval;
2609e2046dfSNick Hibma 				break;
2619e2046dfSNick Hibma 			case 3:
2629e2046dfSNick Hibma 				c->physical_maximum = dval;
2639e2046dfSNick Hibma 				break;
2649e2046dfSNick Hibma 			case 4:
2659e2046dfSNick Hibma 				c->physical_maximum = dval;
2669e2046dfSNick Hibma 				break;
2679e2046dfSNick Hibma 			case 5:
2689e2046dfSNick Hibma 				c->unit_exponent = dval;
2699e2046dfSNick Hibma 				break;
2709e2046dfSNick Hibma 			case 6:
2719e2046dfSNick Hibma 				c->unit = dval;
2729e2046dfSNick Hibma 				break;
2739e2046dfSNick Hibma 			case 7:
2749e2046dfSNick Hibma 				c->report_size = dval;
2759e2046dfSNick Hibma 				break;
2769e2046dfSNick Hibma 			case 8:
2779e2046dfSNick Hibma 				c->report_ID = dval;
2789e2046dfSNick Hibma 				break;
2799e2046dfSNick Hibma 			case 9:
2809e2046dfSNick Hibma 				c->report_count = dval;
2819e2046dfSNick Hibma 				break;
2829e2046dfSNick Hibma 			case 10: /* Push */
2839e2046dfSNick Hibma 				hi = malloc(sizeof *hi);
2849e2046dfSNick Hibma 				*hi = s->cur;
2859e2046dfSNick Hibma 				c->next = hi;
2869e2046dfSNick Hibma 				break;
2879e2046dfSNick Hibma 			case 11: /* Pop */
2889e2046dfSNick Hibma 				hi = c->next;
2899e2046dfSNick Hibma 				s->cur = *hi;
2909e2046dfSNick Hibma 				free(hi);
2919e2046dfSNick Hibma 				break;
2929e2046dfSNick Hibma 			default:
2939e2046dfSNick Hibma 				return (-3);
2949e2046dfSNick Hibma 			}
2959e2046dfSNick Hibma 			break;
2969e2046dfSNick Hibma 		case 2:		/* Local */
2979e2046dfSNick Hibma 			switch (bTag) {
2989e2046dfSNick Hibma 			case 0:
2999e2046dfSNick Hibma 				if (bSize == 1)
3009e2046dfSNick Hibma 					dval = c->_usage_page | (dval&0xff);
3019e2046dfSNick Hibma 				else if (bSize == 2)
3029e2046dfSNick Hibma 					dval = c->_usage_page | (dval&0xffff);
3039e2046dfSNick Hibma 				c->usage = dval;
3049e2046dfSNick Hibma 				if (s->nusage < MAXUSAGE)
3059e2046dfSNick Hibma 					s->usages[s->nusage++] = dval;
3069e2046dfSNick Hibma 				/* else XXX */
3079e2046dfSNick Hibma 				break;
3089e2046dfSNick Hibma 			case 1:
3099e2046dfSNick Hibma 				s->minset = 1;
3109e2046dfSNick Hibma 				if (bSize == 1)
3119e2046dfSNick Hibma 					dval = c->_usage_page | (dval&0xff);
3129e2046dfSNick Hibma 				else if (bSize == 2)
3139e2046dfSNick Hibma 					dval = c->_usage_page | (dval&0xffff);
3149e2046dfSNick Hibma 				c->usage_minimum = dval;
3159e2046dfSNick Hibma 				break;
3169e2046dfSNick Hibma 			case 2:
3179e2046dfSNick Hibma 				if (bSize == 1)
3189e2046dfSNick Hibma 					dval = c->_usage_page | (dval&0xff);
3199e2046dfSNick Hibma 				else if (bSize == 2)
3209e2046dfSNick Hibma 					dval = c->_usage_page | (dval&0xffff);
3219e2046dfSNick Hibma 				c->usage_maximum = dval;
3229e2046dfSNick Hibma 				break;
3239e2046dfSNick Hibma 			case 3:
3249e2046dfSNick Hibma 				c->designator_index = dval;
3259e2046dfSNick Hibma 				break;
3269e2046dfSNick Hibma 			case 4:
3279e2046dfSNick Hibma 				c->designator_minimum = dval;
3289e2046dfSNick Hibma 				break;
3299e2046dfSNick Hibma 			case 5:
3309e2046dfSNick Hibma 				c->designator_maximum = dval;
3319e2046dfSNick Hibma 				break;
3329e2046dfSNick Hibma 			case 7:
3339e2046dfSNick Hibma 				c->string_index = dval;
3349e2046dfSNick Hibma 				break;
3359e2046dfSNick Hibma 			case 8:
3369e2046dfSNick Hibma 				c->string_minimum = dval;
3379e2046dfSNick Hibma 				break;
3389e2046dfSNick Hibma 			case 9:
3399e2046dfSNick Hibma 				c->string_maximum = dval;
3409e2046dfSNick Hibma 				break;
3419e2046dfSNick Hibma 			case 10:
3429e2046dfSNick Hibma 				c->set_delimiter = dval;
3439e2046dfSNick Hibma 				break;
3449e2046dfSNick Hibma 			default:
3459e2046dfSNick Hibma 				return (-4);
3469e2046dfSNick Hibma 			}
3479e2046dfSNick Hibma 			break;
3489e2046dfSNick Hibma 		default:
3499e2046dfSNick Hibma 			return (-5);
3509e2046dfSNick Hibma 		}
3519e2046dfSNick Hibma 	}
3529e2046dfSNick Hibma }
3539e2046dfSNick Hibma 
3549e2046dfSNick Hibma int
355b425e319SNick Hibma hid_report_size(report_desc_t r, unsigned int id, enum hid_kind k)
3569e2046dfSNick Hibma {
3579e2046dfSNick Hibma 	struct hid_data *d;
3589e2046dfSNick Hibma 	hid_item_t h;
359b425e319SNick Hibma 	unsigned int size = 0;
3609e2046dfSNick Hibma 
3619e2046dfSNick Hibma 	memset(&h, 0, sizeof h);
362b425e319SNick Hibma 	d = hid_start_parse(r, 1<<k);
363b425e319SNick Hibma 	while (hid_get_item(d, &h)) {
364b425e319SNick Hibma 		if (h.report_ID == id && h.kind == k) {
365b425e319SNick Hibma 			unsigned int newsize = h.pos + h.report_size;
366b425e319SNick Hibma 			if (newsize > size)
367b425e319SNick Hibma 			    size = newsize;
3689e2046dfSNick Hibma 		}
3699e2046dfSNick Hibma 	}
3709e2046dfSNick Hibma 	hid_end_parse(d);
371b425e319SNick Hibma 
372b425e319SNick Hibma 	if (id != NO_REPORT_ID)
373b425e319SNick Hibma 		size += 8;		/* add 8 bits for the report ID */
374b425e319SNick Hibma 
375b425e319SNick Hibma 	return ((size + 7) / 8);	/* return size in bytes */
3769e2046dfSNick Hibma }
3779e2046dfSNick Hibma 
3789e2046dfSNick Hibma int
379b425e319SNick Hibma hid_locate(report_desc_t desc, unsigned int u, enum hid_kind k, hid_item_t *h)
3809e2046dfSNick Hibma {
3819e2046dfSNick Hibma 	hid_data_t d;
3829e2046dfSNick Hibma 
3839e2046dfSNick Hibma 	for (d = hid_start_parse(desc, 1<<k); hid_get_item(d, h); ) {
3849e2046dfSNick Hibma 		if (h->kind == k && !(h->flags & HIO_CONST) && h->usage == u) {
3859e2046dfSNick Hibma 			hid_end_parse(d);
3869e2046dfSNick Hibma 			return (1);
3879e2046dfSNick Hibma 		}
3889e2046dfSNick Hibma 	}
3899e2046dfSNick Hibma 	hid_end_parse(d);
3909e2046dfSNick Hibma 	h->report_size = 0;
3919e2046dfSNick Hibma 	return (0);
3929e2046dfSNick Hibma }
393