1 /* $NetBSD: parse.c,v 1.7 2010/08/13 19:56:34 jakllsch 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 29 #include <sys/cdefs.h> 30 __RCSID("$NetBSD: parse.c,v 1.7 2010/08/13 19:56:34 jakllsch Exp $"); 31 32 #include <assert.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <sys/time.h> 36 37 #include <dev/usb/usb.h> 38 #include <dev/usb/usbhid.h> 39 40 #include "usbhid.h" 41 #include "usbvar.h" 42 43 #define MAXUSAGE 100 44 struct hid_data { 45 u_char *start; 46 u_char *end; 47 u_char *p; 48 hid_item_t cur; 49 unsigned int usages[MAXUSAGE]; 50 int nusage; 51 int minset; 52 int logminsize; 53 int phyminsize; 54 int multi; 55 int multimax; 56 int kindset; 57 int reportid; 58 59 /* 60 * The start of collection item has no report ID set, so save 61 * it until we know the ID. 62 */ 63 hid_item_t savedcoll; 64 u_char hassavedcoll; 65 /* 66 * Absolute data position (bits) for input/output/feature. 67 * Assumes that hid_input, hid_output and hid_feature have 68 * values 0, 1 and 2. 69 */ 70 unsigned int kindpos[3]; 71 }; 72 73 static int min(int x, int y) { return x < y ? x : y; } 74 75 static int hid_get_item_raw(hid_data_t s, hid_item_t *h); 76 77 static void 78 hid_clear_local(hid_item_t *c) 79 { 80 81 _DIAGASSERT(c != NULL); 82 83 c->usage = 0; 84 c->usage_minimum = 0; 85 c->usage_maximum = 0; 86 c->designator_index = 0; 87 c->designator_minimum = 0; 88 c->designator_maximum = 0; 89 c->string_index = 0; 90 c->string_minimum = 0; 91 c->string_maximum = 0; 92 c->set_delimiter = 0; 93 } 94 95 hid_data_t 96 hid_start_parse(report_desc_t d, int kindset, int id) 97 { 98 struct hid_data *s; 99 100 _DIAGASSERT(d != NULL); 101 102 s = malloc(sizeof *s); 103 memset(s, 0, sizeof *s); 104 s->start = s->p = d->data; 105 s->end = d->data + d->size; 106 s->kindset = kindset; 107 s->reportid = id; 108 s->hassavedcoll = 0; 109 return (s); 110 } 111 112 void 113 hid_end_parse(hid_data_t s) 114 { 115 116 _DIAGASSERT(s != NULL); 117 118 while (s->cur.next) { 119 hid_item_t *hi = s->cur.next->next; 120 free(s->cur.next); 121 s->cur.next = hi; 122 } 123 free(s); 124 } 125 126 int 127 hid_get_item(hid_data_t s, hid_item_t *h) 128 { 129 int r; 130 131 for (;;) { 132 r = hid_get_item_raw(s, h); 133 if (r <= 0) 134 break; 135 if (h->report_ID == s->reportid || s->reportid == -1) 136 break; 137 } 138 return (r); 139 } 140 141 #define REPORT_SAVED_COLL \ 142 do { \ 143 if (s->hassavedcoll) { \ 144 *h = s->savedcoll; \ 145 h->report_ID = c->report_ID; \ 146 s->hassavedcoll = 0; \ 147 return (1); \ 148 } \ 149 } while(/*LINTED*/ 0) 150 151 static int 152 hid_get_item_raw(hid_data_t s, hid_item_t *h) 153 { 154 hid_item_t *c; 155 unsigned int bTag = 0, bType = 0, bSize; 156 unsigned char *data; 157 int dval; 158 unsigned char *p; 159 hid_item_t *hi; 160 hid_item_t nc; 161 int i; 162 hid_kind_t retkind; 163 164 _DIAGASSERT(s != NULL); 165 _DIAGASSERT(h != NULL); 166 167 c = &s->cur; 168 169 top: 170 if (s->multimax) { 171 REPORT_SAVED_COLL; 172 if (c->logical_minimum >= c->logical_maximum) { 173 if (s->logminsize == 1) 174 c->logical_minimum =(int8_t)c->logical_minimum; 175 else if (s->logminsize == 2) 176 c->logical_minimum =(int16_t)c->logical_minimum; 177 } 178 if (c->physical_minimum >= c->physical_maximum) { 179 if (s->phyminsize == 1) 180 c->physical_minimum = 181 (int8_t)c->physical_minimum; 182 else if (s->phyminsize == 2) 183 c->physical_minimum = 184 (int16_t)c->physical_minimum; 185 } 186 if (s->multi < s->multimax) { 187 c->usage = s->usages[min(s->multi, s->nusage-1)]; 188 s->multi++; 189 *h = *c; 190 /* 191 * 'multimax' is only non-zero if the current 192 * item kind is input/output/feature 193 */ 194 h->pos = s->kindpos[c->kind]; 195 s->kindpos[c->kind] += c->report_size; 196 h->next = 0; 197 return (1); 198 } else { 199 c->report_count = s->multimax; 200 s->multimax = 0; 201 s->nusage = 0; 202 hid_clear_local(c); 203 } 204 } 205 for (;;) { 206 p = s->p; 207 if (p >= s->end) 208 return (0); 209 210 bSize = *p++; 211 if (bSize == 0xfe) { 212 /* long item */ 213 bSize = *p++; 214 bSize |= *p++ << 8; 215 bTag = *p++; 216 data = p; 217 p += bSize; 218 } else { 219 /* short item */ 220 bTag = bSize >> 4; 221 bType = (bSize >> 2) & 3; 222 bSize &= 3; 223 if (bSize == 3) bSize = 4; 224 data = p; 225 p += bSize; 226 } 227 s->p = p; 228 /* 229 * The spec is unclear if the data is signed or unsigned. 230 */ 231 switch(bSize) { 232 case 0: 233 dval = 0; 234 break; 235 case 1: 236 dval = /*(int8_t)*/*data++; 237 break; 238 case 2: 239 dval = *data++; 240 dval |= *data++ << 8; 241 dval = /*(int16_t)*/dval; 242 break; 243 case 4: 244 dval = *data++; 245 dval |= *data++ << 8; 246 dval |= *data++ << 16; 247 dval |= *data++ << 24; 248 break; 249 default: 250 return (-1); 251 } 252 253 switch (bType) { 254 case 0: /* Main */ 255 switch (bTag) { 256 case 8: /* Input */ 257 retkind = hid_input; 258 ret: 259 if (!(s->kindset & (1 << retkind))) { 260 /* Drop the items of this kind */ 261 s->nusage = 0; 262 continue; 263 } 264 c->kind = retkind; 265 c->flags = dval; 266 if (c->flags & HIO_VARIABLE) { 267 s->multimax = c->report_count; 268 s->multi = 0; 269 c->report_count = 1; 270 if (s->minset) { 271 for (i = c->usage_minimum; 272 i <= c->usage_maximum; 273 i++) { 274 s->usages[s->nusage] = i; 275 if (s->nusage < MAXUSAGE-1) 276 s->nusage++; 277 } 278 c->usage_minimum = 0; 279 c->usage_maximum = 0; 280 s->minset = 0; 281 } 282 goto top; 283 } else { 284 if (s->minset) 285 c->usage = c->usage_minimum; 286 *h = *c; 287 h->next = 0; 288 h->pos = s->kindpos[c->kind]; 289 s->kindpos[c->kind] += 290 c->report_size * c->report_count; 291 hid_clear_local(c); 292 s->minset = 0; 293 return (1); 294 } 295 case 9: /* Output */ 296 retkind = hid_output; 297 goto ret; 298 case 10: /* Collection */ 299 c->kind = hid_collection; 300 c->collection = dval; 301 c->collevel++; 302 nc = *c; 303 hid_clear_local(c); 304 /*c->report_ID = NO_REPORT_ID;*/ 305 s->nusage = 0; 306 if (s->hassavedcoll) { 307 *h = s->savedcoll; 308 h->report_ID = nc.report_ID; 309 s->savedcoll = nc; 310 return (1); 311 } else { 312 s->hassavedcoll = 1; 313 s->savedcoll = nc; 314 } 315 break; 316 case 11: /* Feature */ 317 retkind = hid_feature; 318 goto ret; 319 case 12: /* End collection */ 320 REPORT_SAVED_COLL; 321 c->kind = hid_endcollection; 322 c->collevel--; 323 *h = *c; 324 /*hid_clear_local(c);*/ 325 s->nusage = 0; 326 return (1); 327 default: 328 return (-2); 329 } 330 break; 331 332 case 1: /* Global */ 333 switch (bTag) { 334 case 0: 335 c->_usage_page = dval << 16; 336 break; 337 case 1: 338 c->logical_minimum = dval; 339 s->logminsize = bSize; 340 break; 341 case 2: 342 c->logical_maximum = dval; 343 break; 344 case 3: 345 c->physical_minimum = dval; 346 s->phyminsize = bSize; 347 break; 348 case 4: 349 c->physical_maximum = dval; 350 break; 351 case 5: 352 if ( dval > 7 && dval < 0x10) 353 c->unit_exponent = -16 + dval; 354 else 355 c->unit_exponent = dval; 356 break; 357 case 6: 358 c->unit = dval; 359 break; 360 case 7: 361 c->report_size = dval; 362 break; 363 case 8: 364 c->report_ID = dval; 365 s->kindpos[hid_input] = 366 s->kindpos[hid_output] = 367 s->kindpos[hid_feature] = 0; 368 break; 369 case 9: 370 c->report_count = dval; 371 break; 372 case 10: /* Push */ 373 hi = malloc(sizeof *hi); 374 *hi = s->cur; 375 c->next = hi; 376 break; 377 case 11: /* Pop */ 378 hi = c->next; 379 s->cur = *hi; 380 free(hi); 381 break; 382 default: 383 return (-3); 384 } 385 break; 386 case 2: /* Local */ 387 switch (bTag) { 388 case 0: 389 c->usage = c->_usage_page | dval; 390 if (s->nusage < MAXUSAGE) 391 s->usages[s->nusage++] = c->usage; 392 /* else XXX */ 393 break; 394 case 1: 395 s->minset = 1; 396 c->usage_minimum = c->_usage_page | dval; 397 break; 398 case 2: 399 c->usage_maximum = c->_usage_page | dval; 400 break; 401 case 3: 402 c->designator_index = dval; 403 break; 404 case 4: 405 c->designator_minimum = dval; 406 break; 407 case 5: 408 c->designator_maximum = dval; 409 break; 410 case 7: 411 c->string_index = dval; 412 break; 413 case 8: 414 c->string_minimum = dval; 415 break; 416 case 9: 417 c->string_maximum = dval; 418 break; 419 case 10: 420 c->set_delimiter = dval; 421 break; 422 default: 423 return (-4); 424 } 425 break; 426 default: 427 return (-5); 428 } 429 } 430 } 431 432 int 433 hid_report_size(report_desc_t r, enum hid_kind k, int id) 434 { 435 struct hid_data *d; 436 hid_item_t h; 437 int size; 438 439 _DIAGASSERT(r != NULL); 440 441 memset(&h, 0, sizeof h); 442 size = 0; 443 for (d = hid_start_parse(r, 1<<k, id); hid_get_item(d, &h); ) { 444 if (h.report_ID == id && h.kind == k) { 445 size = d->kindpos[k]; 446 } 447 } 448 hid_end_parse(d); 449 return ((size + 7) / 8); 450 } 451 452 int 453 hid_locate(report_desc_t desc, unsigned int u, enum hid_kind k, 454 hid_item_t *h, int id) 455 { 456 hid_data_t d; 457 458 _DIAGASSERT(desc != NULL); 459 _DIAGASSERT(h != NULL); 460 461 for (d = hid_start_parse(desc, 1<<k, id); hid_get_item(d, h); ) { 462 if (h->kind == k && !(h->flags & HIO_CONST) && h->usage == u) { 463 hid_end_parse(d); 464 return (1); 465 } 466 } 467 hid_end_parse(d); 468 h->report_size = 0; 469 return (0); 470 } 471