1 /* $NetBSD: hid.c,v 1.8 2024/12/19 00:50:47 jmcneill Exp $ */ 2 /* $FreeBSD: src/sys/dev/usb/hid.c,v 1.11 1999/11/17 22:33:39 n_hibma Exp $ */ 3 4 /* 5 * Copyright (c) 1998 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Lennart Augustsson (lennart@augustsson.net) at 10 * Carlstedt Research & Technology. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <sys/cdefs.h> 35 __KERNEL_RCSID(0, "$NetBSD: hid.c,v 1.8 2024/12/19 00:50:47 jmcneill Exp $"); 36 37 #ifdef _KERNEL_OPT 38 #include "opt_usb.h" 39 #endif 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/kernel.h> 44 #include <sys/kmem.h> 45 46 #include <dev/usb/usb.h> 47 #include <dev/usb/usbhid.h> 48 49 #include <dev/hid/hid.h> 50 51 #ifdef UHIDEV_DEBUG 52 #define DPRINTF(x) if (uhidevdebug) printf x 53 #define DPRINTFN(n,x) if (uhidevdebug>(n)) printf x 54 extern int uhidevdebug; 55 #else 56 #define DPRINTF(x) 57 #define DPRINTFN(n,x) 58 #endif 59 60 Static void hid_clear_local(struct hid_item *); 61 62 #define MAXUSAGE 256 63 struct hid_data { 64 const u_char *start; 65 const u_char *end; 66 const u_char *p; 67 struct hid_item cur; 68 int32_t usages[MAXUSAGE]; 69 int nu; 70 int minset; 71 int multi; 72 int multimax; 73 enum hid_kind kind; 74 }; 75 76 Static void 77 hid_clear_local(struct hid_item *c) 78 { 79 80 DPRINTFN(5,("hid_clear_local\n")); 81 c->usage = 0; 82 c->usage_minimum = 0; 83 c->usage_maximum = 0; 84 c->designator_index = 0; 85 c->designator_minimum = 0; 86 c->designator_maximum = 0; 87 c->string_index = 0; 88 c->string_minimum = 0; 89 c->string_maximum = 0; 90 c->set_delimiter = 0; 91 } 92 93 struct hid_data * 94 hid_start_parse(const void *d, int len, enum hid_kind kind) 95 { 96 struct hid_data *s; 97 98 s = kmem_zalloc(sizeof(*s), KM_SLEEP); 99 s->start = s->p = d; 100 s->end = (const char *)d + len; 101 s->kind = kind; 102 return s; 103 } 104 105 void 106 hid_end_parse(struct hid_data *s) 107 { 108 109 while (s->cur.next != NULL) { 110 struct hid_item *hi = s->cur.next->next; 111 kmem_free(s->cur.next, sizeof(*s->cur.next)); 112 s->cur.next = hi; 113 } 114 kmem_free(s, sizeof(*s)); 115 } 116 117 int 118 hid_get_item(struct hid_data *s, struct hid_item *h) 119 { 120 struct hid_item *c = &s->cur; 121 unsigned int bTag, bType, bSize; 122 uint32_t oldpos; 123 const u_char *data; 124 int32_t dval; 125 uint32_t uval; 126 const u_char *p; 127 struct hid_item *hi; 128 int i; 129 enum hid_kind retkind; 130 131 top: 132 DPRINTFN(5,("hid_get_item: multi=%d multimax=%d\n", 133 s->multi, s->multimax)); 134 if (s->multimax != 0) { 135 if (s->multi < s->multimax) { 136 c->usage = s->usages[uimin(s->multi, s->nu-1)]; 137 s->multi++; 138 *h = *c; 139 c->loc.pos += c->loc.size; 140 h->next = NULL; 141 DPRINTFN(5,("return multi\n")); 142 return 1; 143 } else { 144 c->loc.count = s->multimax; 145 s->multimax = 0; 146 s->nu = 0; 147 hid_clear_local(c); 148 } 149 } 150 for (;;) { 151 p = s->p; 152 153 if (s->end - p < 1) 154 return 0; 155 bSize = *p++; 156 157 if (bSize == 0xfe) { 158 /* long item */ 159 if (p + 3 > s->end) 160 return 0; 161 bSize = *p++; 162 bSize |= *p++ << 8; 163 bTag = *p++; 164 bType = 0xff; /* XXX what should it be */ 165 } else { 166 /* short item */ 167 bTag = bSize >> 4; 168 bType = (bSize >> 2) & 3; 169 bSize &= 3; 170 if (bSize == 3) 171 bSize = 4; 172 } 173 174 data = p; 175 if (bSize > s->end - p) 176 return 0; 177 p += bSize; 178 179 s->p = p; 180 switch(bSize) { 181 case 0: 182 dval = 0; 183 uval = dval; 184 break; 185 case 1: 186 dval = *data++; 187 uval = dval; 188 dval = (int8_t)dval; 189 break; 190 case 2: 191 dval = *data++; 192 dval |= *data++ << 8; 193 uval = dval; 194 dval = (int16_t)dval; 195 break; 196 case 4: 197 dval = *data++; 198 dval |= *data++ << 8; 199 dval |= *data++ << 16; 200 dval |= *data++ << 24; 201 uval = dval; 202 dval = (int32_t)dval; 203 break; 204 default: 205 aprint_normal("BAD LENGTH %d\n", bSize); 206 continue; 207 } 208 209 DPRINTFN(5,("hid_get_item: bType=%d bTag=%d dval=%d uval=%u\n", 210 bType, bTag, dval, uval)); 211 switch (bType) { 212 case 0: /* Main */ 213 switch (bTag) { 214 case 8: /* Input */ 215 retkind = hid_input; 216 ret: 217 if (s->kind != retkind) { 218 s->minset = 0; 219 s->nu = 0; 220 hid_clear_local(c); 221 continue; 222 } 223 c->kind = retkind; 224 c->flags = uval; 225 if (c->flags & HIO_VARIABLE) { 226 s->multimax = c->loc.count; 227 s->multi = 0; 228 c->loc.count = 1; 229 if (s->minset) { 230 for (i = c->usage_minimum; 231 i <= c->usage_maximum; 232 i++) { 233 s->usages[s->nu] = i; 234 if (s->nu < MAXUSAGE-1) 235 s->nu++; 236 } 237 s->minset = 0; 238 } 239 goto top; 240 } else { 241 if (s->minset) 242 c->usage = c->usage_minimum; 243 *h = *c; 244 h->next = NULL; 245 c->loc.pos += 246 c->loc.size * c->loc.count; 247 s->minset = 0; 248 s->nu = 0; 249 hid_clear_local(c); 250 return 1; 251 } 252 case 9: /* Output */ 253 retkind = hid_output; 254 goto ret; 255 case 10: /* Collection */ 256 c->kind = hid_collection; 257 c->collection = uval; 258 c->collevel++; 259 *h = *c; 260 hid_clear_local(c); 261 s->nu = 0; 262 return 1; 263 case 11: /* Feature */ 264 retkind = hid_feature; 265 goto ret; 266 case 12: /* End collection */ 267 c->kind = hid_endcollection; 268 c->collevel--; 269 *h = *c; 270 s->nu = 0; 271 return 1; 272 default: 273 aprint_normal("Main bTag=%d\n", bTag); 274 break; 275 } 276 break; 277 case 1: /* Global */ 278 switch (bTag) { 279 case 0: 280 c->_usage_page = uval << 16; 281 break; 282 case 1: 283 c->logical_minimum = dval; 284 break; 285 case 2: 286 c->logical_maximum = dval; 287 break; 288 case 3: 289 c->physical_minimum = dval; 290 break; 291 case 4: 292 c->physical_maximum = dval; 293 break; 294 case 5: 295 c->unit_exponent = uval; 296 break; 297 case 6: 298 c->unit = uval; 299 break; 300 case 7: 301 c->loc.size = uval; 302 break; 303 case 8: 304 c->report_ID = uval; 305 c->loc.pos = 0; 306 break; 307 case 9: 308 c->loc.count = uval; 309 break; 310 case 10: /* Push */ 311 hi = kmem_alloc(sizeof(*hi), KM_SLEEP); 312 *hi = *c; 313 c->next = hi; 314 break; 315 case 11: /* Pop */ 316 hi = c->next; 317 if (hi == NULL) 318 break; 319 oldpos = c->loc.pos; 320 *c = *hi; 321 c->loc.pos = oldpos; 322 kmem_free(hi, sizeof(*hi)); 323 break; 324 default: 325 aprint_normal("Global bTag=%d\n", bTag); 326 break; 327 } 328 break; 329 case 2: /* Local */ 330 switch (bTag) { 331 case 0: 332 if (bSize < 4) 333 uval = c->_usage_page | uval; 334 c->usage = uval; 335 if (s->nu < MAXUSAGE) 336 s->usages[s->nu++] = uval; 337 /* else XXX */ 338 break; 339 case 1: 340 s->minset = 1; 341 if (bSize < 4) 342 uval = c->_usage_page | uval; 343 c->usage_minimum = uval; 344 break; 345 case 2: 346 if (bSize < 4) 347 uval = c->_usage_page | uval; 348 c->usage_maximum = uval; 349 break; 350 case 3: 351 c->designator_index = uval; 352 break; 353 case 4: 354 c->designator_minimum = uval; 355 break; 356 case 5: 357 c->designator_maximum = uval; 358 break; 359 case 7: 360 c->string_index = uval; 361 break; 362 case 8: 363 c->string_minimum = uval; 364 break; 365 case 9: 366 c->string_maximum = uval; 367 break; 368 case 10: 369 c->set_delimiter = uval; 370 break; 371 default: 372 aprint_normal("Local bTag=%d\n", bTag); 373 break; 374 } 375 break; 376 default: 377 aprint_normal("default bType=%d\n", bType); 378 break; 379 } 380 } 381 } 382 383 int 384 hid_report_size(const void *buf, int len, enum hid_kind k, uint8_t id) 385 { 386 struct hid_data *d; 387 struct hid_item h; 388 int lo, hi; 389 390 h.report_ID = 0; 391 lo = hi = -1; 392 DPRINTFN(2,("hid_report_size: kind=%d id=%d\n", k, id)); 393 for (d = hid_start_parse(buf, len, k); hid_get_item(d, &h); ) { 394 DPRINTFN(2,("hid_report_size: item kind=%d id=%d pos=%d " 395 "size=%d count=%d\n", 396 h.kind, h.report_ID, h.loc.pos, h.loc.size, 397 h.loc.count)); 398 if (h.report_ID == id && h.kind == k) { 399 if (lo < 0) { 400 lo = h.loc.pos; 401 #ifdef DIAGNOSTIC 402 if (lo != 0) { 403 aprint_normal("hid_report_size:" 404 " lo != 0\n"); 405 } 406 #endif 407 } 408 hi = h.loc.pos + h.loc.size * h.loc.count; 409 DPRINTFN(2,("hid_report_size: lo=%d hi=%d\n", lo, hi)); 410 } 411 } 412 hid_end_parse(d); 413 return (hi - lo + 7) / 8; 414 } 415 416 int 417 hid_locate(const void *desc, int size, uint32_t u, uint8_t id, enum hid_kind k, 418 struct hid_location *loc, uint32_t *flags) 419 { 420 struct hid_data *d; 421 struct hid_item h; 422 423 h.report_ID = 0; 424 DPRINTFN(5,("hid_locate: enter usage=0x%x kind=%d id=%d\n", u, k, id)); 425 for (d = hid_start_parse(desc, size, k); hid_get_item(d, &h); ) { 426 DPRINTFN(5,("hid_locate: usage=0x%x kind=%d id=%d flags=0x%x\n", 427 h.usage, h.kind, h.report_ID, h.flags)); 428 if (h.kind == k && !(h.flags & HIO_CONST) && 429 h.usage == u && h.report_ID == id) { 430 if (loc != NULL) 431 *loc = h.loc; 432 if (flags != NULL) 433 *flags = h.flags; 434 hid_end_parse(d); 435 return 1; 436 } 437 } 438 hid_end_parse(d); 439 if (loc != NULL) 440 loc->size = 0; 441 return 0; 442 } 443 444 long 445 hid_get_data(const u_char *buf, const struct hid_location *loc) 446 { 447 u_int hsize = loc->size; 448 u_long data; 449 450 if (hsize == 0) 451 return 0; 452 453 data = hid_get_udata(buf, loc); 454 if (data < (1UL << (hsize - 1)) || hsize == sizeof(data) * NBBY) 455 return data; 456 return data - (1UL << hsize); 457 } 458 459 u_long 460 hid_get_udata(const u_char *buf, const struct hid_location *loc) 461 { 462 u_int hpos = loc->pos; 463 u_int hsize = loc->size; 464 u_int i, num, off; 465 u_long data; 466 467 if (hsize == 0) 468 return 0; 469 470 data = 0; 471 off = hpos / 8; 472 num = (hpos + hsize + 7) / 8 - off; 473 474 for (i = 0; i < num; i++) 475 data |= (unsigned long)buf[off + i] << (i * 8); 476 477 data >>= hpos % 8; 478 if (hsize < sizeof(data) * NBBY) 479 data &= (1UL << hsize) - 1; 480 481 DPRINTFN(10,("hid_get_udata: loc %d/%d = %lu\n", hpos, hsize, data)); 482 return data; 483 } 484 485 /* 486 * hid_is_collection(desc, size, id, usage) 487 * 488 * This function is broken in the following way. 489 * 490 * It is used to discover if the given 'id' is part of 'usage' collection 491 * in the descriptor in order to match report id against device type. 492 * 493 * The semantics of hid_start_parse() means though, that only a single 494 * kind of report is considered. The current HID code that uses this for 495 * matching is actually only looking for input reports, so this works 496 * for now. 497 * 498 * This function could try all report kinds (input, output and feature) 499 * consecutively if necessary, but it may be better to integrate the 500 * libusbhid code which can consider multiple report kinds simultaneously 501 * 502 * Needs some thought. 503 */ 504 int 505 hid_is_collection(const void *desc, int size, uint8_t id, uint32_t usage) 506 { 507 struct hid_data *hd; 508 struct hid_item hi; 509 uint32_t coll_usage = ~0; 510 511 hd = hid_start_parse(desc, size, hid_input); 512 if (hd == NULL) 513 return 0; 514 515 DPRINTFN(2,("hid_is_collection: id=%d usage=0x%x\n", id, usage)); 516 while (hid_get_item(hd, &hi)) { 517 DPRINTFN(2,("hid_is_collection: kind=%d id=%d usage=0x%x" 518 "(0x%x)\n", 519 hi.kind, hi.report_ID, hi.usage, coll_usage)); 520 521 if (hi.kind == hid_collection && 522 (hi.collection == HCOLL_APPLICATION || 523 hi.collection == HCOLL_PHYSICAL || 524 hi.collection == HCOLL_LOGICAL)) 525 coll_usage = hi.usage; 526 527 if ((hi.kind == hid_collection || 528 hi.kind == hid_endcollection) && 529 coll_usage == usage && 530 hi.report_ID == id) { 531 DPRINTFN(2,("hid_is_collection: found\n")); 532 hid_end_parse(hd); 533 return 1; 534 } 535 536 if (hi.kind == hid_endcollection) 537 coll_usage = ~0; 538 } 539 DPRINTFN(2,("hid_is_collection: not found\n")); 540 hid_end_parse(hd); 541 return 0; 542 } 543