11ffa5c63SVladimir Kondratyev /* $FreeBSD$ */ 21ffa5c63SVladimir Kondratyev /* $NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem Exp $ */ 31ffa5c63SVladimir Kondratyev /*- 41ffa5c63SVladimir Kondratyev * SPDX-License-Identifier: BSD-2-Clause-NetBSD 51ffa5c63SVladimir Kondratyev * 61ffa5c63SVladimir Kondratyev * Copyright (c) 1998 The NetBSD Foundation, Inc. 71ffa5c63SVladimir Kondratyev * All rights reserved. 81ffa5c63SVladimir Kondratyev * 91ffa5c63SVladimir Kondratyev * This code is derived from software contributed to The NetBSD Foundation 101ffa5c63SVladimir Kondratyev * by Lennart Augustsson (lennart@augustsson.net) at 111ffa5c63SVladimir Kondratyev * Carlstedt Research & Technology. 121ffa5c63SVladimir Kondratyev * 131ffa5c63SVladimir Kondratyev * Redistribution and use in source and binary forms, with or without 141ffa5c63SVladimir Kondratyev * modification, are permitted provided that the following conditions 151ffa5c63SVladimir Kondratyev * are met: 161ffa5c63SVladimir Kondratyev * 1. Redistributions of source code must retain the above copyright 171ffa5c63SVladimir Kondratyev * notice, this list of conditions and the following disclaimer. 181ffa5c63SVladimir Kondratyev * 2. Redistributions in binary form must reproduce the above copyright 191ffa5c63SVladimir Kondratyev * notice, this list of conditions and the following disclaimer in the 201ffa5c63SVladimir Kondratyev * documentation and/or other materials provided with the distribution. 211ffa5c63SVladimir Kondratyev * 221ffa5c63SVladimir Kondratyev * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 231ffa5c63SVladimir Kondratyev * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 241ffa5c63SVladimir Kondratyev * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 251ffa5c63SVladimir Kondratyev * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 261ffa5c63SVladimir Kondratyev * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 271ffa5c63SVladimir Kondratyev * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 281ffa5c63SVladimir Kondratyev * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 291ffa5c63SVladimir Kondratyev * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 301ffa5c63SVladimir Kondratyev * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 311ffa5c63SVladimir Kondratyev * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 321ffa5c63SVladimir Kondratyev * POSSIBILITY OF SUCH DAMAGE. 331ffa5c63SVladimir Kondratyev */ 341ffa5c63SVladimir Kondratyev 351ffa5c63SVladimir Kondratyev #ifdef USB_GLOBAL_INCLUDE_FILE 361ffa5c63SVladimir Kondratyev #include USB_GLOBAL_INCLUDE_FILE 371ffa5c63SVladimir Kondratyev #else 381ffa5c63SVladimir Kondratyev #include <sys/stdint.h> 391ffa5c63SVladimir Kondratyev #include <sys/stddef.h> 401ffa5c63SVladimir Kondratyev #include <sys/param.h> 411ffa5c63SVladimir Kondratyev #include <sys/queue.h> 421ffa5c63SVladimir Kondratyev #include <sys/types.h> 431ffa5c63SVladimir Kondratyev #include <sys/systm.h> 441ffa5c63SVladimir Kondratyev #include <sys/kernel.h> 451ffa5c63SVladimir Kondratyev #include <sys/bus.h> 461ffa5c63SVladimir Kondratyev #include <sys/module.h> 471ffa5c63SVladimir Kondratyev #include <sys/lock.h> 481ffa5c63SVladimir Kondratyev #include <sys/mutex.h> 491ffa5c63SVladimir Kondratyev #include <sys/condvar.h> 501ffa5c63SVladimir Kondratyev #include <sys/sysctl.h> 511ffa5c63SVladimir Kondratyev #include <sys/sx.h> 521ffa5c63SVladimir Kondratyev #include <sys/unistd.h> 531ffa5c63SVladimir Kondratyev #include <sys/callout.h> 541ffa5c63SVladimir Kondratyev #include <sys/malloc.h> 551ffa5c63SVladimir Kondratyev #include <sys/priv.h> 561ffa5c63SVladimir Kondratyev 571ffa5c63SVladimir Kondratyev #include <dev/usb/usb.h> 581ffa5c63SVladimir Kondratyev #include <dev/usb/usbdi.h> 591ffa5c63SVladimir Kondratyev #include <dev/usb/usbdi_util.h> 601ffa5c63SVladimir Kondratyev #include <dev/usb/usbhid.h> 611ffa5c63SVladimir Kondratyev 621ffa5c63SVladimir Kondratyev #define USB_DEBUG_VAR usb_debug 631ffa5c63SVladimir Kondratyev 641ffa5c63SVladimir Kondratyev #include <dev/usb/usb_core.h> 651ffa5c63SVladimir Kondratyev #include <dev/usb/usb_debug.h> 661ffa5c63SVladimir Kondratyev #include <dev/usb/usb_process.h> 671ffa5c63SVladimir Kondratyev #include <dev/usb/usb_device.h> 681ffa5c63SVladimir Kondratyev #include <dev/usb/usb_request.h> 691ffa5c63SVladimir Kondratyev #endif /* USB_GLOBAL_INCLUDE_FILE */ 701ffa5c63SVladimir Kondratyev 711ffa5c63SVladimir Kondratyev static void hid_clear_local(struct hid_item *); 721ffa5c63SVladimir Kondratyev static uint8_t hid_get_byte(struct hid_data *s, const uint16_t wSize); 731ffa5c63SVladimir Kondratyev 741ffa5c63SVladimir Kondratyev #define MAXUSAGE 64 751ffa5c63SVladimir Kondratyev #define MAXPUSH 4 761ffa5c63SVladimir Kondratyev #define MAXID 16 771ffa5c63SVladimir Kondratyev #define MAXLOCCNT 2048 781ffa5c63SVladimir Kondratyev 791ffa5c63SVladimir Kondratyev struct hid_pos_data { 801ffa5c63SVladimir Kondratyev int32_t rid; 811ffa5c63SVladimir Kondratyev uint32_t pos; 821ffa5c63SVladimir Kondratyev }; 831ffa5c63SVladimir Kondratyev 841ffa5c63SVladimir Kondratyev struct hid_data { 851ffa5c63SVladimir Kondratyev const uint8_t *start; 861ffa5c63SVladimir Kondratyev const uint8_t *end; 871ffa5c63SVladimir Kondratyev const uint8_t *p; 881ffa5c63SVladimir Kondratyev struct hid_item cur[MAXPUSH]; 891ffa5c63SVladimir Kondratyev struct hid_pos_data last_pos[MAXID]; 901ffa5c63SVladimir Kondratyev int32_t usages_min[MAXUSAGE]; 911ffa5c63SVladimir Kondratyev int32_t usages_max[MAXUSAGE]; 921ffa5c63SVladimir Kondratyev int32_t usage_last; /* last seen usage */ 931ffa5c63SVladimir Kondratyev uint32_t loc_size; /* last seen size */ 941ffa5c63SVladimir Kondratyev uint32_t loc_count; /* last seen count */ 951ffa5c63SVladimir Kondratyev uint32_t ncount; /* end usage item count */ 961ffa5c63SVladimir Kondratyev uint32_t icount; /* current usage item count */ 971ffa5c63SVladimir Kondratyev uint8_t kindset; /* we have 5 kinds so 8 bits are enough */ 981ffa5c63SVladimir Kondratyev uint8_t pushlevel; /* current pushlevel */ 991ffa5c63SVladimir Kondratyev uint8_t nusage; /* end "usages_min/max" index */ 1001ffa5c63SVladimir Kondratyev uint8_t iusage; /* current "usages_min/max" index */ 1011ffa5c63SVladimir Kondratyev uint8_t ousage; /* current "usages_min/max" offset */ 1021ffa5c63SVladimir Kondratyev uint8_t susage; /* usage set flags */ 1031ffa5c63SVladimir Kondratyev }; 1041ffa5c63SVladimir Kondratyev 1051ffa5c63SVladimir Kondratyev /*------------------------------------------------------------------------* 1061ffa5c63SVladimir Kondratyev * hid_clear_local 1071ffa5c63SVladimir Kondratyev *------------------------------------------------------------------------*/ 1081ffa5c63SVladimir Kondratyev static void 1091ffa5c63SVladimir Kondratyev hid_clear_local(struct hid_item *c) 1101ffa5c63SVladimir Kondratyev { 1111ffa5c63SVladimir Kondratyev 1121ffa5c63SVladimir Kondratyev c->loc.count = 0; 1131ffa5c63SVladimir Kondratyev c->loc.size = 0; 1141ffa5c63SVladimir Kondratyev c->nusages = 0; 1151ffa5c63SVladimir Kondratyev memset(c->usages, 0, sizeof(c->usages)); 1161ffa5c63SVladimir Kondratyev c->usage_minimum = 0; 1171ffa5c63SVladimir Kondratyev c->usage_maximum = 0; 1181ffa5c63SVladimir Kondratyev c->designator_index = 0; 1191ffa5c63SVladimir Kondratyev c->designator_minimum = 0; 1201ffa5c63SVladimir Kondratyev c->designator_maximum = 0; 1211ffa5c63SVladimir Kondratyev c->string_index = 0; 1221ffa5c63SVladimir Kondratyev c->string_minimum = 0; 1231ffa5c63SVladimir Kondratyev c->string_maximum = 0; 1241ffa5c63SVladimir Kondratyev c->set_delimiter = 0; 1251ffa5c63SVladimir Kondratyev } 1261ffa5c63SVladimir Kondratyev 1271ffa5c63SVladimir Kondratyev static void 1281ffa5c63SVladimir Kondratyev hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t next_rID) 1291ffa5c63SVladimir Kondratyev { 1301ffa5c63SVladimir Kondratyev uint8_t i; 1311ffa5c63SVladimir Kondratyev 1321ffa5c63SVladimir Kondratyev /* check for same report ID - optimise */ 1331ffa5c63SVladimir Kondratyev 1341ffa5c63SVladimir Kondratyev if (c->report_ID == next_rID) 1351ffa5c63SVladimir Kondratyev return; 1361ffa5c63SVladimir Kondratyev 1371ffa5c63SVladimir Kondratyev /* save current position for current rID */ 1381ffa5c63SVladimir Kondratyev 1391ffa5c63SVladimir Kondratyev if (c->report_ID == 0) { 1401ffa5c63SVladimir Kondratyev i = 0; 1411ffa5c63SVladimir Kondratyev } else { 1421ffa5c63SVladimir Kondratyev for (i = 1; i != MAXID; i++) { 1431ffa5c63SVladimir Kondratyev if (s->last_pos[i].rid == c->report_ID) 1441ffa5c63SVladimir Kondratyev break; 1451ffa5c63SVladimir Kondratyev if (s->last_pos[i].rid == 0) 1461ffa5c63SVladimir Kondratyev break; 1471ffa5c63SVladimir Kondratyev } 1481ffa5c63SVladimir Kondratyev } 1491ffa5c63SVladimir Kondratyev if (i != MAXID) { 1501ffa5c63SVladimir Kondratyev s->last_pos[i].rid = c->report_ID; 1511ffa5c63SVladimir Kondratyev s->last_pos[i].pos = c->loc.pos; 1521ffa5c63SVladimir Kondratyev } 1531ffa5c63SVladimir Kondratyev 1541ffa5c63SVladimir Kondratyev /* store next report ID */ 1551ffa5c63SVladimir Kondratyev 1561ffa5c63SVladimir Kondratyev c->report_ID = next_rID; 1571ffa5c63SVladimir Kondratyev 1581ffa5c63SVladimir Kondratyev /* lookup last position for next rID */ 1591ffa5c63SVladimir Kondratyev 1601ffa5c63SVladimir Kondratyev if (next_rID == 0) { 1611ffa5c63SVladimir Kondratyev i = 0; 1621ffa5c63SVladimir Kondratyev } else { 1631ffa5c63SVladimir Kondratyev for (i = 1; i != MAXID; i++) { 1641ffa5c63SVladimir Kondratyev if (s->last_pos[i].rid == next_rID) 1651ffa5c63SVladimir Kondratyev break; 1661ffa5c63SVladimir Kondratyev if (s->last_pos[i].rid == 0) 1671ffa5c63SVladimir Kondratyev break; 1681ffa5c63SVladimir Kondratyev } 1691ffa5c63SVladimir Kondratyev } 1701ffa5c63SVladimir Kondratyev if (i != MAXID) { 1711ffa5c63SVladimir Kondratyev s->last_pos[i].rid = next_rID; 1721ffa5c63SVladimir Kondratyev c->loc.pos = s->last_pos[i].pos; 1731ffa5c63SVladimir Kondratyev } else { 1741ffa5c63SVladimir Kondratyev DPRINTF("Out of RID entries, position is set to zero!\n"); 1751ffa5c63SVladimir Kondratyev c->loc.pos = 0; 1761ffa5c63SVladimir Kondratyev } 1771ffa5c63SVladimir Kondratyev } 1781ffa5c63SVladimir Kondratyev 1791ffa5c63SVladimir Kondratyev /*------------------------------------------------------------------------* 1801ffa5c63SVladimir Kondratyev * hid_start_parse 1811ffa5c63SVladimir Kondratyev *------------------------------------------------------------------------*/ 1821ffa5c63SVladimir Kondratyev struct hid_data * 1831ffa5c63SVladimir Kondratyev hid_start_parse(const void *d, usb_size_t len, int kindset) 1841ffa5c63SVladimir Kondratyev { 1851ffa5c63SVladimir Kondratyev struct hid_data *s; 1861ffa5c63SVladimir Kondratyev 1871ffa5c63SVladimir Kondratyev if ((kindset-1) & kindset) { 1881ffa5c63SVladimir Kondratyev DPRINTFN(0, "Only one bit can be " 1891ffa5c63SVladimir Kondratyev "set in the kindset\n"); 1901ffa5c63SVladimir Kondratyev return (NULL); 1911ffa5c63SVladimir Kondratyev } 1921ffa5c63SVladimir Kondratyev 1931ffa5c63SVladimir Kondratyev s = malloc(sizeof *s, M_TEMP, M_WAITOK | M_ZERO); 1941ffa5c63SVladimir Kondratyev s->start = s->p = d; 1951ffa5c63SVladimir Kondratyev s->end = ((const uint8_t *)d) + len; 1961ffa5c63SVladimir Kondratyev s->kindset = kindset; 1971ffa5c63SVladimir Kondratyev return (s); 1981ffa5c63SVladimir Kondratyev } 1991ffa5c63SVladimir Kondratyev 2001ffa5c63SVladimir Kondratyev /*------------------------------------------------------------------------* 2011ffa5c63SVladimir Kondratyev * hid_end_parse 2021ffa5c63SVladimir Kondratyev *------------------------------------------------------------------------*/ 2031ffa5c63SVladimir Kondratyev void 2041ffa5c63SVladimir Kondratyev hid_end_parse(struct hid_data *s) 2051ffa5c63SVladimir Kondratyev { 2061ffa5c63SVladimir Kondratyev if (s == NULL) 2071ffa5c63SVladimir Kondratyev return; 2081ffa5c63SVladimir Kondratyev 2091ffa5c63SVladimir Kondratyev free(s, M_TEMP); 2101ffa5c63SVladimir Kondratyev } 2111ffa5c63SVladimir Kondratyev 2121ffa5c63SVladimir Kondratyev /*------------------------------------------------------------------------* 2131ffa5c63SVladimir Kondratyev * get byte from HID descriptor 2141ffa5c63SVladimir Kondratyev *------------------------------------------------------------------------*/ 2151ffa5c63SVladimir Kondratyev static uint8_t 2161ffa5c63SVladimir Kondratyev hid_get_byte(struct hid_data *s, const uint16_t wSize) 2171ffa5c63SVladimir Kondratyev { 2181ffa5c63SVladimir Kondratyev const uint8_t *ptr; 2191ffa5c63SVladimir Kondratyev uint8_t retval; 2201ffa5c63SVladimir Kondratyev 2211ffa5c63SVladimir Kondratyev ptr = s->p; 2221ffa5c63SVladimir Kondratyev 2231ffa5c63SVladimir Kondratyev /* check if end is reached */ 2241ffa5c63SVladimir Kondratyev if (ptr == s->end) 2251ffa5c63SVladimir Kondratyev return (0); 2261ffa5c63SVladimir Kondratyev 2271ffa5c63SVladimir Kondratyev /* read out a byte */ 2281ffa5c63SVladimir Kondratyev retval = *ptr; 2291ffa5c63SVladimir Kondratyev 2301ffa5c63SVladimir Kondratyev /* check if data pointer can be advanced by "wSize" bytes */ 2311ffa5c63SVladimir Kondratyev if ((s->end - ptr) < wSize) 2321ffa5c63SVladimir Kondratyev ptr = s->end; 2331ffa5c63SVladimir Kondratyev else 2341ffa5c63SVladimir Kondratyev ptr += wSize; 2351ffa5c63SVladimir Kondratyev 2361ffa5c63SVladimir Kondratyev /* update pointer */ 2371ffa5c63SVladimir Kondratyev s->p = ptr; 2381ffa5c63SVladimir Kondratyev 2391ffa5c63SVladimir Kondratyev return (retval); 2401ffa5c63SVladimir Kondratyev } 2411ffa5c63SVladimir Kondratyev 2421ffa5c63SVladimir Kondratyev /*------------------------------------------------------------------------* 2431ffa5c63SVladimir Kondratyev * hid_get_item 2441ffa5c63SVladimir Kondratyev *------------------------------------------------------------------------*/ 2451ffa5c63SVladimir Kondratyev int 2461ffa5c63SVladimir Kondratyev hid_get_item(struct hid_data *s, struct hid_item *h) 2471ffa5c63SVladimir Kondratyev { 2481ffa5c63SVladimir Kondratyev struct hid_item *c; 2491ffa5c63SVladimir Kondratyev unsigned int bTag, bType, bSize; 2501ffa5c63SVladimir Kondratyev uint32_t oldpos; 2511ffa5c63SVladimir Kondratyev int32_t mask; 2521ffa5c63SVladimir Kondratyev int32_t dval; 2531ffa5c63SVladimir Kondratyev 2541ffa5c63SVladimir Kondratyev if (s == NULL) 2551ffa5c63SVladimir Kondratyev return (0); 2561ffa5c63SVladimir Kondratyev 2571ffa5c63SVladimir Kondratyev c = &s->cur[s->pushlevel]; 2581ffa5c63SVladimir Kondratyev 2591ffa5c63SVladimir Kondratyev top: 2601ffa5c63SVladimir Kondratyev /* check if there is an array of items */ 2611ffa5c63SVladimir Kondratyev if (s->icount < s->ncount) { 2621ffa5c63SVladimir Kondratyev /* get current usage */ 2631ffa5c63SVladimir Kondratyev if (s->iusage < s->nusage) { 2641ffa5c63SVladimir Kondratyev dval = s->usages_min[s->iusage] + s->ousage; 2651ffa5c63SVladimir Kondratyev c->usage = dval; 2661ffa5c63SVladimir Kondratyev s->usage_last = dval; 2671ffa5c63SVladimir Kondratyev if (dval == s->usages_max[s->iusage]) { 2681ffa5c63SVladimir Kondratyev s->iusage ++; 2691ffa5c63SVladimir Kondratyev s->ousage = 0; 2701ffa5c63SVladimir Kondratyev } else { 2711ffa5c63SVladimir Kondratyev s->ousage ++; 2721ffa5c63SVladimir Kondratyev } 2731ffa5c63SVladimir Kondratyev } else { 2741ffa5c63SVladimir Kondratyev DPRINTFN(1, "Using last usage\n"); 2751ffa5c63SVladimir Kondratyev dval = s->usage_last; 2761ffa5c63SVladimir Kondratyev } 2771ffa5c63SVladimir Kondratyev c->nusages = 1; 2781ffa5c63SVladimir Kondratyev /* array type HID item may have multiple usages */ 2791ffa5c63SVladimir Kondratyev while ((c->flags & HIO_VARIABLE) == 0 && s->ousage == 0 && 2801ffa5c63SVladimir Kondratyev s->iusage < s->nusage && c->nusages < HID_ITEM_MAXUSAGE) 2811ffa5c63SVladimir Kondratyev c->usages[c->nusages++] = s->usages_min[s->iusage++]; 2821ffa5c63SVladimir Kondratyev if ((c->flags & HIO_VARIABLE) == 0 && s->ousage == 0 && 2831ffa5c63SVladimir Kondratyev s->iusage < s->nusage) 2841ffa5c63SVladimir Kondratyev DPRINTFN(0, "HID_ITEM_MAXUSAGE should be increased " 2851ffa5c63SVladimir Kondratyev "up to %hhu to parse the HID report descriptor\n", 2861ffa5c63SVladimir Kondratyev s->nusage); 2871ffa5c63SVladimir Kondratyev s->icount ++; 2881ffa5c63SVladimir Kondratyev /* 2891ffa5c63SVladimir Kondratyev * Only copy HID item, increment position and return 2901ffa5c63SVladimir Kondratyev * if correct kindset! 2911ffa5c63SVladimir Kondratyev */ 2921ffa5c63SVladimir Kondratyev if (s->kindset & (1 << c->kind)) { 2931ffa5c63SVladimir Kondratyev *h = *c; 2941ffa5c63SVladimir Kondratyev DPRINTFN(1, "%u,%u,%u\n", h->loc.pos, 2951ffa5c63SVladimir Kondratyev h->loc.size, h->loc.count); 2961ffa5c63SVladimir Kondratyev c->loc.pos += c->loc.size * c->loc.count; 2971ffa5c63SVladimir Kondratyev return (1); 2981ffa5c63SVladimir Kondratyev } 2991ffa5c63SVladimir Kondratyev } 3001ffa5c63SVladimir Kondratyev 3011ffa5c63SVladimir Kondratyev /* reset state variables */ 3021ffa5c63SVladimir Kondratyev s->icount = 0; 3031ffa5c63SVladimir Kondratyev s->ncount = 0; 3041ffa5c63SVladimir Kondratyev s->iusage = 0; 3051ffa5c63SVladimir Kondratyev s->nusage = 0; 3061ffa5c63SVladimir Kondratyev s->susage = 0; 3071ffa5c63SVladimir Kondratyev s->ousage = 0; 3081ffa5c63SVladimir Kondratyev hid_clear_local(c); 3091ffa5c63SVladimir Kondratyev 3101ffa5c63SVladimir Kondratyev /* get next item */ 3111ffa5c63SVladimir Kondratyev while (s->p != s->end) { 3121ffa5c63SVladimir Kondratyev bSize = hid_get_byte(s, 1); 3131ffa5c63SVladimir Kondratyev if (bSize == 0xfe) { 3141ffa5c63SVladimir Kondratyev /* long item */ 3151ffa5c63SVladimir Kondratyev bSize = hid_get_byte(s, 1); 3161ffa5c63SVladimir Kondratyev bSize |= hid_get_byte(s, 1) << 8; 3171ffa5c63SVladimir Kondratyev bTag = hid_get_byte(s, 1); 3181ffa5c63SVladimir Kondratyev bType = 0xff; /* XXX what should it be */ 3191ffa5c63SVladimir Kondratyev } else { 3201ffa5c63SVladimir Kondratyev /* short item */ 3211ffa5c63SVladimir Kondratyev bTag = bSize >> 4; 3221ffa5c63SVladimir Kondratyev bType = (bSize >> 2) & 3; 3231ffa5c63SVladimir Kondratyev bSize &= 3; 3241ffa5c63SVladimir Kondratyev if (bSize == 3) 3251ffa5c63SVladimir Kondratyev bSize = 4; 3261ffa5c63SVladimir Kondratyev } 3271ffa5c63SVladimir Kondratyev switch (bSize) { 3281ffa5c63SVladimir Kondratyev case 0: 3291ffa5c63SVladimir Kondratyev dval = 0; 3301ffa5c63SVladimir Kondratyev mask = 0; 3311ffa5c63SVladimir Kondratyev break; 3321ffa5c63SVladimir Kondratyev case 1: 3331ffa5c63SVladimir Kondratyev dval = (int8_t)hid_get_byte(s, 1); 3341ffa5c63SVladimir Kondratyev mask = 0xFF; 3351ffa5c63SVladimir Kondratyev break; 3361ffa5c63SVladimir Kondratyev case 2: 3371ffa5c63SVladimir Kondratyev dval = hid_get_byte(s, 1); 3381ffa5c63SVladimir Kondratyev dval |= hid_get_byte(s, 1) << 8; 3391ffa5c63SVladimir Kondratyev dval = (int16_t)dval; 3401ffa5c63SVladimir Kondratyev mask = 0xFFFF; 3411ffa5c63SVladimir Kondratyev break; 3421ffa5c63SVladimir Kondratyev case 4: 3431ffa5c63SVladimir Kondratyev dval = hid_get_byte(s, 1); 3441ffa5c63SVladimir Kondratyev dval |= hid_get_byte(s, 1) << 8; 3451ffa5c63SVladimir Kondratyev dval |= hid_get_byte(s, 1) << 16; 3461ffa5c63SVladimir Kondratyev dval |= hid_get_byte(s, 1) << 24; 3471ffa5c63SVladimir Kondratyev mask = 0xFFFFFFFF; 3481ffa5c63SVladimir Kondratyev break; 3491ffa5c63SVladimir Kondratyev default: 3501ffa5c63SVladimir Kondratyev dval = hid_get_byte(s, bSize); 3511ffa5c63SVladimir Kondratyev DPRINTFN(0, "bad length %u (data=0x%02x)\n", 3521ffa5c63SVladimir Kondratyev bSize, dval); 3531ffa5c63SVladimir Kondratyev continue; 3541ffa5c63SVladimir Kondratyev } 3551ffa5c63SVladimir Kondratyev 3561ffa5c63SVladimir Kondratyev switch (bType) { 3571ffa5c63SVladimir Kondratyev case 0: /* Main */ 3581ffa5c63SVladimir Kondratyev switch (bTag) { 3591ffa5c63SVladimir Kondratyev case 8: /* Input */ 3601ffa5c63SVladimir Kondratyev c->kind = hid_input; 3611ffa5c63SVladimir Kondratyev ret: 3621ffa5c63SVladimir Kondratyev c->flags = dval; 3631ffa5c63SVladimir Kondratyev c->loc.count = s->loc_count; 3641ffa5c63SVladimir Kondratyev c->loc.size = s->loc_size; 3651ffa5c63SVladimir Kondratyev 3661ffa5c63SVladimir Kondratyev if (c->flags & HIO_VARIABLE) { 3671ffa5c63SVladimir Kondratyev /* range check usage count */ 3681ffa5c63SVladimir Kondratyev if (c->loc.count > MAXLOCCNT) { 3691ffa5c63SVladimir Kondratyev DPRINTFN(0, "Number of " 3701ffa5c63SVladimir Kondratyev "items(%u) truncated to %u\n", 3711ffa5c63SVladimir Kondratyev (unsigned)(c->loc.count), 3721ffa5c63SVladimir Kondratyev MAXLOCCNT); 3731ffa5c63SVladimir Kondratyev s->ncount = MAXLOCCNT; 3741ffa5c63SVladimir Kondratyev } else 3751ffa5c63SVladimir Kondratyev s->ncount = c->loc.count; 3761ffa5c63SVladimir Kondratyev 3771ffa5c63SVladimir Kondratyev /* 3781ffa5c63SVladimir Kondratyev * The "top" loop will return 3791ffa5c63SVladimir Kondratyev * one and one item: 3801ffa5c63SVladimir Kondratyev */ 3811ffa5c63SVladimir Kondratyev c->loc.count = 1; 3821ffa5c63SVladimir Kondratyev } else { 3831ffa5c63SVladimir Kondratyev s->ncount = 1; 3841ffa5c63SVladimir Kondratyev } 3851ffa5c63SVladimir Kondratyev goto top; 3861ffa5c63SVladimir Kondratyev 3871ffa5c63SVladimir Kondratyev case 9: /* Output */ 3881ffa5c63SVladimir Kondratyev c->kind = hid_output; 3891ffa5c63SVladimir Kondratyev goto ret; 3901ffa5c63SVladimir Kondratyev case 10: /* Collection */ 3911ffa5c63SVladimir Kondratyev c->kind = hid_collection; 3921ffa5c63SVladimir Kondratyev c->collection = dval; 3931ffa5c63SVladimir Kondratyev c->collevel++; 3941ffa5c63SVladimir Kondratyev c->usage = s->usage_last; 3951ffa5c63SVladimir Kondratyev c->nusages = 1; 3961ffa5c63SVladimir Kondratyev *h = *c; 3971ffa5c63SVladimir Kondratyev return (1); 3981ffa5c63SVladimir Kondratyev case 11: /* Feature */ 3991ffa5c63SVladimir Kondratyev c->kind = hid_feature; 4001ffa5c63SVladimir Kondratyev goto ret; 4011ffa5c63SVladimir Kondratyev case 12: /* End collection */ 4021ffa5c63SVladimir Kondratyev c->kind = hid_endcollection; 4031ffa5c63SVladimir Kondratyev if (c->collevel == 0) { 4041ffa5c63SVladimir Kondratyev DPRINTFN(0, "invalid end collection\n"); 4051ffa5c63SVladimir Kondratyev return (0); 4061ffa5c63SVladimir Kondratyev } 4071ffa5c63SVladimir Kondratyev c->collevel--; 4081ffa5c63SVladimir Kondratyev *h = *c; 4091ffa5c63SVladimir Kondratyev return (1); 4101ffa5c63SVladimir Kondratyev default: 4111ffa5c63SVladimir Kondratyev DPRINTFN(0, "Main bTag=%d\n", bTag); 4121ffa5c63SVladimir Kondratyev break; 4131ffa5c63SVladimir Kondratyev } 4141ffa5c63SVladimir Kondratyev break; 4151ffa5c63SVladimir Kondratyev case 1: /* Global */ 4161ffa5c63SVladimir Kondratyev switch (bTag) { 4171ffa5c63SVladimir Kondratyev case 0: 4181ffa5c63SVladimir Kondratyev c->_usage_page = dval << 16; 4191ffa5c63SVladimir Kondratyev break; 4201ffa5c63SVladimir Kondratyev case 1: 4211ffa5c63SVladimir Kondratyev c->logical_minimum = dval; 4221ffa5c63SVladimir Kondratyev break; 4231ffa5c63SVladimir Kondratyev case 2: 4241ffa5c63SVladimir Kondratyev c->logical_maximum = dval; 4251ffa5c63SVladimir Kondratyev break; 4261ffa5c63SVladimir Kondratyev case 3: 4271ffa5c63SVladimir Kondratyev c->physical_minimum = dval; 4281ffa5c63SVladimir Kondratyev break; 4291ffa5c63SVladimir Kondratyev case 4: 4301ffa5c63SVladimir Kondratyev c->physical_maximum = dval; 4311ffa5c63SVladimir Kondratyev break; 4321ffa5c63SVladimir Kondratyev case 5: 4331ffa5c63SVladimir Kondratyev c->unit_exponent = dval; 4341ffa5c63SVladimir Kondratyev break; 4351ffa5c63SVladimir Kondratyev case 6: 4361ffa5c63SVladimir Kondratyev c->unit = dval; 4371ffa5c63SVladimir Kondratyev break; 4381ffa5c63SVladimir Kondratyev case 7: 4391ffa5c63SVladimir Kondratyev /* mask because value is unsigned */ 4401ffa5c63SVladimir Kondratyev s->loc_size = dval & mask; 4411ffa5c63SVladimir Kondratyev break; 4421ffa5c63SVladimir Kondratyev case 8: 4431ffa5c63SVladimir Kondratyev hid_switch_rid(s, c, dval & mask); 4441ffa5c63SVladimir Kondratyev break; 4451ffa5c63SVladimir Kondratyev case 9: 4461ffa5c63SVladimir Kondratyev /* mask because value is unsigned */ 4471ffa5c63SVladimir Kondratyev s->loc_count = dval & mask; 4481ffa5c63SVladimir Kondratyev break; 4491ffa5c63SVladimir Kondratyev case 10: /* Push */ 4501ffa5c63SVladimir Kondratyev /* stop parsing, if invalid push level */ 4511ffa5c63SVladimir Kondratyev if ((s->pushlevel + 1) >= MAXPUSH) { 4521ffa5c63SVladimir Kondratyev DPRINTFN(0, "Cannot push item @ %d\n", s->pushlevel); 4531ffa5c63SVladimir Kondratyev return (0); 4541ffa5c63SVladimir Kondratyev } 4551ffa5c63SVladimir Kondratyev s->pushlevel ++; 4561ffa5c63SVladimir Kondratyev s->cur[s->pushlevel] = *c; 4571ffa5c63SVladimir Kondratyev /* store size and count */ 4581ffa5c63SVladimir Kondratyev c->loc.size = s->loc_size; 4591ffa5c63SVladimir Kondratyev c->loc.count = s->loc_count; 4601ffa5c63SVladimir Kondratyev /* update current item pointer */ 4611ffa5c63SVladimir Kondratyev c = &s->cur[s->pushlevel]; 4621ffa5c63SVladimir Kondratyev break; 4631ffa5c63SVladimir Kondratyev case 11: /* Pop */ 4641ffa5c63SVladimir Kondratyev /* stop parsing, if invalid push level */ 4651ffa5c63SVladimir Kondratyev if (s->pushlevel == 0) { 4661ffa5c63SVladimir Kondratyev DPRINTFN(0, "Cannot pop item @ 0\n"); 4671ffa5c63SVladimir Kondratyev return (0); 4681ffa5c63SVladimir Kondratyev } 4691ffa5c63SVladimir Kondratyev s->pushlevel --; 4701ffa5c63SVladimir Kondratyev /* preserve position */ 4711ffa5c63SVladimir Kondratyev oldpos = c->loc.pos; 4721ffa5c63SVladimir Kondratyev c = &s->cur[s->pushlevel]; 4731ffa5c63SVladimir Kondratyev /* restore size and count */ 4741ffa5c63SVladimir Kondratyev s->loc_size = c->loc.size; 4751ffa5c63SVladimir Kondratyev s->loc_count = c->loc.count; 4761ffa5c63SVladimir Kondratyev /* set default item location */ 4771ffa5c63SVladimir Kondratyev c->loc.pos = oldpos; 4781ffa5c63SVladimir Kondratyev c->loc.size = 0; 4791ffa5c63SVladimir Kondratyev c->loc.count = 0; 4801ffa5c63SVladimir Kondratyev break; 4811ffa5c63SVladimir Kondratyev default: 4821ffa5c63SVladimir Kondratyev DPRINTFN(0, "Global bTag=%d\n", bTag); 4831ffa5c63SVladimir Kondratyev break; 4841ffa5c63SVladimir Kondratyev } 4851ffa5c63SVladimir Kondratyev break; 4861ffa5c63SVladimir Kondratyev case 2: /* Local */ 4871ffa5c63SVladimir Kondratyev switch (bTag) { 4881ffa5c63SVladimir Kondratyev case 0: 4891ffa5c63SVladimir Kondratyev if (bSize != 4) 4901ffa5c63SVladimir Kondratyev dval = (dval & mask) | c->_usage_page; 4911ffa5c63SVladimir Kondratyev 4921ffa5c63SVladimir Kondratyev /* set last usage, in case of a collection */ 4931ffa5c63SVladimir Kondratyev s->usage_last = dval; 4941ffa5c63SVladimir Kondratyev 4951ffa5c63SVladimir Kondratyev if (s->nusage < MAXUSAGE) { 4961ffa5c63SVladimir Kondratyev s->usages_min[s->nusage] = dval; 4971ffa5c63SVladimir Kondratyev s->usages_max[s->nusage] = dval; 4981ffa5c63SVladimir Kondratyev s->nusage ++; 4991ffa5c63SVladimir Kondratyev } else { 5001ffa5c63SVladimir Kondratyev DPRINTFN(0, "max usage reached\n"); 5011ffa5c63SVladimir Kondratyev } 5021ffa5c63SVladimir Kondratyev 5031ffa5c63SVladimir Kondratyev /* clear any pending usage sets */ 5041ffa5c63SVladimir Kondratyev s->susage = 0; 5051ffa5c63SVladimir Kondratyev break; 5061ffa5c63SVladimir Kondratyev case 1: 5071ffa5c63SVladimir Kondratyev s->susage |= 1; 5081ffa5c63SVladimir Kondratyev 5091ffa5c63SVladimir Kondratyev if (bSize != 4) 5101ffa5c63SVladimir Kondratyev dval = (dval & mask) | c->_usage_page; 5111ffa5c63SVladimir Kondratyev c->usage_minimum = dval; 5121ffa5c63SVladimir Kondratyev 5131ffa5c63SVladimir Kondratyev goto check_set; 5141ffa5c63SVladimir Kondratyev case 2: 5151ffa5c63SVladimir Kondratyev s->susage |= 2; 5161ffa5c63SVladimir Kondratyev 5171ffa5c63SVladimir Kondratyev if (bSize != 4) 5181ffa5c63SVladimir Kondratyev dval = (dval & mask) | c->_usage_page; 5191ffa5c63SVladimir Kondratyev c->usage_maximum = dval; 5201ffa5c63SVladimir Kondratyev 5211ffa5c63SVladimir Kondratyev check_set: 5221ffa5c63SVladimir Kondratyev if (s->susage != 3) 5231ffa5c63SVladimir Kondratyev break; 5241ffa5c63SVladimir Kondratyev 5251ffa5c63SVladimir Kondratyev /* sanity check */ 5261ffa5c63SVladimir Kondratyev if ((s->nusage < MAXUSAGE) && 5271ffa5c63SVladimir Kondratyev (c->usage_minimum <= c->usage_maximum)) { 5281ffa5c63SVladimir Kondratyev /* add usage range */ 5291ffa5c63SVladimir Kondratyev s->usages_min[s->nusage] = 5301ffa5c63SVladimir Kondratyev c->usage_minimum; 5311ffa5c63SVladimir Kondratyev s->usages_max[s->nusage] = 5321ffa5c63SVladimir Kondratyev c->usage_maximum; 5331ffa5c63SVladimir Kondratyev s->nusage ++; 5341ffa5c63SVladimir Kondratyev } else { 5351ffa5c63SVladimir Kondratyev DPRINTFN(0, "Usage set dropped\n"); 5361ffa5c63SVladimir Kondratyev } 5371ffa5c63SVladimir Kondratyev s->susage = 0; 5381ffa5c63SVladimir Kondratyev break; 5391ffa5c63SVladimir Kondratyev case 3: 5401ffa5c63SVladimir Kondratyev c->designator_index = dval; 5411ffa5c63SVladimir Kondratyev break; 5421ffa5c63SVladimir Kondratyev case 4: 5431ffa5c63SVladimir Kondratyev c->designator_minimum = dval; 5441ffa5c63SVladimir Kondratyev break; 5451ffa5c63SVladimir Kondratyev case 5: 5461ffa5c63SVladimir Kondratyev c->designator_maximum = dval; 5471ffa5c63SVladimir Kondratyev break; 5481ffa5c63SVladimir Kondratyev case 7: 5491ffa5c63SVladimir Kondratyev c->string_index = dval; 5501ffa5c63SVladimir Kondratyev break; 5511ffa5c63SVladimir Kondratyev case 8: 5521ffa5c63SVladimir Kondratyev c->string_minimum = dval; 5531ffa5c63SVladimir Kondratyev break; 5541ffa5c63SVladimir Kondratyev case 9: 5551ffa5c63SVladimir Kondratyev c->string_maximum = dval; 5561ffa5c63SVladimir Kondratyev break; 5571ffa5c63SVladimir Kondratyev case 10: 5581ffa5c63SVladimir Kondratyev c->set_delimiter = dval; 5591ffa5c63SVladimir Kondratyev break; 5601ffa5c63SVladimir Kondratyev default: 5611ffa5c63SVladimir Kondratyev DPRINTFN(0, "Local bTag=%d\n", bTag); 5621ffa5c63SVladimir Kondratyev break; 5631ffa5c63SVladimir Kondratyev } 5641ffa5c63SVladimir Kondratyev break; 5651ffa5c63SVladimir Kondratyev default: 5661ffa5c63SVladimir Kondratyev DPRINTFN(0, "default bType=%d\n", bType); 5671ffa5c63SVladimir Kondratyev break; 5681ffa5c63SVladimir Kondratyev } 5691ffa5c63SVladimir Kondratyev } 5701ffa5c63SVladimir Kondratyev return (0); 5711ffa5c63SVladimir Kondratyev } 5721ffa5c63SVladimir Kondratyev 5731ffa5c63SVladimir Kondratyev /*------------------------------------------------------------------------* 5741ffa5c63SVladimir Kondratyev * hid_report_size 5751ffa5c63SVladimir Kondratyev *------------------------------------------------------------------------*/ 5761ffa5c63SVladimir Kondratyev int 5771ffa5c63SVladimir Kondratyev hid_report_size(const void *buf, usb_size_t len, enum hid_kind k, uint8_t *id) 5781ffa5c63SVladimir Kondratyev { 5791ffa5c63SVladimir Kondratyev struct hid_data *d; 5801ffa5c63SVladimir Kondratyev struct hid_item h; 5811ffa5c63SVladimir Kondratyev uint32_t temp; 5821ffa5c63SVladimir Kondratyev uint32_t hpos; 5831ffa5c63SVladimir Kondratyev uint32_t lpos; 5841ffa5c63SVladimir Kondratyev uint8_t any_id; 5851ffa5c63SVladimir Kondratyev 5861ffa5c63SVladimir Kondratyev any_id = 0; 5871ffa5c63SVladimir Kondratyev hpos = 0; 5881ffa5c63SVladimir Kondratyev lpos = 0xFFFFFFFF; 5891ffa5c63SVladimir Kondratyev 5901ffa5c63SVladimir Kondratyev for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);) { 5911ffa5c63SVladimir Kondratyev if (h.kind == k) { 5921ffa5c63SVladimir Kondratyev /* check for ID-byte presence */ 5931ffa5c63SVladimir Kondratyev if ((h.report_ID != 0) && !any_id) { 5941ffa5c63SVladimir Kondratyev if (id != NULL) 5951ffa5c63SVladimir Kondratyev *id = h.report_ID; 5961ffa5c63SVladimir Kondratyev any_id = 1; 5971ffa5c63SVladimir Kondratyev } 5981ffa5c63SVladimir Kondratyev /* compute minimum */ 5991ffa5c63SVladimir Kondratyev if (lpos > h.loc.pos) 6001ffa5c63SVladimir Kondratyev lpos = h.loc.pos; 6011ffa5c63SVladimir Kondratyev /* compute end position */ 6021ffa5c63SVladimir Kondratyev temp = h.loc.pos + (h.loc.size * h.loc.count); 6031ffa5c63SVladimir Kondratyev /* compute maximum */ 6041ffa5c63SVladimir Kondratyev if (hpos < temp) 6051ffa5c63SVladimir Kondratyev hpos = temp; 6061ffa5c63SVladimir Kondratyev } 6071ffa5c63SVladimir Kondratyev } 6081ffa5c63SVladimir Kondratyev hid_end_parse(d); 6091ffa5c63SVladimir Kondratyev 6101ffa5c63SVladimir Kondratyev /* safety check - can happen in case of currupt descriptors */ 6111ffa5c63SVladimir Kondratyev if (lpos > hpos) 6121ffa5c63SVladimir Kondratyev temp = 0; 6131ffa5c63SVladimir Kondratyev else 6141ffa5c63SVladimir Kondratyev temp = hpos - lpos; 6151ffa5c63SVladimir Kondratyev 6161ffa5c63SVladimir Kondratyev /* check for ID byte */ 6171ffa5c63SVladimir Kondratyev if (any_id) 6181ffa5c63SVladimir Kondratyev temp += 8; 6191ffa5c63SVladimir Kondratyev else if (id != NULL) 6201ffa5c63SVladimir Kondratyev *id = 0; 6211ffa5c63SVladimir Kondratyev 6221ffa5c63SVladimir Kondratyev /* return length in bytes rounded up */ 6231ffa5c63SVladimir Kondratyev return ((temp + 7) / 8); 6241ffa5c63SVladimir Kondratyev } 6251ffa5c63SVladimir Kondratyev 6261ffa5c63SVladimir Kondratyev /*------------------------------------------------------------------------* 6271ffa5c63SVladimir Kondratyev * hid_locate 6281ffa5c63SVladimir Kondratyev *------------------------------------------------------------------------*/ 6291ffa5c63SVladimir Kondratyev int 6301ffa5c63SVladimir Kondratyev hid_locate(const void *desc, usb_size_t size, int32_t u, enum hid_kind k, 6311ffa5c63SVladimir Kondratyev uint8_t index, struct hid_location *loc, uint32_t *flags, uint8_t *id) 6321ffa5c63SVladimir Kondratyev { 6331ffa5c63SVladimir Kondratyev struct hid_data *d; 6341ffa5c63SVladimir Kondratyev struct hid_item h; 6351ffa5c63SVladimir Kondratyev int i; 6361ffa5c63SVladimir Kondratyev 6371ffa5c63SVladimir Kondratyev for (d = hid_start_parse(desc, size, 1 << k); hid_get_item(d, &h);) { 6381ffa5c63SVladimir Kondratyev for (i = 0; i < h.nusages; i++) { 6391ffa5c63SVladimir Kondratyev if (h.kind == k && h.usages[i] == u) { 6401ffa5c63SVladimir Kondratyev if (index--) 6411ffa5c63SVladimir Kondratyev break; 6421ffa5c63SVladimir Kondratyev if (loc != NULL) 6431ffa5c63SVladimir Kondratyev *loc = h.loc; 6441ffa5c63SVladimir Kondratyev if (flags != NULL) 6451ffa5c63SVladimir Kondratyev *flags = h.flags; 6461ffa5c63SVladimir Kondratyev if (id != NULL) 6471ffa5c63SVladimir Kondratyev *id = h.report_ID; 6481ffa5c63SVladimir Kondratyev hid_end_parse(d); 6491ffa5c63SVladimir Kondratyev return (1); 6501ffa5c63SVladimir Kondratyev } 6511ffa5c63SVladimir Kondratyev } 6521ffa5c63SVladimir Kondratyev } 6531ffa5c63SVladimir Kondratyev if (loc != NULL) 6541ffa5c63SVladimir Kondratyev loc->size = 0; 6551ffa5c63SVladimir Kondratyev if (flags != NULL) 6561ffa5c63SVladimir Kondratyev *flags = 0; 6571ffa5c63SVladimir Kondratyev if (id != NULL) 6581ffa5c63SVladimir Kondratyev *id = 0; 6591ffa5c63SVladimir Kondratyev hid_end_parse(d); 6601ffa5c63SVladimir Kondratyev return (0); 6611ffa5c63SVladimir Kondratyev } 6621ffa5c63SVladimir Kondratyev 6631ffa5c63SVladimir Kondratyev /*------------------------------------------------------------------------* 6641ffa5c63SVladimir Kondratyev * hid_get_data 6651ffa5c63SVladimir Kondratyev *------------------------------------------------------------------------*/ 6661ffa5c63SVladimir Kondratyev static uint32_t 6671ffa5c63SVladimir Kondratyev hid_get_data_sub(const uint8_t *buf, usb_size_t len, struct hid_location *loc, 6681ffa5c63SVladimir Kondratyev int is_signed) 6691ffa5c63SVladimir Kondratyev { 6701ffa5c63SVladimir Kondratyev uint32_t hpos = loc->pos; 6711ffa5c63SVladimir Kondratyev uint32_t hsize = loc->size; 6721ffa5c63SVladimir Kondratyev uint32_t data; 6731ffa5c63SVladimir Kondratyev uint32_t rpos; 6741ffa5c63SVladimir Kondratyev uint8_t n; 6751ffa5c63SVladimir Kondratyev 6761ffa5c63SVladimir Kondratyev DPRINTFN(11, "hid_get_data: loc %d/%d\n", hpos, hsize); 6771ffa5c63SVladimir Kondratyev 6781ffa5c63SVladimir Kondratyev /* Range check and limit */ 6791ffa5c63SVladimir Kondratyev if (hsize == 0) 6801ffa5c63SVladimir Kondratyev return (0); 6811ffa5c63SVladimir Kondratyev if (hsize > 32) 6821ffa5c63SVladimir Kondratyev hsize = 32; 6831ffa5c63SVladimir Kondratyev 6841ffa5c63SVladimir Kondratyev /* Get data in a safe way */ 6851ffa5c63SVladimir Kondratyev data = 0; 6861ffa5c63SVladimir Kondratyev rpos = (hpos / 8); 6871ffa5c63SVladimir Kondratyev n = (hsize + 7) / 8; 6881ffa5c63SVladimir Kondratyev rpos += n; 6891ffa5c63SVladimir Kondratyev while (n--) { 6901ffa5c63SVladimir Kondratyev rpos--; 6911ffa5c63SVladimir Kondratyev if (rpos < len) 6921ffa5c63SVladimir Kondratyev data |= buf[rpos] << (8 * n); 6931ffa5c63SVladimir Kondratyev } 6941ffa5c63SVladimir Kondratyev 6951ffa5c63SVladimir Kondratyev /* Correctly shift down data */ 6961ffa5c63SVladimir Kondratyev data = (data >> (hpos % 8)); 6971ffa5c63SVladimir Kondratyev n = 32 - hsize; 6981ffa5c63SVladimir Kondratyev 6991ffa5c63SVladimir Kondratyev /* Mask and sign extend in one */ 7001ffa5c63SVladimir Kondratyev if (is_signed != 0) 7011ffa5c63SVladimir Kondratyev data = (int32_t)((int32_t)data << n) >> n; 7021ffa5c63SVladimir Kondratyev else 7031ffa5c63SVladimir Kondratyev data = (uint32_t)((uint32_t)data << n) >> n; 7041ffa5c63SVladimir Kondratyev 7051ffa5c63SVladimir Kondratyev DPRINTFN(11, "hid_get_data: loc %d/%d = %lu\n", 7061ffa5c63SVladimir Kondratyev loc->pos, loc->size, (long)data); 7071ffa5c63SVladimir Kondratyev return (data); 7081ffa5c63SVladimir Kondratyev } 7091ffa5c63SVladimir Kondratyev 7101ffa5c63SVladimir Kondratyev int32_t 7111ffa5c63SVladimir Kondratyev hid_get_data(const uint8_t *buf, usb_size_t len, struct hid_location *loc) 7121ffa5c63SVladimir Kondratyev { 7131ffa5c63SVladimir Kondratyev return (hid_get_data_sub(buf, len, loc, 1)); 7141ffa5c63SVladimir Kondratyev } 7151ffa5c63SVladimir Kondratyev 7161ffa5c63SVladimir Kondratyev uint32_t 7171ffa5c63SVladimir Kondratyev hid_get_data_unsigned(const uint8_t *buf, usb_size_t len, struct hid_location *loc) 7181ffa5c63SVladimir Kondratyev { 7191ffa5c63SVladimir Kondratyev return (hid_get_data_sub(buf, len, loc, 0)); 7201ffa5c63SVladimir Kondratyev } 7211ffa5c63SVladimir Kondratyev 7221ffa5c63SVladimir Kondratyev /*------------------------------------------------------------------------* 7231ffa5c63SVladimir Kondratyev * hid_put_data 7241ffa5c63SVladimir Kondratyev *------------------------------------------------------------------------*/ 7251ffa5c63SVladimir Kondratyev void 7261ffa5c63SVladimir Kondratyev hid_put_data_unsigned(uint8_t *buf, usb_size_t len, 7271ffa5c63SVladimir Kondratyev struct hid_location *loc, unsigned int value) 7281ffa5c63SVladimir Kondratyev { 7291ffa5c63SVladimir Kondratyev uint32_t hpos = loc->pos; 7301ffa5c63SVladimir Kondratyev uint32_t hsize = loc->size; 7311ffa5c63SVladimir Kondratyev uint64_t data; 7321ffa5c63SVladimir Kondratyev uint64_t mask; 7331ffa5c63SVladimir Kondratyev uint32_t rpos; 7341ffa5c63SVladimir Kondratyev uint8_t n; 7351ffa5c63SVladimir Kondratyev 7361ffa5c63SVladimir Kondratyev DPRINTFN(11, "hid_put_data: loc %d/%d = %u\n", hpos, hsize, value); 7371ffa5c63SVladimir Kondratyev 7381ffa5c63SVladimir Kondratyev /* Range check and limit */ 7391ffa5c63SVladimir Kondratyev if (hsize == 0) 7401ffa5c63SVladimir Kondratyev return; 7411ffa5c63SVladimir Kondratyev if (hsize > 32) 7421ffa5c63SVladimir Kondratyev hsize = 32; 7431ffa5c63SVladimir Kondratyev 7441ffa5c63SVladimir Kondratyev /* Put data in a safe way */ 7451ffa5c63SVladimir Kondratyev rpos = (hpos / 8); 7461ffa5c63SVladimir Kondratyev n = (hsize + 7) / 8; 7471ffa5c63SVladimir Kondratyev data = ((uint64_t)value) << (hpos % 8); 7481ffa5c63SVladimir Kondratyev mask = ((1ULL << hsize) - 1ULL) << (hpos % 8); 7491ffa5c63SVladimir Kondratyev rpos += n; 7501ffa5c63SVladimir Kondratyev while (n--) { 7511ffa5c63SVladimir Kondratyev rpos--; 7521ffa5c63SVladimir Kondratyev if (rpos < len) { 7531ffa5c63SVladimir Kondratyev buf[rpos] &= ~(mask >> (8 * n)); 7541ffa5c63SVladimir Kondratyev buf[rpos] |= (data >> (8 * n)); 7551ffa5c63SVladimir Kondratyev } 7561ffa5c63SVladimir Kondratyev } 7571ffa5c63SVladimir Kondratyev } 7581ffa5c63SVladimir Kondratyev 7591ffa5c63SVladimir Kondratyev /*------------------------------------------------------------------------* 7601ffa5c63SVladimir Kondratyev * hid_is_collection 7611ffa5c63SVladimir Kondratyev *------------------------------------------------------------------------*/ 7621ffa5c63SVladimir Kondratyev int 7631ffa5c63SVladimir Kondratyev hid_is_collection(const void *desc, usb_size_t size, int32_t usage) 7641ffa5c63SVladimir Kondratyev { 7651ffa5c63SVladimir Kondratyev struct hid_data *hd; 7661ffa5c63SVladimir Kondratyev struct hid_item hi; 7671ffa5c63SVladimir Kondratyev int err; 7681ffa5c63SVladimir Kondratyev 7691ffa5c63SVladimir Kondratyev hd = hid_start_parse(desc, size, hid_input); 7701ffa5c63SVladimir Kondratyev if (hd == NULL) 7711ffa5c63SVladimir Kondratyev return (0); 7721ffa5c63SVladimir Kondratyev 7731ffa5c63SVladimir Kondratyev while ((err = hid_get_item(hd, &hi))) { 7741ffa5c63SVladimir Kondratyev if (hi.kind == hid_collection && 7751ffa5c63SVladimir Kondratyev hi.usage == usage) 7761ffa5c63SVladimir Kondratyev break; 7771ffa5c63SVladimir Kondratyev } 7781ffa5c63SVladimir Kondratyev hid_end_parse(hd); 7791ffa5c63SVladimir Kondratyev return (err); 7801ffa5c63SVladimir Kondratyev } 7811ffa5c63SVladimir Kondratyev 7821ffa5c63SVladimir Kondratyev /*------------------------------------------------------------------------* 7831ffa5c63SVladimir Kondratyev * calculate HID item resolution. unit/mm for distances, unit/rad for angles 7841ffa5c63SVladimir Kondratyev *------------------------------------------------------------------------*/ 7851ffa5c63SVladimir Kondratyev int32_t 7861ffa5c63SVladimir Kondratyev hid_item_resolution(struct hid_item *hi) 7871ffa5c63SVladimir Kondratyev { 7881ffa5c63SVladimir Kondratyev /* 7891ffa5c63SVladimir Kondratyev * hid unit scaling table according to HID Usage Table Review 7901ffa5c63SVladimir Kondratyev * Request 39 Tbl 17 http://www.usb.org/developers/hidpage/HUTRR39b.pdf 7911ffa5c63SVladimir Kondratyev */ 7921ffa5c63SVladimir Kondratyev static const int64_t scale[0x10][2] = { 7931ffa5c63SVladimir Kondratyev [0x00] = { 1, 1 }, 7941ffa5c63SVladimir Kondratyev [0x01] = { 1, 10 }, 7951ffa5c63SVladimir Kondratyev [0x02] = { 1, 100 }, 7961ffa5c63SVladimir Kondratyev [0x03] = { 1, 1000 }, 7971ffa5c63SVladimir Kondratyev [0x04] = { 1, 10000 }, 7981ffa5c63SVladimir Kondratyev [0x05] = { 1, 100000 }, 7991ffa5c63SVladimir Kondratyev [0x06] = { 1, 1000000 }, 8001ffa5c63SVladimir Kondratyev [0x07] = { 1, 10000000 }, 8011ffa5c63SVladimir Kondratyev [0x08] = { 100000000, 1 }, 8021ffa5c63SVladimir Kondratyev [0x09] = { 10000000, 1 }, 8031ffa5c63SVladimir Kondratyev [0x0A] = { 1000000, 1 }, 8041ffa5c63SVladimir Kondratyev [0x0B] = { 100000, 1 }, 8051ffa5c63SVladimir Kondratyev [0x0C] = { 10000, 1 }, 8061ffa5c63SVladimir Kondratyev [0x0D] = { 1000, 1 }, 8071ffa5c63SVladimir Kondratyev [0x0E] = { 100, 1 }, 8081ffa5c63SVladimir Kondratyev [0x0F] = { 10, 1 }, 8091ffa5c63SVladimir Kondratyev }; 8101ffa5c63SVladimir Kondratyev int64_t logical_size; 8111ffa5c63SVladimir Kondratyev int64_t physical_size; 8121ffa5c63SVladimir Kondratyev int64_t multiplier; 8131ffa5c63SVladimir Kondratyev int64_t divisor; 8141ffa5c63SVladimir Kondratyev int64_t resolution; 8151ffa5c63SVladimir Kondratyev 8161ffa5c63SVladimir Kondratyev switch (hi->unit) { 8171ffa5c63SVladimir Kondratyev case HUM_CENTIMETER: 8181ffa5c63SVladimir Kondratyev multiplier = 1; 8191ffa5c63SVladimir Kondratyev divisor = 10; 8201ffa5c63SVladimir Kondratyev break; 8211ffa5c63SVladimir Kondratyev case HUM_INCH: 8221ffa5c63SVladimir Kondratyev multiplier = 10; 8231ffa5c63SVladimir Kondratyev divisor = 254; 8241ffa5c63SVladimir Kondratyev break; 8251ffa5c63SVladimir Kondratyev case HUM_RADIAN: 8261ffa5c63SVladimir Kondratyev multiplier = 1; 8271ffa5c63SVladimir Kondratyev divisor = 1; 8281ffa5c63SVladimir Kondratyev break; 8291ffa5c63SVladimir Kondratyev case HUM_DEGREE: 8301ffa5c63SVladimir Kondratyev multiplier = 573; 8311ffa5c63SVladimir Kondratyev divisor = 10; 8321ffa5c63SVladimir Kondratyev break; 8331ffa5c63SVladimir Kondratyev default: 8341ffa5c63SVladimir Kondratyev return (0); 8351ffa5c63SVladimir Kondratyev } 8361ffa5c63SVladimir Kondratyev 8371ffa5c63SVladimir Kondratyev if ((hi->logical_maximum <= hi->logical_minimum) || 8381ffa5c63SVladimir Kondratyev (hi->physical_maximum <= hi->physical_minimum) || 8391ffa5c63SVladimir Kondratyev (hi->unit_exponent < 0) || (hi->unit_exponent >= nitems(scale))) 8401ffa5c63SVladimir Kondratyev return (0); 8411ffa5c63SVladimir Kondratyev 8421ffa5c63SVladimir Kondratyev logical_size = (int64_t)hi->logical_maximum - 8431ffa5c63SVladimir Kondratyev (int64_t)hi->logical_minimum; 8441ffa5c63SVladimir Kondratyev physical_size = (int64_t)hi->physical_maximum - 8451ffa5c63SVladimir Kondratyev (int64_t)hi->physical_minimum; 8461ffa5c63SVladimir Kondratyev /* Round to ceiling */ 8471ffa5c63SVladimir Kondratyev resolution = logical_size * multiplier * scale[hi->unit_exponent][0] / 8481ffa5c63SVladimir Kondratyev (physical_size * divisor * scale[hi->unit_exponent][1]); 8491ffa5c63SVladimir Kondratyev 8501ffa5c63SVladimir Kondratyev if (resolution > INT32_MAX) 8511ffa5c63SVladimir Kondratyev return (0); 8521ffa5c63SVladimir Kondratyev 8531ffa5c63SVladimir Kondratyev return (resolution); 8541ffa5c63SVladimir Kondratyev } 8551ffa5c63SVladimir Kondratyev 8561ffa5c63SVladimir Kondratyev /*------------------------------------------------------------------------* 8571ffa5c63SVladimir Kondratyev * hid_is_mouse 8581ffa5c63SVladimir Kondratyev * 8591ffa5c63SVladimir Kondratyev * This function will decide if a USB descriptor belongs to a USB mouse. 8601ffa5c63SVladimir Kondratyev * 8611ffa5c63SVladimir Kondratyev * Return values: 8621ffa5c63SVladimir Kondratyev * Zero: Not a USB mouse. 8631ffa5c63SVladimir Kondratyev * Else: Is a USB mouse. 8641ffa5c63SVladimir Kondratyev *------------------------------------------------------------------------*/ 8651ffa5c63SVladimir Kondratyev int 8661ffa5c63SVladimir Kondratyev hid_is_mouse(const void *d_ptr, uint16_t d_len) 8671ffa5c63SVladimir Kondratyev { 8681ffa5c63SVladimir Kondratyev struct hid_data *hd; 8691ffa5c63SVladimir Kondratyev struct hid_item hi; 8701ffa5c63SVladimir Kondratyev int mdepth; 8711ffa5c63SVladimir Kondratyev int found; 8721ffa5c63SVladimir Kondratyev 8731ffa5c63SVladimir Kondratyev hd = hid_start_parse(d_ptr, d_len, 1 << hid_input); 8741ffa5c63SVladimir Kondratyev if (hd == NULL) 8751ffa5c63SVladimir Kondratyev return (0); 8761ffa5c63SVladimir Kondratyev 8771ffa5c63SVladimir Kondratyev mdepth = 0; 8781ffa5c63SVladimir Kondratyev found = 0; 8791ffa5c63SVladimir Kondratyev 8801ffa5c63SVladimir Kondratyev while (hid_get_item(hd, &hi)) { 8811ffa5c63SVladimir Kondratyev switch (hi.kind) { 8821ffa5c63SVladimir Kondratyev case hid_collection: 8831ffa5c63SVladimir Kondratyev if (mdepth != 0) 8841ffa5c63SVladimir Kondratyev mdepth++; 8851ffa5c63SVladimir Kondratyev else if (hi.collection == 1 && 8861ffa5c63SVladimir Kondratyev hi.usage == 8871ffa5c63SVladimir Kondratyev HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)) 8881ffa5c63SVladimir Kondratyev mdepth++; 8891ffa5c63SVladimir Kondratyev break; 8901ffa5c63SVladimir Kondratyev case hid_endcollection: 8911ffa5c63SVladimir Kondratyev if (mdepth != 0) 8921ffa5c63SVladimir Kondratyev mdepth--; 8931ffa5c63SVladimir Kondratyev break; 8941ffa5c63SVladimir Kondratyev case hid_input: 8951ffa5c63SVladimir Kondratyev if (mdepth == 0) 8961ffa5c63SVladimir Kondratyev break; 8971ffa5c63SVladimir Kondratyev if (hi.usage == 8981ffa5c63SVladimir Kondratyev HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X) && 8991ffa5c63SVladimir Kondratyev (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE) 9001ffa5c63SVladimir Kondratyev found++; 9011ffa5c63SVladimir Kondratyev if (hi.usage == 9021ffa5c63SVladimir Kondratyev HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y) && 9031ffa5c63SVladimir Kondratyev (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE) 9041ffa5c63SVladimir Kondratyev found++; 9051ffa5c63SVladimir Kondratyev break; 9061ffa5c63SVladimir Kondratyev default: 9071ffa5c63SVladimir Kondratyev break; 9081ffa5c63SVladimir Kondratyev } 9091ffa5c63SVladimir Kondratyev } 9101ffa5c63SVladimir Kondratyev hid_end_parse(hd); 9111ffa5c63SVladimir Kondratyev return (found); 9121ffa5c63SVladimir Kondratyev } 9131ffa5c63SVladimir Kondratyev 9141ffa5c63SVladimir Kondratyev /*------------------------------------------------------------------------* 9151ffa5c63SVladimir Kondratyev * hid_is_keyboard 9161ffa5c63SVladimir Kondratyev * 9171ffa5c63SVladimir Kondratyev * This function will decide if a USB descriptor belongs to a USB keyboard. 9181ffa5c63SVladimir Kondratyev * 9191ffa5c63SVladimir Kondratyev * Return values: 9201ffa5c63SVladimir Kondratyev * Zero: Not a USB keyboard. 9211ffa5c63SVladimir Kondratyev * Else: Is a USB keyboard. 9221ffa5c63SVladimir Kondratyev *------------------------------------------------------------------------*/ 9231ffa5c63SVladimir Kondratyev int 9241ffa5c63SVladimir Kondratyev hid_is_keyboard(const void *d_ptr, uint16_t d_len) 9251ffa5c63SVladimir Kondratyev { 9261ffa5c63SVladimir Kondratyev if (hid_is_collection(d_ptr, d_len, 9271ffa5c63SVladimir Kondratyev HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD))) 9281ffa5c63SVladimir Kondratyev return (1); 9291ffa5c63SVladimir Kondratyev return (0); 9301ffa5c63SVladimir Kondratyev } 931*67de2db2SVladimir Kondratyev 932*67de2db2SVladimir Kondratyev MODULE_VERSION(hid, 1); 933