xref: /dragonfly/lib/libu4bhid/parse.c (revision ee284e10)
112bd3c8bSSascha Wildner /*	$NetBSD: parse.c,v 1.11 2000/09/24 02:19:54 augustss Exp $	*/
212bd3c8bSSascha Wildner 
312bd3c8bSSascha Wildner /*
412bd3c8bSSascha Wildner  * Copyright (c) 1999, 2001 Lennart Augustsson <augustss@netbsd.org>
512bd3c8bSSascha Wildner  * All rights reserved.
612bd3c8bSSascha Wildner  *
712bd3c8bSSascha Wildner  * Redistribution and use in source and binary forms, with or without
812bd3c8bSSascha Wildner  * modification, are permitted provided that the following conditions
912bd3c8bSSascha Wildner  * are met:
1012bd3c8bSSascha Wildner  * 1. Redistributions of source code must retain the above copyright
1112bd3c8bSSascha Wildner  *    notice, this list of conditions and the following disclaimer.
1212bd3c8bSSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
1312bd3c8bSSascha Wildner  *    notice, this list of conditions and the following disclaimer in the
1412bd3c8bSSascha Wildner  *    documentation and/or other materials provided with the distribution.
1512bd3c8bSSascha Wildner  *
1612bd3c8bSSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1712bd3c8bSSascha Wildner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1812bd3c8bSSascha Wildner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1912bd3c8bSSascha Wildner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2012bd3c8bSSascha Wildner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2112bd3c8bSSascha Wildner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2212bd3c8bSSascha Wildner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2312bd3c8bSSascha Wildner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2412bd3c8bSSascha Wildner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2512bd3c8bSSascha Wildner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2612bd3c8bSSascha Wildner  * SUCH DAMAGE.
27*ee284e10SSascha Wildner  *
28*ee284e10SSascha Wildner  * $FreeBSD: head/lib/libusbhid/parse.c 240762 2012-09-20 18:56:27Z mav $
2912bd3c8bSSascha Wildner  */
3012bd3c8bSSascha Wildner 
3112bd3c8bSSascha Wildner #include <assert.h>
3212bd3c8bSSascha Wildner #include <stdlib.h>
3312bd3c8bSSascha Wildner #include <string.h>
3412bd3c8bSSascha Wildner #include <sys/time.h>
3512bd3c8bSSascha Wildner 
363186c89eSSascha Wildner #include <bus/u4b/usb.h>
373186c89eSSascha Wildner #include <bus/u4b/usbhid.h>
3812bd3c8bSSascha Wildner 
3912bd3c8bSSascha Wildner #include "usbhid.h"
4012bd3c8bSSascha Wildner #include "usbvar.h"
4112bd3c8bSSascha Wildner 
4212bd3c8bSSascha Wildner #define	MAXUSAGE 100
4312bd3c8bSSascha Wildner #define	MAXPUSH 4
4412bd3c8bSSascha Wildner #define	MAXID 64
4512bd3c8bSSascha Wildner #define	ITEMTYPES 3
4612bd3c8bSSascha Wildner 
4712bd3c8bSSascha Wildner struct hid_pos_data {
4812bd3c8bSSascha Wildner 	int32_t rid;
4912bd3c8bSSascha Wildner 	uint32_t pos[ITEMTYPES];
5012bd3c8bSSascha Wildner };
5112bd3c8bSSascha Wildner 
5212bd3c8bSSascha Wildner struct hid_data {
5312bd3c8bSSascha Wildner 	const uint8_t *start;
5412bd3c8bSSascha Wildner 	const uint8_t *end;
5512bd3c8bSSascha Wildner 	const uint8_t *p;
5612bd3c8bSSascha Wildner 	struct hid_item cur[MAXPUSH];
5712bd3c8bSSascha Wildner 	struct hid_pos_data last_pos[MAXID];
5812bd3c8bSSascha Wildner 	uint32_t pos[ITEMTYPES];
5912bd3c8bSSascha Wildner 	int32_t usages_min[MAXUSAGE];
6012bd3c8bSSascha Wildner 	int32_t usages_max[MAXUSAGE];
6112bd3c8bSSascha Wildner 	int32_t usage_last;	/* last seen usage */
6212bd3c8bSSascha Wildner 	uint32_t loc_size;	/* last seen size */
6312bd3c8bSSascha Wildner 	uint32_t loc_count;	/* last seen count */
6412bd3c8bSSascha Wildner 	uint8_t	kindset;	/* we have 5 kinds so 8 bits are enough */
6512bd3c8bSSascha Wildner 	uint8_t	pushlevel;	/* current pushlevel */
6612bd3c8bSSascha Wildner 	uint8_t	ncount;		/* end usage item count */
6712bd3c8bSSascha Wildner 	uint8_t icount;		/* current usage item count */
6812bd3c8bSSascha Wildner 	uint8_t	nusage;		/* end "usages_min/max" index */
6912bd3c8bSSascha Wildner 	uint8_t	iusage;		/* current "usages_min/max" index */
7012bd3c8bSSascha Wildner 	uint8_t ousage;		/* current "usages_min/max" offset */
7112bd3c8bSSascha Wildner 	uint8_t	susage;		/* usage set flags */
72*ee284e10SSascha Wildner 	int32_t	reportid;	/* requested report ID */
7312bd3c8bSSascha Wildner };
7412bd3c8bSSascha Wildner 
7512bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
7612bd3c8bSSascha Wildner  *	hid_clear_local
7712bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
7812bd3c8bSSascha Wildner static void
hid_clear_local(hid_item_t * c)7912bd3c8bSSascha Wildner hid_clear_local(hid_item_t *c)
8012bd3c8bSSascha Wildner {
8112bd3c8bSSascha Wildner 
8212bd3c8bSSascha Wildner 	c->usage = 0;
8312bd3c8bSSascha Wildner 	c->usage_minimum = 0;
8412bd3c8bSSascha Wildner 	c->usage_maximum = 0;
8512bd3c8bSSascha Wildner 	c->designator_index = 0;
8612bd3c8bSSascha Wildner 	c->designator_minimum = 0;
8712bd3c8bSSascha Wildner 	c->designator_maximum = 0;
8812bd3c8bSSascha Wildner 	c->string_index = 0;
8912bd3c8bSSascha Wildner 	c->string_minimum = 0;
9012bd3c8bSSascha Wildner 	c->string_maximum = 0;
9112bd3c8bSSascha Wildner 	c->set_delimiter = 0;
9212bd3c8bSSascha Wildner }
9312bd3c8bSSascha Wildner 
9412bd3c8bSSascha Wildner static void
hid_switch_rid(struct hid_data * s,struct hid_item * c,int32_t next_rID)9512bd3c8bSSascha Wildner hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t next_rID)
9612bd3c8bSSascha Wildner {
9712bd3c8bSSascha Wildner 	uint8_t i, j;
9812bd3c8bSSascha Wildner 
9912bd3c8bSSascha Wildner 	/* check for same report ID - optimise */
10012bd3c8bSSascha Wildner 
10112bd3c8bSSascha Wildner 	if (c->report_ID == next_rID)
10212bd3c8bSSascha Wildner 		return;
10312bd3c8bSSascha Wildner 
10412bd3c8bSSascha Wildner 	/* save current position for current rID */
10512bd3c8bSSascha Wildner 
10612bd3c8bSSascha Wildner 	if (c->report_ID == 0) {
10712bd3c8bSSascha Wildner 		i = 0;
10812bd3c8bSSascha Wildner 	} else {
10912bd3c8bSSascha Wildner 		for (i = 1; i != MAXID; i++) {
11012bd3c8bSSascha Wildner 			if (s->last_pos[i].rid == c->report_ID)
11112bd3c8bSSascha Wildner 				break;
11212bd3c8bSSascha Wildner 			if (s->last_pos[i].rid == 0)
11312bd3c8bSSascha Wildner 				break;
11412bd3c8bSSascha Wildner 		}
11512bd3c8bSSascha Wildner 	}
11612bd3c8bSSascha Wildner 	if (i != MAXID) {
11712bd3c8bSSascha Wildner 		s->last_pos[i].rid = c->report_ID;
11812bd3c8bSSascha Wildner 		for (j = 0; j < ITEMTYPES; j++)
11912bd3c8bSSascha Wildner 			s->last_pos[i].pos[j] = s->pos[j];
12012bd3c8bSSascha Wildner 	}
12112bd3c8bSSascha Wildner 
12212bd3c8bSSascha Wildner 	/* store next report ID */
12312bd3c8bSSascha Wildner 
12412bd3c8bSSascha Wildner 	c->report_ID = next_rID;
12512bd3c8bSSascha Wildner 
12612bd3c8bSSascha Wildner 	/* lookup last position for next rID */
12712bd3c8bSSascha Wildner 
12812bd3c8bSSascha Wildner 	if (next_rID == 0) {
12912bd3c8bSSascha Wildner 		i = 0;
13012bd3c8bSSascha Wildner 	} else {
13112bd3c8bSSascha Wildner 		for (i = 1; i != MAXID; i++) {
13212bd3c8bSSascha Wildner 			if (s->last_pos[i].rid == next_rID)
13312bd3c8bSSascha Wildner 				break;
13412bd3c8bSSascha Wildner 			if (s->last_pos[i].rid == 0)
13512bd3c8bSSascha Wildner 				break;
13612bd3c8bSSascha Wildner 		}
13712bd3c8bSSascha Wildner 	}
13812bd3c8bSSascha Wildner 	if (i != MAXID) {
13912bd3c8bSSascha Wildner 		s->last_pos[i].rid = next_rID;
14012bd3c8bSSascha Wildner 		for (j = 0; j < ITEMTYPES; j++)
14112bd3c8bSSascha Wildner 			s->pos[j] = s->last_pos[i].pos[j];
14212bd3c8bSSascha Wildner 	} else {
14312bd3c8bSSascha Wildner 		for (j = 0; j < ITEMTYPES; j++)
14412bd3c8bSSascha Wildner 			s->pos[j] = 0;	/* Out of RID entries. */
14512bd3c8bSSascha Wildner 	}
14612bd3c8bSSascha Wildner }
14712bd3c8bSSascha Wildner 
14812bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
14912bd3c8bSSascha Wildner  *	hid_start_parse
15012bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
15112bd3c8bSSascha Wildner hid_data_t
hid_start_parse(report_desc_t d,int kindset,int id)152*ee284e10SSascha Wildner hid_start_parse(report_desc_t d, int kindset, int id)
15312bd3c8bSSascha Wildner {
15412bd3c8bSSascha Wildner 	struct hid_data *s;
15512bd3c8bSSascha Wildner 
15612bd3c8bSSascha Wildner 	s = malloc(sizeof *s);
15712bd3c8bSSascha Wildner 	memset(s, 0, sizeof *s);
15812bd3c8bSSascha Wildner 	s->start = s->p = d->data;
15912bd3c8bSSascha Wildner 	s->end = d->data + d->size;
16012bd3c8bSSascha Wildner 	s->kindset = kindset;
161*ee284e10SSascha Wildner 	s->reportid = id;
16212bd3c8bSSascha Wildner 	return (s);
16312bd3c8bSSascha Wildner }
16412bd3c8bSSascha Wildner 
16512bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
16612bd3c8bSSascha Wildner  *	hid_end_parse
16712bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
16812bd3c8bSSascha Wildner void
hid_end_parse(hid_data_t s)16912bd3c8bSSascha Wildner hid_end_parse(hid_data_t s)
17012bd3c8bSSascha Wildner {
17112bd3c8bSSascha Wildner 
17212bd3c8bSSascha Wildner 	if (s == NULL)
17312bd3c8bSSascha Wildner 		return;
17412bd3c8bSSascha Wildner 
17512bd3c8bSSascha Wildner 	free(s);
17612bd3c8bSSascha Wildner }
17712bd3c8bSSascha Wildner 
17812bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
17912bd3c8bSSascha Wildner  *	get byte from HID descriptor
18012bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
18112bd3c8bSSascha Wildner static uint8_t
hid_get_byte(struct hid_data * s,const uint16_t wSize)18212bd3c8bSSascha Wildner hid_get_byte(struct hid_data *s, const uint16_t wSize)
18312bd3c8bSSascha Wildner {
18412bd3c8bSSascha Wildner 	const uint8_t *ptr;
18512bd3c8bSSascha Wildner 	uint8_t retval;
18612bd3c8bSSascha Wildner 
18712bd3c8bSSascha Wildner 	ptr = s->p;
18812bd3c8bSSascha Wildner 
18912bd3c8bSSascha Wildner 	/* check if end is reached */
19012bd3c8bSSascha Wildner 	if (ptr == s->end)
19112bd3c8bSSascha Wildner 		return (0);
19212bd3c8bSSascha Wildner 
19312bd3c8bSSascha Wildner 	/* read out a byte */
19412bd3c8bSSascha Wildner 	retval = *ptr;
19512bd3c8bSSascha Wildner 
19612bd3c8bSSascha Wildner 	/* check if data pointer can be advanced by "wSize" bytes */
19712bd3c8bSSascha Wildner 	if ((s->end - ptr) < wSize)
19812bd3c8bSSascha Wildner 		ptr = s->end;
19912bd3c8bSSascha Wildner 	else
20012bd3c8bSSascha Wildner 		ptr += wSize;
20112bd3c8bSSascha Wildner 
20212bd3c8bSSascha Wildner 	/* update pointer */
20312bd3c8bSSascha Wildner 	s->p = ptr;
20412bd3c8bSSascha Wildner 
20512bd3c8bSSascha Wildner 	return (retval);
20612bd3c8bSSascha Wildner }
20712bd3c8bSSascha Wildner 
20812bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
20912bd3c8bSSascha Wildner  *	hid_get_item
21012bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
211*ee284e10SSascha Wildner static int
hid_get_item_raw(hid_data_t s,hid_item_t * h)212*ee284e10SSascha Wildner hid_get_item_raw(hid_data_t s, hid_item_t *h)
21312bd3c8bSSascha Wildner {
21412bd3c8bSSascha Wildner 	hid_item_t *c;
21512bd3c8bSSascha Wildner 	unsigned int bTag, bType, bSize;
21612bd3c8bSSascha Wildner 	int32_t mask;
21712bd3c8bSSascha Wildner 	int32_t dval;
21812bd3c8bSSascha Wildner 
21912bd3c8bSSascha Wildner 	if (s == NULL)
22012bd3c8bSSascha Wildner 		return (0);
22112bd3c8bSSascha Wildner 
22212bd3c8bSSascha Wildner 	c = &s->cur[s->pushlevel];
22312bd3c8bSSascha Wildner 
22412bd3c8bSSascha Wildner  top:
22512bd3c8bSSascha Wildner 	/* check if there is an array of items */
22612bd3c8bSSascha Wildner 	if (s->icount < s->ncount) {
22712bd3c8bSSascha Wildner 		/* get current usage */
22812bd3c8bSSascha Wildner 		if (s->iusage < s->nusage) {
22912bd3c8bSSascha Wildner 			dval = s->usages_min[s->iusage] + s->ousage;
23012bd3c8bSSascha Wildner 			c->usage = dval;
23112bd3c8bSSascha Wildner 			s->usage_last = dval;
23212bd3c8bSSascha Wildner 			if (dval == s->usages_max[s->iusage]) {
23312bd3c8bSSascha Wildner 				s->iusage ++;
23412bd3c8bSSascha Wildner 				s->ousage = 0;
23512bd3c8bSSascha Wildner 			} else {
23612bd3c8bSSascha Wildner 				s->ousage ++;
23712bd3c8bSSascha Wildner 			}
23812bd3c8bSSascha Wildner 		} else {
23912bd3c8bSSascha Wildner 			/* Using last usage */
24012bd3c8bSSascha Wildner 			dval = s->usage_last;
24112bd3c8bSSascha Wildner 		}
24212bd3c8bSSascha Wildner 		s->icount ++;
24312bd3c8bSSascha Wildner 		/*
24412bd3c8bSSascha Wildner 		 * Only copy HID item, increment position and return
24512bd3c8bSSascha Wildner 		 * if correct kindset!
24612bd3c8bSSascha Wildner 		 */
24712bd3c8bSSascha Wildner 		if (s->kindset & (1 << c->kind)) {
24812bd3c8bSSascha Wildner 			*h = *c;
24912bd3c8bSSascha Wildner 			h->pos = s->pos[c->kind];
25012bd3c8bSSascha Wildner 			s->pos[c->kind] += c->report_size * c->report_count;
25112bd3c8bSSascha Wildner 			return (1);
25212bd3c8bSSascha Wildner 		}
25312bd3c8bSSascha Wildner 	}
25412bd3c8bSSascha Wildner 
25512bd3c8bSSascha Wildner 	/* reset state variables */
25612bd3c8bSSascha Wildner 	s->icount = 0;
25712bd3c8bSSascha Wildner 	s->ncount = 0;
25812bd3c8bSSascha Wildner 	s->iusage = 0;
25912bd3c8bSSascha Wildner 	s->nusage = 0;
26012bd3c8bSSascha Wildner 	s->susage = 0;
26112bd3c8bSSascha Wildner 	s->ousage = 0;
26212bd3c8bSSascha Wildner 	hid_clear_local(c);
26312bd3c8bSSascha Wildner 
26412bd3c8bSSascha Wildner 	/* get next item */
26512bd3c8bSSascha Wildner 	while (s->p != s->end) {
26612bd3c8bSSascha Wildner 
26712bd3c8bSSascha Wildner 		bSize = hid_get_byte(s, 1);
26812bd3c8bSSascha Wildner 		if (bSize == 0xfe) {
26912bd3c8bSSascha Wildner 			/* long item */
27012bd3c8bSSascha Wildner 			bSize = hid_get_byte(s, 1);
27112bd3c8bSSascha Wildner 			bSize |= hid_get_byte(s, 1) << 8;
27212bd3c8bSSascha Wildner 			bTag = hid_get_byte(s, 1);
27312bd3c8bSSascha Wildner 			bType = 0xff;	/* XXX what should it be */
27412bd3c8bSSascha Wildner 		} else {
27512bd3c8bSSascha Wildner 			/* short item */
27612bd3c8bSSascha Wildner 			bTag = bSize >> 4;
27712bd3c8bSSascha Wildner 			bType = (bSize >> 2) & 3;
27812bd3c8bSSascha Wildner 			bSize &= 3;
27912bd3c8bSSascha Wildner 			if (bSize == 3)
28012bd3c8bSSascha Wildner 				bSize = 4;
28112bd3c8bSSascha Wildner 		}
28212bd3c8bSSascha Wildner 
28312bd3c8bSSascha Wildner 		switch(bSize) {
28412bd3c8bSSascha Wildner 		case 0:
28512bd3c8bSSascha Wildner 			dval = 0;
28612bd3c8bSSascha Wildner 			mask = 0;
28712bd3c8bSSascha Wildner 			break;
28812bd3c8bSSascha Wildner 		case 1:
28912bd3c8bSSascha Wildner 			dval = (int8_t)hid_get_byte(s, 1);
29012bd3c8bSSascha Wildner 			mask = 0xFF;
29112bd3c8bSSascha Wildner 			break;
29212bd3c8bSSascha Wildner 		case 2:
29312bd3c8bSSascha Wildner 			dval = hid_get_byte(s, 1);
29412bd3c8bSSascha Wildner 			dval |= hid_get_byte(s, 1) << 8;
29512bd3c8bSSascha Wildner 			dval = (int16_t)dval;
29612bd3c8bSSascha Wildner 			mask = 0xFFFF;
29712bd3c8bSSascha Wildner 			break;
29812bd3c8bSSascha Wildner 		case 4:
29912bd3c8bSSascha Wildner 			dval = hid_get_byte(s, 1);
30012bd3c8bSSascha Wildner 			dval |= hid_get_byte(s, 1) << 8;
30112bd3c8bSSascha Wildner 			dval |= hid_get_byte(s, 1) << 16;
30212bd3c8bSSascha Wildner 			dval |= hid_get_byte(s, 1) << 24;
30312bd3c8bSSascha Wildner 			mask = 0xFFFFFFFF;
30412bd3c8bSSascha Wildner 			break;
30512bd3c8bSSascha Wildner 		default:
30612bd3c8bSSascha Wildner 			dval = hid_get_byte(s, bSize);
30712bd3c8bSSascha Wildner 			continue;
30812bd3c8bSSascha Wildner 		}
30912bd3c8bSSascha Wildner 
31012bd3c8bSSascha Wildner 		switch (bType) {
31112bd3c8bSSascha Wildner 		case 0:		/* Main */
31212bd3c8bSSascha Wildner 			switch (bTag) {
31312bd3c8bSSascha Wildner 			case 8:	/* Input */
31412bd3c8bSSascha Wildner 				c->kind = hid_input;
31512bd3c8bSSascha Wildner 				c->flags = dval;
31612bd3c8bSSascha Wildner 		ret:
31712bd3c8bSSascha Wildner 				c->report_count = s->loc_count;
31812bd3c8bSSascha Wildner 				c->report_size = s->loc_size;
31912bd3c8bSSascha Wildner 
32012bd3c8bSSascha Wildner 				if (c->flags & HIO_VARIABLE) {
32112bd3c8bSSascha Wildner 					/* range check usage count */
32212bd3c8bSSascha Wildner 					if (c->report_count > 255) {
32312bd3c8bSSascha Wildner 						s->ncount = 255;
32412bd3c8bSSascha Wildner 					} else
32512bd3c8bSSascha Wildner 						s->ncount = c->report_count;
32612bd3c8bSSascha Wildner 
32712bd3c8bSSascha Wildner 					/*
32812bd3c8bSSascha Wildner 					 * The "top" loop will return
32912bd3c8bSSascha Wildner 					 * one and one item:
33012bd3c8bSSascha Wildner 					 */
33112bd3c8bSSascha Wildner 					c->report_count = 1;
33212bd3c8bSSascha Wildner 					c->usage_minimum = 0;
33312bd3c8bSSascha Wildner 					c->usage_maximum = 0;
33412bd3c8bSSascha Wildner 				} else {
33512bd3c8bSSascha Wildner 					s->ncount = 1;
33612bd3c8bSSascha Wildner 				}
33712bd3c8bSSascha Wildner 				goto top;
33812bd3c8bSSascha Wildner 
33912bd3c8bSSascha Wildner 			case 9:	/* Output */
34012bd3c8bSSascha Wildner 				c->kind = hid_output;
34112bd3c8bSSascha Wildner 				c->flags = dval;
34212bd3c8bSSascha Wildner 				goto ret;
34312bd3c8bSSascha Wildner 			case 10:	/* Collection */
34412bd3c8bSSascha Wildner 				c->kind = hid_collection;
34512bd3c8bSSascha Wildner 				c->collection = dval;
34612bd3c8bSSascha Wildner 				c->collevel++;
34712bd3c8bSSascha Wildner 				c->usage = s->usage_last;
34812bd3c8bSSascha Wildner 				*h = *c;
34912bd3c8bSSascha Wildner 				return (1);
35012bd3c8bSSascha Wildner 			case 11:	/* Feature */
35112bd3c8bSSascha Wildner 				c->kind = hid_feature;
35212bd3c8bSSascha Wildner 				c->flags = dval;
35312bd3c8bSSascha Wildner 				goto ret;
35412bd3c8bSSascha Wildner 			case 12:	/* End collection */
35512bd3c8bSSascha Wildner 				c->kind = hid_endcollection;
35612bd3c8bSSascha Wildner 				if (c->collevel == 0) {
35712bd3c8bSSascha Wildner 					/* Invalid end collection. */
35812bd3c8bSSascha Wildner 					return (0);
35912bd3c8bSSascha Wildner 				}
36012bd3c8bSSascha Wildner 				c->collevel--;
36112bd3c8bSSascha Wildner 				*h = *c;
36212bd3c8bSSascha Wildner 				return (1);
36312bd3c8bSSascha Wildner 			default:
36412bd3c8bSSascha Wildner 				break;
36512bd3c8bSSascha Wildner 			}
36612bd3c8bSSascha Wildner 			break;
36712bd3c8bSSascha Wildner 
36812bd3c8bSSascha Wildner 		case 1:		/* Global */
36912bd3c8bSSascha Wildner 			switch (bTag) {
37012bd3c8bSSascha Wildner 			case 0:
37112bd3c8bSSascha Wildner 				c->_usage_page = dval << 16;
37212bd3c8bSSascha Wildner 				break;
37312bd3c8bSSascha Wildner 			case 1:
37412bd3c8bSSascha Wildner 				c->logical_minimum = dval;
37512bd3c8bSSascha Wildner 				break;
37612bd3c8bSSascha Wildner 			case 2:
37712bd3c8bSSascha Wildner 				c->logical_maximum = dval;
37812bd3c8bSSascha Wildner 				break;
37912bd3c8bSSascha Wildner 			case 3:
38012bd3c8bSSascha Wildner 				c->physical_minimum = dval;
38112bd3c8bSSascha Wildner 				break;
38212bd3c8bSSascha Wildner 			case 4:
38312bd3c8bSSascha Wildner 				c->physical_maximum = dval;
38412bd3c8bSSascha Wildner 				break;
38512bd3c8bSSascha Wildner 			case 5:
38612bd3c8bSSascha Wildner 				c->unit_exponent = dval;
38712bd3c8bSSascha Wildner 				break;
38812bd3c8bSSascha Wildner 			case 6:
38912bd3c8bSSascha Wildner 				c->unit = dval;
39012bd3c8bSSascha Wildner 				break;
39112bd3c8bSSascha Wildner 			case 7:
39212bd3c8bSSascha Wildner 				/* mask because value is unsigned */
39312bd3c8bSSascha Wildner 				s->loc_size = dval & mask;
39412bd3c8bSSascha Wildner 				break;
39512bd3c8bSSascha Wildner 			case 8:
396*ee284e10SSascha Wildner 				hid_switch_rid(s, c, dval & mask);
39712bd3c8bSSascha Wildner 				break;
39812bd3c8bSSascha Wildner 			case 9:
39912bd3c8bSSascha Wildner 				/* mask because value is unsigned */
40012bd3c8bSSascha Wildner 				s->loc_count = dval & mask;
40112bd3c8bSSascha Wildner 				break;
40212bd3c8bSSascha Wildner 			case 10:	/* Push */
40312bd3c8bSSascha Wildner 				s->pushlevel ++;
40412bd3c8bSSascha Wildner 				if (s->pushlevel < MAXPUSH) {
40512bd3c8bSSascha Wildner 					s->cur[s->pushlevel] = *c;
40612bd3c8bSSascha Wildner 					/* store size and count */
40712bd3c8bSSascha Wildner 					c->report_size = s->loc_size;
40812bd3c8bSSascha Wildner 					c->report_count = s->loc_count;
40912bd3c8bSSascha Wildner 					/* update current item pointer */
41012bd3c8bSSascha Wildner 					c = &s->cur[s->pushlevel];
41112bd3c8bSSascha Wildner 				}
41212bd3c8bSSascha Wildner 				break;
41312bd3c8bSSascha Wildner 			case 11:	/* Pop */
41412bd3c8bSSascha Wildner 				s->pushlevel --;
41512bd3c8bSSascha Wildner 				if (s->pushlevel < MAXPUSH) {
41612bd3c8bSSascha Wildner 					c = &s->cur[s->pushlevel];
41712bd3c8bSSascha Wildner 					/* restore size and count */
41812bd3c8bSSascha Wildner 					s->loc_size = c->report_size;
41912bd3c8bSSascha Wildner 					s->loc_count = c->report_count;
42012bd3c8bSSascha Wildner 					c->report_size = 0;
42112bd3c8bSSascha Wildner 					c->report_count = 0;
42212bd3c8bSSascha Wildner 				}
42312bd3c8bSSascha Wildner 				break;
42412bd3c8bSSascha Wildner 			default:
42512bd3c8bSSascha Wildner 				break;
42612bd3c8bSSascha Wildner 			}
42712bd3c8bSSascha Wildner 			break;
42812bd3c8bSSascha Wildner 		case 2:		/* Local */
42912bd3c8bSSascha Wildner 			switch (bTag) {
43012bd3c8bSSascha Wildner 			case 0:
43112bd3c8bSSascha Wildner 				if (bSize != 4)
43212bd3c8bSSascha Wildner 					dval = (dval & mask) | c->_usage_page;
43312bd3c8bSSascha Wildner 
43412bd3c8bSSascha Wildner 				/* set last usage, in case of a collection */
43512bd3c8bSSascha Wildner 				s->usage_last = dval;
43612bd3c8bSSascha Wildner 
43712bd3c8bSSascha Wildner 				if (s->nusage < MAXUSAGE) {
43812bd3c8bSSascha Wildner 					s->usages_min[s->nusage] = dval;
43912bd3c8bSSascha Wildner 					s->usages_max[s->nusage] = dval;
44012bd3c8bSSascha Wildner 					s->nusage ++;
44112bd3c8bSSascha Wildner 				}
44212bd3c8bSSascha Wildner 				/* else XXX */
44312bd3c8bSSascha Wildner 
44412bd3c8bSSascha Wildner 				/* clear any pending usage sets */
44512bd3c8bSSascha Wildner 				s->susage = 0;
44612bd3c8bSSascha Wildner 				break;
44712bd3c8bSSascha Wildner 			case 1:
44812bd3c8bSSascha Wildner 				s->susage |= 1;
44912bd3c8bSSascha Wildner 
45012bd3c8bSSascha Wildner 				if (bSize != 4)
45112bd3c8bSSascha Wildner 					dval = (dval & mask) | c->_usage_page;
45212bd3c8bSSascha Wildner 				c->usage_minimum = dval;
45312bd3c8bSSascha Wildner 
45412bd3c8bSSascha Wildner 				goto check_set;
45512bd3c8bSSascha Wildner 			case 2:
45612bd3c8bSSascha Wildner 				s->susage |= 2;
45712bd3c8bSSascha Wildner 
45812bd3c8bSSascha Wildner 				if (bSize != 4)
45912bd3c8bSSascha Wildner 					dval = (dval & mask) | c->_usage_page;
46012bd3c8bSSascha Wildner 				c->usage_maximum = dval;
46112bd3c8bSSascha Wildner 
46212bd3c8bSSascha Wildner 			check_set:
46312bd3c8bSSascha Wildner 				if (s->susage != 3)
46412bd3c8bSSascha Wildner 					break;
46512bd3c8bSSascha Wildner 
46612bd3c8bSSascha Wildner 				/* sanity check */
46712bd3c8bSSascha Wildner 				if ((s->nusage < MAXUSAGE) &&
46812bd3c8bSSascha Wildner 				    (c->usage_minimum <= c->usage_maximum)) {
46912bd3c8bSSascha Wildner 					/* add usage range */
47012bd3c8bSSascha Wildner 					s->usages_min[s->nusage] =
47112bd3c8bSSascha Wildner 					    c->usage_minimum;
47212bd3c8bSSascha Wildner 					s->usages_max[s->nusage] =
47312bd3c8bSSascha Wildner 					    c->usage_maximum;
47412bd3c8bSSascha Wildner 					s->nusage ++;
47512bd3c8bSSascha Wildner 				}
47612bd3c8bSSascha Wildner 				/* else XXX */
47712bd3c8bSSascha Wildner 
47812bd3c8bSSascha Wildner 				s->susage = 0;
47912bd3c8bSSascha Wildner 				break;
48012bd3c8bSSascha Wildner 			case 3:
48112bd3c8bSSascha Wildner 				c->designator_index = dval;
48212bd3c8bSSascha Wildner 				break;
48312bd3c8bSSascha Wildner 			case 4:
48412bd3c8bSSascha Wildner 				c->designator_minimum = dval;
48512bd3c8bSSascha Wildner 				break;
48612bd3c8bSSascha Wildner 			case 5:
48712bd3c8bSSascha Wildner 				c->designator_maximum = dval;
48812bd3c8bSSascha Wildner 				break;
48912bd3c8bSSascha Wildner 			case 7:
49012bd3c8bSSascha Wildner 				c->string_index = dval;
49112bd3c8bSSascha Wildner 				break;
49212bd3c8bSSascha Wildner 			case 8:
49312bd3c8bSSascha Wildner 				c->string_minimum = dval;
49412bd3c8bSSascha Wildner 				break;
49512bd3c8bSSascha Wildner 			case 9:
49612bd3c8bSSascha Wildner 				c->string_maximum = dval;
49712bd3c8bSSascha Wildner 				break;
49812bd3c8bSSascha Wildner 			case 10:
49912bd3c8bSSascha Wildner 				c->set_delimiter = dval;
50012bd3c8bSSascha Wildner 				break;
50112bd3c8bSSascha Wildner 			default:
50212bd3c8bSSascha Wildner 				break;
50312bd3c8bSSascha Wildner 			}
50412bd3c8bSSascha Wildner 			break;
50512bd3c8bSSascha Wildner 		default:
50612bd3c8bSSascha Wildner 			break;
50712bd3c8bSSascha Wildner 		}
50812bd3c8bSSascha Wildner 	}
50912bd3c8bSSascha Wildner 	return (0);
51012bd3c8bSSascha Wildner }
51112bd3c8bSSascha Wildner 
51212bd3c8bSSascha Wildner int
hid_get_item(hid_data_t s,hid_item_t * h)513*ee284e10SSascha Wildner hid_get_item(hid_data_t s, hid_item_t *h)
514*ee284e10SSascha Wildner {
515*ee284e10SSascha Wildner 	int r;
516*ee284e10SSascha Wildner 
517*ee284e10SSascha Wildner 	for (;;) {
518*ee284e10SSascha Wildner 		r = hid_get_item_raw(s, h);
519*ee284e10SSascha Wildner 		if (r <= 0 || s->reportid == -1 || h->report_ID == s->reportid)
520*ee284e10SSascha Wildner 			break;
521*ee284e10SSascha Wildner 	}
522*ee284e10SSascha Wildner 	return (r);
523*ee284e10SSascha Wildner }
524*ee284e10SSascha Wildner 
525*ee284e10SSascha Wildner int
hid_report_size(report_desc_t r,enum hid_kind k,int id)52612bd3c8bSSascha Wildner hid_report_size(report_desc_t r, enum hid_kind k, int id)
52712bd3c8bSSascha Wildner {
52812bd3c8bSSascha Wildner 	struct hid_data *d;
52912bd3c8bSSascha Wildner 	struct hid_item h;
53012bd3c8bSSascha Wildner 	uint32_t temp;
53112bd3c8bSSascha Wildner 	uint32_t hpos;
53212bd3c8bSSascha Wildner 	uint32_t lpos;
53312bd3c8bSSascha Wildner 	int report_id = 0;
53412bd3c8bSSascha Wildner 
53512bd3c8bSSascha Wildner 	hpos = 0;
53612bd3c8bSSascha Wildner 	lpos = 0xFFFFFFFF;
53712bd3c8bSSascha Wildner 
53812bd3c8bSSascha Wildner 	memset(&h, 0, sizeof h);
53912bd3c8bSSascha Wildner 	for (d = hid_start_parse(r, 1 << k, id); hid_get_item(d, &h); ) {
540*ee284e10SSascha Wildner 		if (h.kind == k) {
54112bd3c8bSSascha Wildner 			/* compute minimum */
54212bd3c8bSSascha Wildner 			if (lpos > h.pos)
54312bd3c8bSSascha Wildner 				lpos = h.pos;
54412bd3c8bSSascha Wildner 			/* compute end position */
54512bd3c8bSSascha Wildner 			temp = h.pos + (h.report_size * h.report_count);
54612bd3c8bSSascha Wildner 			/* compute maximum */
54712bd3c8bSSascha Wildner 			if (hpos < temp)
54812bd3c8bSSascha Wildner 				hpos = temp;
54912bd3c8bSSascha Wildner 			if (h.report_ID != 0)
55012bd3c8bSSascha Wildner 				report_id = 1;
55112bd3c8bSSascha Wildner 		}
55212bd3c8bSSascha Wildner 	}
55312bd3c8bSSascha Wildner 	hid_end_parse(d);
55412bd3c8bSSascha Wildner 
55512bd3c8bSSascha Wildner 	/* safety check - can happen in case of currupt descriptors */
55612bd3c8bSSascha Wildner 	if (lpos > hpos)
55712bd3c8bSSascha Wildner 		temp = 0;
55812bd3c8bSSascha Wildner 	else
55912bd3c8bSSascha Wildner 		temp = hpos - lpos;
56012bd3c8bSSascha Wildner 
56112bd3c8bSSascha Wildner 	/* return length in bytes rounded up */
56212bd3c8bSSascha Wildner 	return ((temp + 7) / 8 + report_id);
56312bd3c8bSSascha Wildner }
56412bd3c8bSSascha Wildner 
56512bd3c8bSSascha Wildner int
hid_locate(report_desc_t desc,unsigned int u,enum hid_kind k,hid_item_t * h,int id)56612bd3c8bSSascha Wildner hid_locate(report_desc_t desc, unsigned int u, enum hid_kind k,
56712bd3c8bSSascha Wildner 	   hid_item_t *h, int id)
56812bd3c8bSSascha Wildner {
56912bd3c8bSSascha Wildner 	struct hid_data *d;
57012bd3c8bSSascha Wildner 
57112bd3c8bSSascha Wildner 	for (d = hid_start_parse(desc, 1 << k, id); hid_get_item(d, h); ) {
57212bd3c8bSSascha Wildner 		if (h->kind == k && !(h->flags & HIO_CONST) && h->usage == u) {
57312bd3c8bSSascha Wildner 			hid_end_parse(d);
57412bd3c8bSSascha Wildner 			return (1);
57512bd3c8bSSascha Wildner 		}
57612bd3c8bSSascha Wildner 	}
57712bd3c8bSSascha Wildner 	hid_end_parse(d);
57812bd3c8bSSascha Wildner 	h->report_size = 0;
57912bd3c8bSSascha Wildner 	return (0);
58012bd3c8bSSascha Wildner }
581