1d2b99310SHans Petter Selasky /* $FreeBSD$ */ 202ac6454SAndrew Thompson /* $NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem Exp $ */ 302ac6454SAndrew Thompson /*- 402ac6454SAndrew Thompson * Copyright (c) 1998 The NetBSD Foundation, Inc. 502ac6454SAndrew Thompson * All rights reserved. 602ac6454SAndrew Thompson * 702ac6454SAndrew Thompson * This code is derived from software contributed to The NetBSD Foundation 802ac6454SAndrew Thompson * by Lennart Augustsson (lennart@augustsson.net) at 902ac6454SAndrew Thompson * Carlstedt Research & Technology. 1002ac6454SAndrew Thompson * 1102ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without 1202ac6454SAndrew Thompson * modification, are permitted provided that the following conditions 1302ac6454SAndrew Thompson * are met: 1402ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 1502ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer. 1602ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 1702ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 1802ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution. 1902ac6454SAndrew Thompson * 2002ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2102ac6454SAndrew Thompson * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2202ac6454SAndrew Thompson * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2302ac6454SAndrew Thompson * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2402ac6454SAndrew Thompson * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2502ac6454SAndrew Thompson * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2602ac6454SAndrew Thompson * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2702ac6454SAndrew Thompson * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2802ac6454SAndrew Thompson * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2902ac6454SAndrew Thompson * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3002ac6454SAndrew Thompson * POSSIBILITY OF SUCH DAMAGE. 3102ac6454SAndrew Thompson */ 3202ac6454SAndrew Thompson 33d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE 34d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE 35d2b99310SHans Petter Selasky #else 36ed6d949aSAndrew Thompson #include <sys/stdint.h> 37ed6d949aSAndrew Thompson #include <sys/stddef.h> 38ed6d949aSAndrew Thompson #include <sys/param.h> 39ed6d949aSAndrew Thompson #include <sys/queue.h> 40ed6d949aSAndrew Thompson #include <sys/types.h> 41ed6d949aSAndrew Thompson #include <sys/systm.h> 42ed6d949aSAndrew Thompson #include <sys/kernel.h> 43ed6d949aSAndrew Thompson #include <sys/bus.h> 44ed6d949aSAndrew Thompson #include <sys/module.h> 45ed6d949aSAndrew Thompson #include <sys/lock.h> 46ed6d949aSAndrew Thompson #include <sys/mutex.h> 47ed6d949aSAndrew Thompson #include <sys/condvar.h> 48ed6d949aSAndrew Thompson #include <sys/sysctl.h> 49ed6d949aSAndrew Thompson #include <sys/sx.h> 50ed6d949aSAndrew Thompson #include <sys/unistd.h> 51ed6d949aSAndrew Thompson #include <sys/callout.h> 52ed6d949aSAndrew Thompson #include <sys/malloc.h> 53ed6d949aSAndrew Thompson #include <sys/priv.h> 54ed6d949aSAndrew Thompson 5502ac6454SAndrew Thompson #include <dev/usb/usb.h> 56ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 57ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h> 5802ac6454SAndrew Thompson #include <dev/usb/usbhid.h> 5902ac6454SAndrew Thompson 60a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_debug 6102ac6454SAndrew Thompson 6202ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 6302ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 6402ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 6502ac6454SAndrew Thompson #include <dev/usb/usb_device.h> 6602ac6454SAndrew Thompson #include <dev/usb/usb_request.h> 67d2b99310SHans Petter Selasky #endif /* USB_GLOBAL_INCLUDE_FILE */ 6802ac6454SAndrew Thompson 6902ac6454SAndrew Thompson static void hid_clear_local(struct hid_item *); 70da6c1373SAndrew Thompson static uint8_t hid_get_byte(struct hid_data *s, const uint16_t wSize); 7102ac6454SAndrew Thompson 72da6c1373SAndrew Thompson #define MAXUSAGE 64 73da6c1373SAndrew Thompson #define MAXPUSH 4 7465c7bc9cSAlfred Perlstein #define MAXID 16 7565c7bc9cSAlfred Perlstein 7665c7bc9cSAlfred Perlstein struct hid_pos_data { 7765c7bc9cSAlfred Perlstein int32_t rid; 7865c7bc9cSAlfred Perlstein uint32_t pos; 7965c7bc9cSAlfred Perlstein }; 8065c7bc9cSAlfred Perlstein 8102ac6454SAndrew Thompson struct hid_data { 8202ac6454SAndrew Thompson const uint8_t *start; 8302ac6454SAndrew Thompson const uint8_t *end; 8402ac6454SAndrew Thompson const uint8_t *p; 85da6c1373SAndrew Thompson struct hid_item cur[MAXPUSH]; 8665c7bc9cSAlfred Perlstein struct hid_pos_data last_pos[MAXID]; 87da6c1373SAndrew Thompson int32_t usages_min[MAXUSAGE]; 88da6c1373SAndrew Thompson int32_t usages_max[MAXUSAGE]; 8946364b29SAndrew Thompson int32_t usage_last; /* last seen usage */ 9046364b29SAndrew Thompson uint32_t loc_size; /* last seen size */ 9146364b29SAndrew Thompson uint32_t loc_count; /* last seen count */ 9246364b29SAndrew Thompson uint8_t kindset; /* we have 5 kinds so 8 bits are enough */ 93da6c1373SAndrew Thompson uint8_t pushlevel; /* current pushlevel */ 94da6c1373SAndrew Thompson uint8_t ncount; /* end usage item count */ 95da6c1373SAndrew Thompson uint8_t icount; /* current usage item count */ 96da6c1373SAndrew Thompson uint8_t nusage; /* end "usages_min/max" index */ 97da6c1373SAndrew Thompson uint8_t iusage; /* current "usages_min/max" index */ 98da6c1373SAndrew Thompson uint8_t ousage; /* current "usages_min/max" offset */ 99da6c1373SAndrew Thompson uint8_t susage; /* usage set flags */ 10002ac6454SAndrew Thompson }; 10102ac6454SAndrew Thompson 10202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 10302ac6454SAndrew Thompson * hid_clear_local 10402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 10502ac6454SAndrew Thompson static void 10602ac6454SAndrew Thompson hid_clear_local(struct hid_item *c) 10702ac6454SAndrew Thompson { 10802ac6454SAndrew Thompson 109da6c1373SAndrew Thompson c->loc.count = 0; 110da6c1373SAndrew Thompson c->loc.size = 0; 11102ac6454SAndrew Thompson c->usage = 0; 11202ac6454SAndrew Thompson c->usage_minimum = 0; 11302ac6454SAndrew Thompson c->usage_maximum = 0; 11402ac6454SAndrew Thompson c->designator_index = 0; 11502ac6454SAndrew Thompson c->designator_minimum = 0; 11602ac6454SAndrew Thompson c->designator_maximum = 0; 11702ac6454SAndrew Thompson c->string_index = 0; 11802ac6454SAndrew Thompson c->string_minimum = 0; 11902ac6454SAndrew Thompson c->string_maximum = 0; 12002ac6454SAndrew Thompson c->set_delimiter = 0; 12102ac6454SAndrew Thompson } 12202ac6454SAndrew Thompson 12365c7bc9cSAlfred Perlstein static void 12465c7bc9cSAlfred Perlstein hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t next_rID) 12565c7bc9cSAlfred Perlstein { 12665c7bc9cSAlfred Perlstein uint8_t i; 12765c7bc9cSAlfred Perlstein 12865c7bc9cSAlfred Perlstein /* check for same report ID - optimise */ 12965c7bc9cSAlfred Perlstein 13065c7bc9cSAlfred Perlstein if (c->report_ID == next_rID) 13165c7bc9cSAlfred Perlstein return; 13265c7bc9cSAlfred Perlstein 13365c7bc9cSAlfred Perlstein /* save current position for current rID */ 13465c7bc9cSAlfred Perlstein 13565c7bc9cSAlfred Perlstein if (c->report_ID == 0) { 13665c7bc9cSAlfred Perlstein i = 0; 13765c7bc9cSAlfred Perlstein } else { 13865c7bc9cSAlfred Perlstein for (i = 1; i != MAXID; i++) { 13965c7bc9cSAlfred Perlstein if (s->last_pos[i].rid == c->report_ID) 14065c7bc9cSAlfred Perlstein break; 14165c7bc9cSAlfred Perlstein if (s->last_pos[i].rid == 0) 14265c7bc9cSAlfred Perlstein break; 14365c7bc9cSAlfred Perlstein } 14465c7bc9cSAlfred Perlstein } 14565c7bc9cSAlfred Perlstein if (i != MAXID) { 14665c7bc9cSAlfred Perlstein s->last_pos[i].rid = c->report_ID; 14765c7bc9cSAlfred Perlstein s->last_pos[i].pos = c->loc.pos; 14865c7bc9cSAlfred Perlstein } 14965c7bc9cSAlfred Perlstein 15065c7bc9cSAlfred Perlstein /* store next report ID */ 15165c7bc9cSAlfred Perlstein 15265c7bc9cSAlfred Perlstein c->report_ID = next_rID; 15365c7bc9cSAlfred Perlstein 15465c7bc9cSAlfred Perlstein /* lookup last position for next rID */ 15565c7bc9cSAlfred Perlstein 15665c7bc9cSAlfred Perlstein if (next_rID == 0) { 15765c7bc9cSAlfred Perlstein i = 0; 15865c7bc9cSAlfred Perlstein } else { 15965c7bc9cSAlfred Perlstein for (i = 1; i != MAXID; i++) { 16065c7bc9cSAlfred Perlstein if (s->last_pos[i].rid == next_rID) 16165c7bc9cSAlfred Perlstein break; 16265c7bc9cSAlfred Perlstein if (s->last_pos[i].rid == 0) 16365c7bc9cSAlfred Perlstein break; 16465c7bc9cSAlfred Perlstein } 16565c7bc9cSAlfred Perlstein } 16665c7bc9cSAlfred Perlstein if (i != MAXID) { 16765c7bc9cSAlfred Perlstein s->last_pos[i].rid = next_rID; 16865c7bc9cSAlfred Perlstein c->loc.pos = s->last_pos[i].pos; 16965c7bc9cSAlfred Perlstein } else { 17065c7bc9cSAlfred Perlstein DPRINTF("Out of RID entries, position is set to zero!\n"); 17165c7bc9cSAlfred Perlstein c->loc.pos = 0; 17265c7bc9cSAlfred Perlstein } 17365c7bc9cSAlfred Perlstein } 17465c7bc9cSAlfred Perlstein 17502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 17602ac6454SAndrew Thompson * hid_start_parse 17702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 17802ac6454SAndrew Thompson struct hid_data * 179f9cb546cSAndrew Thompson hid_start_parse(const void *d, usb_size_t len, int kindset) 18002ac6454SAndrew Thompson { 18102ac6454SAndrew Thompson struct hid_data *s; 18202ac6454SAndrew Thompson 183da6c1373SAndrew Thompson if ((kindset-1) & kindset) { 184da6c1373SAndrew Thompson DPRINTFN(0, "Only one bit can be " 185da6c1373SAndrew Thompson "set in the kindset\n"); 186da6c1373SAndrew Thompson return (NULL); 187da6c1373SAndrew Thompson } 188da6c1373SAndrew Thompson 18902ac6454SAndrew Thompson s = malloc(sizeof *s, M_TEMP, M_WAITOK | M_ZERO); 19002ac6454SAndrew Thompson s->start = s->p = d; 19102ac6454SAndrew Thompson s->end = ((const uint8_t *)d) + len; 19202ac6454SAndrew Thompson s->kindset = kindset; 19302ac6454SAndrew Thompson return (s); 19402ac6454SAndrew Thompson } 19502ac6454SAndrew Thompson 19602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 19702ac6454SAndrew Thompson * hid_end_parse 19802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 19902ac6454SAndrew Thompson void 20002ac6454SAndrew Thompson hid_end_parse(struct hid_data *s) 20102ac6454SAndrew Thompson { 202da6c1373SAndrew Thompson if (s == NULL) 203da6c1373SAndrew Thompson return; 20402ac6454SAndrew Thompson 20502ac6454SAndrew Thompson free(s, M_TEMP); 20602ac6454SAndrew Thompson } 20702ac6454SAndrew Thompson 20802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 209da6c1373SAndrew Thompson * get byte from HID descriptor 210da6c1373SAndrew Thompson *------------------------------------------------------------------------*/ 211da6c1373SAndrew Thompson static uint8_t 212da6c1373SAndrew Thompson hid_get_byte(struct hid_data *s, const uint16_t wSize) 213da6c1373SAndrew Thompson { 214da6c1373SAndrew Thompson const uint8_t *ptr; 215da6c1373SAndrew Thompson uint8_t retval; 216da6c1373SAndrew Thompson 217da6c1373SAndrew Thompson ptr = s->p; 218da6c1373SAndrew Thompson 219da6c1373SAndrew Thompson /* check if end is reached */ 220da6c1373SAndrew Thompson if (ptr == s->end) 221da6c1373SAndrew Thompson return (0); 222da6c1373SAndrew Thompson 223da6c1373SAndrew Thompson /* read out a byte */ 224da6c1373SAndrew Thompson retval = *ptr; 225da6c1373SAndrew Thompson 226da6c1373SAndrew Thompson /* check if data pointer can be advanced by "wSize" bytes */ 227da6c1373SAndrew Thompson if ((s->end - ptr) < wSize) 228da6c1373SAndrew Thompson ptr = s->end; 229da6c1373SAndrew Thompson else 230da6c1373SAndrew Thompson ptr += wSize; 231da6c1373SAndrew Thompson 232da6c1373SAndrew Thompson /* update pointer */ 233da6c1373SAndrew Thompson s->p = ptr; 234da6c1373SAndrew Thompson 235da6c1373SAndrew Thompson return (retval); 236da6c1373SAndrew Thompson } 237da6c1373SAndrew Thompson 238da6c1373SAndrew Thompson /*------------------------------------------------------------------------* 23902ac6454SAndrew Thompson * hid_get_item 24002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 24102ac6454SAndrew Thompson int 24202ac6454SAndrew Thompson hid_get_item(struct hid_data *s, struct hid_item *h) 24302ac6454SAndrew Thompson { 244da6c1373SAndrew Thompson struct hid_item *c; 24502ac6454SAndrew Thompson unsigned int bTag, bType, bSize; 24602ac6454SAndrew Thompson uint32_t oldpos; 247da6c1373SAndrew Thompson int32_t mask; 24802ac6454SAndrew Thompson int32_t dval; 24902ac6454SAndrew Thompson 250da6c1373SAndrew Thompson if (s == NULL) 25102ac6454SAndrew Thompson return (0); 25202ac6454SAndrew Thompson 253da6c1373SAndrew Thompson c = &s->cur[s->pushlevel]; 254da6c1373SAndrew Thompson 255da6c1373SAndrew Thompson top: 256da6c1373SAndrew Thompson /* check if there is an array of items */ 25746364b29SAndrew Thompson if (s->icount < s->ncount) { 25846364b29SAndrew Thompson /* get current usage */ 25946364b29SAndrew Thompson if (s->iusage < s->nusage) { 260da6c1373SAndrew Thompson dval = s->usages_min[s->iusage] + s->ousage; 261da6c1373SAndrew Thompson c->usage = dval; 26246364b29SAndrew Thompson s->usage_last = dval; 263da6c1373SAndrew Thompson if (dval == s->usages_max[s->iusage]) { 264da6c1373SAndrew Thompson s->iusage ++; 265da6c1373SAndrew Thompson s->ousage = 0; 266da6c1373SAndrew Thompson } else { 267da6c1373SAndrew Thompson s->ousage ++; 268da6c1373SAndrew Thompson } 26946364b29SAndrew Thompson } else { 27046364b29SAndrew Thompson DPRINTFN(1, "Using last usage\n"); 27146364b29SAndrew Thompson dval = s->usage_last; 27246364b29SAndrew Thompson } 273da6c1373SAndrew Thompson s->icount ++; 274da6c1373SAndrew Thompson /* 275da6c1373SAndrew Thompson * Only copy HID item, increment position and return 276da6c1373SAndrew Thompson * if correct kindset! 277da6c1373SAndrew Thompson */ 278da6c1373SAndrew Thompson if (s->kindset & (1 << c->kind)) { 279da6c1373SAndrew Thompson *h = *c; 280da6c1373SAndrew Thompson DPRINTFN(1, "%u,%u,%u\n", h->loc.pos, 281da6c1373SAndrew Thompson h->loc.size, h->loc.count); 282da6c1373SAndrew Thompson c->loc.pos += c->loc.size * c->loc.count; 283da6c1373SAndrew Thompson return (1); 284da6c1373SAndrew Thompson } 285da6c1373SAndrew Thompson } 286da6c1373SAndrew Thompson 287da6c1373SAndrew Thompson /* reset state variables */ 288da6c1373SAndrew Thompson s->icount = 0; 289da6c1373SAndrew Thompson s->ncount = 0; 290da6c1373SAndrew Thompson s->iusage = 0; 291da6c1373SAndrew Thompson s->nusage = 0; 292da6c1373SAndrew Thompson s->susage = 0; 293da6c1373SAndrew Thompson s->ousage = 0; 294da6c1373SAndrew Thompson hid_clear_local(c); 295da6c1373SAndrew Thompson 296da6c1373SAndrew Thompson /* get next item */ 297da6c1373SAndrew Thompson while (s->p != s->end) { 298da6c1373SAndrew Thompson 299da6c1373SAndrew Thompson bSize = hid_get_byte(s, 1); 30002ac6454SAndrew Thompson if (bSize == 0xfe) { 30102ac6454SAndrew Thompson /* long item */ 302da6c1373SAndrew Thompson bSize = hid_get_byte(s, 1); 303da6c1373SAndrew Thompson bSize |= hid_get_byte(s, 1) << 8; 304da6c1373SAndrew Thompson bTag = hid_get_byte(s, 1); 30502ac6454SAndrew Thompson bType = 0xff; /* XXX what should it be */ 30602ac6454SAndrew Thompson } else { 30702ac6454SAndrew Thompson /* short item */ 30802ac6454SAndrew Thompson bTag = bSize >> 4; 30902ac6454SAndrew Thompson bType = (bSize >> 2) & 3; 31002ac6454SAndrew Thompson bSize &= 3; 31102ac6454SAndrew Thompson if (bSize == 3) 31202ac6454SAndrew Thompson bSize = 4; 31302ac6454SAndrew Thompson } 31402ac6454SAndrew Thompson switch (bSize) { 31502ac6454SAndrew Thompson case 0: 31602ac6454SAndrew Thompson dval = 0; 317da6c1373SAndrew Thompson mask = 0; 31802ac6454SAndrew Thompson break; 31902ac6454SAndrew Thompson case 1: 320da6c1373SAndrew Thompson dval = (int8_t)hid_get_byte(s, 1); 321da6c1373SAndrew Thompson mask = 0xFF; 32202ac6454SAndrew Thompson break; 32302ac6454SAndrew Thompson case 2: 324da6c1373SAndrew Thompson dval = hid_get_byte(s, 1); 325da6c1373SAndrew Thompson dval |= hid_get_byte(s, 1) << 8; 32602ac6454SAndrew Thompson dval = (int16_t)dval; 327da6c1373SAndrew Thompson mask = 0xFFFF; 32802ac6454SAndrew Thompson break; 32902ac6454SAndrew Thompson case 4: 330da6c1373SAndrew Thompson dval = hid_get_byte(s, 1); 331da6c1373SAndrew Thompson dval |= hid_get_byte(s, 1) << 8; 332da6c1373SAndrew Thompson dval |= hid_get_byte(s, 1) << 16; 333da6c1373SAndrew Thompson dval |= hid_get_byte(s, 1) << 24; 334da6c1373SAndrew Thompson mask = 0xFFFFFFFF; 33502ac6454SAndrew Thompson break; 33602ac6454SAndrew Thompson default: 337da6c1373SAndrew Thompson dval = hid_get_byte(s, bSize); 338da6c1373SAndrew Thompson DPRINTFN(0, "bad length %u (data=0x%02x)\n", 339da6c1373SAndrew Thompson bSize, dval); 34002ac6454SAndrew Thompson continue; 34102ac6454SAndrew Thompson } 34202ac6454SAndrew Thompson 34302ac6454SAndrew Thompson switch (bType) { 34402ac6454SAndrew Thompson case 0: /* Main */ 34502ac6454SAndrew Thompson switch (bTag) { 34602ac6454SAndrew Thompson case 8: /* Input */ 34702ac6454SAndrew Thompson c->kind = hid_input; 34802ac6454SAndrew Thompson c->flags = dval; 34902ac6454SAndrew Thompson ret: 35046364b29SAndrew Thompson c->loc.count = s->loc_count; 35146364b29SAndrew Thompson c->loc.size = s->loc_size; 35246364b29SAndrew Thompson 35302ac6454SAndrew Thompson if (c->flags & HIO_VARIABLE) { 354da6c1373SAndrew Thompson /* range check usage count */ 355da6c1373SAndrew Thompson if (c->loc.count > 255) { 356da6c1373SAndrew Thompson DPRINTFN(0, "Number of " 357ecc7ce0fSHans Petter Selasky "items(%u) truncated to 255\n", 358ecc7ce0fSHans Petter Selasky (unsigned)(c->loc.count)); 359da6c1373SAndrew Thompson s->ncount = 255; 360da6c1373SAndrew Thompson } else 361da6c1373SAndrew Thompson s->ncount = c->loc.count; 362da6c1373SAndrew Thompson 363da6c1373SAndrew Thompson /* 364da6c1373SAndrew Thompson * The "top" loop will return 365da6c1373SAndrew Thompson * one and one item: 366da6c1373SAndrew Thompson */ 36702ac6454SAndrew Thompson c->loc.count = 1; 368da6c1373SAndrew Thompson } else { 369b1e14b53SAndrew Thompson s->ncount = 1; 370b1e14b53SAndrew Thompson } 37102ac6454SAndrew Thompson goto top; 372da6c1373SAndrew Thompson 37302ac6454SAndrew Thompson case 9: /* Output */ 37402ac6454SAndrew Thompson c->kind = hid_output; 37502ac6454SAndrew Thompson c->flags = dval; 37602ac6454SAndrew Thompson goto ret; 37702ac6454SAndrew Thompson case 10: /* Collection */ 37802ac6454SAndrew Thompson c->kind = hid_collection; 37902ac6454SAndrew Thompson c->collection = dval; 38002ac6454SAndrew Thompson c->collevel++; 381ecf65ed4SAndrew Thompson c->usage = s->usage_last; 38202ac6454SAndrew Thompson *h = *c; 38302ac6454SAndrew Thompson return (1); 38402ac6454SAndrew Thompson case 11: /* Feature */ 38502ac6454SAndrew Thompson c->kind = hid_feature; 38602ac6454SAndrew Thompson c->flags = dval; 38702ac6454SAndrew Thompson goto ret; 38802ac6454SAndrew Thompson case 12: /* End collection */ 38902ac6454SAndrew Thompson c->kind = hid_endcollection; 390da6c1373SAndrew Thompson if (c->collevel == 0) { 391da6c1373SAndrew Thompson DPRINTFN(0, "invalid end collection\n"); 392da6c1373SAndrew Thompson return (0); 393da6c1373SAndrew Thompson } 39402ac6454SAndrew Thompson c->collevel--; 39502ac6454SAndrew Thompson *h = *c; 39602ac6454SAndrew Thompson return (1); 39702ac6454SAndrew Thompson default: 398da6c1373SAndrew Thompson DPRINTFN(0, "Main bTag=%d\n", bTag); 39902ac6454SAndrew Thompson break; 40002ac6454SAndrew Thompson } 40102ac6454SAndrew Thompson break; 40202ac6454SAndrew Thompson case 1: /* Global */ 40302ac6454SAndrew Thompson switch (bTag) { 40402ac6454SAndrew Thompson case 0: 40502ac6454SAndrew Thompson c->_usage_page = dval << 16; 40602ac6454SAndrew Thompson break; 40702ac6454SAndrew Thompson case 1: 40802ac6454SAndrew Thompson c->logical_minimum = dval; 40902ac6454SAndrew Thompson break; 41002ac6454SAndrew Thompson case 2: 41102ac6454SAndrew Thompson c->logical_maximum = dval; 41202ac6454SAndrew Thompson break; 41302ac6454SAndrew Thompson case 3: 41402ac6454SAndrew Thompson c->physical_minimum = dval; 41502ac6454SAndrew Thompson break; 41602ac6454SAndrew Thompson case 4: 41702ac6454SAndrew Thompson c->physical_maximum = dval; 41802ac6454SAndrew Thompson break; 41902ac6454SAndrew Thompson case 5: 42002ac6454SAndrew Thompson c->unit_exponent = dval; 42102ac6454SAndrew Thompson break; 42202ac6454SAndrew Thompson case 6: 42302ac6454SAndrew Thompson c->unit = dval; 42402ac6454SAndrew Thompson break; 42502ac6454SAndrew Thompson case 7: 42646364b29SAndrew Thompson /* mask because value is unsigned */ 42746364b29SAndrew Thompson s->loc_size = dval & mask; 42802ac6454SAndrew Thompson break; 42902ac6454SAndrew Thompson case 8: 430cff79d9eSAlexander Motin hid_switch_rid(s, c, dval & mask); 43102ac6454SAndrew Thompson break; 43202ac6454SAndrew Thompson case 9: 43346364b29SAndrew Thompson /* mask because value is unsigned */ 43446364b29SAndrew Thompson s->loc_count = dval & mask; 43502ac6454SAndrew Thompson break; 43602ac6454SAndrew Thompson case 10: /* Push */ 437da6c1373SAndrew Thompson s->pushlevel ++; 438da6c1373SAndrew Thompson if (s->pushlevel < MAXPUSH) { 439da6c1373SAndrew Thompson s->cur[s->pushlevel] = *c; 44046364b29SAndrew Thompson /* store size and count */ 44146364b29SAndrew Thompson c->loc.size = s->loc_size; 44246364b29SAndrew Thompson c->loc.count = s->loc_count; 44346364b29SAndrew Thompson /* update current item pointer */ 444da6c1373SAndrew Thompson c = &s->cur[s->pushlevel]; 445da6c1373SAndrew Thompson } else { 446da6c1373SAndrew Thompson DPRINTFN(0, "Cannot push " 447767cb2e2SAndrew Thompson "item @ %d\n", s->pushlevel); 448da6c1373SAndrew Thompson } 44902ac6454SAndrew Thompson break; 45002ac6454SAndrew Thompson case 11: /* Pop */ 451da6c1373SAndrew Thompson s->pushlevel --; 452da6c1373SAndrew Thompson if (s->pushlevel < MAXPUSH) { 453da6c1373SAndrew Thompson /* preserve position */ 45402ac6454SAndrew Thompson oldpos = c->loc.pos; 455da6c1373SAndrew Thompson c = &s->cur[s->pushlevel]; 45646364b29SAndrew Thompson /* restore size and count */ 45746364b29SAndrew Thompson s->loc_size = c->loc.size; 45846364b29SAndrew Thompson s->loc_count = c->loc.count; 45946364b29SAndrew Thompson /* set default item location */ 46002ac6454SAndrew Thompson c->loc.pos = oldpos; 46146364b29SAndrew Thompson c->loc.size = 0; 46246364b29SAndrew Thompson c->loc.count = 0; 463da6c1373SAndrew Thompson } else { 464da6c1373SAndrew Thompson DPRINTFN(0, "Cannot pop " 465767cb2e2SAndrew Thompson "item @ %d\n", s->pushlevel); 466da6c1373SAndrew Thompson } 46702ac6454SAndrew Thompson break; 46802ac6454SAndrew Thompson default: 469da6c1373SAndrew Thompson DPRINTFN(0, "Global bTag=%d\n", bTag); 47002ac6454SAndrew Thompson break; 47102ac6454SAndrew Thompson } 47202ac6454SAndrew Thompson break; 47302ac6454SAndrew Thompson case 2: /* Local */ 47402ac6454SAndrew Thompson switch (bTag) { 47502ac6454SAndrew Thompson case 0: 476da6c1373SAndrew Thompson if (bSize != 4) 477da6c1373SAndrew Thompson dval = (dval & mask) | c->_usage_page; 478da6c1373SAndrew Thompson 479ecf65ed4SAndrew Thompson /* set last usage, in case of a collection */ 480ecf65ed4SAndrew Thompson s->usage_last = dval; 481ecf65ed4SAndrew Thompson 482da6c1373SAndrew Thompson if (s->nusage < MAXUSAGE) { 483da6c1373SAndrew Thompson s->usages_min[s->nusage] = dval; 484da6c1373SAndrew Thompson s->usages_max[s->nusage] = dval; 485da6c1373SAndrew Thompson s->nusage ++; 486da6c1373SAndrew Thompson } else { 487767cb2e2SAndrew Thompson DPRINTFN(0, "max usage reached\n"); 488da6c1373SAndrew Thompson } 489da6c1373SAndrew Thompson 490da6c1373SAndrew Thompson /* clear any pending usage sets */ 491da6c1373SAndrew Thompson s->susage = 0; 49202ac6454SAndrew Thompson break; 49302ac6454SAndrew Thompson case 1: 494da6c1373SAndrew Thompson s->susage |= 1; 495da6c1373SAndrew Thompson 496da6c1373SAndrew Thompson if (bSize != 4) 497da6c1373SAndrew Thompson dval = (dval & mask) | c->_usage_page; 49802ac6454SAndrew Thompson c->usage_minimum = dval; 499da6c1373SAndrew Thompson 500da6c1373SAndrew Thompson goto check_set; 50102ac6454SAndrew Thompson case 2: 502da6c1373SAndrew Thompson s->susage |= 2; 503da6c1373SAndrew Thompson 504da6c1373SAndrew Thompson if (bSize != 4) 505da6c1373SAndrew Thompson dval = (dval & mask) | c->_usage_page; 50602ac6454SAndrew Thompson c->usage_maximum = dval; 507da6c1373SAndrew Thompson 508da6c1373SAndrew Thompson check_set: 509da6c1373SAndrew Thompson if (s->susage != 3) 510da6c1373SAndrew Thompson break; 511da6c1373SAndrew Thompson 512da6c1373SAndrew Thompson /* sanity check */ 513da6c1373SAndrew Thompson if ((s->nusage < MAXUSAGE) && 5143f67dc0fSAndrew Thompson (c->usage_minimum <= c->usage_maximum)) { 515da6c1373SAndrew Thompson /* add usage range */ 516da6c1373SAndrew Thompson s->usages_min[s->nusage] = 517da6c1373SAndrew Thompson c->usage_minimum; 518da6c1373SAndrew Thompson s->usages_max[s->nusage] = 519da6c1373SAndrew Thompson c->usage_maximum; 520da6c1373SAndrew Thompson s->nusage ++; 521da6c1373SAndrew Thompson } else { 522767cb2e2SAndrew Thompson DPRINTFN(0, "Usage set dropped\n"); 523da6c1373SAndrew Thompson } 524da6c1373SAndrew Thompson s->susage = 0; 52502ac6454SAndrew Thompson break; 52602ac6454SAndrew Thompson case 3: 52702ac6454SAndrew Thompson c->designator_index = dval; 52802ac6454SAndrew Thompson break; 52902ac6454SAndrew Thompson case 4: 53002ac6454SAndrew Thompson c->designator_minimum = dval; 53102ac6454SAndrew Thompson break; 53202ac6454SAndrew Thompson case 5: 53302ac6454SAndrew Thompson c->designator_maximum = dval; 53402ac6454SAndrew Thompson break; 53502ac6454SAndrew Thompson case 7: 53602ac6454SAndrew Thompson c->string_index = dval; 53702ac6454SAndrew Thompson break; 53802ac6454SAndrew Thompson case 8: 53902ac6454SAndrew Thompson c->string_minimum = dval; 54002ac6454SAndrew Thompson break; 54102ac6454SAndrew Thompson case 9: 54202ac6454SAndrew Thompson c->string_maximum = dval; 54302ac6454SAndrew Thompson break; 54402ac6454SAndrew Thompson case 10: 54502ac6454SAndrew Thompson c->set_delimiter = dval; 54602ac6454SAndrew Thompson break; 54702ac6454SAndrew Thompson default: 548da6c1373SAndrew Thompson DPRINTFN(0, "Local bTag=%d\n", bTag); 54902ac6454SAndrew Thompson break; 55002ac6454SAndrew Thompson } 55102ac6454SAndrew Thompson break; 55202ac6454SAndrew Thompson default: 553da6c1373SAndrew Thompson DPRINTFN(0, "default bType=%d\n", bType); 55402ac6454SAndrew Thompson break; 55502ac6454SAndrew Thompson } 55602ac6454SAndrew Thompson } 557da6c1373SAndrew Thompson return (0); 55802ac6454SAndrew Thompson } 55902ac6454SAndrew Thompson 56002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 56102ac6454SAndrew Thompson * hid_report_size 56202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 56302ac6454SAndrew Thompson int 564f9cb546cSAndrew Thompson hid_report_size(const void *buf, usb_size_t len, enum hid_kind k, uint8_t *id) 56502ac6454SAndrew Thompson { 56602ac6454SAndrew Thompson struct hid_data *d; 56702ac6454SAndrew Thompson struct hid_item h; 5683e5e312aSAndrew Thompson uint32_t temp; 5693e5e312aSAndrew Thompson uint32_t hpos; 5703e5e312aSAndrew Thompson uint32_t lpos; 5713e5e312aSAndrew Thompson uint8_t any_id; 57202ac6454SAndrew Thompson 5733e5e312aSAndrew Thompson any_id = 0; 5743e5e312aSAndrew Thompson hpos = 0; 5753e5e312aSAndrew Thompson lpos = 0xFFFFFFFF; 5763e5e312aSAndrew Thompson 5773e5e312aSAndrew Thompson for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);) { 57802ac6454SAndrew Thompson if (h.kind == k) { 5793e5e312aSAndrew Thompson /* check for ID-byte presense */ 5803e5e312aSAndrew Thompson if ((h.report_ID != 0) && !any_id) { 5813e5e312aSAndrew Thompson if (id != NULL) 5823e5e312aSAndrew Thompson *id = h.report_ID; 5833e5e312aSAndrew Thompson any_id = 1; 5843e5e312aSAndrew Thompson } 5853e5e312aSAndrew Thompson /* compute minimum */ 5863e5e312aSAndrew Thompson if (lpos > h.loc.pos) 5873e5e312aSAndrew Thompson lpos = h.loc.pos; 5883e5e312aSAndrew Thompson /* compute end position */ 5893e5e312aSAndrew Thompson temp = h.loc.pos + (h.loc.size * h.loc.count); 5903e5e312aSAndrew Thompson /* compute maximum */ 5913e5e312aSAndrew Thompson if (hpos < temp) 5923e5e312aSAndrew Thompson hpos = temp; 59302ac6454SAndrew Thompson } 59402ac6454SAndrew Thompson } 59502ac6454SAndrew Thompson hid_end_parse(d); 5963e5e312aSAndrew Thompson 5973e5e312aSAndrew Thompson /* safety check - can happen in case of currupt descriptors */ 5983e5e312aSAndrew Thompson if (lpos > hpos) 5993e5e312aSAndrew Thompson temp = 0; 6003e5e312aSAndrew Thompson else 6013e5e312aSAndrew Thompson temp = hpos - lpos; 6023e5e312aSAndrew Thompson 6033e5e312aSAndrew Thompson /* check for ID byte */ 6043e5e312aSAndrew Thompson if (any_id) 6053e5e312aSAndrew Thompson temp += 8; 6063e5e312aSAndrew Thompson else if (id != NULL) 6073e5e312aSAndrew Thompson *id = 0; 6083e5e312aSAndrew Thompson 6093e5e312aSAndrew Thompson /* return length in bytes rounded up */ 6103e5e312aSAndrew Thompson return ((temp + 7) / 8); 61102ac6454SAndrew Thompson } 61202ac6454SAndrew Thompson 61302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 61402ac6454SAndrew Thompson * hid_locate 61502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 61602ac6454SAndrew Thompson int 6176d917491SHans Petter Selasky hid_locate(const void *desc, usb_size_t size, int32_t u, enum hid_kind k, 618de967835SAndrew Thompson uint8_t index, struct hid_location *loc, uint32_t *flags, uint8_t *id) 61902ac6454SAndrew Thompson { 62002ac6454SAndrew Thompson struct hid_data *d; 62102ac6454SAndrew Thompson struct hid_item h; 62202ac6454SAndrew Thompson 62302ac6454SAndrew Thompson for (d = hid_start_parse(desc, size, 1 << k); hid_get_item(d, &h);) { 62402ac6454SAndrew Thompson if (h.kind == k && !(h.flags & HIO_CONST) && h.usage == u) { 625de967835SAndrew Thompson if (index--) 626de967835SAndrew Thompson continue; 62702ac6454SAndrew Thompson if (loc != NULL) 62802ac6454SAndrew Thompson *loc = h.loc; 62902ac6454SAndrew Thompson if (flags != NULL) 63002ac6454SAndrew Thompson *flags = h.flags; 6313e5e312aSAndrew Thompson if (id != NULL) 6323e5e312aSAndrew Thompson *id = h.report_ID; 63302ac6454SAndrew Thompson hid_end_parse(d); 63402ac6454SAndrew Thompson return (1); 63502ac6454SAndrew Thompson } 63602ac6454SAndrew Thompson } 6373e5e312aSAndrew Thompson if (loc != NULL) 63802ac6454SAndrew Thompson loc->size = 0; 6393e5e312aSAndrew Thompson if (flags != NULL) 6403e5e312aSAndrew Thompson *flags = 0; 6413e5e312aSAndrew Thompson if (id != NULL) 6423e5e312aSAndrew Thompson *id = 0; 6433e5e312aSAndrew Thompson hid_end_parse(d); 64402ac6454SAndrew Thompson return (0); 64502ac6454SAndrew Thompson } 64602ac6454SAndrew Thompson 64702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 64802ac6454SAndrew Thompson * hid_get_data 64902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 6505fdb3a67SAndrew Thompson static uint32_t 6515fdb3a67SAndrew Thompson hid_get_data_sub(const uint8_t *buf, usb_size_t len, struct hid_location *loc, 6525fdb3a67SAndrew Thompson int is_signed) 65302ac6454SAndrew Thompson { 65402ac6454SAndrew Thompson uint32_t hpos = loc->pos; 65502ac6454SAndrew Thompson uint32_t hsize = loc->size; 65602ac6454SAndrew Thompson uint32_t data; 6573e5e312aSAndrew Thompson uint32_t rpos; 6583e5e312aSAndrew Thompson uint8_t n; 65902ac6454SAndrew Thompson 66002ac6454SAndrew Thompson DPRINTFN(11, "hid_get_data: loc %d/%d\n", hpos, hsize); 66102ac6454SAndrew Thompson 6623e5e312aSAndrew Thompson /* Range check and limit */ 66302ac6454SAndrew Thompson if (hsize == 0) 66402ac6454SAndrew Thompson return (0); 6653e5e312aSAndrew Thompson if (hsize > 32) 6663e5e312aSAndrew Thompson hsize = 32; 66702ac6454SAndrew Thompson 6683e5e312aSAndrew Thompson /* Get data in a safe way */ 66902ac6454SAndrew Thompson data = 0; 6703e5e312aSAndrew Thompson rpos = (hpos / 8); 6713e5e312aSAndrew Thompson n = (hsize + 7) / 8; 6723e5e312aSAndrew Thompson rpos += n; 6733e5e312aSAndrew Thompson while (n--) { 6743e5e312aSAndrew Thompson rpos--; 6753e5e312aSAndrew Thompson if (rpos < len) 6763e5e312aSAndrew Thompson data |= buf[rpos] << (8 * n); 67702ac6454SAndrew Thompson } 6783e5e312aSAndrew Thompson 6793e5e312aSAndrew Thompson /* Correctly shift down data */ 6803e5e312aSAndrew Thompson data = (data >> (hpos % 8)); 6815fdb3a67SAndrew Thompson n = 32 - hsize; 6823e5e312aSAndrew Thompson 6833e5e312aSAndrew Thompson /* Mask and sign extend in one */ 6845fdb3a67SAndrew Thompson if (is_signed != 0) 6855fdb3a67SAndrew Thompson data = (int32_t)((int32_t)data << n) >> n; 6865fdb3a67SAndrew Thompson else 6875fdb3a67SAndrew Thompson data = (uint32_t)((uint32_t)data << n) >> n; 6883e5e312aSAndrew Thompson 68902ac6454SAndrew Thompson DPRINTFN(11, "hid_get_data: loc %d/%d = %lu\n", 69002ac6454SAndrew Thompson loc->pos, loc->size, (long)data); 69102ac6454SAndrew Thompson return (data); 69202ac6454SAndrew Thompson } 69302ac6454SAndrew Thompson 6945fdb3a67SAndrew Thompson int32_t 6955fdb3a67SAndrew Thompson hid_get_data(const uint8_t *buf, usb_size_t len, struct hid_location *loc) 6965fdb3a67SAndrew Thompson { 6975fdb3a67SAndrew Thompson return (hid_get_data_sub(buf, len, loc, 1)); 6985fdb3a67SAndrew Thompson } 6995fdb3a67SAndrew Thompson 7005fdb3a67SAndrew Thompson uint32_t 7015fdb3a67SAndrew Thompson hid_get_data_unsigned(const uint8_t *buf, usb_size_t len, struct hid_location *loc) 7025fdb3a67SAndrew Thompson { 7035fdb3a67SAndrew Thompson return (hid_get_data_sub(buf, len, loc, 0)); 7045fdb3a67SAndrew Thompson } 7055fdb3a67SAndrew Thompson 70602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 707e92091adSHans Petter Selasky * hid_put_data 708e92091adSHans Petter Selasky *------------------------------------------------------------------------*/ 709e92091adSHans Petter Selasky void 710e92091adSHans Petter Selasky hid_put_data_unsigned(uint8_t *buf, usb_size_t len, 711e92091adSHans Petter Selasky struct hid_location *loc, unsigned int value) 712e92091adSHans Petter Selasky { 713e92091adSHans Petter Selasky uint32_t hpos = loc->pos; 714e92091adSHans Petter Selasky uint32_t hsize = loc->size; 715e92091adSHans Petter Selasky uint64_t data; 716e92091adSHans Petter Selasky uint64_t mask; 717e92091adSHans Petter Selasky uint32_t rpos; 718e92091adSHans Petter Selasky uint8_t n; 719e92091adSHans Petter Selasky 720e92091adSHans Petter Selasky DPRINTFN(11, "hid_put_data: loc %d/%d = %u\n", hpos, hsize, value); 721e92091adSHans Petter Selasky 722e92091adSHans Petter Selasky /* Range check and limit */ 723e92091adSHans Petter Selasky if (hsize == 0) 724e92091adSHans Petter Selasky return; 725e92091adSHans Petter Selasky if (hsize > 32) 726e92091adSHans Petter Selasky hsize = 32; 727e92091adSHans Petter Selasky 728e92091adSHans Petter Selasky /* Put data in a safe way */ 729e92091adSHans Petter Selasky rpos = (hpos / 8); 730e92091adSHans Petter Selasky n = (hsize + 7) / 8; 731e92091adSHans Petter Selasky data = ((uint64_t)value) << (hpos % 8); 732e92091adSHans Petter Selasky mask = ((1ULL << hsize) - 1ULL) << (hpos % 8); 733e92091adSHans Petter Selasky rpos += n; 734e92091adSHans Petter Selasky while (n--) { 735e92091adSHans Petter Selasky rpos--; 736e92091adSHans Petter Selasky if (rpos < len) { 737e92091adSHans Petter Selasky buf[rpos] &= ~(mask >> (8 * n)); 738e92091adSHans Petter Selasky buf[rpos] |= (data >> (8 * n)); 739e92091adSHans Petter Selasky } 740e92091adSHans Petter Selasky } 741e92091adSHans Petter Selasky } 742e92091adSHans Petter Selasky 743e92091adSHans Petter Selasky /*------------------------------------------------------------------------* 74402ac6454SAndrew Thompson * hid_is_collection 74502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 74602ac6454SAndrew Thompson int 7476d917491SHans Petter Selasky hid_is_collection(const void *desc, usb_size_t size, int32_t usage) 74802ac6454SAndrew Thompson { 74902ac6454SAndrew Thompson struct hid_data *hd; 75002ac6454SAndrew Thompson struct hid_item hi; 75102ac6454SAndrew Thompson int err; 75202ac6454SAndrew Thompson 75302ac6454SAndrew Thompson hd = hid_start_parse(desc, size, hid_input); 75402ac6454SAndrew Thompson if (hd == NULL) 75502ac6454SAndrew Thompson return (0); 75602ac6454SAndrew Thompson 757ecf65ed4SAndrew Thompson while ((err = hid_get_item(hd, &hi))) { 758ecf65ed4SAndrew Thompson if (hi.kind == hid_collection && 759ecf65ed4SAndrew Thompson hi.usage == usage) 760ecf65ed4SAndrew Thompson break; 761ecf65ed4SAndrew Thompson } 76202ac6454SAndrew Thompson hid_end_parse(hd); 76302ac6454SAndrew Thompson return (err); 76402ac6454SAndrew Thompson } 76502ac6454SAndrew Thompson 76602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 76702ac6454SAndrew Thompson * hid_get_descriptor_from_usb 76802ac6454SAndrew Thompson * 76902ac6454SAndrew Thompson * This function will search for a HID descriptor between two USB 77002ac6454SAndrew Thompson * interface descriptors. 77102ac6454SAndrew Thompson * 77202ac6454SAndrew Thompson * Return values: 77302ac6454SAndrew Thompson * NULL: No more HID descriptors. 77402ac6454SAndrew Thompson * Else: Pointer to HID descriptor. 77502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 776760bc48eSAndrew Thompson struct usb_hid_descriptor * 777760bc48eSAndrew Thompson hid_get_descriptor_from_usb(struct usb_config_descriptor *cd, 778760bc48eSAndrew Thompson struct usb_interface_descriptor *id) 77902ac6454SAndrew Thompson { 780760bc48eSAndrew Thompson struct usb_descriptor *desc = (void *)id; 78102ac6454SAndrew Thompson 78202ac6454SAndrew Thompson if (desc == NULL) { 78302ac6454SAndrew Thompson return (NULL); 78402ac6454SAndrew Thompson } 785a593f6b8SAndrew Thompson while ((desc = usb_desc_foreach(cd, desc))) { 78602ac6454SAndrew Thompson if ((desc->bDescriptorType == UDESC_HID) && 78702ac6454SAndrew Thompson (desc->bLength >= USB_HID_DESCRIPTOR_SIZE(0))) { 78802ac6454SAndrew Thompson return (void *)desc; 78902ac6454SAndrew Thompson } 79002ac6454SAndrew Thompson if (desc->bDescriptorType == UDESC_INTERFACE) { 79102ac6454SAndrew Thompson break; 79202ac6454SAndrew Thompson } 79302ac6454SAndrew Thompson } 79402ac6454SAndrew Thompson return (NULL); 79502ac6454SAndrew Thompson } 79602ac6454SAndrew Thompson 79702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 798a593f6b8SAndrew Thompson * usbd_req_get_hid_desc 79902ac6454SAndrew Thompson * 80002ac6454SAndrew Thompson * This function will read out an USB report descriptor from the USB 80102ac6454SAndrew Thompson * device. 80202ac6454SAndrew Thompson * 80302ac6454SAndrew Thompson * Return values: 80402ac6454SAndrew Thompson * NULL: Failure. 80502ac6454SAndrew Thompson * Else: Success. The pointer should eventually be passed to free(). 80602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 807e0a69b51SAndrew Thompson usb_error_t 808a593f6b8SAndrew Thompson usbd_req_get_hid_desc(struct usb_device *udev, struct mtx *mtx, 80902ac6454SAndrew Thompson void **descp, uint16_t *sizep, 810e0a69b51SAndrew Thompson struct malloc_type *mem, uint8_t iface_index) 81102ac6454SAndrew Thompson { 812a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index); 813760bc48eSAndrew Thompson struct usb_hid_descriptor *hid; 814e0a69b51SAndrew Thompson usb_error_t err; 81502ac6454SAndrew Thompson 81602ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 81702ac6454SAndrew Thompson return (USB_ERR_INVAL); 81802ac6454SAndrew Thompson } 81902ac6454SAndrew Thompson hid = hid_get_descriptor_from_usb 820a593f6b8SAndrew Thompson (usbd_get_config_descriptor(udev), iface->idesc); 82102ac6454SAndrew Thompson 82202ac6454SAndrew Thompson if (hid == NULL) { 82302ac6454SAndrew Thompson return (USB_ERR_IOERROR); 82402ac6454SAndrew Thompson } 82502ac6454SAndrew Thompson *sizep = UGETW(hid->descrs[0].wDescriptorLength); 82602ac6454SAndrew Thompson if (*sizep == 0) { 82702ac6454SAndrew Thompson return (USB_ERR_IOERROR); 82802ac6454SAndrew Thompson } 82902ac6454SAndrew Thompson if (mtx) 83002ac6454SAndrew Thompson mtx_unlock(mtx); 83102ac6454SAndrew Thompson 83202ac6454SAndrew Thompson *descp = malloc(*sizep, mem, M_ZERO | M_WAITOK); 83302ac6454SAndrew Thompson 83402ac6454SAndrew Thompson if (mtx) 83502ac6454SAndrew Thompson mtx_lock(mtx); 83602ac6454SAndrew Thompson 83702ac6454SAndrew Thompson if (*descp == NULL) { 83802ac6454SAndrew Thompson return (USB_ERR_NOMEM); 83902ac6454SAndrew Thompson } 840a593f6b8SAndrew Thompson err = usbd_req_get_report_descriptor 84102ac6454SAndrew Thompson (udev, mtx, *descp, *sizep, iface_index); 84202ac6454SAndrew Thompson 84302ac6454SAndrew Thompson if (err) { 84402ac6454SAndrew Thompson free(*descp, mem); 84502ac6454SAndrew Thompson *descp = NULL; 84602ac6454SAndrew Thompson return (err); 84702ac6454SAndrew Thompson } 84802ac6454SAndrew Thompson return (USB_ERR_NORMAL_COMPLETION); 84902ac6454SAndrew Thompson } 85060867f57SHans Petter Selasky 85160867f57SHans Petter Selasky /*------------------------------------------------------------------------* 85260867f57SHans Petter Selasky * hid_is_mouse 85360867f57SHans Petter Selasky * 85460867f57SHans Petter Selasky * This function will decide if a USB descriptor belongs to a USB mouse. 85560867f57SHans Petter Selasky * 85660867f57SHans Petter Selasky * Return values: 85760867f57SHans Petter Selasky * Zero: Not a USB mouse. 85860867f57SHans Petter Selasky * Else: Is a USB mouse. 85960867f57SHans Petter Selasky *------------------------------------------------------------------------*/ 86060867f57SHans Petter Selasky int 86160867f57SHans Petter Selasky hid_is_mouse(const void *d_ptr, uint16_t d_len) 86260867f57SHans Petter Selasky { 86360867f57SHans Petter Selasky struct hid_data *hd; 86460867f57SHans Petter Selasky struct hid_item hi; 86560867f57SHans Petter Selasky int mdepth; 86660867f57SHans Petter Selasky int found; 86760867f57SHans Petter Selasky 86860867f57SHans Petter Selasky hd = hid_start_parse(d_ptr, d_len, 1 << hid_input); 86960867f57SHans Petter Selasky if (hd == NULL) 87060867f57SHans Petter Selasky return (0); 87160867f57SHans Petter Selasky 87260867f57SHans Petter Selasky mdepth = 0; 87360867f57SHans Petter Selasky found = 0; 87460867f57SHans Petter Selasky 87560867f57SHans Petter Selasky while (hid_get_item(hd, &hi)) { 87660867f57SHans Petter Selasky switch (hi.kind) { 87760867f57SHans Petter Selasky case hid_collection: 87860867f57SHans Petter Selasky if (mdepth != 0) 87960867f57SHans Petter Selasky mdepth++; 88060867f57SHans Petter Selasky else if (hi.collection == 1 && 88160867f57SHans Petter Selasky hi.usage == 88260867f57SHans Petter Selasky HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)) 88360867f57SHans Petter Selasky mdepth++; 88460867f57SHans Petter Selasky break; 88560867f57SHans Petter Selasky case hid_endcollection: 88660867f57SHans Petter Selasky if (mdepth != 0) 88760867f57SHans Petter Selasky mdepth--; 88860867f57SHans Petter Selasky break; 88960867f57SHans Petter Selasky case hid_input: 89060867f57SHans Petter Selasky if (mdepth == 0) 89160867f57SHans Petter Selasky break; 89260867f57SHans Petter Selasky if (hi.usage == 89360867f57SHans Petter Selasky HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X) && 89460867f57SHans Petter Selasky (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE) 89560867f57SHans Petter Selasky found++; 89660867f57SHans Petter Selasky if (hi.usage == 89760867f57SHans Petter Selasky HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y) && 89860867f57SHans Petter Selasky (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE) 89960867f57SHans Petter Selasky found++; 90060867f57SHans Petter Selasky break; 90160867f57SHans Petter Selasky default: 90260867f57SHans Petter Selasky break; 90360867f57SHans Petter Selasky } 90460867f57SHans Petter Selasky } 90560867f57SHans Petter Selasky hid_end_parse(hd); 90660867f57SHans Petter Selasky return (found); 90760867f57SHans Petter Selasky } 90860867f57SHans Petter Selasky 90960867f57SHans Petter Selasky /*------------------------------------------------------------------------* 91060867f57SHans Petter Selasky * hid_is_keyboard 91160867f57SHans Petter Selasky * 91260867f57SHans Petter Selasky * This function will decide if a USB descriptor belongs to a USB keyboard. 91360867f57SHans Petter Selasky * 91460867f57SHans Petter Selasky * Return values: 91560867f57SHans Petter Selasky * Zero: Not a USB keyboard. 91660867f57SHans Petter Selasky * Else: Is a USB keyboard. 91760867f57SHans Petter Selasky *------------------------------------------------------------------------*/ 91860867f57SHans Petter Selasky int 91960867f57SHans Petter Selasky hid_is_keyboard(const void *d_ptr, uint16_t d_len) 92060867f57SHans Petter Selasky { 92160867f57SHans Petter Selasky if (hid_is_collection(d_ptr, d_len, 92260867f57SHans Petter Selasky HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD))) 92360867f57SHans Petter Selasky return (1); 92460867f57SHans Petter Selasky return (0); 92560867f57SHans Petter Selasky } 926