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