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