1/* $OpenBSD: parse.c,v 1.13 2022/12/27 17:10:07 jmc Exp $ */ 2/* $NetBSD: parse.c,v 1.2 2001/12/29 20:44:22 augustss Exp $ */ 3 4/* 5 * Copyright (c) 1999, 2001 Lennart Augustsson <augustss@netbsd.org> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <stdlib.h> 31#include <string.h> 32 33#include <dev/usb/usb.h> 34#include <dev/usb/usbhid.h> 35 36#include "usbhid.h" 37#include "usbvar.h" 38 39#define MAXUSAGE 100 40#define MAXPUSH 4 41#define MAXID 64 42#define ITEMTYPES 3 43 44struct hid_pos_data { 45 int32_t rid; 46 uint32_t pos[ITEMTYPES]; 47}; 48 49struct hid_data { 50 const uint8_t *start; 51 const uint8_t *end; 52 const uint8_t *p; 53 struct hid_item cur[MAXPUSH]; 54 struct hid_pos_data last_pos[MAXID]; 55 uint32_t pos[ITEMTYPES]; 56 int32_t usages_min[MAXUSAGE]; 57 int32_t usages_max[MAXUSAGE]; 58 int32_t usage_last; /* last seen usage */ 59 uint32_t loc_size; /* last seen size */ 60 uint32_t loc_count; /* last seen count */ 61 uint8_t kindset; /* we have 5 kinds so 8 bits are enough */ 62 uint8_t pushlevel; /* current pushlevel */ 63 uint8_t ncount; /* end usage item count */ 64 uint8_t icount; /* current usage item count */ 65 uint8_t nusage; /* end "usages_min/max" index */ 66 uint8_t iusage; /* current "usages_min/max" index */ 67 uint8_t ousage; /* current "usages_min/max" offset */ 68 uint8_t susage; /* usage set flags */ 69 int32_t reportid; /* requested report ID */ 70 struct hid_item savedcoll; /* save coll until we know the ID */ 71 uint8_t hassavedcoll; 72}; 73 74static void 75hid_clear_local(hid_item_t *c) 76{ 77 78 c->usage = 0; 79 c->usage_minimum = 0; 80 c->usage_maximum = 0; 81 c->designator_index = 0; 82 c->designator_minimum = 0; 83 c->designator_maximum = 0; 84 c->string_index = 0; 85 c->string_minimum = 0; 86 c->string_maximum = 0; 87 c->set_delimiter = 0; 88} 89 90static void 91hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t next_rID) 92{ 93 uint8_t i, j; 94 95 /* check for same report ID - optimise */ 96 97 if (c->report_ID == next_rID) 98 return; 99 100 /* save current position for current rID */ 101 102 if (c->report_ID == 0) { 103 i = 0; 104 } else { 105 for (i = 1; i != MAXID; i++) { 106 if (s->last_pos[i].rid == c->report_ID) 107 break; 108 if (s->last_pos[i].rid == 0) 109 break; 110 } 111 } 112 if (i != MAXID) { 113 s->last_pos[i].rid = c->report_ID; 114 for (j = 0; j < ITEMTYPES; j++) 115 s->last_pos[i].pos[j] = s->pos[j]; 116 } 117 118 /* store next report ID */ 119 120 c->report_ID = next_rID; 121 122 /* lookup last position for next rID */ 123 124 if (next_rID == 0) { 125 i = 0; 126 } else { 127 for (i = 1; i != MAXID; i++) { 128 if (s->last_pos[i].rid == next_rID) 129 break; 130 if (s->last_pos[i].rid == 0) 131 break; 132 } 133 } 134 if (i != MAXID) { 135 s->last_pos[i].rid = next_rID; 136 for (j = 0; j < ITEMTYPES; j++) 137 s->pos[j] = s->last_pos[i].pos[j]; 138 } else { 139 for (j = 0; j < ITEMTYPES; j++) 140 s->pos[j] = 0; /* Out of RID entries. */ 141 } 142} 143 144hid_data_t 145hid_start_parse(report_desc_t d, int kindset, int id) 146{ 147 struct hid_data *s; 148 149 s = calloc(1, sizeof *s); 150 if (s == NULL) 151 return (NULL); 152 s->start = s->p = d->data; 153 s->end = d->data + d->size; 154 s->kindset = kindset; 155 s->reportid = id; 156 s->hassavedcoll = 0; 157 return (s); 158} 159 160void 161hid_end_parse(hid_data_t s) 162{ 163 164 if (s == NULL) 165 return; 166 167 free(s); 168} 169 170static uint8_t 171hid_get_byte(struct hid_data *s, const uint16_t wSize) 172{ 173 const uint8_t *ptr; 174 uint8_t retval; 175 176 ptr = s->p; 177 178 /* check if end is reached */ 179 if (ptr == s->end) 180 return (0); 181 182 /* read out a byte */ 183 retval = *ptr; 184 185 /* check if data pointer can be advanced by "wSize" bytes */ 186 if ((s->end - ptr) < wSize) 187 ptr = s->end; 188 else 189 ptr += wSize; 190 191 /* update pointer */ 192 s->p = ptr; 193 194 return (retval); 195} 196 197#define REPORT_SAVED_COLL \ 198 do { \ 199 if (s->hassavedcoll) { \ 200 *h = s->savedcoll; \ 201 h->report_ID = c->report_ID; \ 202 s->hassavedcoll = 0; \ 203 return (1); \ 204 } \ 205 } while(0) 206 207static int 208hid_get_item_raw(hid_data_t s, hid_item_t *h) 209{ 210 hid_item_t nc, *c; 211 unsigned int bTag, bType, bSize; 212 int32_t mask; 213 int32_t dval; 214 215 if (s == NULL) 216 return (0); 217 218 if (s->pushlevel >= MAXPUSH) 219 return (0); 220 221 c = &s->cur[s->pushlevel]; 222 223 top: 224 /* check if there is an array of items */ 225 if (s->icount < s->ncount) { 226 REPORT_SAVED_COLL; 227 /* get current usage */ 228 if (s->iusage < s->nusage) { 229 dval = s->usages_min[s->iusage] + s->ousage; 230 c->usage = dval; 231 s->usage_last = dval; 232 if (dval == s->usages_max[s->iusage]) { 233 s->iusage ++; 234 s->ousage = 0; 235 } else { 236 s->ousage ++; 237 } 238 } else { 239 /* Using last usage */ 240 dval = s->usage_last; 241 } 242 s->icount ++; 243 /* 244 * Only copy HID item, increment position and return 245 * if correct kindset! 246 */ 247 if (s->kindset & (1 << c->kind)) { 248 *h = *c; 249 h->pos = s->pos[c->kind]; 250 s->pos[c->kind] += c->report_size * c->report_count; 251 return (1); 252 } 253 } 254 255 /* reset state variables */ 256 s->icount = 0; 257 s->ncount = 0; 258 s->iusage = 0; 259 s->nusage = 0; 260 s->susage = 0; 261 s->ousage = 0; 262 hid_clear_local(c); 263 264 /* get next item */ 265 while (s->p != s->end) { 266 267 bSize = hid_get_byte(s, 1); 268 if (bSize == 0xfe) { 269 /* long item */ 270 bSize = hid_get_byte(s, 1); 271 bSize |= hid_get_byte(s, 1) << 8; 272 bTag = hid_get_byte(s, 1); 273 bType = 0xff; /* XXX what should it be */ 274 } else { 275 /* short item */ 276 bTag = bSize >> 4; 277 bType = (bSize >> 2) & 3; 278 bSize &= 3; 279 if (bSize == 3) 280 bSize = 4; 281 } 282 283 switch(bSize) { 284 case 0: 285 dval = 0; 286 mask = 0; 287 break; 288 case 1: 289 dval = (int8_t)hid_get_byte(s, 1); 290 mask = 0xFF; 291 break; 292 case 2: 293 dval = hid_get_byte(s, 1); 294 dval |= hid_get_byte(s, 1) << 8; 295 dval = (int16_t)dval; 296 mask = 0xFFFF; 297 break; 298 case 4: 299 dval = hid_get_byte(s, 1); 300 dval |= hid_get_byte(s, 1) << 8; 301 dval |= hid_get_byte(s, 1) << 16; 302 dval |= hid_get_byte(s, 1) << 24; 303 mask = 0xFFFFFFFF; 304 break; 305 default: 306 dval = hid_get_byte(s, bSize); 307 continue; 308 } 309 310 switch (bType) { 311 case 0: /* Main */ 312 switch (bTag) { 313 case 8: /* Input */ 314 c->kind = hid_input; 315 c->flags = dval; 316 ret: 317 c->report_count = s->loc_count; 318 c->report_size = s->loc_size; 319 320 if (c->flags & HIO_VARIABLE) { 321 /* range check usage count */ 322 if (c->report_count > 255) { 323 s->ncount = 255; 324 } else 325 s->ncount = c->report_count; 326 327 /* 328 * The "top" loop will return 329 * one and one item: 330 */ 331 c->report_count = 1; 332 c->usage_minimum = 0; 333 c->usage_maximum = 0; 334 } else { 335 s->ncount = 1; 336 } 337 goto top; 338 339 case 9: /* Output */ 340 c->kind = hid_output; 341 c->flags = dval; 342 goto ret; 343 case 10: /* Collection */ 344 c->kind = hid_collection; 345 c->collection = dval; 346 c->collevel++; 347 c->usage = s->usage_last; 348 nc = *c; 349 if (s->hassavedcoll) { 350 *h = s->savedcoll; 351 h->report_ID = nc.report_ID; 352 s->savedcoll = nc; 353 return (1); 354 } else { 355 s->hassavedcoll = 1; 356 s->savedcoll = nc; 357 } 358 goto top; 359 case 11: /* Feature */ 360 c->kind = hid_feature; 361 c->flags = dval; 362 goto ret; 363 case 12: /* End collection */ 364 REPORT_SAVED_COLL; 365 c->kind = hid_endcollection; 366 if (c->collevel == 0) { 367 /* Invalid end collection. */ 368 return (0); 369 } 370 c->collevel--; 371 *h = *c; 372 return (1); 373 default: 374 break; 375 } 376 break; 377 378 case 1: /* Global */ 379 switch (bTag) { 380 case 0: 381 c->_usage_page = dval << 16; 382 break; 383 case 1: 384 c->logical_minimum = dval; 385 break; 386 case 2: 387 c->logical_maximum = dval; 388 break; 389 case 3: 390 c->physical_minimum = dval; 391 break; 392 case 4: 393 c->physical_maximum = dval; 394 break; 395 case 5: 396 c->unit_exponent = dval; 397 break; 398 case 6: 399 c->unit = dval; 400 break; 401 case 7: 402 /* mask because value is unsigned */ 403 s->loc_size = dval & mask; 404 break; 405 case 8: 406 hid_switch_rid(s, c, dval & mask); 407 break; 408 case 9: 409 /* mask because value is unsigned */ 410 s->loc_count = dval & mask; 411 break; 412 case 10: /* Push */ 413 if (s->pushlevel < MAXPUSH - 1) { 414 s->pushlevel++; 415 s->cur[s->pushlevel] = *c; 416 /* store size and count */ 417 c->report_size = s->loc_size; 418 c->report_count = s->loc_count; 419 /* update current item pointer */ 420 c = &s->cur[s->pushlevel]; 421 } 422 break; 423 case 11: /* Pop */ 424 if (s->pushlevel > 0) { 425 s->pushlevel--; 426 c = &s->cur[s->pushlevel]; 427 /* restore size and count */ 428 s->loc_size = c->report_size; 429 s->loc_count = c->report_count; 430 c->report_size = 0; 431 c->report_count = 0; 432 } 433 break; 434 default: 435 break; 436 } 437 break; 438 case 2: /* Local */ 439 switch (bTag) { 440 case 0: 441 if (bSize != 4) 442 dval = (dval & mask) | c->_usage_page; 443 444 /* set last usage, in case of a collection */ 445 s->usage_last = dval; 446 447 if (s->nusage < MAXUSAGE) { 448 s->usages_min[s->nusage] = dval; 449 s->usages_max[s->nusage] = dval; 450 s->nusage ++; 451 } 452 /* else XXX */ 453 454 /* clear any pending usage sets */ 455 s->susage = 0; 456 break; 457 case 1: 458 s->susage |= 1; 459 460 if (bSize != 4) 461 dval = (dval & mask) | c->_usage_page; 462 c->usage_minimum = dval; 463 464 goto check_set; 465 case 2: 466 s->susage |= 2; 467 468 if (bSize != 4) 469 dval = (dval & mask) | c->_usage_page; 470 c->usage_maximum = dval; 471 472 check_set: 473 if (s->susage != 3) 474 break; 475 476 /* sanity check */ 477 if ((s->nusage < MAXUSAGE) && 478 (c->usage_minimum <= c->usage_maximum)) { 479 /* add usage range */ 480 s->usages_min[s->nusage] = 481 c->usage_minimum; 482 s->usages_max[s->nusage] = 483 c->usage_maximum; 484 s->nusage ++; 485 } 486 /* else XXX */ 487 488 s->susage = 0; 489 break; 490 case 3: 491 c->designator_index = dval; 492 break; 493 case 4: 494 c->designator_minimum = dval; 495 break; 496 case 5: 497 c->designator_maximum = dval; 498 break; 499 case 7: 500 c->string_index = dval; 501 break; 502 case 8: 503 c->string_minimum = dval; 504 break; 505 case 9: 506 c->string_maximum = dval; 507 break; 508 case 10: 509 c->set_delimiter = dval; 510 break; 511 default: 512 break; 513 } 514 break; 515 default: 516 break; 517 } 518 } 519 return (0); 520} 521 522int 523hid_get_item(hid_data_t s, hid_item_t *h) 524{ 525 int r; 526 527 for (;;) { 528 r = hid_get_item_raw(s, h); 529 if (r <= 0 || s->reportid == -1 || h->report_ID == s->reportid) 530 break; 531 } 532 return (r); 533} 534 535int 536hid_report_size(report_desc_t r, enum hid_kind k, int id) 537{ 538 struct hid_data *d; 539 struct hid_item h; 540 uint32_t temp; 541 uint32_t hpos; 542 uint32_t lpos; 543 int report_id = 0; 544 545 hpos = 0; 546 lpos = 0xFFFFFFFF; 547 548 memset(&h, 0, sizeof h); 549 for (d = hid_start_parse(r, 1 << k, id); hid_get_item(d, &h); ) { 550 if (h.kind == k) { 551 /* compute minimum */ 552 if (lpos > h.pos) 553 lpos = h.pos; 554 /* compute end position */ 555 temp = h.pos + (h.report_size * h.report_count); 556 /* compute maximum */ 557 if (hpos < temp) 558 hpos = temp; 559 if (h.report_ID != 0) 560 report_id = 1; 561 } 562 } 563 hid_end_parse(d); 564 565 /* safety check - can happen in case of corrupt descriptors */ 566 if (lpos > hpos) 567 temp = 0; 568 else 569 temp = hpos - lpos; 570 571 /* No extra byte for the reportID because the kernel skips it. */ 572 return ((temp + 7) / 8); 573} 574 575int 576hid_locate(report_desc_t desc, unsigned int u, enum hid_kind k, 577 hid_item_t *h, int id) 578{ 579 struct hid_data *d; 580 581 for (d = hid_start_parse(desc, 1 << k, id); hid_get_item(d, h); ) { 582 if (h->kind == k && !(h->flags & HIO_CONST) && h->usage == u) { 583 hid_end_parse(d); 584 return (1); 585 } 586 } 587 hid_end_parse(d); 588 h->report_size = 0; 589 return (0); 590} 591