xref: /openbsd/sys/dev/hid/hid.c (revision 8a83145e)
1*8a83145eSjcs /*	$OpenBSD: hid.c,v 1.1 2016/01/08 15:54:13 jcs Exp $ */
2*8a83145eSjcs /*	$NetBSD: hid.c,v 1.23 2002/07/11 21:14:25 augustss Exp $	*/
3*8a83145eSjcs /*	$FreeBSD: src/sys/dev/usb/hid.c,v 1.11 1999/11/17 22:33:39 n_hibma Exp $ */
4*8a83145eSjcs 
5*8a83145eSjcs /*
6*8a83145eSjcs  * Copyright (c) 1998 The NetBSD Foundation, Inc.
7*8a83145eSjcs  * All rights reserved.
8*8a83145eSjcs  *
9*8a83145eSjcs  * This code is derived from software contributed to The NetBSD Foundation
10*8a83145eSjcs  * by Lennart Augustsson (lennart@augustsson.net) at
11*8a83145eSjcs  * Carlstedt Research & Technology.
12*8a83145eSjcs  *
13*8a83145eSjcs  * Redistribution and use in source and binary forms, with or without
14*8a83145eSjcs  * modification, are permitted provided that the following conditions
15*8a83145eSjcs  * are met:
16*8a83145eSjcs  * 1. Redistributions of source code must retain the above copyright
17*8a83145eSjcs  *    notice, this list of conditions and the following disclaimer.
18*8a83145eSjcs  * 2. Redistributions in binary form must reproduce the above copyright
19*8a83145eSjcs  *    notice, this list of conditions and the following disclaimer in the
20*8a83145eSjcs  *    documentation and/or other materials provided with the distribution.
21*8a83145eSjcs  *
22*8a83145eSjcs  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23*8a83145eSjcs  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24*8a83145eSjcs  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25*8a83145eSjcs  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26*8a83145eSjcs  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27*8a83145eSjcs  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28*8a83145eSjcs  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29*8a83145eSjcs  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30*8a83145eSjcs  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31*8a83145eSjcs  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32*8a83145eSjcs  * POSSIBILITY OF SUCH DAMAGE.
33*8a83145eSjcs  */
34*8a83145eSjcs 
35*8a83145eSjcs #include <sys/param.h>
36*8a83145eSjcs #include <sys/systm.h>
37*8a83145eSjcs #include <sys/malloc.h>
38*8a83145eSjcs 
39*8a83145eSjcs #include <dev/hid/hid.h>
40*8a83145eSjcs 
41*8a83145eSjcs #ifdef USBHID_DEBUG
42*8a83145eSjcs #define DPRINTF(x...)	 do { printf(x); } while (0)
43*8a83145eSjcs #else
44*8a83145eSjcs #define DPRINTF(x...)
45*8a83145eSjcs #endif
46*8a83145eSjcs 
47*8a83145eSjcs #define	MAXUSAGE 64
48*8a83145eSjcs #define	MAXPUSH 4
49*8a83145eSjcs #define	MAXID 16
50*8a83145eSjcs 
51*8a83145eSjcs struct hid_pos_data {
52*8a83145eSjcs 	int32_t rid;
53*8a83145eSjcs 	uint32_t pos;
54*8a83145eSjcs };
55*8a83145eSjcs 
56*8a83145eSjcs struct hid_data {
57*8a83145eSjcs 	const uint8_t *start;
58*8a83145eSjcs 	const uint8_t *end;
59*8a83145eSjcs 	const uint8_t *p;
60*8a83145eSjcs 	struct hid_item cur[MAXPUSH];
61*8a83145eSjcs 	struct hid_pos_data last_pos[MAXID];
62*8a83145eSjcs 	int32_t	usages_min[MAXUSAGE];
63*8a83145eSjcs 	int32_t	usages_max[MAXUSAGE];
64*8a83145eSjcs 	int32_t usage_last;	/* last seen usage */
65*8a83145eSjcs 	uint32_t loc_size;	/* last seen size */
66*8a83145eSjcs 	uint32_t loc_count;	/* last seen count */
67*8a83145eSjcs 	enum hid_kind kind;
68*8a83145eSjcs 	uint8_t	pushlevel;	/* current pushlevel */
69*8a83145eSjcs 	uint8_t	ncount;		/* end usage item count */
70*8a83145eSjcs 	uint8_t icount;		/* current usage item count */
71*8a83145eSjcs 	uint8_t	nusage;		/* end "usages_min/max" index */
72*8a83145eSjcs 	uint8_t	iusage;		/* current "usages_min/max" index */
73*8a83145eSjcs 	uint8_t ousage;		/* current "usages_min/max" offset */
74*8a83145eSjcs 	uint8_t	susage;		/* usage set flags */
75*8a83145eSjcs };
76*8a83145eSjcs 
77*8a83145eSjcs static void
78*8a83145eSjcs hid_clear_local(struct hid_item *c)
79*8a83145eSjcs {
80*8a83145eSjcs 	c->loc.count = 0;
81*8a83145eSjcs 	c->loc.size = 0;
82*8a83145eSjcs 	c->usage = 0;
83*8a83145eSjcs 	c->usage_minimum = 0;
84*8a83145eSjcs 	c->usage_maximum = 0;
85*8a83145eSjcs 	c->designator_index = 0;
86*8a83145eSjcs 	c->designator_minimum = 0;
87*8a83145eSjcs 	c->designator_maximum = 0;
88*8a83145eSjcs 	c->string_index = 0;
89*8a83145eSjcs 	c->string_minimum = 0;
90*8a83145eSjcs 	c->string_maximum = 0;
91*8a83145eSjcs 	c->set_delimiter = 0;
92*8a83145eSjcs }
93*8a83145eSjcs 
94*8a83145eSjcs static void
95*8a83145eSjcs hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t nextid)
96*8a83145eSjcs {
97*8a83145eSjcs 	uint8_t i;
98*8a83145eSjcs 
99*8a83145eSjcs 	if (c->report_ID == nextid)
100*8a83145eSjcs 		return;
101*8a83145eSjcs 
102*8a83145eSjcs 	/* save current position for current rID */
103*8a83145eSjcs 	if (c->report_ID == 0) {
104*8a83145eSjcs 		i = 0;
105*8a83145eSjcs 	} else {
106*8a83145eSjcs 		for (i = 1; i != MAXID; i++) {
107*8a83145eSjcs 			if (s->last_pos[i].rid == c->report_ID)
108*8a83145eSjcs 				break;
109*8a83145eSjcs 			if (s->last_pos[i].rid == 0)
110*8a83145eSjcs 				break;
111*8a83145eSjcs 		}
112*8a83145eSjcs 	}
113*8a83145eSjcs 	if (i != MAXID) {
114*8a83145eSjcs 		s->last_pos[i].rid = c->report_ID;
115*8a83145eSjcs 		s->last_pos[i].pos = c->loc.pos;
116*8a83145eSjcs 	}
117*8a83145eSjcs 
118*8a83145eSjcs 	/* store next report ID */
119*8a83145eSjcs 	c->report_ID = nextid;
120*8a83145eSjcs 
121*8a83145eSjcs 	/* lookup last position for next rID */
122*8a83145eSjcs 	if (nextid == 0) {
123*8a83145eSjcs 		i = 0;
124*8a83145eSjcs 	} else {
125*8a83145eSjcs 		for (i = 1; i != MAXID; i++) {
126*8a83145eSjcs 			if (s->last_pos[i].rid == nextid)
127*8a83145eSjcs 				break;
128*8a83145eSjcs 			if (s->last_pos[i].rid == 0)
129*8a83145eSjcs 				break;
130*8a83145eSjcs 		}
131*8a83145eSjcs 	}
132*8a83145eSjcs 	if (i != MAXID) {
133*8a83145eSjcs 		s->last_pos[i].rid = nextid;
134*8a83145eSjcs 		c->loc.pos = s->last_pos[i].pos;
135*8a83145eSjcs 	} else {
136*8a83145eSjcs 		DPRINTF("Out of RID entries, position is set to zero!\n");
137*8a83145eSjcs 		c->loc.pos = 0;
138*8a83145eSjcs 	}
139*8a83145eSjcs }
140*8a83145eSjcs 
141*8a83145eSjcs struct hid_data *
142*8a83145eSjcs hid_start_parse(const void *d, int len, enum hid_kind kind)
143*8a83145eSjcs {
144*8a83145eSjcs 	struct hid_data *s;
145*8a83145eSjcs 
146*8a83145eSjcs 	s = malloc(sizeof(*s), M_TEMP, M_WAITOK | M_ZERO);
147*8a83145eSjcs 
148*8a83145eSjcs 	s->start = s->p = d;
149*8a83145eSjcs 	s->end = ((const uint8_t *)d) + len;
150*8a83145eSjcs 	s->kind = kind;
151*8a83145eSjcs 	return (s);
152*8a83145eSjcs }
153*8a83145eSjcs 
154*8a83145eSjcs void
155*8a83145eSjcs hid_end_parse(struct hid_data *s)
156*8a83145eSjcs {
157*8a83145eSjcs 	if (s == NULL)
158*8a83145eSjcs 		return;
159*8a83145eSjcs 
160*8a83145eSjcs 	free(s, M_TEMP, 0);
161*8a83145eSjcs }
162*8a83145eSjcs 
163*8a83145eSjcs static uint8_t
164*8a83145eSjcs hid_get_byte(struct hid_data *s, const uint16_t wSize)
165*8a83145eSjcs {
166*8a83145eSjcs 	const uint8_t *ptr;
167*8a83145eSjcs 	uint8_t retval;
168*8a83145eSjcs 
169*8a83145eSjcs 	ptr = s->p;
170*8a83145eSjcs 
171*8a83145eSjcs 	/* check if end is reached */
172*8a83145eSjcs 	if (ptr == s->end)
173*8a83145eSjcs 		return (0);
174*8a83145eSjcs 
175*8a83145eSjcs 	/* read out a byte */
176*8a83145eSjcs 	retval = *ptr;
177*8a83145eSjcs 
178*8a83145eSjcs 	/* check if data pointer can be advanced by "wSize" bytes */
179*8a83145eSjcs 	if ((s->end - ptr) < wSize)
180*8a83145eSjcs 		ptr = s->end;
181*8a83145eSjcs 	else
182*8a83145eSjcs 		ptr += wSize;
183*8a83145eSjcs 
184*8a83145eSjcs 	/* update pointer */
185*8a83145eSjcs 	s->p = ptr;
186*8a83145eSjcs 
187*8a83145eSjcs 	return (retval);
188*8a83145eSjcs }
189*8a83145eSjcs 
190*8a83145eSjcs int
191*8a83145eSjcs hid_get_item(struct hid_data *s, struct hid_item *h)
192*8a83145eSjcs {
193*8a83145eSjcs 	struct hid_item *c;
194*8a83145eSjcs 	unsigned int bTag, bType, bSize;
195*8a83145eSjcs 	uint32_t oldpos;
196*8a83145eSjcs 	int32_t mask;
197*8a83145eSjcs 	int32_t dval;
198*8a83145eSjcs 
199*8a83145eSjcs 	if (s == NULL)
200*8a83145eSjcs 		return (0);
201*8a83145eSjcs 
202*8a83145eSjcs 	c = &s->cur[s->pushlevel];
203*8a83145eSjcs 
204*8a83145eSjcs  top:
205*8a83145eSjcs 	/* check if there is an array of items */
206*8a83145eSjcs 	DPRINTF("%s: icount=%d ncount=%d\n", __func__,
207*8a83145eSjcs 	    s->icount, s->ncount);
208*8a83145eSjcs 	if (s->icount < s->ncount) {
209*8a83145eSjcs 		/* get current usage */
210*8a83145eSjcs 		if (s->iusage < s->nusage) {
211*8a83145eSjcs 			dval = s->usages_min[s->iusage] + s->ousage;
212*8a83145eSjcs 			c->usage = dval;
213*8a83145eSjcs 			s->usage_last = dval;
214*8a83145eSjcs 			if (dval == s->usages_max[s->iusage]) {
215*8a83145eSjcs 				s->iusage ++;
216*8a83145eSjcs 				s->ousage = 0;
217*8a83145eSjcs 			} else {
218*8a83145eSjcs 				s->ousage ++;
219*8a83145eSjcs 			}
220*8a83145eSjcs 		} else {
221*8a83145eSjcs 			DPRINTF("Using last usage\n");
222*8a83145eSjcs 			dval = s->usage_last;
223*8a83145eSjcs 		}
224*8a83145eSjcs 		s->icount ++;
225*8a83145eSjcs 		/*
226*8a83145eSjcs 		 * Only copy HID item, increment position and return
227*8a83145eSjcs 		 * if correct kind!
228*8a83145eSjcs 		 */
229*8a83145eSjcs 		if (s->kind == c->kind) {
230*8a83145eSjcs 			*h = *c;
231*8a83145eSjcs 			DPRINTF("%u,%u,%u\n", h->loc.pos,
232*8a83145eSjcs 			    h->loc.size, h->loc.count);
233*8a83145eSjcs 			c->loc.pos += c->loc.size * c->loc.count;
234*8a83145eSjcs 			return (1);
235*8a83145eSjcs 		}
236*8a83145eSjcs 	}
237*8a83145eSjcs 
238*8a83145eSjcs 	/* reset state variables */
239*8a83145eSjcs 	s->icount = 0;
240*8a83145eSjcs 	s->ncount = 0;
241*8a83145eSjcs 	s->iusage = 0;
242*8a83145eSjcs 	s->nusage = 0;
243*8a83145eSjcs 	s->susage = 0;
244*8a83145eSjcs 	s->ousage = 0;
245*8a83145eSjcs 	hid_clear_local(c);
246*8a83145eSjcs 
247*8a83145eSjcs 	/* get next item */
248*8a83145eSjcs 	while (s->p != s->end) {
249*8a83145eSjcs 
250*8a83145eSjcs 		bSize = hid_get_byte(s, 1);
251*8a83145eSjcs 		if (bSize == 0xfe) {
252*8a83145eSjcs 			/* long item */
253*8a83145eSjcs 			bSize = hid_get_byte(s, 1);
254*8a83145eSjcs 			bSize |= hid_get_byte(s, 1) << 8;
255*8a83145eSjcs 			bTag = hid_get_byte(s, 1);
256*8a83145eSjcs 			bType = 0xff;	/* XXX what should it be */
257*8a83145eSjcs 		} else {
258*8a83145eSjcs 			/* short item */
259*8a83145eSjcs 			bTag = bSize >> 4;
260*8a83145eSjcs 			bType = (bSize >> 2) & 3;
261*8a83145eSjcs 			bSize &= 3;
262*8a83145eSjcs 			if (bSize == 3)
263*8a83145eSjcs 				bSize = 4;
264*8a83145eSjcs 		}
265*8a83145eSjcs 		switch (bSize) {
266*8a83145eSjcs 		case 0:
267*8a83145eSjcs 			dval = 0;
268*8a83145eSjcs 			mask = 0;
269*8a83145eSjcs 			break;
270*8a83145eSjcs 		case 1:
271*8a83145eSjcs 			dval = hid_get_byte(s, 1);
272*8a83145eSjcs 			mask = 0xFF;
273*8a83145eSjcs 			break;
274*8a83145eSjcs 		case 2:
275*8a83145eSjcs 			dval = hid_get_byte(s, 1);
276*8a83145eSjcs 			dval |= hid_get_byte(s, 1) << 8;
277*8a83145eSjcs 			mask = 0xFFFF;
278*8a83145eSjcs 			break;
279*8a83145eSjcs 		case 4:
280*8a83145eSjcs 			dval = hid_get_byte(s, 1);
281*8a83145eSjcs 			dval |= hid_get_byte(s, 1) << 8;
282*8a83145eSjcs 			dval |= hid_get_byte(s, 1) << 16;
283*8a83145eSjcs 			dval |= hid_get_byte(s, 1) << 24;
284*8a83145eSjcs 			mask = 0xFFFFFFFF;
285*8a83145eSjcs 			break;
286*8a83145eSjcs 		default:
287*8a83145eSjcs 			dval = hid_get_byte(s, bSize);
288*8a83145eSjcs 			DPRINTF("bad length %u (data=0x%02x)\n",
289*8a83145eSjcs 			    bSize, dval);
290*8a83145eSjcs 			continue;
291*8a83145eSjcs 		}
292*8a83145eSjcs 
293*8a83145eSjcs 		DPRINTF("%s: bType=%d bTag=%d dval=%d\n", __func__,
294*8a83145eSjcs 		    bType, bTag, dval);
295*8a83145eSjcs 		switch (bType) {
296*8a83145eSjcs 		case 0:		/* Main */
297*8a83145eSjcs 			switch (bTag) {
298*8a83145eSjcs 			case 8:	/* Input */
299*8a83145eSjcs 				c->kind = hid_input;
300*8a83145eSjcs 				c->flags = dval;
301*8a83145eSjcs 		ret:
302*8a83145eSjcs 				c->loc.count = s->loc_count;
303*8a83145eSjcs 				c->loc.size = s->loc_size;
304*8a83145eSjcs 
305*8a83145eSjcs 				if (c->flags & HIO_VARIABLE) {
306*8a83145eSjcs 					/* range check usage count */
307*8a83145eSjcs 					if (c->loc.count > 255) {
308*8a83145eSjcs 						DPRINTF("Number of "
309*8a83145eSjcs 						    "items truncated to 255\n");
310*8a83145eSjcs 						s->ncount = 255;
311*8a83145eSjcs 					} else
312*8a83145eSjcs 						s->ncount = c->loc.count;
313*8a83145eSjcs 
314*8a83145eSjcs 					/*
315*8a83145eSjcs 					 * The "top" loop will return
316*8a83145eSjcs 					 * one and one item:
317*8a83145eSjcs 					 */
318*8a83145eSjcs 					c->loc.count = 1;
319*8a83145eSjcs 				} else {
320*8a83145eSjcs 					s->ncount = 1;
321*8a83145eSjcs 				}
322*8a83145eSjcs 				goto top;
323*8a83145eSjcs 
324*8a83145eSjcs 			case 9:	/* Output */
325*8a83145eSjcs 				c->kind = hid_output;
326*8a83145eSjcs 				c->flags = dval;
327*8a83145eSjcs 				goto ret;
328*8a83145eSjcs 			case 10:	/* Collection */
329*8a83145eSjcs 				c->kind = hid_collection;
330*8a83145eSjcs 				c->collection = dval;
331*8a83145eSjcs 				c->collevel++;
332*8a83145eSjcs 				c->usage = s->usage_last;
333*8a83145eSjcs 				*h = *c;
334*8a83145eSjcs 				return (1);
335*8a83145eSjcs 			case 11:	/* Feature */
336*8a83145eSjcs 				c->kind = hid_feature;
337*8a83145eSjcs 				c->flags = dval;
338*8a83145eSjcs 				goto ret;
339*8a83145eSjcs 			case 12:	/* End collection */
340*8a83145eSjcs 				c->kind = hid_endcollection;
341*8a83145eSjcs 				if (c->collevel == 0) {
342*8a83145eSjcs 					DPRINTF("invalid end collection\n");
343*8a83145eSjcs 					return (0);
344*8a83145eSjcs 				}
345*8a83145eSjcs 				c->collevel--;
346*8a83145eSjcs 				*h = *c;
347*8a83145eSjcs 				return (1);
348*8a83145eSjcs 			default:
349*8a83145eSjcs 				DPRINTF("Main bTag=%d\n", bTag);
350*8a83145eSjcs 				break;
351*8a83145eSjcs 			}
352*8a83145eSjcs 			break;
353*8a83145eSjcs 		case 1:		/* Global */
354*8a83145eSjcs 			switch (bTag) {
355*8a83145eSjcs 			case 0:
356*8a83145eSjcs 				c->_usage_page = dval << 16;
357*8a83145eSjcs 				break;
358*8a83145eSjcs 			case 1:
359*8a83145eSjcs 				c->logical_minimum = dval;
360*8a83145eSjcs 				break;
361*8a83145eSjcs 			case 2:
362*8a83145eSjcs 				c->logical_maximum = dval;
363*8a83145eSjcs 				break;
364*8a83145eSjcs 			case 3:
365*8a83145eSjcs 				c->physical_minimum = dval;
366*8a83145eSjcs 				break;
367*8a83145eSjcs 			case 4:
368*8a83145eSjcs 				c->physical_maximum = dval;
369*8a83145eSjcs 				break;
370*8a83145eSjcs 			case 5:
371*8a83145eSjcs 				c->unit_exponent = dval;
372*8a83145eSjcs 				break;
373*8a83145eSjcs 			case 6:
374*8a83145eSjcs 				c->unit = dval;
375*8a83145eSjcs 				break;
376*8a83145eSjcs 			case 7:
377*8a83145eSjcs 				/* mask because value is unsigned */
378*8a83145eSjcs 				s->loc_size = dval & mask;
379*8a83145eSjcs 				break;
380*8a83145eSjcs 			case 8:
381*8a83145eSjcs 				hid_switch_rid(s, c, dval & mask);
382*8a83145eSjcs 				break;
383*8a83145eSjcs 			case 9:
384*8a83145eSjcs 				/* mask because value is unsigned */
385*8a83145eSjcs 				s->loc_count = dval & mask;
386*8a83145eSjcs 				break;
387*8a83145eSjcs 			case 10:	/* Push */
388*8a83145eSjcs 				s->pushlevel ++;
389*8a83145eSjcs 				if (s->pushlevel < MAXPUSH) {
390*8a83145eSjcs 					s->cur[s->pushlevel] = *c;
391*8a83145eSjcs 					/* store size and count */
392*8a83145eSjcs 					c->loc.size = s->loc_size;
393*8a83145eSjcs 					c->loc.count = s->loc_count;
394*8a83145eSjcs 					/* update current item pointer */
395*8a83145eSjcs 					c = &s->cur[s->pushlevel];
396*8a83145eSjcs 				} else {
397*8a83145eSjcs 					DPRINTF("Cannot push "
398*8a83145eSjcs 					    "item @ %d\n", s->pushlevel);
399*8a83145eSjcs 				}
400*8a83145eSjcs 				break;
401*8a83145eSjcs 			case 11:	/* Pop */
402*8a83145eSjcs 				s->pushlevel --;
403*8a83145eSjcs 				if (s->pushlevel < MAXPUSH) {
404*8a83145eSjcs 					/* preserve position */
405*8a83145eSjcs 					oldpos = c->loc.pos;
406*8a83145eSjcs 					c = &s->cur[s->pushlevel];
407*8a83145eSjcs 					/* restore size and count */
408*8a83145eSjcs 					s->loc_size = c->loc.size;
409*8a83145eSjcs 					s->loc_count = c->loc.count;
410*8a83145eSjcs 					/* set default item location */
411*8a83145eSjcs 					c->loc.pos = oldpos;
412*8a83145eSjcs 					c->loc.size = 0;
413*8a83145eSjcs 					c->loc.count = 0;
414*8a83145eSjcs 				} else {
415*8a83145eSjcs 					DPRINTF("Cannot pop "
416*8a83145eSjcs 					    "item @ %d\n", s->pushlevel);
417*8a83145eSjcs 				}
418*8a83145eSjcs 				break;
419*8a83145eSjcs 			default:
420*8a83145eSjcs 				DPRINTF("Global bTag=%d\n", bTag);
421*8a83145eSjcs 				break;
422*8a83145eSjcs 			}
423*8a83145eSjcs 			break;
424*8a83145eSjcs 		case 2:		/* Local */
425*8a83145eSjcs 			switch (bTag) {
426*8a83145eSjcs 			case 0:
427*8a83145eSjcs 				if (bSize != 4)
428*8a83145eSjcs 					dval = (dval & mask) | c->_usage_page;
429*8a83145eSjcs 
430*8a83145eSjcs 				/* set last usage, in case of a collection */
431*8a83145eSjcs 				s->usage_last = dval;
432*8a83145eSjcs 
433*8a83145eSjcs 				if (s->nusage < MAXUSAGE) {
434*8a83145eSjcs 					s->usages_min[s->nusage] = dval;
435*8a83145eSjcs 					s->usages_max[s->nusage] = dval;
436*8a83145eSjcs 					s->nusage ++;
437*8a83145eSjcs 				} else {
438*8a83145eSjcs 					DPRINTF("max usage reached\n");
439*8a83145eSjcs 				}
440*8a83145eSjcs 
441*8a83145eSjcs 				/* clear any pending usage sets */
442*8a83145eSjcs 				s->susage = 0;
443*8a83145eSjcs 				break;
444*8a83145eSjcs 			case 1:
445*8a83145eSjcs 				s->susage |= 1;
446*8a83145eSjcs 
447*8a83145eSjcs 				if (bSize != 4)
448*8a83145eSjcs 					dval = (dval & mask) | c->_usage_page;
449*8a83145eSjcs 				c->usage_minimum = dval;
450*8a83145eSjcs 
451*8a83145eSjcs 				goto check_set;
452*8a83145eSjcs 			case 2:
453*8a83145eSjcs 				s->susage |= 2;
454*8a83145eSjcs 
455*8a83145eSjcs 				if (bSize != 4)
456*8a83145eSjcs 					dval = (dval & mask) | c->_usage_page;
457*8a83145eSjcs 				c->usage_maximum = dval;
458*8a83145eSjcs 
459*8a83145eSjcs 			check_set:
460*8a83145eSjcs 				if (s->susage != 3)
461*8a83145eSjcs 					break;
462*8a83145eSjcs 
463*8a83145eSjcs 				/* sanity check */
464*8a83145eSjcs 				if ((s->nusage < MAXUSAGE) &&
465*8a83145eSjcs 				    (c->usage_minimum <= c->usage_maximum)) {
466*8a83145eSjcs 					/* add usage range */
467*8a83145eSjcs 					s->usages_min[s->nusage] =
468*8a83145eSjcs 					    c->usage_minimum;
469*8a83145eSjcs 					s->usages_max[s->nusage] =
470*8a83145eSjcs 					    c->usage_maximum;
471*8a83145eSjcs 					s->nusage ++;
472*8a83145eSjcs 				} else {
473*8a83145eSjcs 					DPRINTF("Usage set dropped\n");
474*8a83145eSjcs 				}
475*8a83145eSjcs 				s->susage = 0;
476*8a83145eSjcs 				break;
477*8a83145eSjcs 			case 3:
478*8a83145eSjcs 				c->designator_index = dval;
479*8a83145eSjcs 				break;
480*8a83145eSjcs 			case 4:
481*8a83145eSjcs 				c->designator_minimum = dval;
482*8a83145eSjcs 				break;
483*8a83145eSjcs 			case 5:
484*8a83145eSjcs 				c->designator_maximum = dval;
485*8a83145eSjcs 				break;
486*8a83145eSjcs 			case 7:
487*8a83145eSjcs 				c->string_index = dval;
488*8a83145eSjcs 				break;
489*8a83145eSjcs 			case 8:
490*8a83145eSjcs 				c->string_minimum = dval;
491*8a83145eSjcs 				break;
492*8a83145eSjcs 			case 9:
493*8a83145eSjcs 				c->string_maximum = dval;
494*8a83145eSjcs 				break;
495*8a83145eSjcs 			case 10:
496*8a83145eSjcs 				c->set_delimiter = dval;
497*8a83145eSjcs 				break;
498*8a83145eSjcs 			default:
499*8a83145eSjcs 				DPRINTF("Local bTag=%d\n", bTag);
500*8a83145eSjcs 				break;
501*8a83145eSjcs 			}
502*8a83145eSjcs 			break;
503*8a83145eSjcs 		default:
504*8a83145eSjcs 			DPRINTF("default bType=%d\n", bType);
505*8a83145eSjcs 			break;
506*8a83145eSjcs 		}
507*8a83145eSjcs 	}
508*8a83145eSjcs 	return (0);
509*8a83145eSjcs }
510*8a83145eSjcs 
511*8a83145eSjcs int
512*8a83145eSjcs hid_report_size(const void *buf, int len, enum hid_kind k, u_int8_t id)
513*8a83145eSjcs {
514*8a83145eSjcs 	struct hid_data *d;
515*8a83145eSjcs 	struct hid_item h;
516*8a83145eSjcs 	int lo, hi;
517*8a83145eSjcs 
518*8a83145eSjcs 	h.report_ID = 0;
519*8a83145eSjcs 	lo = hi = -1;
520*8a83145eSjcs 	DPRINTF("hid_report_size: kind=%d id=%d\n", k, id);
521*8a83145eSjcs 	for (d = hid_start_parse(buf, len, k); hid_get_item(d, &h); ) {
522*8a83145eSjcs 		DPRINTF("hid_report_size: item kind=%d id=%d pos=%d "
523*8a83145eSjcs 			  "size=%d count=%d\n",
524*8a83145eSjcs 			  h.kind, h.report_ID, h.loc.pos, h.loc.size,
525*8a83145eSjcs 			  h.loc.count);
526*8a83145eSjcs 		if (h.report_ID == id && h.kind == k) {
527*8a83145eSjcs 			if (lo < 0) {
528*8a83145eSjcs 				lo = h.loc.pos;
529*8a83145eSjcs #ifdef DIAGNOSTIC
530*8a83145eSjcs 				if (lo != 0) {
531*8a83145eSjcs 					printf("hid_report_size: lo != 0\n");
532*8a83145eSjcs 				}
533*8a83145eSjcs #endif
534*8a83145eSjcs 			}
535*8a83145eSjcs 			hi = h.loc.pos + h.loc.size * h.loc.count;
536*8a83145eSjcs 			DPRINTF("hid_report_size: lo=%d hi=%d\n", lo, hi);
537*8a83145eSjcs 
538*8a83145eSjcs 		}
539*8a83145eSjcs 	}
540*8a83145eSjcs 	hid_end_parse(d);
541*8a83145eSjcs 	return ((hi - lo + 7) / 8);
542*8a83145eSjcs }
543*8a83145eSjcs 
544*8a83145eSjcs int
545*8a83145eSjcs hid_locate(const void *desc, int size, int32_t u, uint8_t id, enum hid_kind k,
546*8a83145eSjcs     struct hid_location *loc, uint32_t *flags)
547*8a83145eSjcs {
548*8a83145eSjcs 	struct hid_data *d;
549*8a83145eSjcs 	struct hid_item h;
550*8a83145eSjcs 
551*8a83145eSjcs 	h.report_ID = 0;
552*8a83145eSjcs 	DPRINTF("hid_locate: enter usage=0x%x kind=%d id=%d\n", u, k, id);
553*8a83145eSjcs 	for (d = hid_start_parse(desc, size, k); hid_get_item(d, &h); ) {
554*8a83145eSjcs 		DPRINTF("hid_locate: usage=0x%x kind=%d id=%d flags=0x%x\n",
555*8a83145eSjcs 			    h.usage, h.kind, h.report_ID, h.flags);
556*8a83145eSjcs 		if (h.kind == k && !(h.flags & HIO_CONST) &&
557*8a83145eSjcs 		    h.usage == u && h.report_ID == id) {
558*8a83145eSjcs 			if (loc != NULL)
559*8a83145eSjcs 				*loc = h.loc;
560*8a83145eSjcs 			if (flags != NULL)
561*8a83145eSjcs 				*flags = h.flags;
562*8a83145eSjcs 			hid_end_parse(d);
563*8a83145eSjcs 			return (1);
564*8a83145eSjcs 		}
565*8a83145eSjcs 	}
566*8a83145eSjcs 	hid_end_parse(d);
567*8a83145eSjcs 	if (loc != NULL)
568*8a83145eSjcs 		loc->size = 0;
569*8a83145eSjcs 	if (flags != NULL)
570*8a83145eSjcs 		*flags = 0;
571*8a83145eSjcs 	return (0);
572*8a83145eSjcs }
573*8a83145eSjcs 
574*8a83145eSjcs int32_t
575*8a83145eSjcs hid_get_data(const uint8_t *buf, int len, struct hid_location *loc)
576*8a83145eSjcs {
577*8a83145eSjcs 	uint32_t hpos = loc->pos;
578*8a83145eSjcs 	uint32_t hsize = loc->size;
579*8a83145eSjcs 	uint32_t data;
580*8a83145eSjcs 	uint32_t rpos;
581*8a83145eSjcs 	uint8_t n;
582*8a83145eSjcs 
583*8a83145eSjcs 	DPRINTF("hid_get_data: loc %d/%d\n", hpos, hsize);
584*8a83145eSjcs 
585*8a83145eSjcs 	/* Range check and limit */
586*8a83145eSjcs 	if (hsize == 0)
587*8a83145eSjcs 		return (0);
588*8a83145eSjcs 	if (hsize > 32)
589*8a83145eSjcs 		hsize = 32;
590*8a83145eSjcs 
591*8a83145eSjcs 	/* Get data in a safe way */
592*8a83145eSjcs 	data = 0;
593*8a83145eSjcs 	rpos = (hpos / 8);
594*8a83145eSjcs 	n = (hsize + 7) / 8;
595*8a83145eSjcs 	rpos += n;
596*8a83145eSjcs 	while (n--) {
597*8a83145eSjcs 		rpos--;
598*8a83145eSjcs 		if (rpos < len)
599*8a83145eSjcs 			data |= buf[rpos] << (8 * n);
600*8a83145eSjcs 	}
601*8a83145eSjcs 
602*8a83145eSjcs 	/* Correctly shift down data */
603*8a83145eSjcs 	data = (data >> (hpos % 8));
604*8a83145eSjcs 	n = 32 - hsize;
605*8a83145eSjcs 
606*8a83145eSjcs 	data = (int32_t)((int32_t)data << n) >> n;
607*8a83145eSjcs 
608*8a83145eSjcs 	DPRINTF("hid_get_data: loc %d/%d = %lu\n",
609*8a83145eSjcs 	    loc->pos, loc->size, (long)data);
610*8a83145eSjcs 	return (data);
611*8a83145eSjcs }
612*8a83145eSjcs 
613*8a83145eSjcs int
614*8a83145eSjcs hid_is_collection(const void *desc, int size, uint8_t id, int32_t usage)
615*8a83145eSjcs {
616*8a83145eSjcs 	struct hid_data *hd;
617*8a83145eSjcs 	struct hid_item hi;
618*8a83145eSjcs 	uint32_t coll_usage = ~0;
619*8a83145eSjcs 
620*8a83145eSjcs 	hd = hid_start_parse(desc, size, hid_none);
621*8a83145eSjcs 
622*8a83145eSjcs 	DPRINTF("%s: id=%d usage=0x%x\n", __func__, id, usage);
623*8a83145eSjcs 	while (hid_get_item(hd, &hi)) {
624*8a83145eSjcs 		DPRINTF("%s: kind=%d id=%d usage=0x%x(0x%x)\n", __func__,
625*8a83145eSjcs 			    hi.kind, hi.report_ID, hi.usage, coll_usage);
626*8a83145eSjcs 		if (hi.kind == hid_collection &&
627*8a83145eSjcs 		    hi.collection == HCOLL_APPLICATION)
628*8a83145eSjcs 			coll_usage = hi.usage;
629*8a83145eSjcs 		if (hi.kind == hid_endcollection &&
630*8a83145eSjcs 		    coll_usage == usage && hi.report_ID == id) {
631*8a83145eSjcs 			DPRINTF("%s: found\n", __func__);
632*8a83145eSjcs 			hid_end_parse(hd);
633*8a83145eSjcs 			return (1);
634*8a83145eSjcs 		}
635*8a83145eSjcs 	}
636*8a83145eSjcs 	DPRINTF("%s: not found\n", __func__);
637*8a83145eSjcs 	hid_end_parse(hd);
638*8a83145eSjcs 	return (0);
639*8a83145eSjcs }
640