1246122Shselasky/* $FreeBSD: stable/10/sys/dev/usb/usb_hid.c 361921 2020-06-08 09:34:16Z hselasky $ */
2184610Salfred/*	$NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem Exp $	*/
3184610Salfred/*-
4184610Salfred * Copyright (c) 1998 The NetBSD Foundation, Inc.
5184610Salfred * All rights reserved.
6184610Salfred *
7184610Salfred * This code is derived from software contributed to The NetBSD Foundation
8184610Salfred * by Lennart Augustsson (lennart@augustsson.net) at
9184610Salfred * Carlstedt Research & Technology.
10184610Salfred *
11184610Salfred * Redistribution and use in source and binary forms, with or without
12184610Salfred * modification, are permitted provided that the following conditions
13184610Salfred * are met:
14184610Salfred * 1. Redistributions of source code must retain the above copyright
15184610Salfred *    notice, this list of conditions and the following disclaimer.
16184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
17184610Salfred *    notice, this list of conditions and the following disclaimer in the
18184610Salfred *    documentation and/or other materials provided with the distribution.
19184610Salfred *
20184610Salfred * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21184610Salfred * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22184610Salfred * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23184610Salfred * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24184610Salfred * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25184610Salfred * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26184610Salfred * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27184610Salfred * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28184610Salfred * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29184610Salfred * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30184610Salfred * POSSIBILITY OF SUCH DAMAGE.
31184610Salfred */
32184610Salfred
33246122Shselasky#ifdef USB_GLOBAL_INCLUDE_FILE
34246122Shselasky#include USB_GLOBAL_INCLUDE_FILE
35246122Shselasky#else
36194677Sthompsa#include <sys/stdint.h>
37194677Sthompsa#include <sys/stddef.h>
38194677Sthompsa#include <sys/param.h>
39194677Sthompsa#include <sys/queue.h>
40194677Sthompsa#include <sys/types.h>
41194677Sthompsa#include <sys/systm.h>
42194677Sthompsa#include <sys/kernel.h>
43194677Sthompsa#include <sys/bus.h>
44194677Sthompsa#include <sys/module.h>
45194677Sthompsa#include <sys/lock.h>
46194677Sthompsa#include <sys/mutex.h>
47194677Sthompsa#include <sys/condvar.h>
48194677Sthompsa#include <sys/sysctl.h>
49194677Sthompsa#include <sys/sx.h>
50194677Sthompsa#include <sys/unistd.h>
51194677Sthompsa#include <sys/callout.h>
52194677Sthompsa#include <sys/malloc.h>
53194677Sthompsa#include <sys/priv.h>
54194677Sthompsa
55188942Sthompsa#include <dev/usb/usb.h>
56194677Sthompsa#include <dev/usb/usbdi.h>
57194677Sthompsa#include <dev/usb/usbdi_util.h>
58188942Sthompsa#include <dev/usb/usbhid.h>
59184610Salfred
60194228Sthompsa#define	USB_DEBUG_VAR usb_debug
61184610Salfred
62188942Sthompsa#include <dev/usb/usb_core.h>
63188942Sthompsa#include <dev/usb/usb_debug.h>
64188942Sthompsa#include <dev/usb/usb_process.h>
65188942Sthompsa#include <dev/usb/usb_device.h>
66188942Sthompsa#include <dev/usb/usb_request.h>
67246122Shselasky#endif			/* USB_GLOBAL_INCLUDE_FILE */
68184610Salfred
69184610Salfredstatic void hid_clear_local(struct hid_item *);
70189547Sthompsastatic uint8_t hid_get_byte(struct hid_data *s, const uint16_t wSize);
71184610Salfred
72189547Sthompsa#define	MAXUSAGE 64
73189547Sthompsa#define	MAXPUSH 4
74195967Salfred#define	MAXID 16
75195967Salfred
76195967Salfredstruct hid_pos_data {
77195967Salfred	int32_t rid;
78195967Salfred	uint32_t pos;
79195967Salfred};
80195967Salfred
81184610Salfredstruct hid_data {
82184610Salfred	const uint8_t *start;
83184610Salfred	const uint8_t *end;
84184610Salfred	const uint8_t *p;
85189547Sthompsa	struct hid_item cur[MAXPUSH];
86195967Salfred	struct hid_pos_data last_pos[MAXID];
87189547Sthompsa	int32_t	usages_min[MAXUSAGE];
88189547Sthompsa	int32_t	usages_max[MAXUSAGE];
89189718Sthompsa	int32_t usage_last;	/* last seen usage */
90189718Sthompsa	uint32_t loc_size;	/* last seen size */
91189718Sthompsa	uint32_t loc_count;	/* last seen count */
92189718Sthompsa	uint8_t	kindset;	/* we have 5 kinds so 8 bits are enough */
93189547Sthompsa	uint8_t	pushlevel;	/* current pushlevel */
94189547Sthompsa	uint8_t	ncount;		/* end usage item count */
95189547Sthompsa	uint8_t icount;		/* current usage item count */
96189547Sthompsa	uint8_t	nusage;		/* end "usages_min/max" index */
97189547Sthompsa	uint8_t	iusage;		/* current "usages_min/max" index */
98189547Sthompsa	uint8_t ousage;		/* current "usages_min/max" offset */
99189547Sthompsa	uint8_t	susage;		/* usage set flags */
100184610Salfred};
101184610Salfred
102184610Salfred/*------------------------------------------------------------------------*
103184610Salfred *	hid_clear_local
104184610Salfred *------------------------------------------------------------------------*/
105184610Salfredstatic void
106184610Salfredhid_clear_local(struct hid_item *c)
107184610Salfred{
108184610Salfred
109189547Sthompsa	c->loc.count = 0;
110189547Sthompsa	c->loc.size = 0;
111184610Salfred	c->usage = 0;
112184610Salfred	c->usage_minimum = 0;
113184610Salfred	c->usage_maximum = 0;
114184610Salfred	c->designator_index = 0;
115184610Salfred	c->designator_minimum = 0;
116184610Salfred	c->designator_maximum = 0;
117184610Salfred	c->string_index = 0;
118184610Salfred	c->string_minimum = 0;
119184610Salfred	c->string_maximum = 0;
120184610Salfred	c->set_delimiter = 0;
121184610Salfred}
122184610Salfred
123195967Salfredstatic void
124195967Salfredhid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t next_rID)
125195967Salfred{
126195967Salfred	uint8_t i;
127195967Salfred
128195967Salfred	/* check for same report ID - optimise */
129195967Salfred
130195967Salfred	if (c->report_ID == next_rID)
131195967Salfred		return;
132195967Salfred
133195967Salfred	/* save current position for current rID */
134195967Salfred
135195967Salfred	if (c->report_ID == 0) {
136195967Salfred		i = 0;
137195967Salfred	} else {
138195967Salfred		for (i = 1; i != MAXID; i++) {
139195967Salfred			if (s->last_pos[i].rid == c->report_ID)
140195967Salfred				break;
141195967Salfred			if (s->last_pos[i].rid == 0)
142195967Salfred				break;
143195967Salfred		}
144195967Salfred	}
145195967Salfred	if (i != MAXID) {
146195967Salfred		s->last_pos[i].rid = c->report_ID;
147195967Salfred		s->last_pos[i].pos = c->loc.pos;
148195967Salfred	}
149195967Salfred
150195967Salfred	/* store next report ID */
151195967Salfred
152195967Salfred	c->report_ID = next_rID;
153195967Salfred
154195967Salfred	/* lookup last position for next rID */
155195967Salfred
156195967Salfred	if (next_rID == 0) {
157195967Salfred		i = 0;
158195967Salfred	} else {
159195967Salfred		for (i = 1; i != MAXID; i++) {
160195967Salfred			if (s->last_pos[i].rid == next_rID)
161195967Salfred				break;
162195967Salfred			if (s->last_pos[i].rid == 0)
163195967Salfred				break;
164195967Salfred		}
165195967Salfred	}
166195967Salfred	if (i != MAXID) {
167195967Salfred		s->last_pos[i].rid = next_rID;
168195967Salfred		c->loc.pos = s->last_pos[i].pos;
169195967Salfred	} else {
170195967Salfred		DPRINTF("Out of RID entries, position is set to zero!\n");
171195967Salfred		c->loc.pos = 0;
172195967Salfred	}
173195967Salfred}
174195967Salfred
175184610Salfred/*------------------------------------------------------------------------*
176184610Salfred *	hid_start_parse
177184610Salfred *------------------------------------------------------------------------*/
178184610Salfredstruct hid_data *
179193074Sthompsahid_start_parse(const void *d, usb_size_t len, int kindset)
180184610Salfred{
181184610Salfred	struct hid_data *s;
182184610Salfred
183189547Sthompsa	if ((kindset-1) & kindset) {
184189547Sthompsa		DPRINTFN(0, "Only one bit can be "
185189547Sthompsa		    "set in the kindset\n");
186189547Sthompsa		return (NULL);
187189547Sthompsa	}
188189547Sthompsa
189184610Salfred	s = malloc(sizeof *s, M_TEMP, M_WAITOK | M_ZERO);
190184610Salfred	s->start = s->p = d;
191184610Salfred	s->end = ((const uint8_t *)d) + len;
192184610Salfred	s->kindset = kindset;
193184610Salfred	return (s);
194184610Salfred}
195184610Salfred
196184610Salfred/*------------------------------------------------------------------------*
197184610Salfred *	hid_end_parse
198184610Salfred *------------------------------------------------------------------------*/
199184610Salfredvoid
200184610Salfredhid_end_parse(struct hid_data *s)
201184610Salfred{
202189547Sthompsa	if (s == NULL)
203189547Sthompsa		return;
204184610Salfred
205184610Salfred	free(s, M_TEMP);
206184610Salfred}
207184610Salfred
208184610Salfred/*------------------------------------------------------------------------*
209189547Sthompsa *	get byte from HID descriptor
210189547Sthompsa *------------------------------------------------------------------------*/
211189547Sthompsastatic uint8_t
212189547Sthompsahid_get_byte(struct hid_data *s, const uint16_t wSize)
213189547Sthompsa{
214189547Sthompsa	const uint8_t *ptr;
215189547Sthompsa	uint8_t retval;
216189547Sthompsa
217189547Sthompsa	ptr = s->p;
218189547Sthompsa
219189547Sthompsa	/* check if end is reached */
220189547Sthompsa	if (ptr == s->end)
221189547Sthompsa		return (0);
222189547Sthompsa
223189547Sthompsa	/* read out a byte */
224189547Sthompsa	retval = *ptr;
225189547Sthompsa
226189547Sthompsa	/* check if data pointer can be advanced by "wSize" bytes */
227189547Sthompsa	if ((s->end - ptr) < wSize)
228189547Sthompsa		ptr = s->end;
229189547Sthompsa	else
230189547Sthompsa		ptr += wSize;
231189547Sthompsa
232189547Sthompsa	/* update pointer */
233189547Sthompsa	s->p = ptr;
234189547Sthompsa
235189547Sthompsa	return (retval);
236189547Sthompsa}
237189547Sthompsa
238189547Sthompsa/*------------------------------------------------------------------------*
239184610Salfred *	hid_get_item
240184610Salfred *------------------------------------------------------------------------*/
241184610Salfredint
242184610Salfredhid_get_item(struct hid_data *s, struct hid_item *h)
243184610Salfred{
244189547Sthompsa	struct hid_item *c;
245184610Salfred	unsigned int bTag, bType, bSize;
246184610Salfred	uint32_t oldpos;
247189547Sthompsa	int32_t mask;
248184610Salfred	int32_t dval;
249184610Salfred
250189547Sthompsa	if (s == NULL)
251189547Sthompsa		return (0);
252189547Sthompsa
253189547Sthompsa	c = &s->cur[s->pushlevel];
254189547Sthompsa
255189547Sthompsa top:
256189547Sthompsa	/* check if there is an array of items */
257189718Sthompsa	if (s->icount < s->ncount) {
258189718Sthompsa		/* get current usage */
259189718Sthompsa		if (s->iusage < s->nusage) {
260189718Sthompsa			dval = s->usages_min[s->iusage] + s->ousage;
261189718Sthompsa			c->usage = dval;
262189718Sthompsa			s->usage_last = dval;
263189718Sthompsa			if (dval == s->usages_max[s->iusage]) {
264189718Sthompsa				s->iusage ++;
265189718Sthompsa				s->ousage = 0;
266189718Sthompsa			} else {
267189718Sthompsa				s->ousage ++;
268189718Sthompsa			}
269189547Sthompsa		} else {
270189718Sthompsa			DPRINTFN(1, "Using last usage\n");
271189718Sthompsa			dval = s->usage_last;
272189547Sthompsa		}
273189547Sthompsa		s->icount ++;
274189547Sthompsa		/*
275189547Sthompsa		 * Only copy HID item, increment position and return
276189547Sthompsa		 * if correct kindset!
277189547Sthompsa		 */
278189547Sthompsa		if (s->kindset & (1 << c->kind)) {
279184610Salfred			*h = *c;
280189547Sthompsa			DPRINTFN(1, "%u,%u,%u\n", h->loc.pos,
281189547Sthompsa			    h->loc.size, h->loc.count);
282189547Sthompsa			c->loc.pos += c->loc.size * c->loc.count;
283184610Salfred			return (1);
284184610Salfred		}
285184610Salfred	}
286184610Salfred
287189547Sthompsa	/* reset state variables */
288189547Sthompsa	s->icount = 0;
289189547Sthompsa	s->ncount = 0;
290189547Sthompsa	s->iusage = 0;
291189547Sthompsa	s->nusage = 0;
292189547Sthompsa	s->susage = 0;
293189547Sthompsa	s->ousage = 0;
294189547Sthompsa	hid_clear_local(c);
295189547Sthompsa
296189547Sthompsa	/* get next item */
297189547Sthompsa	while (s->p != s->end) {
298189547Sthompsa
299189547Sthompsa		bSize = hid_get_byte(s, 1);
300184610Salfred		if (bSize == 0xfe) {
301184610Salfred			/* long item */
302189547Sthompsa			bSize = hid_get_byte(s, 1);
303189547Sthompsa			bSize |= hid_get_byte(s, 1) << 8;
304189547Sthompsa			bTag = hid_get_byte(s, 1);
305184610Salfred			bType = 0xff;	/* XXX what should it be */
306184610Salfred		} else {
307184610Salfred			/* short item */
308184610Salfred			bTag = bSize >> 4;
309184610Salfred			bType = (bSize >> 2) & 3;
310184610Salfred			bSize &= 3;
311184610Salfred			if (bSize == 3)
312184610Salfred				bSize = 4;
313184610Salfred		}
314184610Salfred		switch (bSize) {
315184610Salfred		case 0:
316184610Salfred			dval = 0;
317189547Sthompsa			mask = 0;
318184610Salfred			break;
319184610Salfred		case 1:
320189547Sthompsa			dval = (int8_t)hid_get_byte(s, 1);
321189547Sthompsa			mask = 0xFF;
322184610Salfred			break;
323184610Salfred		case 2:
324189547Sthompsa			dval = hid_get_byte(s, 1);
325189547Sthompsa			dval |= hid_get_byte(s, 1) << 8;
326184610Salfred			dval = (int16_t)dval;
327189547Sthompsa			mask = 0xFFFF;
328184610Salfred			break;
329184610Salfred		case 4:
330189547Sthompsa			dval = hid_get_byte(s, 1);
331189547Sthompsa			dval |= hid_get_byte(s, 1) << 8;
332189547Sthompsa			dval |= hid_get_byte(s, 1) << 16;
333189547Sthompsa			dval |= hid_get_byte(s, 1) << 24;
334189547Sthompsa			mask = 0xFFFFFFFF;
335184610Salfred			break;
336184610Salfred		default:
337189547Sthompsa			dval = hid_get_byte(s, bSize);
338189547Sthompsa			DPRINTFN(0, "bad length %u (data=0x%02x)\n",
339189547Sthompsa			    bSize, dval);
340184610Salfred			continue;
341184610Salfred		}
342184610Salfred
343184610Salfred		switch (bType) {
344184610Salfred		case 0:		/* Main */
345184610Salfred			switch (bTag) {
346184610Salfred			case 8:	/* Input */
347184610Salfred				c->kind = hid_input;
348184610Salfred				c->flags = dval;
349184610Salfred		ret:
350189718Sthompsa				c->loc.count = s->loc_count;
351189718Sthompsa				c->loc.size = s->loc_size;
352189718Sthompsa
353184610Salfred				if (c->flags & HIO_VARIABLE) {
354189547Sthompsa					/* range check usage count */
355189547Sthompsa					if (c->loc.count > 255) {
356189547Sthompsa						DPRINTFN(0, "Number of "
357296444Shselasky						    "items(%u) truncated to 255\n",
358296444Shselasky						    (unsigned)(c->loc.count));
359189547Sthompsa						s->ncount = 255;
360189547Sthompsa					} else
361189547Sthompsa						s->ncount = c->loc.count;
362189547Sthompsa
363189547Sthompsa					/*
364189547Sthompsa					 * The "top" loop will return
365189547Sthompsa					 * one and one item:
366189547Sthompsa					 */
367184610Salfred					c->loc.count = 1;
368189547Sthompsa				} else {
369189547Sthompsa					s->ncount = 1;
370184610Salfred				}
371189547Sthompsa				goto top;
372189547Sthompsa
373184610Salfred			case 9:	/* Output */
374184610Salfred				c->kind = hid_output;
375184610Salfred				c->flags = dval;
376184610Salfred				goto ret;
377184610Salfred			case 10:	/* Collection */
378184610Salfred				c->kind = hid_collection;
379184610Salfred				c->collection = dval;
380184610Salfred				c->collevel++;
381192055Sthompsa				c->usage = s->usage_last;
382184610Salfred				*h = *c;
383184610Salfred				return (1);
384184610Salfred			case 11:	/* Feature */
385184610Salfred				c->kind = hid_feature;
386184610Salfred				c->flags = dval;
387184610Salfred				goto ret;
388184610Salfred			case 12:	/* End collection */
389184610Salfred				c->kind = hid_endcollection;
390189547Sthompsa				if (c->collevel == 0) {
391189547Sthompsa					DPRINTFN(0, "invalid end collection\n");
392189547Sthompsa					return (0);
393189547Sthompsa				}
394184610Salfred				c->collevel--;
395184610Salfred				*h = *c;
396184610Salfred				return (1);
397184610Salfred			default:
398189547Sthompsa				DPRINTFN(0, "Main bTag=%d\n", bTag);
399184610Salfred				break;
400184610Salfred			}
401184610Salfred			break;
402184610Salfred		case 1:		/* Global */
403184610Salfred			switch (bTag) {
404184610Salfred			case 0:
405184610Salfred				c->_usage_page = dval << 16;
406184610Salfred				break;
407184610Salfred			case 1:
408184610Salfred				c->logical_minimum = dval;
409184610Salfred				break;
410184610Salfred			case 2:
411184610Salfred				c->logical_maximum = dval;
412184610Salfred				break;
413184610Salfred			case 3:
414184610Salfred				c->physical_minimum = dval;
415184610Salfred				break;
416184610Salfred			case 4:
417184610Salfred				c->physical_maximum = dval;
418184610Salfred				break;
419184610Salfred			case 5:
420184610Salfred				c->unit_exponent = dval;
421184610Salfred				break;
422184610Salfred			case 6:
423184610Salfred				c->unit = dval;
424184610Salfred				break;
425184610Salfred			case 7:
426189718Sthompsa				/* mask because value is unsigned */
427189718Sthompsa				s->loc_size = dval & mask;
428184610Salfred				break;
429184610Salfred			case 8:
430235510Smav				hid_switch_rid(s, c, dval & mask);
431184610Salfred				break;
432184610Salfred			case 9:
433189718Sthompsa				/* mask because value is unsigned */
434189718Sthompsa				s->loc_count = dval & mask;
435184610Salfred				break;
436184610Salfred			case 10:	/* Push */
437361921Shselasky				/* stop parsing, if invalid push level */
438361921Shselasky				if ((s->pushlevel + 1) >= MAXPUSH) {
439361921Shselasky					DPRINTFN(0, "Cannot push item @ %d\n", s->pushlevel);
440361921Shselasky					return (0);
441361921Shselasky				}
442189547Sthompsa				s->pushlevel ++;
443361921Shselasky				s->cur[s->pushlevel] = *c;
444361921Shselasky				/* store size and count */
445361921Shselasky				c->loc.size = s->loc_size;
446361921Shselasky				c->loc.count = s->loc_count;
447361921Shselasky				/* update current item pointer */
448361921Shselasky				c = &s->cur[s->pushlevel];
449184610Salfred				break;
450184610Salfred			case 11:	/* Pop */
451361921Shselasky				/* stop parsing, if invalid push level */
452361921Shselasky				if (s->pushlevel == 0) {
453361921Shselasky					DPRINTFN(0, "Cannot pop item @ 0\n");
454361921Shselasky					return (0);
455361921Shselasky				}
456189547Sthompsa				s->pushlevel --;
457361921Shselasky				/* preserve position */
458361921Shselasky				oldpos = c->loc.pos;
459361921Shselasky				c = &s->cur[s->pushlevel];
460361921Shselasky				/* restore size and count */
461361921Shselasky				s->loc_size = c->loc.size;
462361921Shselasky				s->loc_count = c->loc.count;
463361921Shselasky				/* set default item location */
464361921Shselasky				c->loc.pos = oldpos;
465361921Shselasky				c->loc.size = 0;
466361921Shselasky				c->loc.count = 0;
467184610Salfred				break;
468184610Salfred			default:
469189547Sthompsa				DPRINTFN(0, "Global bTag=%d\n", bTag);
470184610Salfred				break;
471184610Salfred			}
472184610Salfred			break;
473184610Salfred		case 2:		/* Local */
474184610Salfred			switch (bTag) {
475184610Salfred			case 0:
476189547Sthompsa				if (bSize != 4)
477189547Sthompsa					dval = (dval & mask) | c->_usage_page;
478189547Sthompsa
479192055Sthompsa				/* set last usage, in case of a collection */
480192055Sthompsa				s->usage_last = dval;
481192055Sthompsa
482189547Sthompsa				if (s->nusage < MAXUSAGE) {
483189547Sthompsa					s->usages_min[s->nusage] = dval;
484189547Sthompsa					s->usages_max[s->nusage] = dval;
485189547Sthompsa					s->nusage ++;
486189547Sthompsa				} else {
487199816Sthompsa					DPRINTFN(0, "max usage reached\n");
488189547Sthompsa				}
489189547Sthompsa
490189547Sthompsa				/* clear any pending usage sets */
491189547Sthompsa				s->susage = 0;
492184610Salfred				break;
493184610Salfred			case 1:
494189547Sthompsa				s->susage |= 1;
495189547Sthompsa
496189547Sthompsa				if (bSize != 4)
497189547Sthompsa					dval = (dval & mask) | c->_usage_page;
498184610Salfred				c->usage_minimum = dval;
499189547Sthompsa
500189547Sthompsa				goto check_set;
501184610Salfred			case 2:
502189547Sthompsa				s->susage |= 2;
503189547Sthompsa
504189547Sthompsa				if (bSize != 4)
505189547Sthompsa					dval = (dval & mask) | c->_usage_page;
506184610Salfred				c->usage_maximum = dval;
507189547Sthompsa
508189547Sthompsa			check_set:
509189547Sthompsa				if (s->susage != 3)
510189547Sthompsa					break;
511189547Sthompsa
512189547Sthompsa				/* sanity check */
513189547Sthompsa				if ((s->nusage < MAXUSAGE) &&
514189776Sthompsa				    (c->usage_minimum <= c->usage_maximum)) {
515189547Sthompsa					/* add usage range */
516189547Sthompsa					s->usages_min[s->nusage] =
517189547Sthompsa					    c->usage_minimum;
518189547Sthompsa					s->usages_max[s->nusage] =
519189547Sthompsa					    c->usage_maximum;
520189547Sthompsa					s->nusage ++;
521189547Sthompsa				} else {
522199816Sthompsa					DPRINTFN(0, "Usage set dropped\n");
523189547Sthompsa				}
524189547Sthompsa				s->susage = 0;
525184610Salfred				break;
526184610Salfred			case 3:
527184610Salfred				c->designator_index = dval;
528184610Salfred				break;
529184610Salfred			case 4:
530184610Salfred				c->designator_minimum = dval;
531184610Salfred				break;
532184610Salfred			case 5:
533184610Salfred				c->designator_maximum = dval;
534184610Salfred				break;
535184610Salfred			case 7:
536184610Salfred				c->string_index = dval;
537184610Salfred				break;
538184610Salfred			case 8:
539184610Salfred				c->string_minimum = dval;
540184610Salfred				break;
541184610Salfred			case 9:
542184610Salfred				c->string_maximum = dval;
543184610Salfred				break;
544184610Salfred			case 10:
545184610Salfred				c->set_delimiter = dval;
546184610Salfred				break;
547184610Salfred			default:
548189547Sthompsa				DPRINTFN(0, "Local bTag=%d\n", bTag);
549184610Salfred				break;
550184610Salfred			}
551184610Salfred			break;
552184610Salfred		default:
553189547Sthompsa			DPRINTFN(0, "default bType=%d\n", bType);
554184610Salfred			break;
555184610Salfred		}
556184610Salfred	}
557189547Sthompsa	return (0);
558184610Salfred}
559184610Salfred
560184610Salfred/*------------------------------------------------------------------------*
561184610Salfred *	hid_report_size
562184610Salfred *------------------------------------------------------------------------*/
563184610Salfredint
564193074Sthompsahid_report_size(const void *buf, usb_size_t len, enum hid_kind k, uint8_t *id)
565184610Salfred{
566184610Salfred	struct hid_data *d;
567184610Salfred	struct hid_item h;
568188981Sthompsa	uint32_t temp;
569188981Sthompsa	uint32_t hpos;
570188981Sthompsa	uint32_t lpos;
571188981Sthompsa	uint8_t any_id;
572184610Salfred
573188981Sthompsa	any_id = 0;
574188981Sthompsa	hpos = 0;
575188981Sthompsa	lpos = 0xFFFFFFFF;
576188981Sthompsa
577188981Sthompsa	for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);) {
578184610Salfred		if (h.kind == k) {
579188981Sthompsa			/* check for ID-byte presense */
580188981Sthompsa			if ((h.report_ID != 0) && !any_id) {
581188981Sthompsa				if (id != NULL)
582188981Sthompsa					*id = h.report_ID;
583188981Sthompsa				any_id = 1;
584184610Salfred			}
585188981Sthompsa			/* compute minimum */
586188981Sthompsa			if (lpos > h.loc.pos)
587188981Sthompsa				lpos = h.loc.pos;
588188981Sthompsa			/* compute end position */
589188981Sthompsa			temp = h.loc.pos + (h.loc.size * h.loc.count);
590188981Sthompsa			/* compute maximum */
591188981Sthompsa			if (hpos < temp)
592188981Sthompsa				hpos = temp;
593184610Salfred		}
594188981Sthompsa	}
595184610Salfred	hid_end_parse(d);
596188981Sthompsa
597188981Sthompsa	/* safety check - can happen in case of currupt descriptors */
598188981Sthompsa	if (lpos > hpos)
599188981Sthompsa		temp = 0;
600188981Sthompsa	else
601188981Sthompsa		temp = hpos - lpos;
602188981Sthompsa
603188981Sthompsa	/* check for ID byte */
604188981Sthompsa	if (any_id)
605188981Sthompsa		temp += 8;
606188981Sthompsa	else if (id != NULL)
607188981Sthompsa		*id = 0;
608188981Sthompsa
609188981Sthompsa	/* return length in bytes rounded up */
610188981Sthompsa	return ((temp + 7) / 8);
611184610Salfred}
612184610Salfred
613184610Salfred/*------------------------------------------------------------------------*
614184610Salfred *	hid_locate
615184610Salfred *------------------------------------------------------------------------*/
616184610Salfredint
617233774Shselaskyhid_locate(const void *desc, usb_size_t size, int32_t u, enum hid_kind k,
618190741Sthompsa    uint8_t index, struct hid_location *loc, uint32_t *flags, uint8_t *id)
619184610Salfred{
620184610Salfred	struct hid_data *d;
621184610Salfred	struct hid_item h;
622184610Salfred
623184610Salfred	for (d = hid_start_parse(desc, size, 1 << k); hid_get_item(d, &h);) {
624184610Salfred		if (h.kind == k && !(h.flags & HIO_CONST) && h.usage == u) {
625190741Sthompsa			if (index--)
626190741Sthompsa				continue;
627184610Salfred			if (loc != NULL)
628184610Salfred				*loc = h.loc;
629184610Salfred			if (flags != NULL)
630184610Salfred				*flags = h.flags;
631188981Sthompsa			if (id != NULL)
632188981Sthompsa				*id = h.report_ID;
633184610Salfred			hid_end_parse(d);
634184610Salfred			return (1);
635184610Salfred		}
636184610Salfred	}
637188981Sthompsa	if (loc != NULL)
638188981Sthompsa		loc->size = 0;
639188981Sthompsa	if (flags != NULL)
640188981Sthompsa		*flags = 0;
641188981Sthompsa	if (id != NULL)
642188981Sthompsa		*id = 0;
643184610Salfred	hid_end_parse(d);
644184610Salfred	return (0);
645184610Salfred}
646184610Salfred
647184610Salfred/*------------------------------------------------------------------------*
648184610Salfred *	hid_get_data
649184610Salfred *------------------------------------------------------------------------*/
650208012Sthompsastatic uint32_t
651208012Sthompsahid_get_data_sub(const uint8_t *buf, usb_size_t len, struct hid_location *loc,
652208012Sthompsa    int is_signed)
653184610Salfred{
654184610Salfred	uint32_t hpos = loc->pos;
655184610Salfred	uint32_t hsize = loc->size;
656184610Salfred	uint32_t data;
657188981Sthompsa	uint32_t rpos;
658188981Sthompsa	uint8_t n;
659184610Salfred
660184610Salfred	DPRINTFN(11, "hid_get_data: loc %d/%d\n", hpos, hsize);
661184610Salfred
662188981Sthompsa	/* Range check and limit */
663184610Salfred	if (hsize == 0)
664184610Salfred		return (0);
665188981Sthompsa	if (hsize > 32)
666188981Sthompsa		hsize = 32;
667184610Salfred
668188981Sthompsa	/* Get data in a safe way */
669184610Salfred	data = 0;
670188981Sthompsa	rpos = (hpos / 8);
671188981Sthompsa	n = (hsize + 7) / 8;
672188981Sthompsa	rpos += n;
673188981Sthompsa	while (n--) {
674188981Sthompsa		rpos--;
675188981Sthompsa		if (rpos < len)
676188981Sthompsa			data |= buf[rpos] << (8 * n);
677184610Salfred	}
678188981Sthompsa
679188981Sthompsa	/* Correctly shift down data */
680188981Sthompsa	data = (data >> (hpos % 8));
681208012Sthompsa	n = 32 - hsize;
682188981Sthompsa
683188981Sthompsa	/* Mask and sign extend in one */
684208012Sthompsa	if (is_signed != 0)
685208012Sthompsa		data = (int32_t)((int32_t)data << n) >> n;
686208012Sthompsa	else
687208012Sthompsa		data = (uint32_t)((uint32_t)data << n) >> n;
688188981Sthompsa
689184610Salfred	DPRINTFN(11, "hid_get_data: loc %d/%d = %lu\n",
690184610Salfred	    loc->pos, loc->size, (long)data);
691184610Salfred	return (data);
692184610Salfred}
693184610Salfred
694208012Sthompsaint32_t
695208012Sthompsahid_get_data(const uint8_t *buf, usb_size_t len, struct hid_location *loc)
696208012Sthompsa{
697208012Sthompsa	return (hid_get_data_sub(buf, len, loc, 1));
698208012Sthompsa}
699208012Sthompsa
700208012Sthompsauint32_t
701208012Sthompsahid_get_data_unsigned(const uint8_t *buf, usb_size_t len, struct hid_location *loc)
702208012Sthompsa{
703208012Sthompsa        return (hid_get_data_sub(buf, len, loc, 0));
704208012Sthompsa}
705208012Sthompsa
706184610Salfred/*------------------------------------------------------------------------*
707223755Shselasky *	hid_put_data
708223755Shselasky *------------------------------------------------------------------------*/
709223755Shselaskyvoid
710223755Shselaskyhid_put_data_unsigned(uint8_t *buf, usb_size_t len,
711223755Shselasky    struct hid_location *loc, unsigned int value)
712223755Shselasky{
713223755Shselasky	uint32_t hpos = loc->pos;
714223755Shselasky	uint32_t hsize = loc->size;
715223755Shselasky	uint64_t data;
716223755Shselasky	uint64_t mask;
717223755Shselasky	uint32_t rpos;
718223755Shselasky	uint8_t n;
719223755Shselasky
720223755Shselasky	DPRINTFN(11, "hid_put_data: loc %d/%d = %u\n", hpos, hsize, value);
721223755Shselasky
722223755Shselasky	/* Range check and limit */
723223755Shselasky	if (hsize == 0)
724223755Shselasky		return;
725223755Shselasky	if (hsize > 32)
726223755Shselasky		hsize = 32;
727223755Shselasky
728223755Shselasky	/* Put data in a safe way */
729223755Shselasky	rpos = (hpos / 8);
730223755Shselasky	n = (hsize + 7) / 8;
731223755Shselasky	data = ((uint64_t)value) << (hpos % 8);
732223755Shselasky	mask = ((1ULL << hsize) - 1ULL) << (hpos % 8);
733223755Shselasky	rpos += n;
734223755Shselasky	while (n--) {
735223755Shselasky		rpos--;
736223755Shselasky		if (rpos < len) {
737223755Shselasky			buf[rpos] &= ~(mask >> (8 * n));
738223755Shselasky			buf[rpos] |= (data >> (8 * n));
739223755Shselasky		}
740223755Shselasky	}
741223755Shselasky}
742223755Shselasky
743223755Shselasky/*------------------------------------------------------------------------*
744184610Salfred *	hid_is_collection
745184610Salfred *------------------------------------------------------------------------*/
746184610Salfredint
747233774Shselaskyhid_is_collection(const void *desc, usb_size_t size, int32_t usage)
748184610Salfred{
749184610Salfred	struct hid_data *hd;
750184610Salfred	struct hid_item hi;
751184610Salfred	int err;
752184610Salfred
753184610Salfred	hd = hid_start_parse(desc, size, hid_input);
754184610Salfred	if (hd == NULL)
755184610Salfred		return (0);
756184610Salfred
757192055Sthompsa	while ((err = hid_get_item(hd, &hi))) {
758192055Sthompsa		 if (hi.kind == hid_collection &&
759192055Sthompsa		     hi.usage == usage)
760192055Sthompsa			break;
761192055Sthompsa	}
762184610Salfred	hid_end_parse(hd);
763184610Salfred	return (err);
764184610Salfred}
765184610Salfred
766184610Salfred/*------------------------------------------------------------------------*
767184610Salfred *	hid_get_descriptor_from_usb
768184610Salfred *
769184610Salfred * This function will search for a HID descriptor between two USB
770184610Salfred * interface descriptors.
771184610Salfred *
772184610Salfred * Return values:
773184610Salfred * NULL: No more HID descriptors.
774184610Salfred * Else: Pointer to HID descriptor.
775184610Salfred *------------------------------------------------------------------------*/
776192984Sthompsastruct usb_hid_descriptor *
777192984Sthompsahid_get_descriptor_from_usb(struct usb_config_descriptor *cd,
778192984Sthompsa    struct usb_interface_descriptor *id)
779184610Salfred{
780192984Sthompsa	struct usb_descriptor *desc = (void *)id;
781184610Salfred
782184610Salfred	if (desc == NULL) {
783184610Salfred		return (NULL);
784184610Salfred	}
785194228Sthompsa	while ((desc = usb_desc_foreach(cd, desc))) {
786184610Salfred		if ((desc->bDescriptorType == UDESC_HID) &&
787184610Salfred		    (desc->bLength >= USB_HID_DESCRIPTOR_SIZE(0))) {
788184610Salfred			return (void *)desc;
789184610Salfred		}
790184610Salfred		if (desc->bDescriptorType == UDESC_INTERFACE) {
791184610Salfred			break;
792184610Salfred		}
793184610Salfred	}
794184610Salfred	return (NULL);
795184610Salfred}
796184610Salfred
797184610Salfred/*------------------------------------------------------------------------*
798194228Sthompsa *	usbd_req_get_hid_desc
799184610Salfred *
800184610Salfred * This function will read out an USB report descriptor from the USB
801184610Salfred * device.
802184610Salfred *
803184610Salfred * Return values:
804184610Salfred * NULL: Failure.
805184610Salfred * Else: Success. The pointer should eventually be passed to free().
806184610Salfred *------------------------------------------------------------------------*/
807193045Sthompsausb_error_t
808194228Sthompsausbd_req_get_hid_desc(struct usb_device *udev, struct mtx *mtx,
809184610Salfred    void **descp, uint16_t *sizep,
810193045Sthompsa    struct malloc_type *mem, uint8_t iface_index)
811184610Salfred{
812194228Sthompsa	struct usb_interface *iface = usbd_get_iface(udev, iface_index);
813192984Sthompsa	struct usb_hid_descriptor *hid;
814193045Sthompsa	usb_error_t err;
815184610Salfred
816184610Salfred	if ((iface == NULL) || (iface->idesc == NULL)) {
817184610Salfred		return (USB_ERR_INVAL);
818184610Salfred	}
819184610Salfred	hid = hid_get_descriptor_from_usb
820194228Sthompsa	    (usbd_get_config_descriptor(udev), iface->idesc);
821184610Salfred
822184610Salfred	if (hid == NULL) {
823184610Salfred		return (USB_ERR_IOERROR);
824184610Salfred	}
825184610Salfred	*sizep = UGETW(hid->descrs[0].wDescriptorLength);
826184610Salfred	if (*sizep == 0) {
827184610Salfred		return (USB_ERR_IOERROR);
828184610Salfred	}
829184610Salfred	if (mtx)
830184610Salfred		mtx_unlock(mtx);
831184610Salfred
832184610Salfred	*descp = malloc(*sizep, mem, M_ZERO | M_WAITOK);
833184610Salfred
834184610Salfred	if (mtx)
835184610Salfred		mtx_lock(mtx);
836184610Salfred
837184610Salfred	if (*descp == NULL) {
838184610Salfred		return (USB_ERR_NOMEM);
839184610Salfred	}
840194228Sthompsa	err = usbd_req_get_report_descriptor
841184610Salfred	    (udev, mtx, *descp, *sizep, iface_index);
842184610Salfred
843184610Salfred	if (err) {
844184610Salfred		free(*descp, mem);
845184610Salfred		*descp = NULL;
846184610Salfred		return (err);
847184610Salfred	}
848184610Salfred	return (USB_ERR_NORMAL_COMPLETION);
849184610Salfred}
850245248Shselasky
851245248Shselasky/*------------------------------------------------------------------------*
852245248Shselasky *	hid_is_mouse
853245248Shselasky *
854245248Shselasky * This function will decide if a USB descriptor belongs to a USB mouse.
855245248Shselasky *
856245248Shselasky * Return values:
857245248Shselasky * Zero: Not a USB mouse.
858245248Shselasky * Else: Is a USB mouse.
859245248Shselasky *------------------------------------------------------------------------*/
860245248Shselaskyint
861245248Shselaskyhid_is_mouse(const void *d_ptr, uint16_t d_len)
862245248Shselasky{
863245248Shselasky	struct hid_data *hd;
864245248Shselasky	struct hid_item hi;
865245248Shselasky	int mdepth;
866245248Shselasky	int found;
867245248Shselasky
868245248Shselasky	hd = hid_start_parse(d_ptr, d_len, 1 << hid_input);
869245248Shselasky	if (hd == NULL)
870245248Shselasky		return (0);
871245248Shselasky
872245248Shselasky	mdepth = 0;
873245248Shselasky	found = 0;
874245248Shselasky
875245248Shselasky	while (hid_get_item(hd, &hi)) {
876245248Shselasky		switch (hi.kind) {
877245248Shselasky		case hid_collection:
878245248Shselasky			if (mdepth != 0)
879245248Shselasky				mdepth++;
880245248Shselasky			else if (hi.collection == 1 &&
881245248Shselasky			     hi.usage ==
882245248Shselasky			      HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))
883245248Shselasky				mdepth++;
884245248Shselasky			break;
885245248Shselasky		case hid_endcollection:
886245248Shselasky			if (mdepth != 0)
887245248Shselasky				mdepth--;
888245248Shselasky			break;
889245248Shselasky		case hid_input:
890245248Shselasky			if (mdepth == 0)
891245248Shselasky				break;
892245248Shselasky			if (hi.usage ==
893245248Shselasky			     HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X) &&
894245248Shselasky			    (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE)
895245248Shselasky				found++;
896245248Shselasky			if (hi.usage ==
897245248Shselasky			     HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y) &&
898245248Shselasky			    (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE)
899245248Shselasky				found++;
900245248Shselasky			break;
901245248Shselasky		default:
902245248Shselasky			break;
903245248Shselasky		}
904245248Shselasky	}
905245248Shselasky	hid_end_parse(hd);
906245248Shselasky	return (found);
907245248Shselasky}
908245248Shselasky
909245248Shselasky/*------------------------------------------------------------------------*
910245248Shselasky *	hid_is_keyboard
911245248Shselasky *
912245248Shselasky * This function will decide if a USB descriptor belongs to a USB keyboard.
913245248Shselasky *
914245248Shselasky * Return values:
915245248Shselasky * Zero: Not a USB keyboard.
916245248Shselasky * Else: Is a USB keyboard.
917245248Shselasky *------------------------------------------------------------------------*/
918245248Shselaskyint
919245248Shselaskyhid_is_keyboard(const void *d_ptr, uint16_t d_len)
920245248Shselasky{
921245248Shselasky	if (hid_is_collection(d_ptr, d_len,
922245248Shselasky	    HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD)))
923245248Shselasky		return (1);
924245248Shselasky	return (0);
925245248Shselasky}
926