parse.c revision 67217
186222Swollman/*	$NetBSD: parse.c,v 1.11 2000/09/24 02:19:54 augustss Exp $	*/
267578Swollman
358787Sru/*
42742Swollman * Copyright (c) 1999 Lennart Augustsson <augustss@netbsd.org>
52742Swollman * All rights reserved.
62742Swollman *
72742Swollman * Redistribution and use in source and binary forms, with or without
82742Swollman * modification, are permitted provided that the following conditions
958787Sru * are met:
102742Swollman * 1. Redistributions of source code must retain the above copyright
112742Swollman *    notice, this list of conditions and the following disclaimer.
1258787Sru * 2. Redistributions in binary form must reproduce the above copyright
1358787Sru *    notice, this list of conditions and the following disclaimer in the
142742Swollman *    documentation and/or other materials provided with the distribution.
1586222Swollman *
1620094Swollman * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1720094Swollman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1820094Swollman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1920094Swollman * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2020094Swollman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2120094Swollman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2220094Swollman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2320094Swollman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
242742Swollman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
252742Swollman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
262742Swollman * SUCH DAMAGE.
272742Swollman *
2814343Swollman * $FreeBSD: head/lib/libusbhid/parse.c 67217 2000-10-16 18:13:59Z n_hibma $
2958787Sru *
3014343Swollman */
3130711Swollman
3230711Swollman#include <assert.h>
3367578Swollman#include <stdlib.h>
3430711Swollman#include <string.h>
3530711Swollman#include <sys/time.h>
3630711Swollman
3730711Swollman#include <dev/usb/usb.h>
3830711Swollman#include <dev/usb/usbhid.h>
392742Swollman
4030711Swollman#include "libusb.h"
4130711Swollman#include "usbvar.h"
4230711Swollman
4330711Swollman#define MAXUSAGE 100
4430711Swollmanstruct hid_data {
452742Swollman	u_char *start;
4630711Swollman	u_char *end;
4730711Swollman	u_char *p;
4830711Swollman	hid_item_t cur;
4930711Swollman	unsigned int usages[MAXUSAGE];
5030711Swollman	int nusage;
5130711Swollman	int minset;
5230711Swollman	int multi;
5330711Swollman	int multimax;
5430711Swollman	int kindset;
5530711Swollman
562742Swollman	/* Absolute data position (bits) for input/output/feature.
5730711Swollman           Assumes that hid_input, hid_output and hid_feature have
5830711Swollman           values 0, 1 and 2. */
5930711Swollman        unsigned int kindpos[3];
6086222Swollman};
6130711Swollman
6230711Swollmanstatic int min(int x, int y) { return x < y ? x : y; }
632742Swollman
6430711Swollmanstatic void
652742Swollmanhid_clear_local(hid_item_t *c)
662742Swollman{
672742Swollman	c->usage = 0;
6819878Swollman	c->usage_minimum = 0;
692742Swollman	c->usage_maximum = 0;
7019878Swollman	c->designator_index = 0;
7119878Swollman	c->designator_minimum = 0;
7219878Swollman	c->designator_maximum = 0;
7319878Swollman	c->string_index = 0;
742742Swollman	c->string_minimum = 0;
7519878Swollman	c->string_maximum = 0;
762742Swollman	c->set_delimiter = 0;
7719878Swollman}
782742Swollman
7919878Swollmanhid_data_t
802742Swollmanhid_start_parse(report_desc_t d, int kindset)
812742Swollman{
8219878Swollman	struct hid_data *s;
832742Swollman
8419878Swollman	s = malloc(sizeof *s);
852742Swollman	memset(s, 0, sizeof *s);
8619878Swollman	s->start = s->p = d->data;
872742Swollman	s->end = d->data + d->size;
8819878Swollman	s->kindset = kindset;
892742Swollman	return (s);
9014343Swollman}
912742Swollman
922742Swollmanvoid
9314343Swollmanhid_end_parse(hid_data_t s)
9419878Swollman{
9519878Swollman	while (s->cur.next) {
962742Swollman		hid_item_t *hi = s->cur.next->next;
9719878Swollman		free(s->cur.next);
9819878Swollman		s->cur.next = hi;
9919878Swollman	}
10019878Swollman	free(s);
10119878Swollman}
1022742Swollman
1032742Swollmanint
1042742Swollmanhid_get_item(hid_data_t s, hid_item_t *h)
1052742Swollman{
10686222Swollman	hid_item_t *c;
10730711Swollman	unsigned int bTag = 0, bType = 0, bSize;
1082742Swollman	unsigned char *data;
1092742Swollman	int dval;
1102742Swollman	unsigned char *p;
1112742Swollman	hid_item_t *hi;
1122742Swollman	int i;
11330711Swollman	hid_kind_t retkind;
11430711Swollman
1152742Swollman	c = &s->cur;
1162742Swollman
1172742Swollman top:
1182742Swollman	if (s->multimax) {
11930711Swollman		if (s->multi < s->multimax) {
12030711Swollman			c->usage = s->usages[min(s->multi, s->nusage-1)];
12130711Swollman			s->multi++;
1222742Swollman			*h = *c;
1232742Swollman
1242742Swollman			/* 'multimax' is only non-zero if the current
1252742Swollman                           item kind is input/output/feature */
12630711Swollman			h->pos = s->kindpos[c->kind];
1272742Swollman			s->kindpos[c->kind] += c->report_size;
1282742Swollman			h->next = 0;
1292742Swollman			return (1);
1302742Swollman		} else {
13130711Swollman			c->report_count = s->multimax;
1322742Swollman			s->multimax = 0;
1332742Swollman			s->nusage = 0;
1342742Swollman			hid_clear_local(c);
1352742Swollman		}
1362742Swollman	}
13730711Swollman	for (;;) {
1382742Swollman		p = s->p;
1392742Swollman		if (p >= s->end)
1402742Swollman			return (0);
1412742Swollman
1422742Swollman		bSize = *p++;
1432742Swollman		if (bSize == 0xfe) {
1442742Swollman			/* long item */
14519878Swollman			bSize = *p++;
1462742Swollman			bSize |= *p++ << 8;
1472742Swollman			bTag = *p++;
1482742Swollman			data = p;
1492742Swollman			p += bSize;
15030711Swollman		} else {
1512742Swollman			/* short item */
1522742Swollman			bTag = bSize >> 4;
1532742Swollman			bType = (bSize >> 2) & 3;
1542742Swollman			bSize &= 3;
15530711Swollman			if (bSize == 3) bSize = 4;
15630711Swollman			data = p;
15730711Swollman			p += bSize;
1582742Swollman		}
1592742Swollman		s->p = p;
1602742Swollman		/*
1612742Swollman		 * The spec is unclear if the data is signed or unsigned.
1622742Swollman		 */
1632742Swollman		switch(bSize) {
16430711Swollman		case 0:
1652742Swollman			dval = 0;
16630711Swollman			break;
16730711Swollman		case 1:
16830711Swollman			dval = (int8_t)*data++;
16930711Swollman			break;
17030711Swollman		case 2:
17130711Swollman			dval = *data++;
17230711Swollman			dval |= *data++ << 8;
1732742Swollman			dval = (int16_t)dval;
17430711Swollman			break;
1752742Swollman		case 4:
1762742Swollman			dval = *data++;
1772742Swollman			dval |= *data++ << 8;
1782742Swollman			dval |= *data++ << 16;
17930711Swollman			dval |= *data++ << 24;
1802742Swollman			break;
1812742Swollman		default:
1822742Swollman			return (-1);
1832742Swollman		}
1842742Swollman
1852742Swollman		switch (bType) {
1862742Swollman		case 0:			/* Main */
1872742Swollman			switch (bTag) {
1882742Swollman			case 8:		/* Input */
1892742Swollman				retkind = hid_input;
1902742Swollman			ret:
19119878Swollman				if (!(s->kindset & (1 << retkind))) {
1922742Swollman					/* Drop the items of this kind */
19319878Swollman					s->nusage = 0;
1942742Swollman					continue;
19519878Swollman				}
1962742Swollman				c->kind = retkind;
1972742Swollman				c->flags = dval;
19819878Swollman				if (c->flags & HIO_VARIABLE) {
19919878Swollman					s->multimax = c->report_count;
2002742Swollman					s->multi = 0;
20119878Swollman					c->report_count = 1;
20219878Swollman					if (s->minset) {
2032742Swollman						for (i = c->usage_minimum;
20430711Swollman						     i <= c->usage_maximum;
20519878Swollman						     i++) {
20619878Swollman							s->usages[s->nusage] = i;
20719878Swollman							if (s->nusage < MAXUSAGE-1)
20819878Swollman								s->nusage++;
20930711Swollman						}
21043014Swollman						s->minset = 0;
21143543Swollman					}
21258787Sru					goto top;
21343543Swollman				} else {
21420094Swollman					if (s->minset)
2152742Swollman						c->usage = c->usage_minimum;
2162742Swollman					*h = *c;
21719878Swollman					h->next = 0;
2182742Swollman					h->pos = s->kindpos[c->kind];
2192742Swollman					s->kindpos[c->kind] += c->report_size * c->report_count;
2202742Swollman					hid_clear_local(c);
2212742Swollman					s->minset = 0;
22230711Swollman					return (1);
22330711Swollman				}
2242742Swollman			case 9:		/* Output */
2252742Swollman				retkind = hid_output;
2262742Swollman				goto ret;
2272742Swollman			case 10:	/* Collection */
22819878Swollman				c->kind = hid_collection;
22930711Swollman				c->collection = dval;
2302742Swollman				c->collevel++;
2312742Swollman				*h = *c;
2322742Swollman				hid_clear_local(c);
23330711Swollman				c->report_ID = NO_REPORT_ID;
23430711Swollman				s->nusage = 0;
23530711Swollman				return (1);
23630711Swollman			case 11:	/* Feature */
2372742Swollman				retkind = hid_feature;
2382742Swollman				goto ret;
23930711Swollman			case 12:	/* End collection */
2402742Swollman				c->kind = hid_endcollection;
2412742Swollman				c->collevel--;
2422742Swollman				*h = *c;
2432742Swollman				/*hid_clear_local(c);*/
2442742Swollman				s->nusage = 0;
24530711Swollman				return (1);
2462742Swollman			default:
2472742Swollman				return (-2);
2482742Swollman			}
2492742Swollman
25019878Swollman		case 1:		/* Global */
25130711Swollman			switch (bTag) {
25230711Swollman			case 0:
2532742Swollman				c->_usage_page = dval << 16;
2542742Swollman				break;
2552742Swollman			case 1:
2562742Swollman				c->logical_minimum = dval;
25730711Swollman				break;
25830711Swollman			case 2:
2592742Swollman				c->logical_maximum = dval;
2602742Swollman				break;
2612742Swollman			case 3:
2622742Swollman				c->physical_maximum = dval;
2632742Swollman				break;
2642742Swollman			case 4:
2652742Swollman				c->physical_maximum = dval;
26630711Swollman				break;
26730711Swollman			case 5:
26830711Swollman				c->unit_exponent = dval;
2692742Swollman				break;
2702742Swollman			case 6:
2712742Swollman				c->unit = dval;
2722742Swollman				break;
27330711Swollman			case 7:
27430711Swollman				c->report_size = dval;
2752742Swollman				break;
2762742Swollman			case 8:
2772742Swollman				c->report_ID = dval;
2782742Swollman				break;
2792742Swollman			case 9:
28030711Swollman				c->report_count = dval;
28186222Swollman				break;
2822742Swollman			case 10: /* Push */
2832742Swollman				hi = malloc(sizeof *hi);
2842742Swollman				*hi = s->cur;
2852742Swollman				c->next = hi;
2862742Swollman				break;
28730711Swollman			case 11: /* Pop */
2882742Swollman				hi = c->next;
28930711Swollman				s->cur = *hi;
2902742Swollman				free(hi);
2912742Swollman				break;
29286222Swollman			default:
2932742Swollman				return (-3);
29486222Swollman			}
29514343Swollman			break;
29614343Swollman		case 2:		/* Local */
29714343Swollman			switch (bTag) {
29814343Swollman			case 0:
2992742Swollman				if (bSize == 1)
3002742Swollman					dval = c->_usage_page | (dval&0xff);
3012742Swollman				else if (bSize == 2)
30219878Swollman					dval = c->_usage_page | (dval&0xffff);
30330711Swollman				c->usage = dval;
3042742Swollman				if (s->nusage < MAXUSAGE)
3052742Swollman					s->usages[s->nusage++] = dval;
3062742Swollman				/* else XXX */
3072742Swollman				break;
3082742Swollman			case 1:
3092742Swollman				s->minset = 1;
31019878Swollman				if (bSize == 1)
3112742Swollman					dval = c->_usage_page | (dval&0xff);
31219878Swollman				else if (bSize == 2)
3132742Swollman					dval = c->_usage_page | (dval&0xffff);
31419878Swollman				c->usage_minimum = dval;
3152742Swollman				break;
31619878Swollman			case 2:
3172742Swollman				if (bSize == 1)
31819878Swollman					dval = c->_usage_page | (dval&0xff);
31919878Swollman				else if (bSize == 2)
3202742Swollman					dval = c->_usage_page | (dval&0xffff);
32119878Swollman				c->usage_maximum = dval;
3222742Swollman				break;
3232742Swollman			case 3:
3242742Swollman				c->designator_index = dval;
32519878Swollman				break;
3262742Swollman			case 4:
32758787Sru				c->designator_minimum = dval;
32858787Sru				break;
32958787Sru			case 5:
33058787Sru				c->designator_maximum = dval;
33158787Sru				break;
33258787Sru			case 7:
33343014Swollman				c->string_index = dval;
3342742Swollman				break;
3352742Swollman			case 8:
3362742Swollman				c->string_minimum = dval;
3372742Swollman				break;
3382742Swollman			case 9:
3392742Swollman				c->string_maximum = dval;
3402742Swollman				break;
3412742Swollman			case 10:
3422742Swollman				c->set_delimiter = dval;
3432742Swollman				break;
3442742Swollman			default:
34530711Swollman				return (-4);
3462742Swollman			}
3472742Swollman			break;
3482742Swollman		default:
3492742Swollman			return (-5);
35030711Swollman		}
35130711Swollman	}
35230711Swollman}
3532742Swollman
3542742Swollmanint
35530711Swollmanhid_report_size(report_desc_t r, unsigned int id, enum hid_kind k)
3562742Swollman{
3572742Swollman	struct hid_data *d;
3582742Swollman	hid_item_t h;
3592742Swollman	unsigned int size = 0;
36030711Swollman
36130711Swollman	memset(&h, 0, sizeof h);
36230711Swollman	d = hid_start_parse(r, 1<<k);
3632742Swollman	while (hid_get_item(d, &h)) {
3642742Swollman		if (h.report_ID == id && h.kind == k) {
3652742Swollman			unsigned int newsize = h.pos + h.report_size;
3662742Swollman			if (newsize > size)
36719878Swollman			    size = newsize;
3682742Swollman		}
3692742Swollman	}
3702742Swollman	hid_end_parse(d);
3712742Swollman
3722742Swollman	if (id != NO_REPORT_ID)
37314343Swollman		size += 8;		/* add 8 bits for the report ID */
3742742Swollman
3752742Swollman	return ((size + 7) / 8);	/* return size in bytes */
3762742Swollman}
37730711Swollman
37820094Swollmanint
37919878Swollmanhid_locate(report_desc_t desc, unsigned int u, enum hid_kind k, hid_item_t *h)
3802742Swollman{
38119878Swollman	hid_data_t d;
3822742Swollman
38319878Swollman	for (d = hid_start_parse(desc, 1<<k); hid_get_item(d, h); ) {
3842742Swollman		if (h->kind == k && !(h->flags & HIO_CONST) && h->usage == u) {
38519878Swollman			hid_end_parse(d);
3862742Swollman			return (1);
38719878Swollman		}
3882742Swollman	}
38919878Swollman	hid_end_parse(d);
3902742Swollman	h->report_size = 0;
3912742Swollman	return (0);
39219878Swollman}
3932742Swollman