1246122Shselasky/* $FreeBSD: stable/10/sys/dev/usb/usb_hid.c 361921 2020-06-08 09:34:16Z hselasky $ */ 2184610Salfred/* $NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem Exp $ */ 3184610Salfred/*- 4184610Salfred * Copyright (c) 1998 The NetBSD Foundation, Inc. 5184610Salfred * All rights reserved. 6184610Salfred * 7184610Salfred * This code is derived from software contributed to The NetBSD Foundation 8184610Salfred * by Lennart Augustsson (lennart@augustsson.net) at 9184610Salfred * Carlstedt Research & Technology. 10184610Salfred * 11184610Salfred * Redistribution and use in source and binary forms, with or without 12184610Salfred * modification, are permitted provided that the following conditions 13184610Salfred * are met: 14184610Salfred * 1. Redistributions of source code must retain the above copyright 15184610Salfred * notice, this list of conditions and the following disclaimer. 16184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 17184610Salfred * notice, this list of conditions and the following disclaimer in the 18184610Salfred * documentation and/or other materials provided with the distribution. 19184610Salfred * 20184610Salfred * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21184610Salfred * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22184610Salfred * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23184610Salfred * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24184610Salfred * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25184610Salfred * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26184610Salfred * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27184610Salfred * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28184610Salfred * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29184610Salfred * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30184610Salfred * POSSIBILITY OF SUCH DAMAGE. 31184610Salfred */ 32184610Salfred 33246122Shselasky#ifdef USB_GLOBAL_INCLUDE_FILE 34246122Shselasky#include USB_GLOBAL_INCLUDE_FILE 35246122Shselasky#else 36194677Sthompsa#include <sys/stdint.h> 37194677Sthompsa#include <sys/stddef.h> 38194677Sthompsa#include <sys/param.h> 39194677Sthompsa#include <sys/queue.h> 40194677Sthompsa#include <sys/types.h> 41194677Sthompsa#include <sys/systm.h> 42194677Sthompsa#include <sys/kernel.h> 43194677Sthompsa#include <sys/bus.h> 44194677Sthompsa#include <sys/module.h> 45194677Sthompsa#include <sys/lock.h> 46194677Sthompsa#include <sys/mutex.h> 47194677Sthompsa#include <sys/condvar.h> 48194677Sthompsa#include <sys/sysctl.h> 49194677Sthompsa#include <sys/sx.h> 50194677Sthompsa#include <sys/unistd.h> 51194677Sthompsa#include <sys/callout.h> 52194677Sthompsa#include <sys/malloc.h> 53194677Sthompsa#include <sys/priv.h> 54194677Sthompsa 55188942Sthompsa#include <dev/usb/usb.h> 56194677Sthompsa#include <dev/usb/usbdi.h> 57194677Sthompsa#include <dev/usb/usbdi_util.h> 58188942Sthompsa#include <dev/usb/usbhid.h> 59184610Salfred 60194228Sthompsa#define USB_DEBUG_VAR usb_debug 61184610Salfred 62188942Sthompsa#include <dev/usb/usb_core.h> 63188942Sthompsa#include <dev/usb/usb_debug.h> 64188942Sthompsa#include <dev/usb/usb_process.h> 65188942Sthompsa#include <dev/usb/usb_device.h> 66188942Sthompsa#include <dev/usb/usb_request.h> 67246122Shselasky#endif /* USB_GLOBAL_INCLUDE_FILE */ 68184610Salfred 69184610Salfredstatic void hid_clear_local(struct hid_item *); 70189547Sthompsastatic uint8_t hid_get_byte(struct hid_data *s, const uint16_t wSize); 71184610Salfred 72189547Sthompsa#define MAXUSAGE 64 73189547Sthompsa#define MAXPUSH 4 74195967Salfred#define MAXID 16 75195967Salfred 76195967Salfredstruct hid_pos_data { 77195967Salfred int32_t rid; 78195967Salfred uint32_t pos; 79195967Salfred}; 80195967Salfred 81184610Salfredstruct hid_data { 82184610Salfred const uint8_t *start; 83184610Salfred const uint8_t *end; 84184610Salfred const uint8_t *p; 85189547Sthompsa struct hid_item cur[MAXPUSH]; 86195967Salfred struct hid_pos_data last_pos[MAXID]; 87189547Sthompsa int32_t usages_min[MAXUSAGE]; 88189547Sthompsa int32_t usages_max[MAXUSAGE]; 89189718Sthompsa int32_t usage_last; /* last seen usage */ 90189718Sthompsa uint32_t loc_size; /* last seen size */ 91189718Sthompsa uint32_t loc_count; /* last seen count */ 92189718Sthompsa uint8_t kindset; /* we have 5 kinds so 8 bits are enough */ 93189547Sthompsa uint8_t pushlevel; /* current pushlevel */ 94189547Sthompsa uint8_t ncount; /* end usage item count */ 95189547Sthompsa uint8_t icount; /* current usage item count */ 96189547Sthompsa uint8_t nusage; /* end "usages_min/max" index */ 97189547Sthompsa uint8_t iusage; /* current "usages_min/max" index */ 98189547Sthompsa uint8_t ousage; /* current "usages_min/max" offset */ 99189547Sthompsa uint8_t susage; /* usage set flags */ 100184610Salfred}; 101184610Salfred 102184610Salfred/*------------------------------------------------------------------------* 103184610Salfred * hid_clear_local 104184610Salfred *------------------------------------------------------------------------*/ 105184610Salfredstatic void 106184610Salfredhid_clear_local(struct hid_item *c) 107184610Salfred{ 108184610Salfred 109189547Sthompsa c->loc.count = 0; 110189547Sthompsa c->loc.size = 0; 111184610Salfred c->usage = 0; 112184610Salfred c->usage_minimum = 0; 113184610Salfred c->usage_maximum = 0; 114184610Salfred c->designator_index = 0; 115184610Salfred c->designator_minimum = 0; 116184610Salfred c->designator_maximum = 0; 117184610Salfred c->string_index = 0; 118184610Salfred c->string_minimum = 0; 119184610Salfred c->string_maximum = 0; 120184610Salfred c->set_delimiter = 0; 121184610Salfred} 122184610Salfred 123195967Salfredstatic void 124195967Salfredhid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t next_rID) 125195967Salfred{ 126195967Salfred uint8_t i; 127195967Salfred 128195967Salfred /* check for same report ID - optimise */ 129195967Salfred 130195967Salfred if (c->report_ID == next_rID) 131195967Salfred return; 132195967Salfred 133195967Salfred /* save current position for current rID */ 134195967Salfred 135195967Salfred if (c->report_ID == 0) { 136195967Salfred i = 0; 137195967Salfred } else { 138195967Salfred for (i = 1; i != MAXID; i++) { 139195967Salfred if (s->last_pos[i].rid == c->report_ID) 140195967Salfred break; 141195967Salfred if (s->last_pos[i].rid == 0) 142195967Salfred break; 143195967Salfred } 144195967Salfred } 145195967Salfred if (i != MAXID) { 146195967Salfred s->last_pos[i].rid = c->report_ID; 147195967Salfred s->last_pos[i].pos = c->loc.pos; 148195967Salfred } 149195967Salfred 150195967Salfred /* store next report ID */ 151195967Salfred 152195967Salfred c->report_ID = next_rID; 153195967Salfred 154195967Salfred /* lookup last position for next rID */ 155195967Salfred 156195967Salfred if (next_rID == 0) { 157195967Salfred i = 0; 158195967Salfred } else { 159195967Salfred for (i = 1; i != MAXID; i++) { 160195967Salfred if (s->last_pos[i].rid == next_rID) 161195967Salfred break; 162195967Salfred if (s->last_pos[i].rid == 0) 163195967Salfred break; 164195967Salfred } 165195967Salfred } 166195967Salfred if (i != MAXID) { 167195967Salfred s->last_pos[i].rid = next_rID; 168195967Salfred c->loc.pos = s->last_pos[i].pos; 169195967Salfred } else { 170195967Salfred DPRINTF("Out of RID entries, position is set to zero!\n"); 171195967Salfred c->loc.pos = 0; 172195967Salfred } 173195967Salfred} 174195967Salfred 175184610Salfred/*------------------------------------------------------------------------* 176184610Salfred * hid_start_parse 177184610Salfred *------------------------------------------------------------------------*/ 178184610Salfredstruct hid_data * 179193074Sthompsahid_start_parse(const void *d, usb_size_t len, int kindset) 180184610Salfred{ 181184610Salfred struct hid_data *s; 182184610Salfred 183189547Sthompsa if ((kindset-1) & kindset) { 184189547Sthompsa DPRINTFN(0, "Only one bit can be " 185189547Sthompsa "set in the kindset\n"); 186189547Sthompsa return (NULL); 187189547Sthompsa } 188189547Sthompsa 189184610Salfred s = malloc(sizeof *s, M_TEMP, M_WAITOK | M_ZERO); 190184610Salfred s->start = s->p = d; 191184610Salfred s->end = ((const uint8_t *)d) + len; 192184610Salfred s->kindset = kindset; 193184610Salfred return (s); 194184610Salfred} 195184610Salfred 196184610Salfred/*------------------------------------------------------------------------* 197184610Salfred * hid_end_parse 198184610Salfred *------------------------------------------------------------------------*/ 199184610Salfredvoid 200184610Salfredhid_end_parse(struct hid_data *s) 201184610Salfred{ 202189547Sthompsa if (s == NULL) 203189547Sthompsa return; 204184610Salfred 205184610Salfred free(s, M_TEMP); 206184610Salfred} 207184610Salfred 208184610Salfred/*------------------------------------------------------------------------* 209189547Sthompsa * get byte from HID descriptor 210189547Sthompsa *------------------------------------------------------------------------*/ 211189547Sthompsastatic uint8_t 212189547Sthompsahid_get_byte(struct hid_data *s, const uint16_t wSize) 213189547Sthompsa{ 214189547Sthompsa const uint8_t *ptr; 215189547Sthompsa uint8_t retval; 216189547Sthompsa 217189547Sthompsa ptr = s->p; 218189547Sthompsa 219189547Sthompsa /* check if end is reached */ 220189547Sthompsa if (ptr == s->end) 221189547Sthompsa return (0); 222189547Sthompsa 223189547Sthompsa /* read out a byte */ 224189547Sthompsa retval = *ptr; 225189547Sthompsa 226189547Sthompsa /* check if data pointer can be advanced by "wSize" bytes */ 227189547Sthompsa if ((s->end - ptr) < wSize) 228189547Sthompsa ptr = s->end; 229189547Sthompsa else 230189547Sthompsa ptr += wSize; 231189547Sthompsa 232189547Sthompsa /* update pointer */ 233189547Sthompsa s->p = ptr; 234189547Sthompsa 235189547Sthompsa return (retval); 236189547Sthompsa} 237189547Sthompsa 238189547Sthompsa/*------------------------------------------------------------------------* 239184610Salfred * hid_get_item 240184610Salfred *------------------------------------------------------------------------*/ 241184610Salfredint 242184610Salfredhid_get_item(struct hid_data *s, struct hid_item *h) 243184610Salfred{ 244189547Sthompsa struct hid_item *c; 245184610Salfred unsigned int bTag, bType, bSize; 246184610Salfred uint32_t oldpos; 247189547Sthompsa int32_t mask; 248184610Salfred int32_t dval; 249184610Salfred 250189547Sthompsa if (s == NULL) 251189547Sthompsa return (0); 252189547Sthompsa 253189547Sthompsa c = &s->cur[s->pushlevel]; 254189547Sthompsa 255189547Sthompsa top: 256189547Sthompsa /* check if there is an array of items */ 257189718Sthompsa if (s->icount < s->ncount) { 258189718Sthompsa /* get current usage */ 259189718Sthompsa if (s->iusage < s->nusage) { 260189718Sthompsa dval = s->usages_min[s->iusage] + s->ousage; 261189718Sthompsa c->usage = dval; 262189718Sthompsa s->usage_last = dval; 263189718Sthompsa if (dval == s->usages_max[s->iusage]) { 264189718Sthompsa s->iusage ++; 265189718Sthompsa s->ousage = 0; 266189718Sthompsa } else { 267189718Sthompsa s->ousage ++; 268189718Sthompsa } 269189547Sthompsa } else { 270189718Sthompsa DPRINTFN(1, "Using last usage\n"); 271189718Sthompsa dval = s->usage_last; 272189547Sthompsa } 273189547Sthompsa s->icount ++; 274189547Sthompsa /* 275189547Sthompsa * Only copy HID item, increment position and return 276189547Sthompsa * if correct kindset! 277189547Sthompsa */ 278189547Sthompsa if (s->kindset & (1 << c->kind)) { 279184610Salfred *h = *c; 280189547Sthompsa DPRINTFN(1, "%u,%u,%u\n", h->loc.pos, 281189547Sthompsa h->loc.size, h->loc.count); 282189547Sthompsa c->loc.pos += c->loc.size * c->loc.count; 283184610Salfred return (1); 284184610Salfred } 285184610Salfred } 286184610Salfred 287189547Sthompsa /* reset state variables */ 288189547Sthompsa s->icount = 0; 289189547Sthompsa s->ncount = 0; 290189547Sthompsa s->iusage = 0; 291189547Sthompsa s->nusage = 0; 292189547Sthompsa s->susage = 0; 293189547Sthompsa s->ousage = 0; 294189547Sthompsa hid_clear_local(c); 295189547Sthompsa 296189547Sthompsa /* get next item */ 297189547Sthompsa while (s->p != s->end) { 298189547Sthompsa 299189547Sthompsa bSize = hid_get_byte(s, 1); 300184610Salfred if (bSize == 0xfe) { 301184610Salfred /* long item */ 302189547Sthompsa bSize = hid_get_byte(s, 1); 303189547Sthompsa bSize |= hid_get_byte(s, 1) << 8; 304189547Sthompsa bTag = hid_get_byte(s, 1); 305184610Salfred bType = 0xff; /* XXX what should it be */ 306184610Salfred } else { 307184610Salfred /* short item */ 308184610Salfred bTag = bSize >> 4; 309184610Salfred bType = (bSize >> 2) & 3; 310184610Salfred bSize &= 3; 311184610Salfred if (bSize == 3) 312184610Salfred bSize = 4; 313184610Salfred } 314184610Salfred switch (bSize) { 315184610Salfred case 0: 316184610Salfred dval = 0; 317189547Sthompsa mask = 0; 318184610Salfred break; 319184610Salfred case 1: 320189547Sthompsa dval = (int8_t)hid_get_byte(s, 1); 321189547Sthompsa mask = 0xFF; 322184610Salfred break; 323184610Salfred case 2: 324189547Sthompsa dval = hid_get_byte(s, 1); 325189547Sthompsa dval |= hid_get_byte(s, 1) << 8; 326184610Salfred dval = (int16_t)dval; 327189547Sthompsa mask = 0xFFFF; 328184610Salfred break; 329184610Salfred case 4: 330189547Sthompsa dval = hid_get_byte(s, 1); 331189547Sthompsa dval |= hid_get_byte(s, 1) << 8; 332189547Sthompsa dval |= hid_get_byte(s, 1) << 16; 333189547Sthompsa dval |= hid_get_byte(s, 1) << 24; 334189547Sthompsa mask = 0xFFFFFFFF; 335184610Salfred break; 336184610Salfred default: 337189547Sthompsa dval = hid_get_byte(s, bSize); 338189547Sthompsa DPRINTFN(0, "bad length %u (data=0x%02x)\n", 339189547Sthompsa bSize, dval); 340184610Salfred continue; 341184610Salfred } 342184610Salfred 343184610Salfred switch (bType) { 344184610Salfred case 0: /* Main */ 345184610Salfred switch (bTag) { 346184610Salfred case 8: /* Input */ 347184610Salfred c->kind = hid_input; 348184610Salfred c->flags = dval; 349184610Salfred ret: 350189718Sthompsa c->loc.count = s->loc_count; 351189718Sthompsa c->loc.size = s->loc_size; 352189718Sthompsa 353184610Salfred if (c->flags & HIO_VARIABLE) { 354189547Sthompsa /* range check usage count */ 355189547Sthompsa if (c->loc.count > 255) { 356189547Sthompsa DPRINTFN(0, "Number of " 357296444Shselasky "items(%u) truncated to 255\n", 358296444Shselasky (unsigned)(c->loc.count)); 359189547Sthompsa s->ncount = 255; 360189547Sthompsa } else 361189547Sthompsa s->ncount = c->loc.count; 362189547Sthompsa 363189547Sthompsa /* 364189547Sthompsa * The "top" loop will return 365189547Sthompsa * one and one item: 366189547Sthompsa */ 367184610Salfred c->loc.count = 1; 368189547Sthompsa } else { 369189547Sthompsa s->ncount = 1; 370184610Salfred } 371189547Sthompsa goto top; 372189547Sthompsa 373184610Salfred case 9: /* Output */ 374184610Salfred c->kind = hid_output; 375184610Salfred c->flags = dval; 376184610Salfred goto ret; 377184610Salfred case 10: /* Collection */ 378184610Salfred c->kind = hid_collection; 379184610Salfred c->collection = dval; 380184610Salfred c->collevel++; 381192055Sthompsa c->usage = s->usage_last; 382184610Salfred *h = *c; 383184610Salfred return (1); 384184610Salfred case 11: /* Feature */ 385184610Salfred c->kind = hid_feature; 386184610Salfred c->flags = dval; 387184610Salfred goto ret; 388184610Salfred case 12: /* End collection */ 389184610Salfred c->kind = hid_endcollection; 390189547Sthompsa if (c->collevel == 0) { 391189547Sthompsa DPRINTFN(0, "invalid end collection\n"); 392189547Sthompsa return (0); 393189547Sthompsa } 394184610Salfred c->collevel--; 395184610Salfred *h = *c; 396184610Salfred return (1); 397184610Salfred default: 398189547Sthompsa DPRINTFN(0, "Main bTag=%d\n", bTag); 399184610Salfred break; 400184610Salfred } 401184610Salfred break; 402184610Salfred case 1: /* Global */ 403184610Salfred switch (bTag) { 404184610Salfred case 0: 405184610Salfred c->_usage_page = dval << 16; 406184610Salfred break; 407184610Salfred case 1: 408184610Salfred c->logical_minimum = dval; 409184610Salfred break; 410184610Salfred case 2: 411184610Salfred c->logical_maximum = dval; 412184610Salfred break; 413184610Salfred case 3: 414184610Salfred c->physical_minimum = dval; 415184610Salfred break; 416184610Salfred case 4: 417184610Salfred c->physical_maximum = dval; 418184610Salfred break; 419184610Salfred case 5: 420184610Salfred c->unit_exponent = dval; 421184610Salfred break; 422184610Salfred case 6: 423184610Salfred c->unit = dval; 424184610Salfred break; 425184610Salfred case 7: 426189718Sthompsa /* mask because value is unsigned */ 427189718Sthompsa s->loc_size = dval & mask; 428184610Salfred break; 429184610Salfred case 8: 430235510Smav hid_switch_rid(s, c, dval & mask); 431184610Salfred break; 432184610Salfred case 9: 433189718Sthompsa /* mask because value is unsigned */ 434189718Sthompsa s->loc_count = dval & mask; 435184610Salfred break; 436184610Salfred case 10: /* Push */ 437361921Shselasky /* stop parsing, if invalid push level */ 438361921Shselasky if ((s->pushlevel + 1) >= MAXPUSH) { 439361921Shselasky DPRINTFN(0, "Cannot push item @ %d\n", s->pushlevel); 440361921Shselasky return (0); 441361921Shselasky } 442189547Sthompsa s->pushlevel ++; 443361921Shselasky s->cur[s->pushlevel] = *c; 444361921Shselasky /* store size and count */ 445361921Shselasky c->loc.size = s->loc_size; 446361921Shselasky c->loc.count = s->loc_count; 447361921Shselasky /* update current item pointer */ 448361921Shselasky c = &s->cur[s->pushlevel]; 449184610Salfred break; 450184610Salfred case 11: /* Pop */ 451361921Shselasky /* stop parsing, if invalid push level */ 452361921Shselasky if (s->pushlevel == 0) { 453361921Shselasky DPRINTFN(0, "Cannot pop item @ 0\n"); 454361921Shselasky return (0); 455361921Shselasky } 456189547Sthompsa s->pushlevel --; 457361921Shselasky /* preserve position */ 458361921Shselasky oldpos = c->loc.pos; 459361921Shselasky c = &s->cur[s->pushlevel]; 460361921Shselasky /* restore size and count */ 461361921Shselasky s->loc_size = c->loc.size; 462361921Shselasky s->loc_count = c->loc.count; 463361921Shselasky /* set default item location */ 464361921Shselasky c->loc.pos = oldpos; 465361921Shselasky c->loc.size = 0; 466361921Shselasky c->loc.count = 0; 467184610Salfred break; 468184610Salfred default: 469189547Sthompsa DPRINTFN(0, "Global bTag=%d\n", bTag); 470184610Salfred break; 471184610Salfred } 472184610Salfred break; 473184610Salfred case 2: /* Local */ 474184610Salfred switch (bTag) { 475184610Salfred case 0: 476189547Sthompsa if (bSize != 4) 477189547Sthompsa dval = (dval & mask) | c->_usage_page; 478189547Sthompsa 479192055Sthompsa /* set last usage, in case of a collection */ 480192055Sthompsa s->usage_last = dval; 481192055Sthompsa 482189547Sthompsa if (s->nusage < MAXUSAGE) { 483189547Sthompsa s->usages_min[s->nusage] = dval; 484189547Sthompsa s->usages_max[s->nusage] = dval; 485189547Sthompsa s->nusage ++; 486189547Sthompsa } else { 487199816Sthompsa DPRINTFN(0, "max usage reached\n"); 488189547Sthompsa } 489189547Sthompsa 490189547Sthompsa /* clear any pending usage sets */ 491189547Sthompsa s->susage = 0; 492184610Salfred break; 493184610Salfred case 1: 494189547Sthompsa s->susage |= 1; 495189547Sthompsa 496189547Sthompsa if (bSize != 4) 497189547Sthompsa dval = (dval & mask) | c->_usage_page; 498184610Salfred c->usage_minimum = dval; 499189547Sthompsa 500189547Sthompsa goto check_set; 501184610Salfred case 2: 502189547Sthompsa s->susage |= 2; 503189547Sthompsa 504189547Sthompsa if (bSize != 4) 505189547Sthompsa dval = (dval & mask) | c->_usage_page; 506184610Salfred c->usage_maximum = dval; 507189547Sthompsa 508189547Sthompsa check_set: 509189547Sthompsa if (s->susage != 3) 510189547Sthompsa break; 511189547Sthompsa 512189547Sthompsa /* sanity check */ 513189547Sthompsa if ((s->nusage < MAXUSAGE) && 514189776Sthompsa (c->usage_minimum <= c->usage_maximum)) { 515189547Sthompsa /* add usage range */ 516189547Sthompsa s->usages_min[s->nusage] = 517189547Sthompsa c->usage_minimum; 518189547Sthompsa s->usages_max[s->nusage] = 519189547Sthompsa c->usage_maximum; 520189547Sthompsa s->nusage ++; 521189547Sthompsa } else { 522199816Sthompsa DPRINTFN(0, "Usage set dropped\n"); 523189547Sthompsa } 524189547Sthompsa s->susage = 0; 525184610Salfred break; 526184610Salfred case 3: 527184610Salfred c->designator_index = dval; 528184610Salfred break; 529184610Salfred case 4: 530184610Salfred c->designator_minimum = dval; 531184610Salfred break; 532184610Salfred case 5: 533184610Salfred c->designator_maximum = dval; 534184610Salfred break; 535184610Salfred case 7: 536184610Salfred c->string_index = dval; 537184610Salfred break; 538184610Salfred case 8: 539184610Salfred c->string_minimum = dval; 540184610Salfred break; 541184610Salfred case 9: 542184610Salfred c->string_maximum = dval; 543184610Salfred break; 544184610Salfred case 10: 545184610Salfred c->set_delimiter = dval; 546184610Salfred break; 547184610Salfred default: 548189547Sthompsa DPRINTFN(0, "Local bTag=%d\n", bTag); 549184610Salfred break; 550184610Salfred } 551184610Salfred break; 552184610Salfred default: 553189547Sthompsa DPRINTFN(0, "default bType=%d\n", bType); 554184610Salfred break; 555184610Salfred } 556184610Salfred } 557189547Sthompsa return (0); 558184610Salfred} 559184610Salfred 560184610Salfred/*------------------------------------------------------------------------* 561184610Salfred * hid_report_size 562184610Salfred *------------------------------------------------------------------------*/ 563184610Salfredint 564193074Sthompsahid_report_size(const void *buf, usb_size_t len, enum hid_kind k, uint8_t *id) 565184610Salfred{ 566184610Salfred struct hid_data *d; 567184610Salfred struct hid_item h; 568188981Sthompsa uint32_t temp; 569188981Sthompsa uint32_t hpos; 570188981Sthompsa uint32_t lpos; 571188981Sthompsa uint8_t any_id; 572184610Salfred 573188981Sthompsa any_id = 0; 574188981Sthompsa hpos = 0; 575188981Sthompsa lpos = 0xFFFFFFFF; 576188981Sthompsa 577188981Sthompsa for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);) { 578184610Salfred if (h.kind == k) { 579188981Sthompsa /* check for ID-byte presense */ 580188981Sthompsa if ((h.report_ID != 0) && !any_id) { 581188981Sthompsa if (id != NULL) 582188981Sthompsa *id = h.report_ID; 583188981Sthompsa any_id = 1; 584184610Salfred } 585188981Sthompsa /* compute minimum */ 586188981Sthompsa if (lpos > h.loc.pos) 587188981Sthompsa lpos = h.loc.pos; 588188981Sthompsa /* compute end position */ 589188981Sthompsa temp = h.loc.pos + (h.loc.size * h.loc.count); 590188981Sthompsa /* compute maximum */ 591188981Sthompsa if (hpos < temp) 592188981Sthompsa hpos = temp; 593184610Salfred } 594188981Sthompsa } 595184610Salfred hid_end_parse(d); 596188981Sthompsa 597188981Sthompsa /* safety check - can happen in case of currupt descriptors */ 598188981Sthompsa if (lpos > hpos) 599188981Sthompsa temp = 0; 600188981Sthompsa else 601188981Sthompsa temp = hpos - lpos; 602188981Sthompsa 603188981Sthompsa /* check for ID byte */ 604188981Sthompsa if (any_id) 605188981Sthompsa temp += 8; 606188981Sthompsa else if (id != NULL) 607188981Sthompsa *id = 0; 608188981Sthompsa 609188981Sthompsa /* return length in bytes rounded up */ 610188981Sthompsa return ((temp + 7) / 8); 611184610Salfred} 612184610Salfred 613184610Salfred/*------------------------------------------------------------------------* 614184610Salfred * hid_locate 615184610Salfred *------------------------------------------------------------------------*/ 616184610Salfredint 617233774Shselaskyhid_locate(const void *desc, usb_size_t size, int32_t u, enum hid_kind k, 618190741Sthompsa uint8_t index, struct hid_location *loc, uint32_t *flags, uint8_t *id) 619184610Salfred{ 620184610Salfred struct hid_data *d; 621184610Salfred struct hid_item h; 622184610Salfred 623184610Salfred for (d = hid_start_parse(desc, size, 1 << k); hid_get_item(d, &h);) { 624184610Salfred if (h.kind == k && !(h.flags & HIO_CONST) && h.usage == u) { 625190741Sthompsa if (index--) 626190741Sthompsa continue; 627184610Salfred if (loc != NULL) 628184610Salfred *loc = h.loc; 629184610Salfred if (flags != NULL) 630184610Salfred *flags = h.flags; 631188981Sthompsa if (id != NULL) 632188981Sthompsa *id = h.report_ID; 633184610Salfred hid_end_parse(d); 634184610Salfred return (1); 635184610Salfred } 636184610Salfred } 637188981Sthompsa if (loc != NULL) 638188981Sthompsa loc->size = 0; 639188981Sthompsa if (flags != NULL) 640188981Sthompsa *flags = 0; 641188981Sthompsa if (id != NULL) 642188981Sthompsa *id = 0; 643184610Salfred hid_end_parse(d); 644184610Salfred return (0); 645184610Salfred} 646184610Salfred 647184610Salfred/*------------------------------------------------------------------------* 648184610Salfred * hid_get_data 649184610Salfred *------------------------------------------------------------------------*/ 650208012Sthompsastatic uint32_t 651208012Sthompsahid_get_data_sub(const uint8_t *buf, usb_size_t len, struct hid_location *loc, 652208012Sthompsa int is_signed) 653184610Salfred{ 654184610Salfred uint32_t hpos = loc->pos; 655184610Salfred uint32_t hsize = loc->size; 656184610Salfred uint32_t data; 657188981Sthompsa uint32_t rpos; 658188981Sthompsa uint8_t n; 659184610Salfred 660184610Salfred DPRINTFN(11, "hid_get_data: loc %d/%d\n", hpos, hsize); 661184610Salfred 662188981Sthompsa /* Range check and limit */ 663184610Salfred if (hsize == 0) 664184610Salfred return (0); 665188981Sthompsa if (hsize > 32) 666188981Sthompsa hsize = 32; 667184610Salfred 668188981Sthompsa /* Get data in a safe way */ 669184610Salfred data = 0; 670188981Sthompsa rpos = (hpos / 8); 671188981Sthompsa n = (hsize + 7) / 8; 672188981Sthompsa rpos += n; 673188981Sthompsa while (n--) { 674188981Sthompsa rpos--; 675188981Sthompsa if (rpos < len) 676188981Sthompsa data |= buf[rpos] << (8 * n); 677184610Salfred } 678188981Sthompsa 679188981Sthompsa /* Correctly shift down data */ 680188981Sthompsa data = (data >> (hpos % 8)); 681208012Sthompsa n = 32 - hsize; 682188981Sthompsa 683188981Sthompsa /* Mask and sign extend in one */ 684208012Sthompsa if (is_signed != 0) 685208012Sthompsa data = (int32_t)((int32_t)data << n) >> n; 686208012Sthompsa else 687208012Sthompsa data = (uint32_t)((uint32_t)data << n) >> n; 688188981Sthompsa 689184610Salfred DPRINTFN(11, "hid_get_data: loc %d/%d = %lu\n", 690184610Salfred loc->pos, loc->size, (long)data); 691184610Salfred return (data); 692184610Salfred} 693184610Salfred 694208012Sthompsaint32_t 695208012Sthompsahid_get_data(const uint8_t *buf, usb_size_t len, struct hid_location *loc) 696208012Sthompsa{ 697208012Sthompsa return (hid_get_data_sub(buf, len, loc, 1)); 698208012Sthompsa} 699208012Sthompsa 700208012Sthompsauint32_t 701208012Sthompsahid_get_data_unsigned(const uint8_t *buf, usb_size_t len, struct hid_location *loc) 702208012Sthompsa{ 703208012Sthompsa return (hid_get_data_sub(buf, len, loc, 0)); 704208012Sthompsa} 705208012Sthompsa 706184610Salfred/*------------------------------------------------------------------------* 707223755Shselasky * hid_put_data 708223755Shselasky *------------------------------------------------------------------------*/ 709223755Shselaskyvoid 710223755Shselaskyhid_put_data_unsigned(uint8_t *buf, usb_size_t len, 711223755Shselasky struct hid_location *loc, unsigned int value) 712223755Shselasky{ 713223755Shselasky uint32_t hpos = loc->pos; 714223755Shselasky uint32_t hsize = loc->size; 715223755Shselasky uint64_t data; 716223755Shselasky uint64_t mask; 717223755Shselasky uint32_t rpos; 718223755Shselasky uint8_t n; 719223755Shselasky 720223755Shselasky DPRINTFN(11, "hid_put_data: loc %d/%d = %u\n", hpos, hsize, value); 721223755Shselasky 722223755Shselasky /* Range check and limit */ 723223755Shselasky if (hsize == 0) 724223755Shselasky return; 725223755Shselasky if (hsize > 32) 726223755Shselasky hsize = 32; 727223755Shselasky 728223755Shselasky /* Put data in a safe way */ 729223755Shselasky rpos = (hpos / 8); 730223755Shselasky n = (hsize + 7) / 8; 731223755Shselasky data = ((uint64_t)value) << (hpos % 8); 732223755Shselasky mask = ((1ULL << hsize) - 1ULL) << (hpos % 8); 733223755Shselasky rpos += n; 734223755Shselasky while (n--) { 735223755Shselasky rpos--; 736223755Shselasky if (rpos < len) { 737223755Shselasky buf[rpos] &= ~(mask >> (8 * n)); 738223755Shselasky buf[rpos] |= (data >> (8 * n)); 739223755Shselasky } 740223755Shselasky } 741223755Shselasky} 742223755Shselasky 743223755Shselasky/*------------------------------------------------------------------------* 744184610Salfred * hid_is_collection 745184610Salfred *------------------------------------------------------------------------*/ 746184610Salfredint 747233774Shselaskyhid_is_collection(const void *desc, usb_size_t size, int32_t usage) 748184610Salfred{ 749184610Salfred struct hid_data *hd; 750184610Salfred struct hid_item hi; 751184610Salfred int err; 752184610Salfred 753184610Salfred hd = hid_start_parse(desc, size, hid_input); 754184610Salfred if (hd == NULL) 755184610Salfred return (0); 756184610Salfred 757192055Sthompsa while ((err = hid_get_item(hd, &hi))) { 758192055Sthompsa if (hi.kind == hid_collection && 759192055Sthompsa hi.usage == usage) 760192055Sthompsa break; 761192055Sthompsa } 762184610Salfred hid_end_parse(hd); 763184610Salfred return (err); 764184610Salfred} 765184610Salfred 766184610Salfred/*------------------------------------------------------------------------* 767184610Salfred * hid_get_descriptor_from_usb 768184610Salfred * 769184610Salfred * This function will search for a HID descriptor between two USB 770184610Salfred * interface descriptors. 771184610Salfred * 772184610Salfred * Return values: 773184610Salfred * NULL: No more HID descriptors. 774184610Salfred * Else: Pointer to HID descriptor. 775184610Salfred *------------------------------------------------------------------------*/ 776192984Sthompsastruct usb_hid_descriptor * 777192984Sthompsahid_get_descriptor_from_usb(struct usb_config_descriptor *cd, 778192984Sthompsa struct usb_interface_descriptor *id) 779184610Salfred{ 780192984Sthompsa struct usb_descriptor *desc = (void *)id; 781184610Salfred 782184610Salfred if (desc == NULL) { 783184610Salfred return (NULL); 784184610Salfred } 785194228Sthompsa while ((desc = usb_desc_foreach(cd, desc))) { 786184610Salfred if ((desc->bDescriptorType == UDESC_HID) && 787184610Salfred (desc->bLength >= USB_HID_DESCRIPTOR_SIZE(0))) { 788184610Salfred return (void *)desc; 789184610Salfred } 790184610Salfred if (desc->bDescriptorType == UDESC_INTERFACE) { 791184610Salfred break; 792184610Salfred } 793184610Salfred } 794184610Salfred return (NULL); 795184610Salfred} 796184610Salfred 797184610Salfred/*------------------------------------------------------------------------* 798194228Sthompsa * usbd_req_get_hid_desc 799184610Salfred * 800184610Salfred * This function will read out an USB report descriptor from the USB 801184610Salfred * device. 802184610Salfred * 803184610Salfred * Return values: 804184610Salfred * NULL: Failure. 805184610Salfred * Else: Success. The pointer should eventually be passed to free(). 806184610Salfred *------------------------------------------------------------------------*/ 807193045Sthompsausb_error_t 808194228Sthompsausbd_req_get_hid_desc(struct usb_device *udev, struct mtx *mtx, 809184610Salfred void **descp, uint16_t *sizep, 810193045Sthompsa struct malloc_type *mem, uint8_t iface_index) 811184610Salfred{ 812194228Sthompsa struct usb_interface *iface = usbd_get_iface(udev, iface_index); 813192984Sthompsa struct usb_hid_descriptor *hid; 814193045Sthompsa usb_error_t err; 815184610Salfred 816184610Salfred if ((iface == NULL) || (iface->idesc == NULL)) { 817184610Salfred return (USB_ERR_INVAL); 818184610Salfred } 819184610Salfred hid = hid_get_descriptor_from_usb 820194228Sthompsa (usbd_get_config_descriptor(udev), iface->idesc); 821184610Salfred 822184610Salfred if (hid == NULL) { 823184610Salfred return (USB_ERR_IOERROR); 824184610Salfred } 825184610Salfred *sizep = UGETW(hid->descrs[0].wDescriptorLength); 826184610Salfred if (*sizep == 0) { 827184610Salfred return (USB_ERR_IOERROR); 828184610Salfred } 829184610Salfred if (mtx) 830184610Salfred mtx_unlock(mtx); 831184610Salfred 832184610Salfred *descp = malloc(*sizep, mem, M_ZERO | M_WAITOK); 833184610Salfred 834184610Salfred if (mtx) 835184610Salfred mtx_lock(mtx); 836184610Salfred 837184610Salfred if (*descp == NULL) { 838184610Salfred return (USB_ERR_NOMEM); 839184610Salfred } 840194228Sthompsa err = usbd_req_get_report_descriptor 841184610Salfred (udev, mtx, *descp, *sizep, iface_index); 842184610Salfred 843184610Salfred if (err) { 844184610Salfred free(*descp, mem); 845184610Salfred *descp = NULL; 846184610Salfred return (err); 847184610Salfred } 848184610Salfred return (USB_ERR_NORMAL_COMPLETION); 849184610Salfred} 850245248Shselasky 851245248Shselasky/*------------------------------------------------------------------------* 852245248Shselasky * hid_is_mouse 853245248Shselasky * 854245248Shselasky * This function will decide if a USB descriptor belongs to a USB mouse. 855245248Shselasky * 856245248Shselasky * Return values: 857245248Shselasky * Zero: Not a USB mouse. 858245248Shselasky * Else: Is a USB mouse. 859245248Shselasky *------------------------------------------------------------------------*/ 860245248Shselaskyint 861245248Shselaskyhid_is_mouse(const void *d_ptr, uint16_t d_len) 862245248Shselasky{ 863245248Shselasky struct hid_data *hd; 864245248Shselasky struct hid_item hi; 865245248Shselasky int mdepth; 866245248Shselasky int found; 867245248Shselasky 868245248Shselasky hd = hid_start_parse(d_ptr, d_len, 1 << hid_input); 869245248Shselasky if (hd == NULL) 870245248Shselasky return (0); 871245248Shselasky 872245248Shselasky mdepth = 0; 873245248Shselasky found = 0; 874245248Shselasky 875245248Shselasky while (hid_get_item(hd, &hi)) { 876245248Shselasky switch (hi.kind) { 877245248Shselasky case hid_collection: 878245248Shselasky if (mdepth != 0) 879245248Shselasky mdepth++; 880245248Shselasky else if (hi.collection == 1 && 881245248Shselasky hi.usage == 882245248Shselasky HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)) 883245248Shselasky mdepth++; 884245248Shselasky break; 885245248Shselasky case hid_endcollection: 886245248Shselasky if (mdepth != 0) 887245248Shselasky mdepth--; 888245248Shselasky break; 889245248Shselasky case hid_input: 890245248Shselasky if (mdepth == 0) 891245248Shselasky break; 892245248Shselasky if (hi.usage == 893245248Shselasky HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X) && 894245248Shselasky (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE) 895245248Shselasky found++; 896245248Shselasky if (hi.usage == 897245248Shselasky HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y) && 898245248Shselasky (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE) 899245248Shselasky found++; 900245248Shselasky break; 901245248Shselasky default: 902245248Shselasky break; 903245248Shselasky } 904245248Shselasky } 905245248Shselasky hid_end_parse(hd); 906245248Shselasky return (found); 907245248Shselasky} 908245248Shselasky 909245248Shselasky/*------------------------------------------------------------------------* 910245248Shselasky * hid_is_keyboard 911245248Shselasky * 912245248Shselasky * This function will decide if a USB descriptor belongs to a USB keyboard. 913245248Shselasky * 914245248Shselasky * Return values: 915245248Shselasky * Zero: Not a USB keyboard. 916245248Shselasky * Else: Is a USB keyboard. 917245248Shselasky *------------------------------------------------------------------------*/ 918245248Shselaskyint 919245248Shselaskyhid_is_keyboard(const void *d_ptr, uint16_t d_len) 920245248Shselasky{ 921245248Shselasky if (hid_is_collection(d_ptr, d_len, 922245248Shselasky HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD))) 923245248Shselasky return (1); 924245248Shselasky return (0); 925245248Shselasky} 926