1 /* $NetBSD: parse.c,v 1.11 2000/09/24 02:19:54 augustss Exp $ */ 2 3 /* 4 * Copyright (c) 1999, 2001 Lennart Augustsson <augustss@netbsd.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD: head/lib/libusbhid/parse.c 240762 2012-09-20 18:56:27Z mav $ 29 */ 30 31 #include <assert.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <sys/time.h> 35 36 #include <bus/u4b/usb.h> 37 #include <bus/u4b/usbhid.h> 38 39 #include "usbhid.h" 40 #include "usbvar.h" 41 42 #define MAXUSAGE 100 43 #define MAXPUSH 4 44 #define MAXID 64 45 #define ITEMTYPES 3 46 47 struct hid_pos_data { 48 int32_t rid; 49 uint32_t pos[ITEMTYPES]; 50 }; 51 52 struct hid_data { 53 const uint8_t *start; 54 const uint8_t *end; 55 const uint8_t *p; 56 struct hid_item cur[MAXPUSH]; 57 struct hid_pos_data last_pos[MAXID]; 58 uint32_t pos[ITEMTYPES]; 59 int32_t usages_min[MAXUSAGE]; 60 int32_t usages_max[MAXUSAGE]; 61 int32_t usage_last; /* last seen usage */ 62 uint32_t loc_size; /* last seen size */ 63 uint32_t loc_count; /* last seen count */ 64 uint8_t kindset; /* we have 5 kinds so 8 bits are enough */ 65 uint8_t pushlevel; /* current pushlevel */ 66 uint8_t ncount; /* end usage item count */ 67 uint8_t icount; /* current usage item count */ 68 uint8_t nusage; /* end "usages_min/max" index */ 69 uint8_t iusage; /* current "usages_min/max" index */ 70 uint8_t ousage; /* current "usages_min/max" offset */ 71 uint8_t susage; /* usage set flags */ 72 int32_t reportid; /* requested report ID */ 73 }; 74 75 /*------------------------------------------------------------------------* 76 * hid_clear_local 77 *------------------------------------------------------------------------*/ 78 static void 79 hid_clear_local(hid_item_t *c) 80 { 81 82 c->usage = 0; 83 c->usage_minimum = 0; 84 c->usage_maximum = 0; 85 c->designator_index = 0; 86 c->designator_minimum = 0; 87 c->designator_maximum = 0; 88 c->string_index = 0; 89 c->string_minimum = 0; 90 c->string_maximum = 0; 91 c->set_delimiter = 0; 92 } 93 94 static void 95 hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t next_rID) 96 { 97 uint8_t i, j; 98 99 /* check for same report ID - optimise */ 100 101 if (c->report_ID == next_rID) 102 return; 103 104 /* save current position for current rID */ 105 106 if (c->report_ID == 0) { 107 i = 0; 108 } else { 109 for (i = 1; i != MAXID; i++) { 110 if (s->last_pos[i].rid == c->report_ID) 111 break; 112 if (s->last_pos[i].rid == 0) 113 break; 114 } 115 } 116 if (i != MAXID) { 117 s->last_pos[i].rid = c->report_ID; 118 for (j = 0; j < ITEMTYPES; j++) 119 s->last_pos[i].pos[j] = s->pos[j]; 120 } 121 122 /* store next report ID */ 123 124 c->report_ID = next_rID; 125 126 /* lookup last position for next rID */ 127 128 if (next_rID == 0) { 129 i = 0; 130 } else { 131 for (i = 1; i != MAXID; i++) { 132 if (s->last_pos[i].rid == next_rID) 133 break; 134 if (s->last_pos[i].rid == 0) 135 break; 136 } 137 } 138 if (i != MAXID) { 139 s->last_pos[i].rid = next_rID; 140 for (j = 0; j < ITEMTYPES; j++) 141 s->pos[j] = s->last_pos[i].pos[j]; 142 } else { 143 for (j = 0; j < ITEMTYPES; j++) 144 s->pos[j] = 0; /* Out of RID entries. */ 145 } 146 } 147 148 /*------------------------------------------------------------------------* 149 * hid_start_parse 150 *------------------------------------------------------------------------*/ 151 hid_data_t 152 hid_start_parse(report_desc_t d, int kindset, int id) 153 { 154 struct hid_data *s; 155 156 s = malloc(sizeof *s); 157 memset(s, 0, sizeof *s); 158 s->start = s->p = d->data; 159 s->end = d->data + d->size; 160 s->kindset = kindset; 161 s->reportid = id; 162 return (s); 163 } 164 165 /*------------------------------------------------------------------------* 166 * hid_end_parse 167 *------------------------------------------------------------------------*/ 168 void 169 hid_end_parse(hid_data_t s) 170 { 171 172 if (s == NULL) 173 return; 174 175 free(s); 176 } 177 178 /*------------------------------------------------------------------------* 179 * get byte from HID descriptor 180 *------------------------------------------------------------------------*/ 181 static uint8_t 182 hid_get_byte(struct hid_data *s, const uint16_t wSize) 183 { 184 const uint8_t *ptr; 185 uint8_t retval; 186 187 ptr = s->p; 188 189 /* check if end is reached */ 190 if (ptr == s->end) 191 return (0); 192 193 /* read out a byte */ 194 retval = *ptr; 195 196 /* check if data pointer can be advanced by "wSize" bytes */ 197 if ((s->end - ptr) < wSize) 198 ptr = s->end; 199 else 200 ptr += wSize; 201 202 /* update pointer */ 203 s->p = ptr; 204 205 return (retval); 206 } 207 208 /*------------------------------------------------------------------------* 209 * hid_get_item 210 *------------------------------------------------------------------------*/ 211 static int 212 hid_get_item_raw(hid_data_t s, hid_item_t *h) 213 { 214 hid_item_t *c; 215 unsigned int bTag, bType, bSize; 216 int32_t mask; 217 int32_t dval; 218 219 if (s == NULL) 220 return (0); 221 222 c = &s->cur[s->pushlevel]; 223 224 top: 225 /* check if there is an array of items */ 226 if (s->icount < s->ncount) { 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 *h = *c; 349 return (1); 350 case 11: /* Feature */ 351 c->kind = hid_feature; 352 c->flags = dval; 353 goto ret; 354 case 12: /* End collection */ 355 c->kind = hid_endcollection; 356 if (c->collevel == 0) { 357 /* Invalid end collection. */ 358 return (0); 359 } 360 c->collevel--; 361 *h = *c; 362 return (1); 363 default: 364 break; 365 } 366 break; 367 368 case 1: /* Global */ 369 switch (bTag) { 370 case 0: 371 c->_usage_page = dval << 16; 372 break; 373 case 1: 374 c->logical_minimum = dval; 375 break; 376 case 2: 377 c->logical_maximum = dval; 378 break; 379 case 3: 380 c->physical_minimum = dval; 381 break; 382 case 4: 383 c->physical_maximum = dval; 384 break; 385 case 5: 386 c->unit_exponent = dval; 387 break; 388 case 6: 389 c->unit = dval; 390 break; 391 case 7: 392 /* mask because value is unsigned */ 393 s->loc_size = dval & mask; 394 break; 395 case 8: 396 hid_switch_rid(s, c, dval & mask); 397 break; 398 case 9: 399 /* mask because value is unsigned */ 400 s->loc_count = dval & mask; 401 break; 402 case 10: /* Push */ 403 s->pushlevel ++; 404 if (s->pushlevel < MAXPUSH) { 405 s->cur[s->pushlevel] = *c; 406 /* store size and count */ 407 c->report_size = s->loc_size; 408 c->report_count = s->loc_count; 409 /* update current item pointer */ 410 c = &s->cur[s->pushlevel]; 411 } 412 break; 413 case 11: /* Pop */ 414 s->pushlevel --; 415 if (s->pushlevel < MAXPUSH) { 416 c = &s->cur[s->pushlevel]; 417 /* restore size and count */ 418 s->loc_size = c->report_size; 419 s->loc_count = c->report_count; 420 c->report_size = 0; 421 c->report_count = 0; 422 } 423 break; 424 default: 425 break; 426 } 427 break; 428 case 2: /* Local */ 429 switch (bTag) { 430 case 0: 431 if (bSize != 4) 432 dval = (dval & mask) | c->_usage_page; 433 434 /* set last usage, in case of a collection */ 435 s->usage_last = dval; 436 437 if (s->nusage < MAXUSAGE) { 438 s->usages_min[s->nusage] = dval; 439 s->usages_max[s->nusage] = dval; 440 s->nusage ++; 441 } 442 /* else XXX */ 443 444 /* clear any pending usage sets */ 445 s->susage = 0; 446 break; 447 case 1: 448 s->susage |= 1; 449 450 if (bSize != 4) 451 dval = (dval & mask) | c->_usage_page; 452 c->usage_minimum = dval; 453 454 goto check_set; 455 case 2: 456 s->susage |= 2; 457 458 if (bSize != 4) 459 dval = (dval & mask) | c->_usage_page; 460 c->usage_maximum = dval; 461 462 check_set: 463 if (s->susage != 3) 464 break; 465 466 /* sanity check */ 467 if ((s->nusage < MAXUSAGE) && 468 (c->usage_minimum <= c->usage_maximum)) { 469 /* add usage range */ 470 s->usages_min[s->nusage] = 471 c->usage_minimum; 472 s->usages_max[s->nusage] = 473 c->usage_maximum; 474 s->nusage ++; 475 } 476 /* else XXX */ 477 478 s->susage = 0; 479 break; 480 case 3: 481 c->designator_index = dval; 482 break; 483 case 4: 484 c->designator_minimum = dval; 485 break; 486 case 5: 487 c->designator_maximum = dval; 488 break; 489 case 7: 490 c->string_index = dval; 491 break; 492 case 8: 493 c->string_minimum = dval; 494 break; 495 case 9: 496 c->string_maximum = dval; 497 break; 498 case 10: 499 c->set_delimiter = dval; 500 break; 501 default: 502 break; 503 } 504 break; 505 default: 506 break; 507 } 508 } 509 return (0); 510 } 511 512 int 513 hid_get_item(hid_data_t s, hid_item_t *h) 514 { 515 int r; 516 517 for (;;) { 518 r = hid_get_item_raw(s, h); 519 if (r <= 0 || s->reportid == -1 || h->report_ID == s->reportid) 520 break; 521 } 522 return (r); 523 } 524 525 int 526 hid_report_size(report_desc_t r, enum hid_kind k, int id) 527 { 528 struct hid_data *d; 529 struct hid_item h; 530 uint32_t temp; 531 uint32_t hpos; 532 uint32_t lpos; 533 int report_id = 0; 534 535 hpos = 0; 536 lpos = 0xFFFFFFFF; 537 538 memset(&h, 0, sizeof h); 539 for (d = hid_start_parse(r, 1 << k, id); hid_get_item(d, &h); ) { 540 if (h.kind == k) { 541 /* compute minimum */ 542 if (lpos > h.pos) 543 lpos = h.pos; 544 /* compute end position */ 545 temp = h.pos + (h.report_size * h.report_count); 546 /* compute maximum */ 547 if (hpos < temp) 548 hpos = temp; 549 if (h.report_ID != 0) 550 report_id = 1; 551 } 552 } 553 hid_end_parse(d); 554 555 /* safety check - can happen in case of currupt descriptors */ 556 if (lpos > hpos) 557 temp = 0; 558 else 559 temp = hpos - lpos; 560 561 /* return length in bytes rounded up */ 562 return ((temp + 7) / 8 + report_id); 563 } 564 565 int 566 hid_locate(report_desc_t desc, unsigned int u, enum hid_kind k, 567 hid_item_t *h, int id) 568 { 569 struct hid_data *d; 570 571 for (d = hid_start_parse(desc, 1 << k, id); hid_get_item(d, h); ) { 572 if (h->kind == k && !(h->flags & HIO_CONST) && h->usage == u) { 573 hid_end_parse(d); 574 return (1); 575 } 576 } 577 hid_end_parse(d); 578 h->report_size = 0; 579 return (0); 580 } 581