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