1184610Salfred/*	$NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem Exp $	*/
2184610Salfred
3184610Salfred
4184610Salfred#include <sys/cdefs.h>
5184610Salfred__FBSDID("$FreeBSD$");
6184610Salfred/*-
7184610Salfred * Copyright (c) 1998 The NetBSD Foundation, Inc.
8184610Salfred * All rights reserved.
9184610Salfred *
10184610Salfred * This code is derived from software contributed to The NetBSD Foundation
11184610Salfred * by Lennart Augustsson (lennart@augustsson.net) at
12184610Salfred * Carlstedt Research & Technology.
13184610Salfred *
14184610Salfred * Redistribution and use in source and binary forms, with or without
15184610Salfred * modification, are permitted provided that the following conditions
16184610Salfred * are met:
17184610Salfred * 1. Redistributions of source code must retain the above copyright
18184610Salfred *    notice, this list of conditions and the following disclaimer.
19184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
20184610Salfred *    notice, this list of conditions and the following disclaimer in the
21184610Salfred *    documentation and/or other materials provided with the distribution.
22184610Salfred *
23184610Salfred * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
24184610Salfred * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25184610Salfred * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26184610Salfred * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
27184610Salfred * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28184610Salfred * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29184610Salfred * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30184610Salfred * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31184610Salfred * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32184610Salfred * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33184610Salfred * POSSIBILITY OF SUCH DAMAGE.
34184610Salfred */
35184610Salfred
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>
67184610Salfred
68184610Salfredstatic void hid_clear_local(struct hid_item *);
69189547Sthompsastatic uint8_t hid_get_byte(struct hid_data *s, const uint16_t wSize);
70184610Salfred
71189547Sthompsa#define	MAXUSAGE 64
72189547Sthompsa#define	MAXPUSH 4
73195967Salfred#define	MAXID 16
74195967Salfred
75195967Salfredstruct hid_pos_data {
76195967Salfred	int32_t rid;
77195967Salfred	uint32_t pos;
78195967Salfred};
79195967Salfred
80184610Salfredstruct hid_data {
81184610Salfred	const uint8_t *start;
82184610Salfred	const uint8_t *end;
83184610Salfred	const uint8_t *p;
84189547Sthompsa	struct hid_item cur[MAXPUSH];
85195967Salfred	struct hid_pos_data last_pos[MAXID];
86189547Sthompsa	int32_t	usages_min[MAXUSAGE];
87189547Sthompsa	int32_t	usages_max[MAXUSAGE];
88189718Sthompsa	int32_t usage_last;	/* last seen usage */
89189718Sthompsa	uint32_t loc_size;	/* last seen size */
90189718Sthompsa	uint32_t loc_count;	/* last seen count */
91189718Sthompsa	uint8_t	kindset;	/* we have 5 kinds so 8 bits are enough */
92189547Sthompsa	uint8_t	pushlevel;	/* current pushlevel */
93189547Sthompsa	uint8_t	ncount;		/* end usage item count */
94189547Sthompsa	uint8_t icount;		/* current usage item count */
95189547Sthompsa	uint8_t	nusage;		/* end "usages_min/max" index */
96189547Sthompsa	uint8_t	iusage;		/* current "usages_min/max" index */
97189547Sthompsa	uint8_t ousage;		/* current "usages_min/max" offset */
98189547Sthompsa	uint8_t	susage;		/* usage set flags */
99184610Salfred};
100184610Salfred
101184610Salfred/*------------------------------------------------------------------------*
102184610Salfred *	hid_clear_local
103184610Salfred *------------------------------------------------------------------------*/
104184610Salfredstatic void
105184610Salfredhid_clear_local(struct hid_item *c)
106184610Salfred{
107184610Salfred
108189547Sthompsa	c->loc.count = 0;
109189547Sthompsa	c->loc.size = 0;
110184610Salfred	c->usage = 0;
111184610Salfred	c->usage_minimum = 0;
112184610Salfred	c->usage_maximum = 0;
113184610Salfred	c->designator_index = 0;
114184610Salfred	c->designator_minimum = 0;
115184610Salfred	c->designator_maximum = 0;
116184610Salfred	c->string_index = 0;
117184610Salfred	c->string_minimum = 0;
118184610Salfred	c->string_maximum = 0;
119184610Salfred	c->set_delimiter = 0;
120184610Salfred}
121184610Salfred
122195967Salfredstatic void
123195967Salfredhid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t next_rID)
124195967Salfred{
125195967Salfred	uint8_t i;
126195967Salfred
127195967Salfred	/* check for same report ID - optimise */
128195967Salfred
129195967Salfred	if (c->report_ID == next_rID)
130195967Salfred		return;
131195967Salfred
132195967Salfred	/* save current position for current rID */
133195967Salfred
134195967Salfred	if (c->report_ID == 0) {
135195967Salfred		i = 0;
136195967Salfred	} else {
137195967Salfred		for (i = 1; i != MAXID; i++) {
138195967Salfred			if (s->last_pos[i].rid == c->report_ID)
139195967Salfred				break;
140195967Salfred			if (s->last_pos[i].rid == 0)
141195967Salfred				break;
142195967Salfred		}
143195967Salfred	}
144195967Salfred	if (i != MAXID) {
145195967Salfred		s->last_pos[i].rid = c->report_ID;
146195967Salfred		s->last_pos[i].pos = c->loc.pos;
147195967Salfred	}
148195967Salfred
149195967Salfred	/* store next report ID */
150195967Salfred
151195967Salfred	c->report_ID = next_rID;
152195967Salfred
153195967Salfred	/* lookup last position for next rID */
154195967Salfred
155195967Salfred	if (next_rID == 0) {
156195967Salfred		i = 0;
157195967Salfred	} else {
158195967Salfred		for (i = 1; i != MAXID; i++) {
159195967Salfred			if (s->last_pos[i].rid == next_rID)
160195967Salfred				break;
161195967Salfred			if (s->last_pos[i].rid == 0)
162195967Salfred				break;
163195967Salfred		}
164195967Salfred	}
165195967Salfred	if (i != MAXID) {
166195967Salfred		s->last_pos[i].rid = next_rID;
167195967Salfred		c->loc.pos = s->last_pos[i].pos;
168195967Salfred	} else {
169195967Salfred		DPRINTF("Out of RID entries, position is set to zero!\n");
170195967Salfred		c->loc.pos = 0;
171195967Salfred	}
172195967Salfred}
173195967Salfred
174184610Salfred/*------------------------------------------------------------------------*
175184610Salfred *	hid_start_parse
176184610Salfred *------------------------------------------------------------------------*/
177184610Salfredstruct hid_data *
178193074Sthompsahid_start_parse(const void *d, usb_size_t len, int kindset)
179184610Salfred{
180184610Salfred	struct hid_data *s;
181184610Salfred
182189547Sthompsa	if ((kindset-1) & kindset) {
183189547Sthompsa		DPRINTFN(0, "Only one bit can be "
184189547Sthompsa		    "set in the kindset\n");
185189547Sthompsa		return (NULL);
186189547Sthompsa	}
187189547Sthompsa
188184610Salfred	s = malloc(sizeof *s, M_TEMP, M_WAITOK | M_ZERO);
189184610Salfred	s->start = s->p = d;
190184610Salfred	s->end = ((const uint8_t *)d) + len;
191184610Salfred	s->kindset = kindset;
192184610Salfred	return (s);
193184610Salfred}
194184610Salfred
195184610Salfred/*------------------------------------------------------------------------*
196184610Salfred *	hid_end_parse
197184610Salfred *------------------------------------------------------------------------*/
198184610Salfredvoid
199184610Salfredhid_end_parse(struct hid_data *s)
200184610Salfred{
201189547Sthompsa	if (s == NULL)
202189547Sthompsa		return;
203184610Salfred
204184610Salfred	free(s, M_TEMP);
205184610Salfred}
206184610Salfred
207184610Salfred/*------------------------------------------------------------------------*
208189547Sthompsa *	get byte from HID descriptor
209189547Sthompsa *------------------------------------------------------------------------*/
210189547Sthompsastatic uint8_t
211189547Sthompsahid_get_byte(struct hid_data *s, const uint16_t wSize)
212189547Sthompsa{
213189547Sthompsa	const uint8_t *ptr;
214189547Sthompsa	uint8_t retval;
215189547Sthompsa
216189547Sthompsa	ptr = s->p;
217189547Sthompsa
218189547Sthompsa	/* check if end is reached */
219189547Sthompsa	if (ptr == s->end)
220189547Sthompsa		return (0);
221189547Sthompsa
222189547Sthompsa	/* read out a byte */
223189547Sthompsa	retval = *ptr;
224189547Sthompsa
225189547Sthompsa	/* check if data pointer can be advanced by "wSize" bytes */
226189547Sthompsa	if ((s->end - ptr) < wSize)
227189547Sthompsa		ptr = s->end;
228189547Sthompsa	else
229189547Sthompsa		ptr += wSize;
230189547Sthompsa
231189547Sthompsa	/* update pointer */
232189547Sthompsa	s->p = ptr;
233189547Sthompsa
234189547Sthompsa	return (retval);
235189547Sthompsa}
236189547Sthompsa
237189547Sthompsa/*------------------------------------------------------------------------*
238184610Salfred *	hid_get_item
239184610Salfred *------------------------------------------------------------------------*/
240184610Salfredint
241184610Salfredhid_get_item(struct hid_data *s, struct hid_item *h)
242184610Salfred{
243189547Sthompsa	struct hid_item *c;
244184610Salfred	unsigned int bTag, bType, bSize;
245184610Salfred	uint32_t oldpos;
246189547Sthompsa	int32_t mask;
247184610Salfred	int32_t dval;
248184610Salfred
249189547Sthompsa	if (s == NULL)
250189547Sthompsa		return (0);
251189547Sthompsa
252189547Sthompsa	c = &s->cur[s->pushlevel];
253189547Sthompsa
254189547Sthompsa top:
255189547Sthompsa	/* check if there is an array of items */
256189718Sthompsa	if (s->icount < s->ncount) {
257189718Sthompsa		/* get current usage */
258189718Sthompsa		if (s->iusage < s->nusage) {
259189718Sthompsa			dval = s->usages_min[s->iusage] + s->ousage;
260189718Sthompsa			c->usage = dval;
261189718Sthompsa			s->usage_last = dval;
262189718Sthompsa			if (dval == s->usages_max[s->iusage]) {
263189718Sthompsa				s->iusage ++;
264189718Sthompsa				s->ousage = 0;
265189718Sthompsa			} else {
266189718Sthompsa				s->ousage ++;
267189718Sthompsa			}
268189547Sthompsa		} else {
269189718Sthompsa			DPRINTFN(1, "Using last usage\n");
270189718Sthompsa			dval = s->usage_last;
271189547Sthompsa		}
272189547Sthompsa		s->icount ++;
273189547Sthompsa		/*
274189547Sthompsa		 * Only copy HID item, increment position and return
275189547Sthompsa		 * if correct kindset!
276189547Sthompsa		 */
277189547Sthompsa		if (s->kindset & (1 << c->kind)) {
278184610Salfred			*h = *c;
279189547Sthompsa			DPRINTFN(1, "%u,%u,%u\n", h->loc.pos,
280189547Sthompsa			    h->loc.size, h->loc.count);
281189547Sthompsa			c->loc.pos += c->loc.size * c->loc.count;
282184610Salfred			return (1);
283184610Salfred		}
284184610Salfred	}
285184610Salfred
286189547Sthompsa	/* reset state variables */
287189547Sthompsa	s->icount = 0;
288189547Sthompsa	s->ncount = 0;
289189547Sthompsa	s->iusage = 0;
290189547Sthompsa	s->nusage = 0;
291189547Sthompsa	s->susage = 0;
292189547Sthompsa	s->ousage = 0;
293189547Sthompsa	hid_clear_local(c);
294189547Sthompsa
295189547Sthompsa	/* get next item */
296189547Sthompsa	while (s->p != s->end) {
297189547Sthompsa
298189547Sthompsa		bSize = hid_get_byte(s, 1);
299184610Salfred		if (bSize == 0xfe) {
300184610Salfred			/* long item */
301189547Sthompsa			bSize = hid_get_byte(s, 1);
302189547Sthompsa			bSize |= hid_get_byte(s, 1) << 8;
303189547Sthompsa			bTag = hid_get_byte(s, 1);
304184610Salfred			bType = 0xff;	/* XXX what should it be */
305184610Salfred		} else {
306184610Salfred			/* short item */
307184610Salfred			bTag = bSize >> 4;
308184610Salfred			bType = (bSize >> 2) & 3;
309184610Salfred			bSize &= 3;
310184610Salfred			if (bSize == 3)
311184610Salfred				bSize = 4;
312184610Salfred		}
313184610Salfred		switch (bSize) {
314184610Salfred		case 0:
315184610Salfred			dval = 0;
316189547Sthompsa			mask = 0;
317184610Salfred			break;
318184610Salfred		case 1:
319189547Sthompsa			dval = (int8_t)hid_get_byte(s, 1);
320189547Sthompsa			mask = 0xFF;
321184610Salfred			break;
322184610Salfred		case 2:
323189547Sthompsa			dval = hid_get_byte(s, 1);
324189547Sthompsa			dval |= hid_get_byte(s, 1) << 8;
325184610Salfred			dval = (int16_t)dval;
326189547Sthompsa			mask = 0xFFFF;
327184610Salfred			break;
328184610Salfred		case 4:
329189547Sthompsa			dval = hid_get_byte(s, 1);
330189547Sthompsa			dval |= hid_get_byte(s, 1) << 8;
331189547Sthompsa			dval |= hid_get_byte(s, 1) << 16;
332189547Sthompsa			dval |= hid_get_byte(s, 1) << 24;
333189547Sthompsa			mask = 0xFFFFFFFF;
334184610Salfred			break;
335184610Salfred		default:
336189547Sthompsa			dval = hid_get_byte(s, bSize);
337189547Sthompsa			DPRINTFN(0, "bad length %u (data=0x%02x)\n",
338189547Sthompsa			    bSize, dval);
339184610Salfred			continue;
340184610Salfred		}
341184610Salfred
342184610Salfred		switch (bType) {
343184610Salfred		case 0:		/* Main */
344184610Salfred			switch (bTag) {
345184610Salfred			case 8:	/* Input */
346184610Salfred				c->kind = hid_input;
347184610Salfred				c->flags = dval;
348184610Salfred		ret:
349189718Sthompsa				c->loc.count = s->loc_count;
350189718Sthompsa				c->loc.size = s->loc_size;
351189718Sthompsa
352184610Salfred				if (c->flags & HIO_VARIABLE) {
353189547Sthompsa					/* range check usage count */
354189547Sthompsa					if (c->loc.count > 255) {
355189547Sthompsa						DPRINTFN(0, "Number of "
356189547Sthompsa						    "items truncated to 255\n");
357189547Sthompsa						s->ncount = 255;
358189547Sthompsa					} else
359189547Sthompsa						s->ncount = c->loc.count;
360189547Sthompsa
361189547Sthompsa					/*
362189547Sthompsa					 * The "top" loop will return
363189547Sthompsa					 * one and one item:
364189547Sthompsa					 */
365184610Salfred					c->loc.count = 1;
366189547Sthompsa				} else {
367189547Sthompsa					s->ncount = 1;
368184610Salfred				}
369189547Sthompsa				goto top;
370189547Sthompsa
371184610Salfred			case 9:	/* Output */
372184610Salfred				c->kind = hid_output;
373184610Salfred				c->flags = dval;
374184610Salfred				goto ret;
375184610Salfred			case 10:	/* Collection */
376184610Salfred				c->kind = hid_collection;
377184610Salfred				c->collection = dval;
378184610Salfred				c->collevel++;
379192055Sthompsa				c->usage = s->usage_last;
380184610Salfred				*h = *c;
381184610Salfred				return (1);
382184610Salfred			case 11:	/* Feature */
383184610Salfred				c->kind = hid_feature;
384184610Salfred				c->flags = dval;
385184610Salfred				goto ret;
386184610Salfred			case 12:	/* End collection */
387184610Salfred				c->kind = hid_endcollection;
388189547Sthompsa				if (c->collevel == 0) {
389189547Sthompsa					DPRINTFN(0, "invalid end collection\n");
390189547Sthompsa					return (0);
391189547Sthompsa				}
392184610Salfred				c->collevel--;
393184610Salfred				*h = *c;
394184610Salfred				return (1);
395184610Salfred			default:
396189547Sthompsa				DPRINTFN(0, "Main bTag=%d\n", bTag);
397184610Salfred				break;
398184610Salfred			}
399184610Salfred			break;
400184610Salfred		case 1:		/* Global */
401184610Salfred			switch (bTag) {
402184610Salfred			case 0:
403184610Salfred				c->_usage_page = dval << 16;
404184610Salfred				break;
405184610Salfred			case 1:
406184610Salfred				c->logical_minimum = dval;
407184610Salfred				break;
408184610Salfred			case 2:
409184610Salfred				c->logical_maximum = dval;
410184610Salfred				break;
411184610Salfred			case 3:
412184610Salfred				c->physical_minimum = dval;
413184610Salfred				break;
414184610Salfred			case 4:
415184610Salfred				c->physical_maximum = dval;
416184610Salfred				break;
417184610Salfred			case 5:
418184610Salfred				c->unit_exponent = dval;
419184610Salfred				break;
420184610Salfred			case 6:
421184610Salfred				c->unit = dval;
422184610Salfred				break;
423184610Salfred			case 7:
424189718Sthompsa				/* mask because value is unsigned */
425189718Sthompsa				s->loc_size = dval & mask;
426184610Salfred				break;
427184610Salfred			case 8:
428235866Smav				hid_switch_rid(s, c, dval & mask);
429184610Salfred				break;
430184610Salfred			case 9:
431189718Sthompsa				/* mask because value is unsigned */
432189718Sthompsa				s->loc_count = dval & mask;
433184610Salfred				break;
434184610Salfred			case 10:	/* Push */
435189547Sthompsa				s->pushlevel ++;
436189547Sthompsa				if (s->pushlevel < MAXPUSH) {
437189547Sthompsa					s->cur[s->pushlevel] = *c;
438189718Sthompsa					/* store size and count */
439189718Sthompsa					c->loc.size = s->loc_size;
440189718Sthompsa					c->loc.count = s->loc_count;
441189718Sthompsa					/* update current item pointer */
442189547Sthompsa					c = &s->cur[s->pushlevel];
443189547Sthompsa				} else {
444189547Sthompsa					DPRINTFN(0, "Cannot push "
445199816Sthompsa					    "item @ %d\n", s->pushlevel);
446189547Sthompsa				}
447184610Salfred				break;
448184610Salfred			case 11:	/* Pop */
449189547Sthompsa				s->pushlevel --;
450189547Sthompsa				if (s->pushlevel < MAXPUSH) {
451189547Sthompsa					/* preserve position */
452189547Sthompsa					oldpos = c->loc.pos;
453189547Sthompsa					c = &s->cur[s->pushlevel];
454189718Sthompsa					/* restore size and count */
455189718Sthompsa					s->loc_size = c->loc.size;
456189718Sthompsa					s->loc_count = c->loc.count;
457189718Sthompsa					/* set default item location */
458189547Sthompsa					c->loc.pos = oldpos;
459189718Sthompsa					c->loc.size = 0;
460189718Sthompsa					c->loc.count = 0;
461189547Sthompsa				} else {
462189547Sthompsa					DPRINTFN(0, "Cannot pop "
463199816Sthompsa					    "item @ %d\n", s->pushlevel);
464189547Sthompsa				}
465184610Salfred				break;
466184610Salfred			default:
467189547Sthompsa				DPRINTFN(0, "Global bTag=%d\n", bTag);
468184610Salfred				break;
469184610Salfred			}
470184610Salfred			break;
471184610Salfred		case 2:		/* Local */
472184610Salfred			switch (bTag) {
473184610Salfred			case 0:
474189547Sthompsa				if (bSize != 4)
475189547Sthompsa					dval = (dval & mask) | c->_usage_page;
476189547Sthompsa
477192055Sthompsa				/* set last usage, in case of a collection */
478192055Sthompsa				s->usage_last = dval;
479192055Sthompsa
480189547Sthompsa				if (s->nusage < MAXUSAGE) {
481189547Sthompsa					s->usages_min[s->nusage] = dval;
482189547Sthompsa					s->usages_max[s->nusage] = dval;
483189547Sthompsa					s->nusage ++;
484189547Sthompsa				} else {
485199816Sthompsa					DPRINTFN(0, "max usage reached\n");
486189547Sthompsa				}
487189547Sthompsa
488189547Sthompsa				/* clear any pending usage sets */
489189547Sthompsa				s->susage = 0;
490184610Salfred				break;
491184610Salfred			case 1:
492189547Sthompsa				s->susage |= 1;
493189547Sthompsa
494189547Sthompsa				if (bSize != 4)
495189547Sthompsa					dval = (dval & mask) | c->_usage_page;
496184610Salfred				c->usage_minimum = dval;
497189547Sthompsa
498189547Sthompsa				goto check_set;
499184610Salfred			case 2:
500189547Sthompsa				s->susage |= 2;
501189547Sthompsa
502189547Sthompsa				if (bSize != 4)
503189547Sthompsa					dval = (dval & mask) | c->_usage_page;
504184610Salfred				c->usage_maximum = dval;
505189547Sthompsa
506189547Sthompsa			check_set:
507189547Sthompsa				if (s->susage != 3)
508189547Sthompsa					break;
509189547Sthompsa
510189547Sthompsa				/* sanity check */
511189547Sthompsa				if ((s->nusage < MAXUSAGE) &&
512189776Sthompsa				    (c->usage_minimum <= c->usage_maximum)) {
513189547Sthompsa					/* add usage range */
514189547Sthompsa					s->usages_min[s->nusage] =
515189547Sthompsa					    c->usage_minimum;
516189547Sthompsa					s->usages_max[s->nusage] =
517189547Sthompsa					    c->usage_maximum;
518189547Sthompsa					s->nusage ++;
519189547Sthompsa				} else {
520199816Sthompsa					DPRINTFN(0, "Usage set dropped\n");
521189547Sthompsa				}
522189547Sthompsa				s->susage = 0;
523184610Salfred				break;
524184610Salfred			case 3:
525184610Salfred				c->designator_index = dval;
526184610Salfred				break;
527184610Salfred			case 4:
528184610Salfred				c->designator_minimum = dval;
529184610Salfred				break;
530184610Salfred			case 5:
531184610Salfred				c->designator_maximum = dval;
532184610Salfred				break;
533184610Salfred			case 7:
534184610Salfred				c->string_index = dval;
535184610Salfred				break;
536184610Salfred			case 8:
537184610Salfred				c->string_minimum = dval;
538184610Salfred				break;
539184610Salfred			case 9:
540184610Salfred				c->string_maximum = dval;
541184610Salfred				break;
542184610Salfred			case 10:
543184610Salfred				c->set_delimiter = dval;
544184610Salfred				break;
545184610Salfred			default:
546189547Sthompsa				DPRINTFN(0, "Local bTag=%d\n", bTag);
547184610Salfred				break;
548184610Salfred			}
549184610Salfred			break;
550184610Salfred		default:
551189547Sthompsa			DPRINTFN(0, "default bType=%d\n", bType);
552184610Salfred			break;
553184610Salfred		}
554184610Salfred	}
555189547Sthompsa	return (0);
556184610Salfred}
557184610Salfred
558184610Salfred/*------------------------------------------------------------------------*
559184610Salfred *	hid_report_size
560184610Salfred *------------------------------------------------------------------------*/
561184610Salfredint
562193074Sthompsahid_report_size(const void *buf, usb_size_t len, enum hid_kind k, uint8_t *id)
563184610Salfred{
564184610Salfred	struct hid_data *d;
565184610Salfred	struct hid_item h;
566188981Sthompsa	uint32_t temp;
567188981Sthompsa	uint32_t hpos;
568188981Sthompsa	uint32_t lpos;
569188981Sthompsa	uint8_t any_id;
570184610Salfred
571188981Sthompsa	any_id = 0;
572188981Sthompsa	hpos = 0;
573188981Sthompsa	lpos = 0xFFFFFFFF;
574188981Sthompsa
575188981Sthompsa	for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);) {
576184610Salfred		if (h.kind == k) {
577188981Sthompsa			/* check for ID-byte presense */
578188981Sthompsa			if ((h.report_ID != 0) && !any_id) {
579188981Sthompsa				if (id != NULL)
580188981Sthompsa					*id = h.report_ID;
581188981Sthompsa				any_id = 1;
582184610Salfred			}
583188981Sthompsa			/* compute minimum */
584188981Sthompsa			if (lpos > h.loc.pos)
585188981Sthompsa				lpos = h.loc.pos;
586188981Sthompsa			/* compute end position */
587188981Sthompsa			temp = h.loc.pos + (h.loc.size * h.loc.count);
588188981Sthompsa			/* compute maximum */
589188981Sthompsa			if (hpos < temp)
590188981Sthompsa				hpos = temp;
591184610Salfred		}
592188981Sthompsa	}
593184610Salfred	hid_end_parse(d);
594188981Sthompsa
595188981Sthompsa	/* safety check - can happen in case of currupt descriptors */
596188981Sthompsa	if (lpos > hpos)
597188981Sthompsa		temp = 0;
598188981Sthompsa	else
599188981Sthompsa		temp = hpos - lpos;
600188981Sthompsa
601188981Sthompsa	/* check for ID byte */
602188981Sthompsa	if (any_id)
603188981Sthompsa		temp += 8;
604188981Sthompsa	else if (id != NULL)
605188981Sthompsa		*id = 0;
606188981Sthompsa
607188981Sthompsa	/* return length in bytes rounded up */
608188981Sthompsa	return ((temp + 7) / 8);
609184610Salfred}
610184610Salfred
611184610Salfred/*------------------------------------------------------------------------*
612184610Salfred *	hid_locate
613184610Salfred *------------------------------------------------------------------------*/
614184610Salfredint
615235000Shselaskyhid_locate(const void *desc, usb_size_t size, int32_t u, enum hid_kind k,
616190741Sthompsa    uint8_t index, struct hid_location *loc, uint32_t *flags, uint8_t *id)
617184610Salfred{
618184610Salfred	struct hid_data *d;
619184610Salfred	struct hid_item h;
620184610Salfred
621184610Salfred	for (d = hid_start_parse(desc, size, 1 << k); hid_get_item(d, &h);) {
622184610Salfred		if (h.kind == k && !(h.flags & HIO_CONST) && h.usage == u) {
623190741Sthompsa			if (index--)
624190741Sthompsa				continue;
625184610Salfred			if (loc != NULL)
626184610Salfred				*loc = h.loc;
627184610Salfred			if (flags != NULL)
628184610Salfred				*flags = h.flags;
629188981Sthompsa			if (id != NULL)
630188981Sthompsa				*id = h.report_ID;
631184610Salfred			hid_end_parse(d);
632184610Salfred			return (1);
633184610Salfred		}
634184610Salfred	}
635188981Sthompsa	if (loc != NULL)
636188981Sthompsa		loc->size = 0;
637188981Sthompsa	if (flags != NULL)
638188981Sthompsa		*flags = 0;
639188981Sthompsa	if (id != NULL)
640188981Sthompsa		*id = 0;
641184610Salfred	hid_end_parse(d);
642184610Salfred	return (0);
643184610Salfred}
644184610Salfred
645184610Salfred/*------------------------------------------------------------------------*
646184610Salfred *	hid_get_data
647184610Salfred *------------------------------------------------------------------------*/
648208012Sthompsastatic uint32_t
649208012Sthompsahid_get_data_sub(const uint8_t *buf, usb_size_t len, struct hid_location *loc,
650208012Sthompsa    int is_signed)
651184610Salfred{
652184610Salfred	uint32_t hpos = loc->pos;
653184610Salfred	uint32_t hsize = loc->size;
654184610Salfred	uint32_t data;
655188981Sthompsa	uint32_t rpos;
656188981Sthompsa	uint8_t n;
657184610Salfred
658184610Salfred	DPRINTFN(11, "hid_get_data: loc %d/%d\n", hpos, hsize);
659184610Salfred
660188981Sthompsa	/* Range check and limit */
661184610Salfred	if (hsize == 0)
662184610Salfred		return (0);
663188981Sthompsa	if (hsize > 32)
664188981Sthompsa		hsize = 32;
665184610Salfred
666188981Sthompsa	/* Get data in a safe way */
667184610Salfred	data = 0;
668188981Sthompsa	rpos = (hpos / 8);
669188981Sthompsa	n = (hsize + 7) / 8;
670188981Sthompsa	rpos += n;
671188981Sthompsa	while (n--) {
672188981Sthompsa		rpos--;
673188981Sthompsa		if (rpos < len)
674188981Sthompsa			data |= buf[rpos] << (8 * n);
675184610Salfred	}
676188981Sthompsa
677188981Sthompsa	/* Correctly shift down data */
678188981Sthompsa	data = (data >> (hpos % 8));
679208012Sthompsa	n = 32 - hsize;
680188981Sthompsa
681188981Sthompsa	/* Mask and sign extend in one */
682208012Sthompsa	if (is_signed != 0)
683208012Sthompsa		data = (int32_t)((int32_t)data << n) >> n;
684208012Sthompsa	else
685208012Sthompsa		data = (uint32_t)((uint32_t)data << n) >> n;
686188981Sthompsa
687184610Salfred	DPRINTFN(11, "hid_get_data: loc %d/%d = %lu\n",
688184610Salfred	    loc->pos, loc->size, (long)data);
689184610Salfred	return (data);
690184610Salfred}
691184610Salfred
692208012Sthompsaint32_t
693208012Sthompsahid_get_data(const uint8_t *buf, usb_size_t len, struct hid_location *loc)
694208012Sthompsa{
695208012Sthompsa	return (hid_get_data_sub(buf, len, loc, 1));
696208012Sthompsa}
697208012Sthompsa
698208012Sthompsauint32_t
699208012Sthompsahid_get_data_unsigned(const uint8_t *buf, usb_size_t len, struct hid_location *loc)
700208012Sthompsa{
701208012Sthompsa        return (hid_get_data_sub(buf, len, loc, 0));
702208012Sthompsa}
703208012Sthompsa
704184610Salfred/*------------------------------------------------------------------------*
705223755Shselasky *	hid_put_data
706223755Shselasky *------------------------------------------------------------------------*/
707223755Shselaskyvoid
708223755Shselaskyhid_put_data_unsigned(uint8_t *buf, usb_size_t len,
709223755Shselasky    struct hid_location *loc, unsigned int value)
710223755Shselasky{
711223755Shselasky	uint32_t hpos = loc->pos;
712223755Shselasky	uint32_t hsize = loc->size;
713223755Shselasky	uint64_t data;
714223755Shselasky	uint64_t mask;
715223755Shselasky	uint32_t rpos;
716223755Shselasky	uint8_t n;
717223755Shselasky
718223755Shselasky	DPRINTFN(11, "hid_put_data: loc %d/%d = %u\n", hpos, hsize, value);
719223755Shselasky
720223755Shselasky	/* Range check and limit */
721223755Shselasky	if (hsize == 0)
722223755Shselasky		return;
723223755Shselasky	if (hsize > 32)
724223755Shselasky		hsize = 32;
725223755Shselasky
726223755Shselasky	/* Put data in a safe way */
727223755Shselasky	rpos = (hpos / 8);
728223755Shselasky	n = (hsize + 7) / 8;
729223755Shselasky	data = ((uint64_t)value) << (hpos % 8);
730223755Shselasky	mask = ((1ULL << hsize) - 1ULL) << (hpos % 8);
731223755Shselasky	rpos += n;
732223755Shselasky	while (n--) {
733223755Shselasky		rpos--;
734223755Shselasky		if (rpos < len) {
735223755Shselasky			buf[rpos] &= ~(mask >> (8 * n));
736223755Shselasky			buf[rpos] |= (data >> (8 * n));
737223755Shselasky		}
738223755Shselasky	}
739223755Shselasky}
740223755Shselasky
741223755Shselasky/*------------------------------------------------------------------------*
742184610Salfred *	hid_is_collection
743184610Salfred *------------------------------------------------------------------------*/
744184610Salfredint
745235000Shselaskyhid_is_collection(const void *desc, usb_size_t size, int32_t usage)
746184610Salfred{
747184610Salfred	struct hid_data *hd;
748184610Salfred	struct hid_item hi;
749184610Salfred	int err;
750184610Salfred
751184610Salfred	hd = hid_start_parse(desc, size, hid_input);
752184610Salfred	if (hd == NULL)
753184610Salfred		return (0);
754184610Salfred
755192055Sthompsa	while ((err = hid_get_item(hd, &hi))) {
756192055Sthompsa		 if (hi.kind == hid_collection &&
757192055Sthompsa		     hi.usage == usage)
758192055Sthompsa			break;
759192055Sthompsa	}
760184610Salfred	hid_end_parse(hd);
761184610Salfred	return (err);
762184610Salfred}
763184610Salfred
764184610Salfred/*------------------------------------------------------------------------*
765184610Salfred *	hid_get_descriptor_from_usb
766184610Salfred *
767184610Salfred * This function will search for a HID descriptor between two USB
768184610Salfred * interface descriptors.
769184610Salfred *
770184610Salfred * Return values:
771184610Salfred * NULL: No more HID descriptors.
772184610Salfred * Else: Pointer to HID descriptor.
773184610Salfred *------------------------------------------------------------------------*/
774192984Sthompsastruct usb_hid_descriptor *
775192984Sthompsahid_get_descriptor_from_usb(struct usb_config_descriptor *cd,
776192984Sthompsa    struct usb_interface_descriptor *id)
777184610Salfred{
778192984Sthompsa	struct usb_descriptor *desc = (void *)id;
779184610Salfred
780184610Salfred	if (desc == NULL) {
781184610Salfred		return (NULL);
782184610Salfred	}
783194228Sthompsa	while ((desc = usb_desc_foreach(cd, desc))) {
784184610Salfred		if ((desc->bDescriptorType == UDESC_HID) &&
785184610Salfred		    (desc->bLength >= USB_HID_DESCRIPTOR_SIZE(0))) {
786184610Salfred			return (void *)desc;
787184610Salfred		}
788184610Salfred		if (desc->bDescriptorType == UDESC_INTERFACE) {
789184610Salfred			break;
790184610Salfred		}
791184610Salfred	}
792184610Salfred	return (NULL);
793184610Salfred}
794184610Salfred
795184610Salfred/*------------------------------------------------------------------------*
796194228Sthompsa *	usbd_req_get_hid_desc
797184610Salfred *
798184610Salfred * This function will read out an USB report descriptor from the USB
799184610Salfred * device.
800184610Salfred *
801184610Salfred * Return values:
802184610Salfred * NULL: Failure.
803184610Salfred * Else: Success. The pointer should eventually be passed to free().
804184610Salfred *------------------------------------------------------------------------*/
805193045Sthompsausb_error_t
806194228Sthompsausbd_req_get_hid_desc(struct usb_device *udev, struct mtx *mtx,
807184610Salfred    void **descp, uint16_t *sizep,
808193045Sthompsa    struct malloc_type *mem, uint8_t iface_index)
809184610Salfred{
810194228Sthompsa	struct usb_interface *iface = usbd_get_iface(udev, iface_index);
811192984Sthompsa	struct usb_hid_descriptor *hid;
812193045Sthompsa	usb_error_t err;
813184610Salfred
814184610Salfred	if ((iface == NULL) || (iface->idesc == NULL)) {
815184610Salfred		return (USB_ERR_INVAL);
816184610Salfred	}
817184610Salfred	hid = hid_get_descriptor_from_usb
818194228Sthompsa	    (usbd_get_config_descriptor(udev), iface->idesc);
819184610Salfred
820184610Salfred	if (hid == NULL) {
821184610Salfred		return (USB_ERR_IOERROR);
822184610Salfred	}
823184610Salfred	*sizep = UGETW(hid->descrs[0].wDescriptorLength);
824184610Salfred	if (*sizep == 0) {
825184610Salfred		return (USB_ERR_IOERROR);
826184610Salfred	}
827184610Salfred	if (mtx)
828184610Salfred		mtx_unlock(mtx);
829184610Salfred
830184610Salfred	*descp = malloc(*sizep, mem, M_ZERO | M_WAITOK);
831184610Salfred
832184610Salfred	if (mtx)
833184610Salfred		mtx_lock(mtx);
834184610Salfred
835184610Salfred	if (*descp == NULL) {
836184610Salfred		return (USB_ERR_NOMEM);
837184610Salfred	}
838194228Sthompsa	err = usbd_req_get_report_descriptor
839184610Salfred	    (udev, mtx, *descp, *sizep, iface_index);
840184610Salfred
841184610Salfred	if (err) {
842184610Salfred		free(*descp, mem);
843184610Salfred		*descp = NULL;
844184610Salfred		return (err);
845184610Salfred	}
846184610Salfred	return (USB_ERR_NORMAL_COMPLETION);
847184610Salfred}
848245733Shselasky
849245733Shselasky/*------------------------------------------------------------------------*
850245733Shselasky *	hid_is_mouse
851245733Shselasky *
852245733Shselasky * This function will decide if a USB descriptor belongs to a USB mouse.
853245733Shselasky *
854245733Shselasky * Return values:
855245733Shselasky * Zero: Not a USB mouse.
856245733Shselasky * Else: Is a USB mouse.
857245733Shselasky *------------------------------------------------------------------------*/
858245733Shselaskyint
859245733Shselaskyhid_is_mouse(const void *d_ptr, uint16_t d_len)
860245733Shselasky{
861245733Shselasky	struct hid_data *hd;
862245733Shselasky	struct hid_item hi;
863245733Shselasky	int mdepth;
864245733Shselasky	int found;
865245733Shselasky
866245733Shselasky	hd = hid_start_parse(d_ptr, d_len, 1 << hid_input);
867245733Shselasky	if (hd == NULL)
868245733Shselasky		return (0);
869245733Shselasky
870245733Shselasky	mdepth = 0;
871245733Shselasky	found = 0;
872245733Shselasky
873245733Shselasky	while (hid_get_item(hd, &hi)) {
874245733Shselasky		switch (hi.kind) {
875245733Shselasky		case hid_collection:
876245733Shselasky			if (mdepth != 0)
877245733Shselasky				mdepth++;
878245733Shselasky			else if (hi.collection == 1 &&
879245733Shselasky			     hi.usage ==
880245733Shselasky			      HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))
881245733Shselasky				mdepth++;
882245733Shselasky			break;
883245733Shselasky		case hid_endcollection:
884245733Shselasky			if (mdepth != 0)
885245733Shselasky				mdepth--;
886245733Shselasky			break;
887245733Shselasky		case hid_input:
888245733Shselasky			if (mdepth == 0)
889245733Shselasky				break;
890245733Shselasky			if (hi.usage ==
891245733Shselasky			     HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X) &&
892245733Shselasky			    (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE)
893245733Shselasky				found++;
894245733Shselasky			if (hi.usage ==
895245733Shselasky			     HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y) &&
896245733Shselasky			    (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE)
897245733Shselasky				found++;
898245733Shselasky			break;
899245733Shselasky		default:
900245733Shselasky			break;
901245733Shselasky		}
902245733Shselasky	}
903245733Shselasky	hid_end_parse(hd);
904245733Shselasky	return (found);
905245733Shselasky}
906245733Shselasky
907245733Shselasky/*------------------------------------------------------------------------*
908245733Shselasky *	hid_is_keyboard
909245733Shselasky *
910245733Shselasky * This function will decide if a USB descriptor belongs to a USB keyboard.
911245733Shselasky *
912245733Shselasky * Return values:
913245733Shselasky * Zero: Not a USB keyboard.
914245733Shselasky * Else: Is a USB keyboard.
915245733Shselasky *------------------------------------------------------------------------*/
916245733Shselaskyint
917245733Shselaskyhid_is_keyboard(const void *d_ptr, uint16_t d_len)
918245733Shselasky{
919245733Shselasky	if (hid_is_collection(d_ptr, d_len,
920245733Shselasky	    HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD)))
921245733Shselasky		return (1);
922245733Shselasky	return (0);
923245733Shselasky}
924