1/* $NetBSD: parse.c,v 1.11 2020/04/04 21:26:16 fox 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.11 2020/04/04 21:26:16 fox 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/hid/hid.h> 39 40#include "usbhid.h" 41#include "usbvar.h" 42 43#define MAXUSAGE 100 44struct 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 73static int min(int x, int y) { return x < y ? x : y; } 74 75static int hid_get_item_raw(hid_data_t s, hid_item_t *h); 76 77static void 78hid_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 95hid_data_t 96hid_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 112void 113hid_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 126int 127hid_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 151static int 152hid_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 break; 242 case 4: 243 dval = *data++; 244 dval |= *data++ << 8; 245 dval |= *data++ << 16; 246 dval |= ((uint32_t)*data++) << 24; 247 break; 248 default: 249 return (-1); 250 } 251 252 switch (bType) { 253 case 0: /* Main */ 254 switch (bTag) { 255 case 8: /* Input */ 256 retkind = hid_input; 257 ret: 258 if (!(s->kindset & (1 << retkind))) { 259 /* Drop the items of this kind */ 260 s->nusage = 0; 261 continue; 262 } 263 c->kind = retkind; 264 c->flags = dval; 265 if (c->flags & HIO_VARIABLE) { 266 s->multimax = c->report_count; 267 s->multi = 0; 268 c->report_count = 1; 269 if (s->minset) { 270 for (i = c->usage_minimum; 271 i <= c->usage_maximum; 272 i++) { 273 s->usages[s->nusage] = i; 274 if (s->nusage < MAXUSAGE-1) 275 s->nusage++; 276 } 277 c->usage_minimum = 0; 278 c->usage_maximum = 0; 279 s->minset = 0; 280 } 281 goto top; 282 } else { 283 if (s->minset) 284 c->usage = c->usage_minimum; 285 *h = *c; 286 h->next = 0; 287 h->pos = s->kindpos[c->kind]; 288 s->kindpos[c->kind] += 289 c->report_size * c->report_count; 290 hid_clear_local(c); 291 s->minset = 0; 292 return (1); 293 } 294 case 9: /* Output */ 295 retkind = hid_output; 296 goto ret; 297 case 10: /* Collection */ 298 c->kind = hid_collection; 299 c->collection = dval; 300 c->collevel++; 301 nc = *c; 302 hid_clear_local(c); 303 /*c->report_ID = NO_REPORT_ID;*/ 304 s->nusage = 0; 305 if (s->hassavedcoll) { 306 *h = s->savedcoll; 307 h->report_ID = nc.report_ID; 308 s->savedcoll = nc; 309 return (1); 310 } else { 311 s->hassavedcoll = 1; 312 s->savedcoll = nc; 313 } 314 break; 315 case 11: /* Feature */ 316 retkind = hid_feature; 317 goto ret; 318 case 12: /* End collection */ 319 REPORT_SAVED_COLL; 320 c->kind = hid_endcollection; 321 c->collevel--; 322 *h = *c; 323 /*hid_clear_local(c);*/ 324 s->nusage = 0; 325 return (1); 326 default: 327 return (-2); 328 } 329 break; 330 331 case 1: /* Global */ 332 switch (bTag) { 333 case 0: 334 c->_usage_page = dval << 16; 335 break; 336 case 1: 337 c->logical_minimum = dval; 338 s->logminsize = bSize; 339 break; 340 case 2: 341 c->logical_maximum = dval; 342 break; 343 case 3: 344 c->physical_minimum = dval; 345 s->phyminsize = bSize; 346 break; 347 case 4: 348 c->physical_maximum = dval; 349 break; 350 case 5: 351 if ( dval > 7 && dval < 0x10) 352 c->unit_exponent = -16 + dval; 353 else 354 c->unit_exponent = dval; 355 break; 356 case 6: 357 c->unit = dval; 358 break; 359 case 7: 360 c->report_size = dval; 361 break; 362 case 8: 363 c->report_ID = dval; 364 s->kindpos[hid_input] = 365 s->kindpos[hid_output] = 366 s->kindpos[hid_feature] = 0; 367 break; 368 case 9: 369 c->report_count = dval; 370 break; 371 case 10: /* Push */ 372 hi = malloc(sizeof *hi); 373 *hi = s->cur; 374 c->next = hi; 375 break; 376 case 11: /* Pop */ 377 hi = c->next; 378 if (hi == NULL) 379 break; 380 s->cur = *hi; 381 free(hi); 382 break; 383 default: 384 return (-3); 385 } 386 break; 387 case 2: /* Local */ 388 switch (bTag) { 389 case 0: 390 c->usage = c->_usage_page | dval; 391 if (s->nusage < MAXUSAGE) 392 s->usages[s->nusage++] = c->usage; 393 /* else XXX */ 394 break; 395 case 1: 396 s->minset = 1; 397 c->usage_minimum = c->_usage_page | dval; 398 break; 399 case 2: 400 c->usage_maximum = c->_usage_page | dval; 401 break; 402 case 3: 403 c->designator_index = dval; 404 break; 405 case 4: 406 c->designator_minimum = dval; 407 break; 408 case 5: 409 c->designator_maximum = dval; 410 break; 411 case 7: 412 c->string_index = dval; 413 break; 414 case 8: 415 c->string_minimum = dval; 416 break; 417 case 9: 418 c->string_maximum = dval; 419 break; 420 case 10: 421 c->set_delimiter = dval; 422 break; 423 default: 424 return (-4); 425 } 426 break; 427 default: 428 return (-5); 429 } 430 } 431} 432 433int 434hid_report_size(report_desc_t r, enum hid_kind k, int id) 435{ 436 struct hid_data *d; 437 hid_item_t h; 438 int size; 439 440 _DIAGASSERT(r != NULL); 441 442 memset(&h, 0, sizeof h); 443 size = 0; 444 for (d = hid_start_parse(r, 1<<k, id); hid_get_item(d, &h); ) { 445 if (h.report_ID == id && h.kind == k) { 446 size = d->kindpos[k]; 447 } 448 } 449 hid_end_parse(d); 450 return ((size + 7) / 8); 451} 452 453int 454hid_locate(report_desc_t desc, unsigned int u, enum hid_kind k, 455 hid_item_t *h, int id) 456{ 457 hid_data_t d; 458 459 _DIAGASSERT(desc != NULL); 460 _DIAGASSERT(h != NULL); 461 462 for (d = hid_start_parse(desc, 1<<k, id); hid_get_item(d, h); ) { 463 if (h->kind == k && !(h->flags & HIO_CONST) && h->usage == u) { 464 hid_end_parse(d); 465 return (1); 466 } 467 } 468 hid_end_parse(d); 469 h->report_size = 0; 470 return (0); 471} 472