1 /* $OpenBSD: parse.c,v 1.10 2015/01/18 17:16:06 mpi 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 44 struct hid_pos_data { 45 int32_t rid; 46 uint32_t pos[ITEMTYPES]; 47 }; 48 49 struct 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 }; 71 72 static void 73 hid_clear_local(hid_item_t *c) 74 { 75 76 c->usage = 0; 77 c->usage_minimum = 0; 78 c->usage_maximum = 0; 79 c->designator_index = 0; 80 c->designator_minimum = 0; 81 c->designator_maximum = 0; 82 c->string_index = 0; 83 c->string_minimum = 0; 84 c->string_maximum = 0; 85 c->set_delimiter = 0; 86 } 87 88 static void 89 hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t next_rID) 90 { 91 uint8_t i, j; 92 93 /* check for same report ID - optimise */ 94 95 if (c->report_ID == next_rID) 96 return; 97 98 /* save current position for current rID */ 99 100 if (c->report_ID == 0) { 101 i = 0; 102 } else { 103 for (i = 1; i != MAXID; i++) { 104 if (s->last_pos[i].rid == c->report_ID) 105 break; 106 if (s->last_pos[i].rid == 0) 107 break; 108 } 109 } 110 if (i != MAXID) { 111 s->last_pos[i].rid = c->report_ID; 112 for (j = 0; j < ITEMTYPES; j++) 113 s->last_pos[i].pos[j] = s->pos[j]; 114 } 115 116 /* store next report ID */ 117 118 c->report_ID = next_rID; 119 120 /* lookup last position for next rID */ 121 122 if (next_rID == 0) { 123 i = 0; 124 } else { 125 for (i = 1; i != MAXID; i++) { 126 if (s->last_pos[i].rid == next_rID) 127 break; 128 if (s->last_pos[i].rid == 0) 129 break; 130 } 131 } 132 if (i != MAXID) { 133 s->last_pos[i].rid = next_rID; 134 for (j = 0; j < ITEMTYPES; j++) 135 s->pos[j] = s->last_pos[i].pos[j]; 136 } else { 137 for (j = 0; j < ITEMTYPES; j++) 138 s->pos[j] = 0; /* Out of RID entries. */ 139 } 140 } 141 142 hid_data_t 143 hid_start_parse(report_desc_t d, int kindset, int id) 144 { 145 struct hid_data *s; 146 147 s = calloc(1, sizeof *s); 148 if (s == NULL) 149 return (NULL); 150 s->start = s->p = d->data; 151 s->end = d->data + d->size; 152 s->kindset = kindset; 153 s->reportid = id; 154 return (s); 155 } 156 157 void 158 hid_end_parse(hid_data_t s) 159 { 160 161 if (s == NULL) 162 return; 163 164 free(s); 165 } 166 167 static uint8_t 168 hid_get_byte(struct hid_data *s, const uint16_t wSize) 169 { 170 const uint8_t *ptr; 171 uint8_t retval; 172 173 ptr = s->p; 174 175 /* check if end is reached */ 176 if (ptr == s->end) 177 return (0); 178 179 /* read out a byte */ 180 retval = *ptr; 181 182 /* check if data pointer can be advanced by "wSize" bytes */ 183 if ((s->end - ptr) < wSize) 184 ptr = s->end; 185 else 186 ptr += wSize; 187 188 /* update pointer */ 189 s->p = ptr; 190 191 return (retval); 192 } 193 194 static int 195 hid_get_item_raw(hid_data_t s, hid_item_t *h) 196 { 197 hid_item_t *c; 198 unsigned int bTag, bType, bSize; 199 int32_t mask; 200 int32_t dval; 201 202 if (s == NULL) 203 return (0); 204 205 c = &s->cur[s->pushlevel]; 206 207 top: 208 /* check if there is an array of items */ 209 if (s->icount < s->ncount) { 210 /* get current usage */ 211 if (s->iusage < s->nusage) { 212 dval = s->usages_min[s->iusage] + s->ousage; 213 c->usage = dval; 214 s->usage_last = dval; 215 if (dval == s->usages_max[s->iusage]) { 216 s->iusage ++; 217 s->ousage = 0; 218 } else { 219 s->ousage ++; 220 } 221 } else { 222 /* Using last usage */ 223 dval = s->usage_last; 224 } 225 s->icount ++; 226 /* 227 * Only copy HID item, increment position and return 228 * if correct kindset! 229 */ 230 if (s->kindset & (1 << c->kind)) { 231 *h = *c; 232 h->pos = s->pos[c->kind]; 233 s->pos[c->kind] += c->report_size * c->report_count; 234 return (1); 235 } 236 } 237 238 /* reset state variables */ 239 s->icount = 0; 240 s->ncount = 0; 241 s->iusage = 0; 242 s->nusage = 0; 243 s->susage = 0; 244 s->ousage = 0; 245 hid_clear_local(c); 246 247 /* get next item */ 248 while (s->p != s->end) { 249 250 bSize = hid_get_byte(s, 1); 251 if (bSize == 0xfe) { 252 /* long item */ 253 bSize = hid_get_byte(s, 1); 254 bSize |= hid_get_byte(s, 1) << 8; 255 bTag = hid_get_byte(s, 1); 256 bType = 0xff; /* XXX what should it be */ 257 } else { 258 /* short item */ 259 bTag = bSize >> 4; 260 bType = (bSize >> 2) & 3; 261 bSize &= 3; 262 if (bSize == 3) 263 bSize = 4; 264 } 265 266 switch(bSize) { 267 case 0: 268 dval = 0; 269 mask = 0; 270 break; 271 case 1: 272 dval = (int8_t)hid_get_byte(s, 1); 273 mask = 0xFF; 274 break; 275 case 2: 276 dval = hid_get_byte(s, 1); 277 dval |= hid_get_byte(s, 1) << 8; 278 dval = (int16_t)dval; 279 mask = 0xFFFF; 280 break; 281 case 4: 282 dval = hid_get_byte(s, 1); 283 dval |= hid_get_byte(s, 1) << 8; 284 dval |= hid_get_byte(s, 1) << 16; 285 dval |= hid_get_byte(s, 1) << 24; 286 mask = 0xFFFFFFFF; 287 break; 288 default: 289 dval = hid_get_byte(s, bSize); 290 continue; 291 } 292 293 switch (bType) { 294 case 0: /* Main */ 295 switch (bTag) { 296 case 8: /* Input */ 297 c->kind = hid_input; 298 c->flags = dval; 299 ret: 300 c->report_count = s->loc_count; 301 c->report_size = s->loc_size; 302 303 if (c->flags & HIO_VARIABLE) { 304 /* range check usage count */ 305 if (c->report_count > 255) { 306 s->ncount = 255; 307 } else 308 s->ncount = c->report_count; 309 310 /* 311 * The "top" loop will return 312 * one and one item: 313 */ 314 c->report_count = 1; 315 c->usage_minimum = 0; 316 c->usage_maximum = 0; 317 } else { 318 s->ncount = 1; 319 } 320 goto top; 321 322 case 9: /* Output */ 323 c->kind = hid_output; 324 c->flags = dval; 325 goto ret; 326 case 10: /* Collection */ 327 c->kind = hid_collection; 328 c->collection = dval; 329 c->collevel++; 330 c->usage = s->usage_last; 331 *h = *c; 332 return (1); 333 case 11: /* Feature */ 334 c->kind = hid_feature; 335 c->flags = dval; 336 goto ret; 337 case 12: /* End collection */ 338 c->kind = hid_endcollection; 339 if (c->collevel == 0) { 340 /* Invalid end collection. */ 341 return (0); 342 } 343 c->collevel--; 344 *h = *c; 345 return (1); 346 default: 347 break; 348 } 349 break; 350 351 case 1: /* Global */ 352 switch (bTag) { 353 case 0: 354 c->_usage_page = dval << 16; 355 break; 356 case 1: 357 c->logical_minimum = dval; 358 break; 359 case 2: 360 c->logical_maximum = dval; 361 break; 362 case 3: 363 c->physical_minimum = dval; 364 break; 365 case 4: 366 c->physical_maximum = dval; 367 break; 368 case 5: 369 c->unit_exponent = dval; 370 break; 371 case 6: 372 c->unit = dval; 373 break; 374 case 7: 375 /* mask because value is unsigned */ 376 s->loc_size = dval & mask; 377 break; 378 case 8: 379 hid_switch_rid(s, c, dval & mask); 380 break; 381 case 9: 382 /* mask because value is unsigned */ 383 s->loc_count = dval & mask; 384 break; 385 case 10: /* Push */ 386 s->pushlevel ++; 387 if (s->pushlevel < MAXPUSH) { 388 s->cur[s->pushlevel] = *c; 389 /* store size and count */ 390 c->report_size = s->loc_size; 391 c->report_count = s->loc_count; 392 /* update current item pointer */ 393 c = &s->cur[s->pushlevel]; 394 } 395 break; 396 case 11: /* Pop */ 397 s->pushlevel --; 398 if (s->pushlevel < MAXPUSH) { 399 c = &s->cur[s->pushlevel]; 400 /* restore size and count */ 401 s->loc_size = c->report_size; 402 s->loc_count = c->report_count; 403 c->report_size = 0; 404 c->report_count = 0; 405 } 406 break; 407 default: 408 break; 409 } 410 break; 411 case 2: /* Local */ 412 switch (bTag) { 413 case 0: 414 if (bSize != 4) 415 dval = (dval & mask) | c->_usage_page; 416 417 /* set last usage, in case of a collection */ 418 s->usage_last = dval; 419 420 if (s->nusage < MAXUSAGE) { 421 s->usages_min[s->nusage] = dval; 422 s->usages_max[s->nusage] = dval; 423 s->nusage ++; 424 } 425 /* else XXX */ 426 427 /* clear any pending usage sets */ 428 s->susage = 0; 429 break; 430 case 1: 431 s->susage |= 1; 432 433 if (bSize != 4) 434 dval = (dval & mask) | c->_usage_page; 435 c->usage_minimum = dval; 436 437 goto check_set; 438 case 2: 439 s->susage |= 2; 440 441 if (bSize != 4) 442 dval = (dval & mask) | c->_usage_page; 443 c->usage_maximum = dval; 444 445 check_set: 446 if (s->susage != 3) 447 break; 448 449 /* sanity check */ 450 if ((s->nusage < MAXUSAGE) && 451 (c->usage_minimum <= c->usage_maximum)) { 452 /* add usage range */ 453 s->usages_min[s->nusage] = 454 c->usage_minimum; 455 s->usages_max[s->nusage] = 456 c->usage_maximum; 457 s->nusage ++; 458 } 459 /* else XXX */ 460 461 s->susage = 0; 462 break; 463 case 3: 464 c->designator_index = dval; 465 break; 466 case 4: 467 c->designator_minimum = dval; 468 break; 469 case 5: 470 c->designator_maximum = dval; 471 break; 472 case 7: 473 c->string_index = dval; 474 break; 475 case 8: 476 c->string_minimum = dval; 477 break; 478 case 9: 479 c->string_maximum = dval; 480 break; 481 case 10: 482 c->set_delimiter = dval; 483 break; 484 default: 485 break; 486 } 487 break; 488 default: 489 break; 490 } 491 } 492 return (0); 493 } 494 495 int 496 hid_get_item(hid_data_t s, hid_item_t *h) 497 { 498 int r; 499 500 for (;;) { 501 r = hid_get_item_raw(s, h); 502 if (r <= 0 || s->reportid == -1 || h->report_ID == s->reportid) 503 break; 504 } 505 return (r); 506 } 507 508 int 509 hid_report_size(report_desc_t r, enum hid_kind k, int id) 510 { 511 struct hid_data *d; 512 struct hid_item h; 513 uint32_t temp; 514 uint32_t hpos; 515 uint32_t lpos; 516 int report_id = 0; 517 518 hpos = 0; 519 lpos = 0xFFFFFFFF; 520 521 memset(&h, 0, sizeof h); 522 for (d = hid_start_parse(r, 1 << k, id); hid_get_item(d, &h); ) { 523 if (h.kind == k) { 524 /* compute minimum */ 525 if (lpos > h.pos) 526 lpos = h.pos; 527 /* compute end position */ 528 temp = h.pos + (h.report_size * h.report_count); 529 /* compute maximum */ 530 if (hpos < temp) 531 hpos = temp; 532 if (h.report_ID != 0) 533 report_id = 1; 534 } 535 } 536 hid_end_parse(d); 537 538 /* safety check - can happen in case of currupt descriptors */ 539 if (lpos > hpos) 540 temp = 0; 541 else 542 temp = hpos - lpos; 543 544 /* No extra byte for the reportID because the kernel skips it. */ 545 return ((temp + 7) / 8); 546 } 547 548 int 549 hid_locate(report_desc_t desc, unsigned int u, enum hid_kind k, 550 hid_item_t *h, int id) 551 { 552 struct hid_data *d; 553 554 for (d = hid_start_parse(desc, 1 << k, id); hid_get_item(d, h); ) { 555 if (h->kind == k && !(h->flags & HIO_CONST) && h->usage == u) { 556 hid_end_parse(d); 557 return (1); 558 } 559 } 560 hid_end_parse(d); 561 h->report_size = 0; 562 return (0); 563 } 564