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