xref: /dflybsd-src/sys/bus/u4b/usb_hid.c (revision 2b3f93ea6d1f70880f3e87f3c2cbe0dc0bfc9332)
145f67c02SMarkus Pfeiffer /* $FreeBSD: head/sys/dev/usb/usb_hid.c 246122 2013-01-30 15:26:04Z hselasky $ */
212bd3c8bSSascha Wildner /*	$NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem Exp $	*/
312bd3c8bSSascha Wildner 
412bd3c8bSSascha Wildner 
512bd3c8bSSascha Wildner /*-
612bd3c8bSSascha Wildner  * Copyright (c) 1998 The NetBSD Foundation, Inc.
712bd3c8bSSascha Wildner  * All rights reserved.
812bd3c8bSSascha Wildner  *
912bd3c8bSSascha Wildner  * This code is derived from software contributed to The NetBSD Foundation
1012bd3c8bSSascha Wildner  * by Lennart Augustsson (lennart@augustsson.net) at
1112bd3c8bSSascha Wildner  * Carlstedt Research & Technology.
1212bd3c8bSSascha Wildner  *
1312bd3c8bSSascha Wildner  * Redistribution and use in source and binary forms, with or without
1412bd3c8bSSascha Wildner  * modification, are permitted provided that the following conditions
1512bd3c8bSSascha Wildner  * are met:
1612bd3c8bSSascha Wildner  * 1. Redistributions of source code must retain the above copyright
1712bd3c8bSSascha Wildner  *    notice, this list of conditions and the following disclaimer.
1812bd3c8bSSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
1912bd3c8bSSascha Wildner  *    notice, this list of conditions and the following disclaimer in the
2012bd3c8bSSascha Wildner  *    documentation and/or other materials provided with the distribution.
2112bd3c8bSSascha Wildner  *
2212bd3c8bSSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2312bd3c8bSSascha Wildner  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2412bd3c8bSSascha Wildner  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2512bd3c8bSSascha Wildner  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2612bd3c8bSSascha Wildner  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2712bd3c8bSSascha Wildner  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2812bd3c8bSSascha Wildner  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2912bd3c8bSSascha Wildner  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3012bd3c8bSSascha Wildner  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3112bd3c8bSSascha Wildner  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3212bd3c8bSSascha Wildner  * POSSIBILITY OF SUCH DAMAGE.
3312bd3c8bSSascha Wildner  */
3412bd3c8bSSascha Wildner 
3512bd3c8bSSascha Wildner #include <sys/stdint.h>
3612bd3c8bSSascha Wildner #include <sys/param.h>
3712bd3c8bSSascha Wildner #include <sys/queue.h>
3812bd3c8bSSascha Wildner #include <sys/types.h>
3912bd3c8bSSascha Wildner #include <sys/systm.h>
4012bd3c8bSSascha Wildner #include <sys/kernel.h>
4112bd3c8bSSascha Wildner #include <sys/bus.h>
4212bd3c8bSSascha Wildner #include <sys/module.h>
4312bd3c8bSSascha Wildner #include <sys/lock.h>
4412bd3c8bSSascha Wildner #include <sys/condvar.h>
4512bd3c8bSSascha Wildner #include <sys/sysctl.h>
4612bd3c8bSSascha Wildner #include <sys/unistd.h>
4712bd3c8bSSascha Wildner #include <sys/callout.h>
4812bd3c8bSSascha Wildner #include <sys/malloc.h>
49*2b3f93eaSMatthew Dillon #include <sys/caps.h>
5012bd3c8bSSascha Wildner 
51722d05c3SSascha Wildner #include <bus/u4b/usb.h>
52722d05c3SSascha Wildner #include <bus/u4b/usbdi.h>
53722d05c3SSascha Wildner #include <bus/u4b/usbdi_util.h>
54722d05c3SSascha Wildner #include <bus/u4b/usbhid.h>
5512bd3c8bSSascha Wildner 
5612bd3c8bSSascha Wildner #define	USB_DEBUG_VAR usb_debug
5712bd3c8bSSascha Wildner 
58722d05c3SSascha Wildner #include <bus/u4b/usb_core.h>
59722d05c3SSascha Wildner #include <bus/u4b/usb_debug.h>
60722d05c3SSascha Wildner #include <bus/u4b/usb_process.h>
61722d05c3SSascha Wildner #include <bus/u4b/usb_device.h>
62722d05c3SSascha Wildner #include <bus/u4b/usb_request.h>
6312bd3c8bSSascha Wildner 
6412bd3c8bSSascha Wildner static void hid_clear_local(struct hid_item *);
6512bd3c8bSSascha Wildner static uint8_t hid_get_byte(struct hid_data *s, const uint16_t wSize);
6612bd3c8bSSascha Wildner 
6712bd3c8bSSascha Wildner #define	MAXUSAGE 64
6812bd3c8bSSascha Wildner #define	MAXPUSH 4
6912bd3c8bSSascha Wildner #define	MAXID 16
7012bd3c8bSSascha Wildner 
7112bd3c8bSSascha Wildner struct hid_pos_data {
7212bd3c8bSSascha Wildner 	int32_t rid;
7312bd3c8bSSascha Wildner 	uint32_t pos;
7412bd3c8bSSascha Wildner };
7512bd3c8bSSascha Wildner 
7612bd3c8bSSascha Wildner struct hid_data {
7712bd3c8bSSascha Wildner 	const uint8_t *start;
7812bd3c8bSSascha Wildner 	const uint8_t *end;
7912bd3c8bSSascha Wildner 	const uint8_t *p;
8012bd3c8bSSascha Wildner 	struct hid_item cur[MAXPUSH];
8112bd3c8bSSascha Wildner 	struct hid_pos_data last_pos[MAXID];
8212bd3c8bSSascha Wildner 	int32_t	usages_min[MAXUSAGE];
8312bd3c8bSSascha Wildner 	int32_t	usages_max[MAXUSAGE];
8412bd3c8bSSascha Wildner 	int32_t usage_last;	/* last seen usage */
8512bd3c8bSSascha Wildner 	uint32_t loc_size;	/* last seen size */
8612bd3c8bSSascha Wildner 	uint32_t loc_count;	/* last seen count */
8712bd3c8bSSascha Wildner 	uint8_t	kindset;	/* we have 5 kinds so 8 bits are enough */
8812bd3c8bSSascha Wildner 	uint8_t	pushlevel;	/* current pushlevel */
8912bd3c8bSSascha Wildner 	uint8_t	ncount;		/* end usage item count */
9012bd3c8bSSascha Wildner 	uint8_t icount;		/* current usage item count */
9112bd3c8bSSascha Wildner 	uint8_t	nusage;		/* end "usages_min/max" index */
9212bd3c8bSSascha Wildner 	uint8_t	iusage;		/* current "usages_min/max" index */
9312bd3c8bSSascha Wildner 	uint8_t ousage;		/* current "usages_min/max" offset */
9412bd3c8bSSascha Wildner 	uint8_t	susage;		/* usage set flags */
9512bd3c8bSSascha Wildner };
9612bd3c8bSSascha Wildner 
9712bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
9812bd3c8bSSascha Wildner  *	hid_clear_local
9912bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
10012bd3c8bSSascha Wildner static void
hid_clear_local(struct hid_item * c)10112bd3c8bSSascha Wildner hid_clear_local(struct hid_item *c)
10212bd3c8bSSascha Wildner {
10312bd3c8bSSascha Wildner 
10412bd3c8bSSascha Wildner 	c->loc.count = 0;
10512bd3c8bSSascha Wildner 	c->loc.size = 0;
10612bd3c8bSSascha Wildner 	c->usage = 0;
10712bd3c8bSSascha Wildner 	c->usage_minimum = 0;
10812bd3c8bSSascha Wildner 	c->usage_maximum = 0;
10912bd3c8bSSascha Wildner 	c->designator_index = 0;
11012bd3c8bSSascha Wildner 	c->designator_minimum = 0;
11112bd3c8bSSascha Wildner 	c->designator_maximum = 0;
11212bd3c8bSSascha Wildner 	c->string_index = 0;
11312bd3c8bSSascha Wildner 	c->string_minimum = 0;
11412bd3c8bSSascha Wildner 	c->string_maximum = 0;
11512bd3c8bSSascha Wildner 	c->set_delimiter = 0;
11612bd3c8bSSascha Wildner }
11712bd3c8bSSascha Wildner 
11812bd3c8bSSascha Wildner static void
hid_switch_rid(struct hid_data * s,struct hid_item * c,int32_t next_rID)11912bd3c8bSSascha Wildner hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t next_rID)
12012bd3c8bSSascha Wildner {
12112bd3c8bSSascha Wildner 	uint8_t i;
12212bd3c8bSSascha Wildner 
12312bd3c8bSSascha Wildner 	/* check for same report ID - optimise */
12412bd3c8bSSascha Wildner 
12512bd3c8bSSascha Wildner 	if (c->report_ID == next_rID)
12612bd3c8bSSascha Wildner 		return;
12712bd3c8bSSascha Wildner 
12812bd3c8bSSascha Wildner 	/* save current position for current rID */
12912bd3c8bSSascha Wildner 
13012bd3c8bSSascha Wildner 	if (c->report_ID == 0) {
13112bd3c8bSSascha Wildner 		i = 0;
13212bd3c8bSSascha Wildner 	} else {
13312bd3c8bSSascha Wildner 		for (i = 1; i != MAXID; i++) {
13412bd3c8bSSascha Wildner 			if (s->last_pos[i].rid == c->report_ID)
13512bd3c8bSSascha Wildner 				break;
13612bd3c8bSSascha Wildner 			if (s->last_pos[i].rid == 0)
13712bd3c8bSSascha Wildner 				break;
13812bd3c8bSSascha Wildner 		}
13912bd3c8bSSascha Wildner 	}
14012bd3c8bSSascha Wildner 	if (i != MAXID) {
14112bd3c8bSSascha Wildner 		s->last_pos[i].rid = c->report_ID;
14212bd3c8bSSascha Wildner 		s->last_pos[i].pos = c->loc.pos;
14312bd3c8bSSascha Wildner 	}
14412bd3c8bSSascha Wildner 
14512bd3c8bSSascha Wildner 	/* store next report ID */
14612bd3c8bSSascha Wildner 
14712bd3c8bSSascha Wildner 	c->report_ID = next_rID;
14812bd3c8bSSascha Wildner 
14912bd3c8bSSascha Wildner 	/* lookup last position for next rID */
15012bd3c8bSSascha Wildner 
15112bd3c8bSSascha Wildner 	if (next_rID == 0) {
15212bd3c8bSSascha Wildner 		i = 0;
15312bd3c8bSSascha Wildner 	} else {
15412bd3c8bSSascha Wildner 		for (i = 1; i != MAXID; i++) {
15512bd3c8bSSascha Wildner 			if (s->last_pos[i].rid == next_rID)
15612bd3c8bSSascha Wildner 				break;
15712bd3c8bSSascha Wildner 			if (s->last_pos[i].rid == 0)
15812bd3c8bSSascha Wildner 				break;
15912bd3c8bSSascha Wildner 		}
16012bd3c8bSSascha Wildner 	}
16112bd3c8bSSascha Wildner 	if (i != MAXID) {
16212bd3c8bSSascha Wildner 		s->last_pos[i].rid = next_rID;
16312bd3c8bSSascha Wildner 		c->loc.pos = s->last_pos[i].pos;
16412bd3c8bSSascha Wildner 	} else {
16512bd3c8bSSascha Wildner 		DPRINTF("Out of RID entries, position is set to zero!\n");
16612bd3c8bSSascha Wildner 		c->loc.pos = 0;
16712bd3c8bSSascha Wildner 	}
16812bd3c8bSSascha Wildner }
16912bd3c8bSSascha Wildner 
17012bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
17112bd3c8bSSascha Wildner  *	hid_start_parse
17212bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
17312bd3c8bSSascha Wildner struct hid_data *
hid_start_parse(const void * d,usb_size_t len,int kindset)17412bd3c8bSSascha Wildner hid_start_parse(const void *d, usb_size_t len, int kindset)
17512bd3c8bSSascha Wildner {
17612bd3c8bSSascha Wildner 	struct hid_data *s;
17712bd3c8bSSascha Wildner 
17812bd3c8bSSascha Wildner 	if ((kindset-1) & kindset) {
17912bd3c8bSSascha Wildner 		DPRINTFN(0, "Only one bit can be "
18012bd3c8bSSascha Wildner 		    "set in the kindset\n");
18112bd3c8bSSascha Wildner 		return (NULL);
18212bd3c8bSSascha Wildner 	}
18312bd3c8bSSascha Wildner 
184722d05c3SSascha Wildner 	s = kmalloc(sizeof *s, M_TEMP, M_WAITOK | M_ZERO);
18512bd3c8bSSascha Wildner 	s->start = s->p = d;
18612bd3c8bSSascha Wildner 	s->end = ((const uint8_t *)d) + len;
18712bd3c8bSSascha Wildner 	s->kindset = kindset;
18812bd3c8bSSascha Wildner 	return (s);
18912bd3c8bSSascha Wildner }
19012bd3c8bSSascha Wildner 
19112bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
19212bd3c8bSSascha Wildner  *	hid_end_parse
19312bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
19412bd3c8bSSascha Wildner void
hid_end_parse(struct hid_data * s)19512bd3c8bSSascha Wildner hid_end_parse(struct hid_data *s)
19612bd3c8bSSascha Wildner {
19712bd3c8bSSascha Wildner 	if (s == NULL)
19812bd3c8bSSascha Wildner 		return;
19912bd3c8bSSascha Wildner 
200722d05c3SSascha Wildner 	kfree(s, M_TEMP);
20112bd3c8bSSascha Wildner }
20212bd3c8bSSascha Wildner 
20312bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
20412bd3c8bSSascha Wildner  *	get byte from HID descriptor
20512bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
20612bd3c8bSSascha Wildner static uint8_t
hid_get_byte(struct hid_data * s,const uint16_t wSize)20712bd3c8bSSascha Wildner hid_get_byte(struct hid_data *s, const uint16_t wSize)
20812bd3c8bSSascha Wildner {
20912bd3c8bSSascha Wildner 	const uint8_t *ptr;
21012bd3c8bSSascha Wildner 	uint8_t retval;
21112bd3c8bSSascha Wildner 
21212bd3c8bSSascha Wildner 	ptr = s->p;
21312bd3c8bSSascha Wildner 
21412bd3c8bSSascha Wildner 	/* check if end is reached */
21512bd3c8bSSascha Wildner 	if (ptr == s->end)
21612bd3c8bSSascha Wildner 		return (0);
21712bd3c8bSSascha Wildner 
21812bd3c8bSSascha Wildner 	/* read out a byte */
21912bd3c8bSSascha Wildner 	retval = *ptr;
22012bd3c8bSSascha Wildner 
22112bd3c8bSSascha Wildner 	/* check if data pointer can be advanced by "wSize" bytes */
22212bd3c8bSSascha Wildner 	if ((s->end - ptr) < wSize)
22312bd3c8bSSascha Wildner 		ptr = s->end;
22412bd3c8bSSascha Wildner 	else
22512bd3c8bSSascha Wildner 		ptr += wSize;
22612bd3c8bSSascha Wildner 
22712bd3c8bSSascha Wildner 	/* update pointer */
22812bd3c8bSSascha Wildner 	s->p = ptr;
22912bd3c8bSSascha Wildner 
23012bd3c8bSSascha Wildner 	return (retval);
23112bd3c8bSSascha Wildner }
23212bd3c8bSSascha Wildner 
23312bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
23412bd3c8bSSascha Wildner  *	hid_get_item
23512bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
23612bd3c8bSSascha Wildner int
hid_get_item(struct hid_data * s,struct hid_item * h)23712bd3c8bSSascha Wildner hid_get_item(struct hid_data *s, struct hid_item *h)
23812bd3c8bSSascha Wildner {
23912bd3c8bSSascha Wildner 	struct hid_item *c;
24012bd3c8bSSascha Wildner 	unsigned int bTag, bType, bSize;
24112bd3c8bSSascha Wildner 	uint32_t oldpos;
24212bd3c8bSSascha Wildner 	int32_t mask;
24312bd3c8bSSascha Wildner 	int32_t dval;
24412bd3c8bSSascha Wildner 
24512bd3c8bSSascha Wildner 	if (s == NULL)
24612bd3c8bSSascha Wildner 		return (0);
24712bd3c8bSSascha Wildner 
24812bd3c8bSSascha Wildner 	c = &s->cur[s->pushlevel];
24912bd3c8bSSascha Wildner 
25012bd3c8bSSascha Wildner  top:
25112bd3c8bSSascha Wildner 	/* check if there is an array of items */
25212bd3c8bSSascha Wildner 	if (s->icount < s->ncount) {
25312bd3c8bSSascha Wildner 		/* get current usage */
25412bd3c8bSSascha Wildner 		if (s->iusage < s->nusage) {
25512bd3c8bSSascha Wildner 			dval = s->usages_min[s->iusage] + s->ousage;
25612bd3c8bSSascha Wildner 			c->usage = dval;
25712bd3c8bSSascha Wildner 			s->usage_last = dval;
25812bd3c8bSSascha Wildner 			if (dval == s->usages_max[s->iusage]) {
25912bd3c8bSSascha Wildner 				s->iusage ++;
26012bd3c8bSSascha Wildner 				s->ousage = 0;
26112bd3c8bSSascha Wildner 			} else {
26212bd3c8bSSascha Wildner 				s->ousage ++;
26312bd3c8bSSascha Wildner 			}
26412bd3c8bSSascha Wildner 		} else {
26512bd3c8bSSascha Wildner 			DPRINTFN(1, "Using last usage\n");
26612bd3c8bSSascha Wildner 			dval = s->usage_last;
26712bd3c8bSSascha Wildner 		}
26812bd3c8bSSascha Wildner 		s->icount ++;
26912bd3c8bSSascha Wildner 		/*
27012bd3c8bSSascha Wildner 		 * Only copy HID item, increment position and return
27112bd3c8bSSascha Wildner 		 * if correct kindset!
27212bd3c8bSSascha Wildner 		 */
27312bd3c8bSSascha Wildner 		if (s->kindset & (1 << c->kind)) {
27412bd3c8bSSascha Wildner 			*h = *c;
27512bd3c8bSSascha Wildner 			DPRINTFN(1, "%u,%u,%u\n", h->loc.pos,
27612bd3c8bSSascha Wildner 			    h->loc.size, h->loc.count);
27712bd3c8bSSascha Wildner 			c->loc.pos += c->loc.size * c->loc.count;
27812bd3c8bSSascha Wildner 			return (1);
27912bd3c8bSSascha Wildner 		}
28012bd3c8bSSascha Wildner 	}
28112bd3c8bSSascha Wildner 
28212bd3c8bSSascha Wildner 	/* reset state variables */
28312bd3c8bSSascha Wildner 	s->icount = 0;
28412bd3c8bSSascha Wildner 	s->ncount = 0;
28512bd3c8bSSascha Wildner 	s->iusage = 0;
28612bd3c8bSSascha Wildner 	s->nusage = 0;
28712bd3c8bSSascha Wildner 	s->susage = 0;
28812bd3c8bSSascha Wildner 	s->ousage = 0;
28912bd3c8bSSascha Wildner 	hid_clear_local(c);
29012bd3c8bSSascha Wildner 
29112bd3c8bSSascha Wildner 	/* get next item */
29212bd3c8bSSascha Wildner 	while (s->p != s->end) {
29312bd3c8bSSascha Wildner 
29412bd3c8bSSascha Wildner 		bSize = hid_get_byte(s, 1);
29512bd3c8bSSascha Wildner 		if (bSize == 0xfe) {
29612bd3c8bSSascha Wildner 			/* long item */
29712bd3c8bSSascha Wildner 			bSize = hid_get_byte(s, 1);
29812bd3c8bSSascha Wildner 			bSize |= hid_get_byte(s, 1) << 8;
29912bd3c8bSSascha Wildner 			bTag = hid_get_byte(s, 1);
30012bd3c8bSSascha Wildner 			bType = 0xff;	/* XXX what should it be */
30112bd3c8bSSascha Wildner 		} else {
30212bd3c8bSSascha Wildner 			/* short item */
30312bd3c8bSSascha Wildner 			bTag = bSize >> 4;
30412bd3c8bSSascha Wildner 			bType = (bSize >> 2) & 3;
30512bd3c8bSSascha Wildner 			bSize &= 3;
30612bd3c8bSSascha Wildner 			if (bSize == 3)
30712bd3c8bSSascha Wildner 				bSize = 4;
30812bd3c8bSSascha Wildner 		}
30912bd3c8bSSascha Wildner 		switch (bSize) {
31012bd3c8bSSascha Wildner 		case 0:
31112bd3c8bSSascha Wildner 			dval = 0;
31212bd3c8bSSascha Wildner 			mask = 0;
31312bd3c8bSSascha Wildner 			break;
31412bd3c8bSSascha Wildner 		case 1:
31512bd3c8bSSascha Wildner 			dval = (int8_t)hid_get_byte(s, 1);
31612bd3c8bSSascha Wildner 			mask = 0xFF;
31712bd3c8bSSascha Wildner 			break;
31812bd3c8bSSascha Wildner 		case 2:
31912bd3c8bSSascha Wildner 			dval = hid_get_byte(s, 1);
32012bd3c8bSSascha Wildner 			dval |= hid_get_byte(s, 1) << 8;
32112bd3c8bSSascha Wildner 			dval = (int16_t)dval;
32212bd3c8bSSascha Wildner 			mask = 0xFFFF;
32312bd3c8bSSascha Wildner 			break;
32412bd3c8bSSascha Wildner 		case 4:
32512bd3c8bSSascha Wildner 			dval = hid_get_byte(s, 1);
32612bd3c8bSSascha Wildner 			dval |= hid_get_byte(s, 1) << 8;
32712bd3c8bSSascha Wildner 			dval |= hid_get_byte(s, 1) << 16;
32812bd3c8bSSascha Wildner 			dval |= hid_get_byte(s, 1) << 24;
32912bd3c8bSSascha Wildner 			mask = 0xFFFFFFFF;
33012bd3c8bSSascha Wildner 			break;
33112bd3c8bSSascha Wildner 		default:
33212bd3c8bSSascha Wildner 			dval = hid_get_byte(s, bSize);
33312bd3c8bSSascha Wildner 			DPRINTFN(0, "bad length %u (data=0x%02x)\n",
33412bd3c8bSSascha Wildner 			    bSize, dval);
33512bd3c8bSSascha Wildner 			continue;
33612bd3c8bSSascha Wildner 		}
33712bd3c8bSSascha Wildner 
33812bd3c8bSSascha Wildner 		switch (bType) {
33912bd3c8bSSascha Wildner 		case 0:		/* Main */
34012bd3c8bSSascha Wildner 			switch (bTag) {
34112bd3c8bSSascha Wildner 			case 8:	/* Input */
34212bd3c8bSSascha Wildner 				c->kind = hid_input;
34312bd3c8bSSascha Wildner 				c->flags = dval;
34412bd3c8bSSascha Wildner 		ret:
34512bd3c8bSSascha Wildner 				c->loc.count = s->loc_count;
34612bd3c8bSSascha Wildner 				c->loc.size = s->loc_size;
34712bd3c8bSSascha Wildner 
34812bd3c8bSSascha Wildner 				if (c->flags & HIO_VARIABLE) {
34912bd3c8bSSascha Wildner 					/* range check usage count */
35012bd3c8bSSascha Wildner 					if (c->loc.count > 255) {
35112bd3c8bSSascha Wildner 						DPRINTFN(0, "Number of "
35212bd3c8bSSascha Wildner 						    "items truncated to 255\n");
35312bd3c8bSSascha Wildner 						s->ncount = 255;
35412bd3c8bSSascha Wildner 					} else
35512bd3c8bSSascha Wildner 						s->ncount = c->loc.count;
35612bd3c8bSSascha Wildner 
35712bd3c8bSSascha Wildner 					/*
35812bd3c8bSSascha Wildner 					 * The "top" loop will return
35912bd3c8bSSascha Wildner 					 * one and one item:
36012bd3c8bSSascha Wildner 					 */
36112bd3c8bSSascha Wildner 					c->loc.count = 1;
36212bd3c8bSSascha Wildner 				} else {
36312bd3c8bSSascha Wildner 					s->ncount = 1;
36412bd3c8bSSascha Wildner 				}
36512bd3c8bSSascha Wildner 				goto top;
36612bd3c8bSSascha Wildner 
36712bd3c8bSSascha Wildner 			case 9:	/* Output */
36812bd3c8bSSascha Wildner 				c->kind = hid_output;
36912bd3c8bSSascha Wildner 				c->flags = dval;
37012bd3c8bSSascha Wildner 				goto ret;
37112bd3c8bSSascha Wildner 			case 10:	/* Collection */
37212bd3c8bSSascha Wildner 				c->kind = hid_collection;
37312bd3c8bSSascha Wildner 				c->collection = dval;
37412bd3c8bSSascha Wildner 				c->collevel++;
37512bd3c8bSSascha Wildner 				c->usage = s->usage_last;
37612bd3c8bSSascha Wildner 				*h = *c;
37712bd3c8bSSascha Wildner 				return (1);
37812bd3c8bSSascha Wildner 			case 11:	/* Feature */
37912bd3c8bSSascha Wildner 				c->kind = hid_feature;
38012bd3c8bSSascha Wildner 				c->flags = dval;
38112bd3c8bSSascha Wildner 				goto ret;
38212bd3c8bSSascha Wildner 			case 12:	/* End collection */
38312bd3c8bSSascha Wildner 				c->kind = hid_endcollection;
38412bd3c8bSSascha Wildner 				if (c->collevel == 0) {
38512bd3c8bSSascha Wildner 					DPRINTFN(0, "invalid end collection\n");
38612bd3c8bSSascha Wildner 					return (0);
38712bd3c8bSSascha Wildner 				}
38812bd3c8bSSascha Wildner 				c->collevel--;
38912bd3c8bSSascha Wildner 				*h = *c;
39012bd3c8bSSascha Wildner 				return (1);
39112bd3c8bSSascha Wildner 			default:
39212bd3c8bSSascha Wildner 				DPRINTFN(0, "Main bTag=%d\n", bTag);
39312bd3c8bSSascha Wildner 				break;
39412bd3c8bSSascha Wildner 			}
39512bd3c8bSSascha Wildner 			break;
39612bd3c8bSSascha Wildner 		case 1:		/* Global */
39712bd3c8bSSascha Wildner 			switch (bTag) {
39812bd3c8bSSascha Wildner 			case 0:
39912bd3c8bSSascha Wildner 				c->_usage_page = dval << 16;
40012bd3c8bSSascha Wildner 				break;
40112bd3c8bSSascha Wildner 			case 1:
40212bd3c8bSSascha Wildner 				c->logical_minimum = dval;
40312bd3c8bSSascha Wildner 				break;
40412bd3c8bSSascha Wildner 			case 2:
40512bd3c8bSSascha Wildner 				c->logical_maximum = dval;
40612bd3c8bSSascha Wildner 				break;
40712bd3c8bSSascha Wildner 			case 3:
40812bd3c8bSSascha Wildner 				c->physical_minimum = dval;
40912bd3c8bSSascha Wildner 				break;
41012bd3c8bSSascha Wildner 			case 4:
41112bd3c8bSSascha Wildner 				c->physical_maximum = dval;
41212bd3c8bSSascha Wildner 				break;
41312bd3c8bSSascha Wildner 			case 5:
41412bd3c8bSSascha Wildner 				c->unit_exponent = dval;
41512bd3c8bSSascha Wildner 				break;
41612bd3c8bSSascha Wildner 			case 6:
41712bd3c8bSSascha Wildner 				c->unit = dval;
41812bd3c8bSSascha Wildner 				break;
41912bd3c8bSSascha Wildner 			case 7:
42012bd3c8bSSascha Wildner 				/* mask because value is unsigned */
42112bd3c8bSSascha Wildner 				s->loc_size = dval & mask;
42212bd3c8bSSascha Wildner 				break;
42312bd3c8bSSascha Wildner 			case 8:
42457bed822SMarkus Pfeiffer 				hid_switch_rid(s, c, dval & mask);
42512bd3c8bSSascha Wildner 				break;
42612bd3c8bSSascha Wildner 			case 9:
42712bd3c8bSSascha Wildner 				/* mask because value is unsigned */
42812bd3c8bSSascha Wildner 				s->loc_count = dval & mask;
42912bd3c8bSSascha Wildner 				break;
43012bd3c8bSSascha Wildner 			case 10:	/* Push */
43112bd3c8bSSascha Wildner 				s->pushlevel ++;
43212bd3c8bSSascha Wildner 				if (s->pushlevel < MAXPUSH) {
43312bd3c8bSSascha Wildner 					s->cur[s->pushlevel] = *c;
43412bd3c8bSSascha Wildner 					/* store size and count */
43512bd3c8bSSascha Wildner 					c->loc.size = s->loc_size;
43612bd3c8bSSascha Wildner 					c->loc.count = s->loc_count;
43712bd3c8bSSascha Wildner 					/* update current item pointer */
43812bd3c8bSSascha Wildner 					c = &s->cur[s->pushlevel];
43912bd3c8bSSascha Wildner 				} else {
44012bd3c8bSSascha Wildner 					DPRINTFN(0, "Cannot push "
44112bd3c8bSSascha Wildner 					    "item @ %d\n", s->pushlevel);
44212bd3c8bSSascha Wildner 				}
44312bd3c8bSSascha Wildner 				break;
44412bd3c8bSSascha Wildner 			case 11:	/* Pop */
44512bd3c8bSSascha Wildner 				s->pushlevel --;
44612bd3c8bSSascha Wildner 				if (s->pushlevel < MAXPUSH) {
44712bd3c8bSSascha Wildner 					/* preserve position */
44812bd3c8bSSascha Wildner 					oldpos = c->loc.pos;
44912bd3c8bSSascha Wildner 					c = &s->cur[s->pushlevel];
45012bd3c8bSSascha Wildner 					/* restore size and count */
45112bd3c8bSSascha Wildner 					s->loc_size = c->loc.size;
45212bd3c8bSSascha Wildner 					s->loc_count = c->loc.count;
45312bd3c8bSSascha Wildner 					/* set default item location */
45412bd3c8bSSascha Wildner 					c->loc.pos = oldpos;
45512bd3c8bSSascha Wildner 					c->loc.size = 0;
45612bd3c8bSSascha Wildner 					c->loc.count = 0;
45712bd3c8bSSascha Wildner 				} else {
45812bd3c8bSSascha Wildner 					DPRINTFN(0, "Cannot pop "
45912bd3c8bSSascha Wildner 					    "item @ %d\n", s->pushlevel);
46012bd3c8bSSascha Wildner 				}
46112bd3c8bSSascha Wildner 				break;
46212bd3c8bSSascha Wildner 			default:
46312bd3c8bSSascha Wildner 				DPRINTFN(0, "Global bTag=%d\n", bTag);
46412bd3c8bSSascha Wildner 				break;
46512bd3c8bSSascha Wildner 			}
46612bd3c8bSSascha Wildner 			break;
46712bd3c8bSSascha Wildner 		case 2:		/* Local */
46812bd3c8bSSascha Wildner 			switch (bTag) {
46912bd3c8bSSascha Wildner 			case 0:
47012bd3c8bSSascha Wildner 				if (bSize != 4)
47112bd3c8bSSascha Wildner 					dval = (dval & mask) | c->_usage_page;
47212bd3c8bSSascha Wildner 
47312bd3c8bSSascha Wildner 				/* set last usage, in case of a collection */
47412bd3c8bSSascha Wildner 				s->usage_last = dval;
47512bd3c8bSSascha Wildner 
47612bd3c8bSSascha Wildner 				if (s->nusage < MAXUSAGE) {
47712bd3c8bSSascha Wildner 					s->usages_min[s->nusage] = dval;
47812bd3c8bSSascha Wildner 					s->usages_max[s->nusage] = dval;
47912bd3c8bSSascha Wildner 					s->nusage ++;
48012bd3c8bSSascha Wildner 				} else {
48112bd3c8bSSascha Wildner 					DPRINTFN(0, "max usage reached\n");
48212bd3c8bSSascha Wildner 				}
48312bd3c8bSSascha Wildner 
48412bd3c8bSSascha Wildner 				/* clear any pending usage sets */
48512bd3c8bSSascha Wildner 				s->susage = 0;
48612bd3c8bSSascha Wildner 				break;
48712bd3c8bSSascha Wildner 			case 1:
48812bd3c8bSSascha Wildner 				s->susage |= 1;
48912bd3c8bSSascha Wildner 
49012bd3c8bSSascha Wildner 				if (bSize != 4)
49112bd3c8bSSascha Wildner 					dval = (dval & mask) | c->_usage_page;
49212bd3c8bSSascha Wildner 				c->usage_minimum = dval;
49312bd3c8bSSascha Wildner 
49412bd3c8bSSascha Wildner 				goto check_set;
49512bd3c8bSSascha Wildner 			case 2:
49612bd3c8bSSascha Wildner 				s->susage |= 2;
49712bd3c8bSSascha Wildner 
49812bd3c8bSSascha Wildner 				if (bSize != 4)
49912bd3c8bSSascha Wildner 					dval = (dval & mask) | c->_usage_page;
50012bd3c8bSSascha Wildner 				c->usage_maximum = dval;
50112bd3c8bSSascha Wildner 
50212bd3c8bSSascha Wildner 			check_set:
50312bd3c8bSSascha Wildner 				if (s->susage != 3)
50412bd3c8bSSascha Wildner 					break;
50512bd3c8bSSascha Wildner 
50612bd3c8bSSascha Wildner 				/* sanity check */
50712bd3c8bSSascha Wildner 				if ((s->nusage < MAXUSAGE) &&
50812bd3c8bSSascha Wildner 				    (c->usage_minimum <= c->usage_maximum)) {
50912bd3c8bSSascha Wildner 					/* add usage range */
51012bd3c8bSSascha Wildner 					s->usages_min[s->nusage] =
51112bd3c8bSSascha Wildner 					    c->usage_minimum;
51212bd3c8bSSascha Wildner 					s->usages_max[s->nusage] =
51312bd3c8bSSascha Wildner 					    c->usage_maximum;
51412bd3c8bSSascha Wildner 					s->nusage ++;
51512bd3c8bSSascha Wildner 				} else {
51612bd3c8bSSascha Wildner 					DPRINTFN(0, "Usage set dropped\n");
51712bd3c8bSSascha Wildner 				}
51812bd3c8bSSascha Wildner 				s->susage = 0;
51912bd3c8bSSascha Wildner 				break;
52012bd3c8bSSascha Wildner 			case 3:
52112bd3c8bSSascha Wildner 				c->designator_index = dval;
52212bd3c8bSSascha Wildner 				break;
52312bd3c8bSSascha Wildner 			case 4:
52412bd3c8bSSascha Wildner 				c->designator_minimum = dval;
52512bd3c8bSSascha Wildner 				break;
52612bd3c8bSSascha Wildner 			case 5:
52712bd3c8bSSascha Wildner 				c->designator_maximum = dval;
52812bd3c8bSSascha Wildner 				break;
52912bd3c8bSSascha Wildner 			case 7:
53012bd3c8bSSascha Wildner 				c->string_index = dval;
53112bd3c8bSSascha Wildner 				break;
53212bd3c8bSSascha Wildner 			case 8:
53312bd3c8bSSascha Wildner 				c->string_minimum = dval;
53412bd3c8bSSascha Wildner 				break;
53512bd3c8bSSascha Wildner 			case 9:
53612bd3c8bSSascha Wildner 				c->string_maximum = dval;
53712bd3c8bSSascha Wildner 				break;
53812bd3c8bSSascha Wildner 			case 10:
53912bd3c8bSSascha Wildner 				c->set_delimiter = dval;
54012bd3c8bSSascha Wildner 				break;
54112bd3c8bSSascha Wildner 			default:
54212bd3c8bSSascha Wildner 				DPRINTFN(0, "Local bTag=%d\n", bTag);
54312bd3c8bSSascha Wildner 				break;
54412bd3c8bSSascha Wildner 			}
54512bd3c8bSSascha Wildner 			break;
54612bd3c8bSSascha Wildner 		default:
54712bd3c8bSSascha Wildner 			DPRINTFN(0, "default bType=%d\n", bType);
54812bd3c8bSSascha Wildner 			break;
54912bd3c8bSSascha Wildner 		}
55012bd3c8bSSascha Wildner 	}
55112bd3c8bSSascha Wildner 	return (0);
55212bd3c8bSSascha Wildner }
55312bd3c8bSSascha Wildner 
55412bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
55512bd3c8bSSascha Wildner  *	hid_report_size
55612bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
55712bd3c8bSSascha Wildner int
hid_report_size(const void * buf,usb_size_t len,enum hid_kind k,uint8_t * id)55812bd3c8bSSascha Wildner hid_report_size(const void *buf, usb_size_t len, enum hid_kind k, uint8_t *id)
55912bd3c8bSSascha Wildner {
56012bd3c8bSSascha Wildner 	struct hid_data *d;
56112bd3c8bSSascha Wildner 	struct hid_item h;
56212bd3c8bSSascha Wildner 	uint32_t temp;
56312bd3c8bSSascha Wildner 	uint32_t hpos;
56412bd3c8bSSascha Wildner 	uint32_t lpos;
56512bd3c8bSSascha Wildner 	uint8_t any_id;
56612bd3c8bSSascha Wildner 
56712bd3c8bSSascha Wildner 	any_id = 0;
56812bd3c8bSSascha Wildner 	hpos = 0;
56912bd3c8bSSascha Wildner 	lpos = 0xFFFFFFFF;
57012bd3c8bSSascha Wildner 
57112bd3c8bSSascha Wildner 	for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);) {
57212bd3c8bSSascha Wildner 		if (h.kind == k) {
57312bd3c8bSSascha Wildner 			/* check for ID-byte presense */
57412bd3c8bSSascha Wildner 			if ((h.report_ID != 0) && !any_id) {
57512bd3c8bSSascha Wildner 				if (id != NULL)
57612bd3c8bSSascha Wildner 					*id = h.report_ID;
57712bd3c8bSSascha Wildner 				any_id = 1;
57812bd3c8bSSascha Wildner 			}
57912bd3c8bSSascha Wildner 			/* compute minimum */
58012bd3c8bSSascha Wildner 			if (lpos > h.loc.pos)
58112bd3c8bSSascha Wildner 				lpos = h.loc.pos;
58212bd3c8bSSascha Wildner 			/* compute end position */
58312bd3c8bSSascha Wildner 			temp = h.loc.pos + (h.loc.size * h.loc.count);
58412bd3c8bSSascha Wildner 			/* compute maximum */
58512bd3c8bSSascha Wildner 			if (hpos < temp)
58612bd3c8bSSascha Wildner 				hpos = temp;
58712bd3c8bSSascha Wildner 		}
58812bd3c8bSSascha Wildner 	}
58912bd3c8bSSascha Wildner 	hid_end_parse(d);
59012bd3c8bSSascha Wildner 
59112bd3c8bSSascha Wildner 	/* safety check - can happen in case of currupt descriptors */
59212bd3c8bSSascha Wildner 	if (lpos > hpos)
59312bd3c8bSSascha Wildner 		temp = 0;
59412bd3c8bSSascha Wildner 	else
59512bd3c8bSSascha Wildner 		temp = hpos - lpos;
59612bd3c8bSSascha Wildner 
59712bd3c8bSSascha Wildner 	/* check for ID byte */
59812bd3c8bSSascha Wildner 	if (any_id)
59912bd3c8bSSascha Wildner 		temp += 8;
60012bd3c8bSSascha Wildner 	else if (id != NULL)
60112bd3c8bSSascha Wildner 		*id = 0;
60212bd3c8bSSascha Wildner 
60312bd3c8bSSascha Wildner 	/* return length in bytes rounded up */
60412bd3c8bSSascha Wildner 	return ((temp + 7) / 8);
60512bd3c8bSSascha Wildner }
60612bd3c8bSSascha Wildner 
60712bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
60812bd3c8bSSascha Wildner  *	hid_locate
60912bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
61012bd3c8bSSascha Wildner int
hid_locate(const void * desc,usb_size_t size,int32_t u,enum hid_kind k,uint8_t index,struct hid_location * loc,uint32_t * flags,uint8_t * id)61157bed822SMarkus Pfeiffer hid_locate(const void *desc, usb_size_t size, int32_t u, enum hid_kind k,
61212bd3c8bSSascha Wildner     uint8_t index, struct hid_location *loc, uint32_t *flags, uint8_t *id)
61312bd3c8bSSascha Wildner {
61412bd3c8bSSascha Wildner 	struct hid_data *d;
61512bd3c8bSSascha Wildner 	struct hid_item h;
61612bd3c8bSSascha Wildner 
61712bd3c8bSSascha Wildner 	for (d = hid_start_parse(desc, size, 1 << k); hid_get_item(d, &h);) {
61812bd3c8bSSascha Wildner 		if (h.kind == k && !(h.flags & HIO_CONST) && h.usage == u) {
61912bd3c8bSSascha Wildner 			if (index--)
62012bd3c8bSSascha Wildner 				continue;
62112bd3c8bSSascha Wildner 			if (loc != NULL)
62212bd3c8bSSascha Wildner 				*loc = h.loc;
62312bd3c8bSSascha Wildner 			if (flags != NULL)
62412bd3c8bSSascha Wildner 				*flags = h.flags;
62512bd3c8bSSascha Wildner 			if (id != NULL)
62612bd3c8bSSascha Wildner 				*id = h.report_ID;
62712bd3c8bSSascha Wildner 			hid_end_parse(d);
62812bd3c8bSSascha Wildner 			return (1);
62912bd3c8bSSascha Wildner 		}
63012bd3c8bSSascha Wildner 	}
63112bd3c8bSSascha Wildner 	if (loc != NULL)
63212bd3c8bSSascha Wildner 		loc->size = 0;
63312bd3c8bSSascha Wildner 	if (flags != NULL)
63412bd3c8bSSascha Wildner 		*flags = 0;
63512bd3c8bSSascha Wildner 	if (id != NULL)
63612bd3c8bSSascha Wildner 		*id = 0;
63712bd3c8bSSascha Wildner 	hid_end_parse(d);
63812bd3c8bSSascha Wildner 	return (0);
63912bd3c8bSSascha Wildner }
64012bd3c8bSSascha Wildner 
64112bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
64212bd3c8bSSascha Wildner  *	hid_get_data
64312bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
64412bd3c8bSSascha Wildner static uint32_t
hid_get_data_sub(const uint8_t * buf,usb_size_t len,struct hid_location * loc,int is_signed)64512bd3c8bSSascha Wildner hid_get_data_sub(const uint8_t *buf, usb_size_t len, struct hid_location *loc,
64612bd3c8bSSascha Wildner     int is_signed)
64712bd3c8bSSascha Wildner {
64812bd3c8bSSascha Wildner 	uint32_t hpos = loc->pos;
64912bd3c8bSSascha Wildner 	uint32_t hsize = loc->size;
65012bd3c8bSSascha Wildner 	uint32_t data;
65112bd3c8bSSascha Wildner 	uint32_t rpos;
65212bd3c8bSSascha Wildner 	uint8_t n;
65312bd3c8bSSascha Wildner 
65412bd3c8bSSascha Wildner 	DPRINTFN(11, "hid_get_data: loc %d/%d\n", hpos, hsize);
65512bd3c8bSSascha Wildner 
65612bd3c8bSSascha Wildner 	/* Range check and limit */
65712bd3c8bSSascha Wildner 	if (hsize == 0)
65812bd3c8bSSascha Wildner 		return (0);
65912bd3c8bSSascha Wildner 	if (hsize > 32)
66012bd3c8bSSascha Wildner 		hsize = 32;
66112bd3c8bSSascha Wildner 
66212bd3c8bSSascha Wildner 	/* Get data in a safe way */
66312bd3c8bSSascha Wildner 	data = 0;
66412bd3c8bSSascha Wildner 	rpos = (hpos / 8);
66512bd3c8bSSascha Wildner 	n = (hsize + 7) / 8;
66612bd3c8bSSascha Wildner 	rpos += n;
66712bd3c8bSSascha Wildner 	while (n--) {
66812bd3c8bSSascha Wildner 		rpos--;
66912bd3c8bSSascha Wildner 		if (rpos < len)
67012bd3c8bSSascha Wildner 			data |= buf[rpos] << (8 * n);
67112bd3c8bSSascha Wildner 	}
67212bd3c8bSSascha Wildner 
67312bd3c8bSSascha Wildner 	/* Correctly shift down data */
67412bd3c8bSSascha Wildner 	data = (data >> (hpos % 8));
67512bd3c8bSSascha Wildner 	n = 32 - hsize;
67612bd3c8bSSascha Wildner 
67712bd3c8bSSascha Wildner 	/* Mask and sign extend in one */
67812bd3c8bSSascha Wildner 	if (is_signed != 0)
67912bd3c8bSSascha Wildner 		data = (int32_t)((int32_t)data << n) >> n;
68012bd3c8bSSascha Wildner 	else
68112bd3c8bSSascha Wildner 		data = (uint32_t)((uint32_t)data << n) >> n;
68212bd3c8bSSascha Wildner 
68312bd3c8bSSascha Wildner 	DPRINTFN(11, "hid_get_data: loc %d/%d = %lu\n",
68412bd3c8bSSascha Wildner 	    loc->pos, loc->size, (long)data);
68512bd3c8bSSascha Wildner 	return (data);
68612bd3c8bSSascha Wildner }
68712bd3c8bSSascha Wildner 
68812bd3c8bSSascha Wildner int32_t
hid_get_data(const uint8_t * buf,usb_size_t len,struct hid_location * loc)68912bd3c8bSSascha Wildner hid_get_data(const uint8_t *buf, usb_size_t len, struct hid_location *loc)
69012bd3c8bSSascha Wildner {
69112bd3c8bSSascha Wildner 	return (hid_get_data_sub(buf, len, loc, 1));
69212bd3c8bSSascha Wildner }
69312bd3c8bSSascha Wildner 
69412bd3c8bSSascha Wildner uint32_t
hid_get_data_unsigned(const uint8_t * buf,usb_size_t len,struct hid_location * loc)69512bd3c8bSSascha Wildner hid_get_data_unsigned(const uint8_t *buf, usb_size_t len, struct hid_location *loc)
69612bd3c8bSSascha Wildner {
69712bd3c8bSSascha Wildner         return (hid_get_data_sub(buf, len, loc, 0));
69812bd3c8bSSascha Wildner }
69912bd3c8bSSascha Wildner 
70012bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
70112bd3c8bSSascha Wildner  *	hid_put_data
70212bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
70312bd3c8bSSascha Wildner void
hid_put_data_unsigned(uint8_t * buf,usb_size_t len,struct hid_location * loc,unsigned int value)70412bd3c8bSSascha Wildner hid_put_data_unsigned(uint8_t *buf, usb_size_t len,
70512bd3c8bSSascha Wildner     struct hid_location *loc, unsigned int value)
70612bd3c8bSSascha Wildner {
70712bd3c8bSSascha Wildner 	uint32_t hpos = loc->pos;
70812bd3c8bSSascha Wildner 	uint32_t hsize = loc->size;
70912bd3c8bSSascha Wildner 	uint64_t data;
71012bd3c8bSSascha Wildner 	uint64_t mask;
71112bd3c8bSSascha Wildner 	uint32_t rpos;
71212bd3c8bSSascha Wildner 	uint8_t n;
71312bd3c8bSSascha Wildner 
71412bd3c8bSSascha Wildner 	DPRINTFN(11, "hid_put_data: loc %d/%d = %u\n", hpos, hsize, value);
71512bd3c8bSSascha Wildner 
71612bd3c8bSSascha Wildner 	/* Range check and limit */
71712bd3c8bSSascha Wildner 	if (hsize == 0)
71812bd3c8bSSascha Wildner 		return;
71912bd3c8bSSascha Wildner 	if (hsize > 32)
72012bd3c8bSSascha Wildner 		hsize = 32;
72112bd3c8bSSascha Wildner 
72212bd3c8bSSascha Wildner 	/* Put data in a safe way */
72312bd3c8bSSascha Wildner 	rpos = (hpos / 8);
72412bd3c8bSSascha Wildner 	n = (hsize + 7) / 8;
72512bd3c8bSSascha Wildner 	data = ((uint64_t)value) << (hpos % 8);
72612bd3c8bSSascha Wildner 	mask = ((1ULL << hsize) - 1ULL) << (hpos % 8);
72712bd3c8bSSascha Wildner 	rpos += n;
72812bd3c8bSSascha Wildner 	while (n--) {
72912bd3c8bSSascha Wildner 		rpos--;
73012bd3c8bSSascha Wildner 		if (rpos < len) {
73112bd3c8bSSascha Wildner 			buf[rpos] &= ~(mask >> (8 * n));
73212bd3c8bSSascha Wildner 			buf[rpos] |= (data >> (8 * n));
73312bd3c8bSSascha Wildner 		}
73412bd3c8bSSascha Wildner 	}
73512bd3c8bSSascha Wildner }
73612bd3c8bSSascha Wildner 
73712bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
73812bd3c8bSSascha Wildner  *	hid_is_collection
73912bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
74012bd3c8bSSascha Wildner int
hid_is_collection(const void * desc,usb_size_t size,int32_t usage)74157bed822SMarkus Pfeiffer hid_is_collection(const void *desc, usb_size_t size, int32_t usage)
74212bd3c8bSSascha Wildner {
74312bd3c8bSSascha Wildner 	struct hid_data *hd;
74412bd3c8bSSascha Wildner 	struct hid_item hi;
74512bd3c8bSSascha Wildner 	int err;
74612bd3c8bSSascha Wildner 
74712bd3c8bSSascha Wildner 	hd = hid_start_parse(desc, size, hid_input);
74812bd3c8bSSascha Wildner 	if (hd == NULL)
74912bd3c8bSSascha Wildner 		return (0);
75012bd3c8bSSascha Wildner 
75112bd3c8bSSascha Wildner 	while ((err = hid_get_item(hd, &hi))) {
75212bd3c8bSSascha Wildner 		 if (hi.kind == hid_collection &&
75312bd3c8bSSascha Wildner 		     hi.usage == usage)
75412bd3c8bSSascha Wildner 			break;
75512bd3c8bSSascha Wildner 	}
75612bd3c8bSSascha Wildner 	hid_end_parse(hd);
75712bd3c8bSSascha Wildner 	return (err);
75812bd3c8bSSascha Wildner }
75912bd3c8bSSascha Wildner 
76012bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
76112bd3c8bSSascha Wildner  *	hid_get_descriptor_from_usb
76212bd3c8bSSascha Wildner  *
76312bd3c8bSSascha Wildner  * This function will search for a HID descriptor between two USB
76412bd3c8bSSascha Wildner  * interface descriptors.
76512bd3c8bSSascha Wildner  *
76612bd3c8bSSascha Wildner  * Return values:
76712bd3c8bSSascha Wildner  * NULL: No more HID descriptors.
76812bd3c8bSSascha Wildner  * Else: Pointer to HID descriptor.
76912bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
77012bd3c8bSSascha Wildner struct usb_hid_descriptor *
hid_get_descriptor_from_usb(struct usb_config_descriptor * cd,struct usb_interface_descriptor * id)77112bd3c8bSSascha Wildner hid_get_descriptor_from_usb(struct usb_config_descriptor *cd,
77212bd3c8bSSascha Wildner     struct usb_interface_descriptor *id)
77312bd3c8bSSascha Wildner {
77412bd3c8bSSascha Wildner 	struct usb_descriptor *desc = (void *)id;
77512bd3c8bSSascha Wildner 
77612bd3c8bSSascha Wildner 	if (desc == NULL) {
77712bd3c8bSSascha Wildner 		return (NULL);
77812bd3c8bSSascha Wildner 	}
77912bd3c8bSSascha Wildner 	while ((desc = usb_desc_foreach(cd, desc))) {
78012bd3c8bSSascha Wildner 		if ((desc->bDescriptorType == UDESC_HID) &&
78112bd3c8bSSascha Wildner 		    (desc->bLength >= USB_HID_DESCRIPTOR_SIZE(0))) {
78212bd3c8bSSascha Wildner 			return (void *)desc;
78312bd3c8bSSascha Wildner 		}
78412bd3c8bSSascha Wildner 		if (desc->bDescriptorType == UDESC_INTERFACE) {
78512bd3c8bSSascha Wildner 			break;
78612bd3c8bSSascha Wildner 		}
78712bd3c8bSSascha Wildner 	}
78812bd3c8bSSascha Wildner 	return (NULL);
78912bd3c8bSSascha Wildner }
79012bd3c8bSSascha Wildner 
79112bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
79212bd3c8bSSascha Wildner  *	usbd_req_get_hid_desc
79312bd3c8bSSascha Wildner  *
79412bd3c8bSSascha Wildner  * This function will read out an USB report descriptor from the USB
79512bd3c8bSSascha Wildner  * device.
79612bd3c8bSSascha Wildner  *
79712bd3c8bSSascha Wildner  * Return values:
79812bd3c8bSSascha Wildner  * NULL: Failure.
79912bd3c8bSSascha Wildner  * Else: Success. The pointer should eventually be passed to free().
80012bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
80112bd3c8bSSascha Wildner usb_error_t
usbd_req_get_hid_desc(struct usb_device * udev,struct lock * lock,void ** descp,uint16_t * sizep,struct malloc_type * mem,uint8_t iface_index)802722d05c3SSascha Wildner usbd_req_get_hid_desc(struct usb_device *udev, struct lock *lock,
80312bd3c8bSSascha Wildner     void **descp, uint16_t *sizep,
80412bd3c8bSSascha Wildner     struct malloc_type *mem, uint8_t iface_index)
80512bd3c8bSSascha Wildner {
80612bd3c8bSSascha Wildner 	struct usb_interface *iface = usbd_get_iface(udev, iface_index);
80712bd3c8bSSascha Wildner 	struct usb_hid_descriptor *hid;
80812bd3c8bSSascha Wildner 	usb_error_t err;
80912bd3c8bSSascha Wildner 
81012bd3c8bSSascha Wildner 	if ((iface == NULL) || (iface->idesc == NULL)) {
81112bd3c8bSSascha Wildner 		return (USB_ERR_INVAL);
81212bd3c8bSSascha Wildner 	}
81312bd3c8bSSascha Wildner 	hid = hid_get_descriptor_from_usb
81412bd3c8bSSascha Wildner 	    (usbd_get_config_descriptor(udev), iface->idesc);
81512bd3c8bSSascha Wildner 
81612bd3c8bSSascha Wildner 	if (hid == NULL) {
81712bd3c8bSSascha Wildner 		return (USB_ERR_IOERROR);
81812bd3c8bSSascha Wildner 	}
81912bd3c8bSSascha Wildner 	*sizep = UGETW(hid->descrs[0].wDescriptorLength);
82012bd3c8bSSascha Wildner 	if (*sizep == 0) {
82112bd3c8bSSascha Wildner 		return (USB_ERR_IOERROR);
82212bd3c8bSSascha Wildner 	}
823722d05c3SSascha Wildner 	if (lock)
824722d05c3SSascha Wildner 		lockmgr(lock, LK_RELEASE);
82512bd3c8bSSascha Wildner 
826722d05c3SSascha Wildner 	*descp = kmalloc(*sizep, mem, M_ZERO | M_WAITOK);
82712bd3c8bSSascha Wildner 
828722d05c3SSascha Wildner 	if (lock)
829722d05c3SSascha Wildner 		lockmgr(lock, LK_EXCLUSIVE);
83012bd3c8bSSascha Wildner 
83112bd3c8bSSascha Wildner 	if (*descp == NULL) {
83212bd3c8bSSascha Wildner 		return (USB_ERR_NOMEM);
83312bd3c8bSSascha Wildner 	}
83412bd3c8bSSascha Wildner 	err = usbd_req_get_report_descriptor
835722d05c3SSascha Wildner 	    (udev, lock, *descp, *sizep, iface_index);
83612bd3c8bSSascha Wildner 
83712bd3c8bSSascha Wildner 	if (err) {
838722d05c3SSascha Wildner 		kfree(*descp, mem);
83912bd3c8bSSascha Wildner 		*descp = NULL;
84012bd3c8bSSascha Wildner 		return (err);
84112bd3c8bSSascha Wildner 	}
84212bd3c8bSSascha Wildner 	return (USB_ERR_NORMAL_COMPLETION);
84312bd3c8bSSascha Wildner }
84457bed822SMarkus Pfeiffer 
84557bed822SMarkus Pfeiffer /*------------------------------------------------------------------------*
84657bed822SMarkus Pfeiffer  *	hid_is_mouse
84757bed822SMarkus Pfeiffer  *
84857bed822SMarkus Pfeiffer  * This function will decide if a USB descriptor belongs to a USB mouse.
84957bed822SMarkus Pfeiffer  *
85057bed822SMarkus Pfeiffer  * Return values:
85157bed822SMarkus Pfeiffer  * Zero: Not a USB mouse.
85257bed822SMarkus Pfeiffer  * Else: Is a USB mouse.
85357bed822SMarkus Pfeiffer  *------------------------------------------------------------------------*/
85457bed822SMarkus Pfeiffer int
hid_is_mouse(const void * d_ptr,uint16_t d_len)85557bed822SMarkus Pfeiffer hid_is_mouse(const void *d_ptr, uint16_t d_len)
85657bed822SMarkus Pfeiffer {
85757bed822SMarkus Pfeiffer 	struct hid_data *hd;
85857bed822SMarkus Pfeiffer 	struct hid_item hi;
85957bed822SMarkus Pfeiffer 	int mdepth;
86057bed822SMarkus Pfeiffer 	int found;
86157bed822SMarkus Pfeiffer 
86257bed822SMarkus Pfeiffer 	hd = hid_start_parse(d_ptr, d_len, 1 << hid_input);
86357bed822SMarkus Pfeiffer 	if (hd == NULL)
86457bed822SMarkus Pfeiffer 		return (0);
86557bed822SMarkus Pfeiffer 
86657bed822SMarkus Pfeiffer 	mdepth = 0;
86757bed822SMarkus Pfeiffer 	found = 0;
86857bed822SMarkus Pfeiffer 
86957bed822SMarkus Pfeiffer 	while (hid_get_item(hd, &hi)) {
87057bed822SMarkus Pfeiffer 		switch (hi.kind) {
87157bed822SMarkus Pfeiffer 		case hid_collection:
87257bed822SMarkus Pfeiffer 			if (mdepth != 0)
87357bed822SMarkus Pfeiffer 				mdepth++;
87457bed822SMarkus Pfeiffer 			else if (hi.collection == 1 &&
87557bed822SMarkus Pfeiffer 			     hi.usage ==
87657bed822SMarkus Pfeiffer 			      HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))
87757bed822SMarkus Pfeiffer 				mdepth++;
87857bed822SMarkus Pfeiffer 			break;
87957bed822SMarkus Pfeiffer 		case hid_endcollection:
88057bed822SMarkus Pfeiffer 			if (mdepth != 0)
88157bed822SMarkus Pfeiffer 				mdepth--;
88257bed822SMarkus Pfeiffer 			break;
88357bed822SMarkus Pfeiffer 		case hid_input:
88457bed822SMarkus Pfeiffer 			if (mdepth == 0)
88557bed822SMarkus Pfeiffer 				break;
88657bed822SMarkus Pfeiffer 			if (hi.usage ==
88757bed822SMarkus Pfeiffer 			     HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X) &&
88857bed822SMarkus Pfeiffer 			    (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE)
88957bed822SMarkus Pfeiffer 				found++;
89057bed822SMarkus Pfeiffer 			if (hi.usage ==
89157bed822SMarkus Pfeiffer 			     HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y) &&
89257bed822SMarkus Pfeiffer 			    (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE)
89357bed822SMarkus Pfeiffer 				found++;
89457bed822SMarkus Pfeiffer 			break;
89557bed822SMarkus Pfeiffer 		default:
89657bed822SMarkus Pfeiffer 			break;
89757bed822SMarkus Pfeiffer 		}
89857bed822SMarkus Pfeiffer 	}
89957bed822SMarkus Pfeiffer 	hid_end_parse(hd);
90057bed822SMarkus Pfeiffer 	return (found);
90157bed822SMarkus Pfeiffer }
90257bed822SMarkus Pfeiffer 
90357bed822SMarkus Pfeiffer /*------------------------------------------------------------------------*
90457bed822SMarkus Pfeiffer  *	hid_is_keyboard
90557bed822SMarkus Pfeiffer  *
90657bed822SMarkus Pfeiffer  * This function will decide if a USB descriptor belongs to a USB keyboard.
90757bed822SMarkus Pfeiffer  *
90857bed822SMarkus Pfeiffer  * Return values:
90957bed822SMarkus Pfeiffer  * Zero: Not a USB keyboard.
91057bed822SMarkus Pfeiffer  * Else: Is a USB keyboard.
91157bed822SMarkus Pfeiffer  *------------------------------------------------------------------------*/
91257bed822SMarkus Pfeiffer int
hid_is_keyboard(const void * d_ptr,uint16_t d_len)91357bed822SMarkus Pfeiffer hid_is_keyboard(const void *d_ptr, uint16_t d_len)
91457bed822SMarkus Pfeiffer {
91557bed822SMarkus Pfeiffer 	if (hid_is_collection(d_ptr, d_len,
91657bed822SMarkus Pfeiffer 	    HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD)))
91757bed822SMarkus Pfeiffer 		return (1);
91857bed822SMarkus Pfeiffer 	return (0);
91957bed822SMarkus Pfeiffer }
920