1b425e319SNick Hibma /* $NetBSD: parse.c,v 1.11 2000/09/24 02:19:54 augustss Exp $ */ 29e2046dfSNick Hibma 39e2046dfSNick Hibma /* 49e2046dfSNick Hibma * Copyright (c) 1999 Lennart Augustsson <augustss@netbsd.org> 59e2046dfSNick Hibma * All rights reserved. 69e2046dfSNick Hibma * 79e2046dfSNick Hibma * Redistribution and use in source and binary forms, with or without 89e2046dfSNick Hibma * modification, are permitted provided that the following conditions 99e2046dfSNick Hibma * are met: 109e2046dfSNick Hibma * 1. Redistributions of source code must retain the above copyright 119e2046dfSNick Hibma * notice, this list of conditions and the following disclaimer. 129e2046dfSNick Hibma * 2. Redistributions in binary form must reproduce the above copyright 139e2046dfSNick Hibma * notice, this list of conditions and the following disclaimer in the 149e2046dfSNick Hibma * documentation and/or other materials provided with the distribution. 159e2046dfSNick Hibma * 169e2046dfSNick Hibma * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 179e2046dfSNick Hibma * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 189e2046dfSNick Hibma * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 199e2046dfSNick Hibma * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 209e2046dfSNick Hibma * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 219e2046dfSNick Hibma * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 229e2046dfSNick Hibma * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 239e2046dfSNick Hibma * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 249e2046dfSNick Hibma * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 259e2046dfSNick Hibma * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 269e2046dfSNick Hibma * SUCH DAMAGE. 279e2046dfSNick Hibma * 289e2046dfSNick Hibma * $FreeBSD$ 299e2046dfSNick Hibma * 309e2046dfSNick Hibma */ 319e2046dfSNick Hibma 329e2046dfSNick Hibma #include <assert.h> 339e2046dfSNick Hibma #include <stdlib.h> 349e2046dfSNick Hibma #include <string.h> 359e2046dfSNick Hibma #include <sys/time.h> 369e2046dfSNick Hibma 379e2046dfSNick Hibma #include <dev/usb/usb.h> 389e2046dfSNick Hibma #include <dev/usb/usbhid.h> 399e2046dfSNick Hibma 409e2046dfSNick Hibma #include "libusb.h" 419e2046dfSNick Hibma #include "usbvar.h" 429e2046dfSNick Hibma 439e2046dfSNick Hibma #define MAXUSAGE 100 449e2046dfSNick Hibma struct hid_data { 459e2046dfSNick Hibma u_char *start; 469e2046dfSNick Hibma u_char *end; 479e2046dfSNick Hibma u_char *p; 489e2046dfSNick Hibma hid_item_t cur; 499e2046dfSNick Hibma unsigned int usages[MAXUSAGE]; 509e2046dfSNick Hibma int nusage; 519e2046dfSNick Hibma int minset; 529e2046dfSNick Hibma int multi; 539e2046dfSNick Hibma int multimax; 549e2046dfSNick Hibma int kindset; 55b425e319SNick Hibma 56b425e319SNick Hibma /* Absolute data position (bits) for input/output/feature. 57b425e319SNick Hibma Assumes that hid_input, hid_output and hid_feature have 58b425e319SNick Hibma values 0, 1 and 2. */ 59b425e319SNick Hibma unsigned int kindpos[3]; 609e2046dfSNick Hibma }; 619e2046dfSNick Hibma 629e2046dfSNick Hibma static int min(int x, int y) { return x < y ? x : y; } 639e2046dfSNick Hibma 649e2046dfSNick Hibma static void 659e2046dfSNick Hibma hid_clear_local(hid_item_t *c) 669e2046dfSNick Hibma { 679e2046dfSNick Hibma c->usage = 0; 689e2046dfSNick Hibma c->usage_minimum = 0; 699e2046dfSNick Hibma c->usage_maximum = 0; 709e2046dfSNick Hibma c->designator_index = 0; 719e2046dfSNick Hibma c->designator_minimum = 0; 729e2046dfSNick Hibma c->designator_maximum = 0; 739e2046dfSNick Hibma c->string_index = 0; 749e2046dfSNick Hibma c->string_minimum = 0; 759e2046dfSNick Hibma c->string_maximum = 0; 769e2046dfSNick Hibma c->set_delimiter = 0; 779e2046dfSNick Hibma } 789e2046dfSNick Hibma 799e2046dfSNick Hibma hid_data_t 809e2046dfSNick Hibma hid_start_parse(report_desc_t d, int kindset) 819e2046dfSNick Hibma { 829e2046dfSNick Hibma struct hid_data *s; 839e2046dfSNick Hibma 849e2046dfSNick Hibma s = malloc(sizeof *s); 859e2046dfSNick Hibma memset(s, 0, sizeof *s); 869e2046dfSNick Hibma s->start = s->p = d->data; 879e2046dfSNick Hibma s->end = d->data + d->size; 889e2046dfSNick Hibma s->kindset = kindset; 899e2046dfSNick Hibma return (s); 909e2046dfSNick Hibma } 919e2046dfSNick Hibma 929e2046dfSNick Hibma void 939e2046dfSNick Hibma hid_end_parse(hid_data_t s) 949e2046dfSNick Hibma { 959e2046dfSNick Hibma while (s->cur.next) { 969e2046dfSNick Hibma hid_item_t *hi = s->cur.next->next; 979e2046dfSNick Hibma free(s->cur.next); 989e2046dfSNick Hibma s->cur.next = hi; 999e2046dfSNick Hibma } 1009e2046dfSNick Hibma free(s); 1019e2046dfSNick Hibma } 1029e2046dfSNick Hibma 1039e2046dfSNick Hibma int 1049e2046dfSNick Hibma hid_get_item(hid_data_t s, hid_item_t *h) 1059e2046dfSNick Hibma { 1069e2046dfSNick Hibma hid_item_t *c; 107b425e319SNick Hibma unsigned int bTag = 0, bType = 0, bSize; 1089e2046dfSNick Hibma unsigned char *data; 1099e2046dfSNick Hibma int dval; 1109e2046dfSNick Hibma unsigned char *p; 1119e2046dfSNick Hibma hid_item_t *hi; 1129e2046dfSNick Hibma int i; 113b425e319SNick Hibma hid_kind_t retkind; 1149e2046dfSNick Hibma 1159e2046dfSNick Hibma c = &s->cur; 1169e2046dfSNick Hibma 1179e2046dfSNick Hibma top: 1189e2046dfSNick Hibma if (s->multimax) { 1199e2046dfSNick Hibma if (s->multi < s->multimax) { 1209e2046dfSNick Hibma c->usage = s->usages[min(s->multi, s->nusage-1)]; 1219e2046dfSNick Hibma s->multi++; 1229e2046dfSNick Hibma *h = *c; 123b425e319SNick Hibma 124b425e319SNick Hibma /* 'multimax' is only non-zero if the current 125b425e319SNick Hibma item kind is input/output/feature */ 126b425e319SNick Hibma h->pos = s->kindpos[c->kind]; 127b425e319SNick Hibma s->kindpos[c->kind] += c->report_size; 1289e2046dfSNick Hibma h->next = 0; 1299e2046dfSNick Hibma return (1); 1309e2046dfSNick Hibma } else { 1319e2046dfSNick Hibma c->report_count = s->multimax; 1329e2046dfSNick Hibma s->multimax = 0; 1339e2046dfSNick Hibma s->nusage = 0; 1349e2046dfSNick Hibma hid_clear_local(c); 1359e2046dfSNick Hibma } 1369e2046dfSNick Hibma } 1379e2046dfSNick Hibma for (;;) { 1389e2046dfSNick Hibma p = s->p; 1399e2046dfSNick Hibma if (p >= s->end) 1409e2046dfSNick Hibma return (0); 1419e2046dfSNick Hibma 1429e2046dfSNick Hibma bSize = *p++; 1439e2046dfSNick Hibma if (bSize == 0xfe) { 1449e2046dfSNick Hibma /* long item */ 1459e2046dfSNick Hibma bSize = *p++; 1469e2046dfSNick Hibma bSize |= *p++ << 8; 1479e2046dfSNick Hibma bTag = *p++; 1489e2046dfSNick Hibma data = p; 1499e2046dfSNick Hibma p += bSize; 1509e2046dfSNick Hibma } else { 1519e2046dfSNick Hibma /* short item */ 1529e2046dfSNick Hibma bTag = bSize >> 4; 1539e2046dfSNick Hibma bType = (bSize >> 2) & 3; 1549e2046dfSNick Hibma bSize &= 3; 1559e2046dfSNick Hibma if (bSize == 3) bSize = 4; 1569e2046dfSNick Hibma data = p; 1579e2046dfSNick Hibma p += bSize; 1589e2046dfSNick Hibma } 1599e2046dfSNick Hibma s->p = p; 1609e2046dfSNick Hibma /* 1619e2046dfSNick Hibma * The spec is unclear if the data is signed or unsigned. 1629e2046dfSNick Hibma */ 1639e2046dfSNick Hibma switch(bSize) { 1649e2046dfSNick Hibma case 0: 1659e2046dfSNick Hibma dval = 0; 1669e2046dfSNick Hibma break; 1679e2046dfSNick Hibma case 1: 1689e2046dfSNick Hibma dval = (int8_t)*data++; 1699e2046dfSNick Hibma break; 1709e2046dfSNick Hibma case 2: 1719e2046dfSNick Hibma dval = *data++; 1729e2046dfSNick Hibma dval |= *data++ << 8; 1739e2046dfSNick Hibma dval = (int16_t)dval; 1749e2046dfSNick Hibma break; 1759e2046dfSNick Hibma case 4: 1769e2046dfSNick Hibma dval = *data++; 1779e2046dfSNick Hibma dval |= *data++ << 8; 1789e2046dfSNick Hibma dval |= *data++ << 16; 1799e2046dfSNick Hibma dval |= *data++ << 24; 1809e2046dfSNick Hibma break; 1819e2046dfSNick Hibma default: 1829e2046dfSNick Hibma return (-1); 1839e2046dfSNick Hibma } 1849e2046dfSNick Hibma 1859e2046dfSNick Hibma switch (bType) { 1869e2046dfSNick Hibma case 0: /* Main */ 1879e2046dfSNick Hibma switch (bTag) { 1889e2046dfSNick Hibma case 8: /* Input */ 189b425e319SNick Hibma retkind = hid_input; 1909e2046dfSNick Hibma ret: 191b425e319SNick Hibma if (!(s->kindset & (1 << retkind))) { 192b425e319SNick Hibma /* Drop the items of this kind */ 193b425e319SNick Hibma s->nusage = 0; 194b425e319SNick Hibma continue; 195b425e319SNick Hibma } 196b425e319SNick Hibma c->kind = retkind; 197b425e319SNick Hibma c->flags = dval; 1989e2046dfSNick Hibma if (c->flags & HIO_VARIABLE) { 1999e2046dfSNick Hibma s->multimax = c->report_count; 2009e2046dfSNick Hibma s->multi = 0; 2019e2046dfSNick Hibma c->report_count = 1; 2029e2046dfSNick Hibma if (s->minset) { 2039e2046dfSNick Hibma for (i = c->usage_minimum; 2049e2046dfSNick Hibma i <= c->usage_maximum; 2059e2046dfSNick Hibma i++) { 2069e2046dfSNick Hibma s->usages[s->nusage] = i; 2079e2046dfSNick Hibma if (s->nusage < MAXUSAGE-1) 2089e2046dfSNick Hibma s->nusage++; 2099e2046dfSNick Hibma } 2109e2046dfSNick Hibma s->minset = 0; 2119e2046dfSNick Hibma } 2129e2046dfSNick Hibma goto top; 2139e2046dfSNick Hibma } else { 2149e2046dfSNick Hibma if (s->minset) 2159e2046dfSNick Hibma c->usage = c->usage_minimum; 2169e2046dfSNick Hibma *h = *c; 2179e2046dfSNick Hibma h->next = 0; 218b425e319SNick Hibma h->pos = s->kindpos[c->kind]; 219b425e319SNick Hibma s->kindpos[c->kind] += c->report_size * c->report_count; 2209e2046dfSNick Hibma hid_clear_local(c); 2219e2046dfSNick Hibma s->minset = 0; 2229e2046dfSNick Hibma return (1); 2239e2046dfSNick Hibma } 2249e2046dfSNick Hibma case 9: /* Output */ 225b425e319SNick Hibma retkind = hid_output; 2269e2046dfSNick Hibma goto ret; 2279e2046dfSNick Hibma case 10: /* Collection */ 2289e2046dfSNick Hibma c->kind = hid_collection; 2299e2046dfSNick Hibma c->collection = dval; 2309e2046dfSNick Hibma c->collevel++; 2319e2046dfSNick Hibma *h = *c; 2329e2046dfSNick Hibma hid_clear_local(c); 2339e2046dfSNick Hibma c->report_ID = NO_REPORT_ID; 2349e2046dfSNick Hibma s->nusage = 0; 2359e2046dfSNick Hibma return (1); 2369e2046dfSNick Hibma case 11: /* Feature */ 237b425e319SNick Hibma retkind = hid_feature; 2389e2046dfSNick Hibma goto ret; 2399e2046dfSNick Hibma case 12: /* End collection */ 2409e2046dfSNick Hibma c->kind = hid_endcollection; 2419e2046dfSNick Hibma c->collevel--; 2429e2046dfSNick Hibma *h = *c; 2439e2046dfSNick Hibma /*hid_clear_local(c);*/ 2449e2046dfSNick Hibma s->nusage = 0; 2459e2046dfSNick Hibma return (1); 2469e2046dfSNick Hibma default: 2479e2046dfSNick Hibma return (-2); 2489e2046dfSNick Hibma } 2499e2046dfSNick Hibma 2509e2046dfSNick Hibma case 1: /* Global */ 2519e2046dfSNick Hibma switch (bTag) { 2529e2046dfSNick Hibma case 0: 2539e2046dfSNick Hibma c->_usage_page = dval << 16; 2549e2046dfSNick Hibma break; 2559e2046dfSNick Hibma case 1: 2569e2046dfSNick Hibma c->logical_minimum = dval; 2579e2046dfSNick Hibma break; 2589e2046dfSNick Hibma case 2: 2599e2046dfSNick Hibma c->logical_maximum = dval; 2609e2046dfSNick Hibma break; 2619e2046dfSNick Hibma case 3: 2629e2046dfSNick Hibma c->physical_maximum = dval; 2639e2046dfSNick Hibma break; 2649e2046dfSNick Hibma case 4: 2659e2046dfSNick Hibma c->physical_maximum = dval; 2669e2046dfSNick Hibma break; 2679e2046dfSNick Hibma case 5: 2689e2046dfSNick Hibma c->unit_exponent = dval; 2699e2046dfSNick Hibma break; 2709e2046dfSNick Hibma case 6: 2719e2046dfSNick Hibma c->unit = dval; 2729e2046dfSNick Hibma break; 2739e2046dfSNick Hibma case 7: 2749e2046dfSNick Hibma c->report_size = dval; 2759e2046dfSNick Hibma break; 2769e2046dfSNick Hibma case 8: 2779e2046dfSNick Hibma c->report_ID = dval; 2789e2046dfSNick Hibma break; 2799e2046dfSNick Hibma case 9: 2809e2046dfSNick Hibma c->report_count = dval; 2819e2046dfSNick Hibma break; 2829e2046dfSNick Hibma case 10: /* Push */ 2839e2046dfSNick Hibma hi = malloc(sizeof *hi); 2849e2046dfSNick Hibma *hi = s->cur; 2859e2046dfSNick Hibma c->next = hi; 2869e2046dfSNick Hibma break; 2879e2046dfSNick Hibma case 11: /* Pop */ 2889e2046dfSNick Hibma hi = c->next; 2899e2046dfSNick Hibma s->cur = *hi; 2909e2046dfSNick Hibma free(hi); 2919e2046dfSNick Hibma break; 2929e2046dfSNick Hibma default: 2939e2046dfSNick Hibma return (-3); 2949e2046dfSNick Hibma } 2959e2046dfSNick Hibma break; 2969e2046dfSNick Hibma case 2: /* Local */ 2979e2046dfSNick Hibma switch (bTag) { 2989e2046dfSNick Hibma case 0: 2999e2046dfSNick Hibma if (bSize == 1) 3009e2046dfSNick Hibma dval = c->_usage_page | (dval&0xff); 3019e2046dfSNick Hibma else if (bSize == 2) 3029e2046dfSNick Hibma dval = c->_usage_page | (dval&0xffff); 3039e2046dfSNick Hibma c->usage = dval; 3049e2046dfSNick Hibma if (s->nusage < MAXUSAGE) 3059e2046dfSNick Hibma s->usages[s->nusage++] = dval; 3069e2046dfSNick Hibma /* else XXX */ 3079e2046dfSNick Hibma break; 3089e2046dfSNick Hibma case 1: 3099e2046dfSNick Hibma s->minset = 1; 3109e2046dfSNick Hibma if (bSize == 1) 3119e2046dfSNick Hibma dval = c->_usage_page | (dval&0xff); 3129e2046dfSNick Hibma else if (bSize == 2) 3139e2046dfSNick Hibma dval = c->_usage_page | (dval&0xffff); 3149e2046dfSNick Hibma c->usage_minimum = dval; 3159e2046dfSNick Hibma break; 3169e2046dfSNick Hibma case 2: 3179e2046dfSNick Hibma if (bSize == 1) 3189e2046dfSNick Hibma dval = c->_usage_page | (dval&0xff); 3199e2046dfSNick Hibma else if (bSize == 2) 3209e2046dfSNick Hibma dval = c->_usage_page | (dval&0xffff); 3219e2046dfSNick Hibma c->usage_maximum = dval; 3229e2046dfSNick Hibma break; 3239e2046dfSNick Hibma case 3: 3249e2046dfSNick Hibma c->designator_index = dval; 3259e2046dfSNick Hibma break; 3269e2046dfSNick Hibma case 4: 3279e2046dfSNick Hibma c->designator_minimum = dval; 3289e2046dfSNick Hibma break; 3299e2046dfSNick Hibma case 5: 3309e2046dfSNick Hibma c->designator_maximum = dval; 3319e2046dfSNick Hibma break; 3329e2046dfSNick Hibma case 7: 3339e2046dfSNick Hibma c->string_index = dval; 3349e2046dfSNick Hibma break; 3359e2046dfSNick Hibma case 8: 3369e2046dfSNick Hibma c->string_minimum = dval; 3379e2046dfSNick Hibma break; 3389e2046dfSNick Hibma case 9: 3399e2046dfSNick Hibma c->string_maximum = dval; 3409e2046dfSNick Hibma break; 3419e2046dfSNick Hibma case 10: 3429e2046dfSNick Hibma c->set_delimiter = dval; 3439e2046dfSNick Hibma break; 3449e2046dfSNick Hibma default: 3459e2046dfSNick Hibma return (-4); 3469e2046dfSNick Hibma } 3479e2046dfSNick Hibma break; 3489e2046dfSNick Hibma default: 3499e2046dfSNick Hibma return (-5); 3509e2046dfSNick Hibma } 3519e2046dfSNick Hibma } 3529e2046dfSNick Hibma } 3539e2046dfSNick Hibma 3549e2046dfSNick Hibma int 355b425e319SNick Hibma hid_report_size(report_desc_t r, unsigned int id, enum hid_kind k) 3569e2046dfSNick Hibma { 3579e2046dfSNick Hibma struct hid_data *d; 3589e2046dfSNick Hibma hid_item_t h; 359b425e319SNick Hibma unsigned int size = 0; 3609e2046dfSNick Hibma 3619e2046dfSNick Hibma memset(&h, 0, sizeof h); 362b425e319SNick Hibma d = hid_start_parse(r, 1<<k); 363b425e319SNick Hibma while (hid_get_item(d, &h)) { 364b425e319SNick Hibma if (h.report_ID == id && h.kind == k) { 365b425e319SNick Hibma unsigned int newsize = h.pos + h.report_size; 366b425e319SNick Hibma if (newsize > size) 367b425e319SNick Hibma size = newsize; 3689e2046dfSNick Hibma } 3699e2046dfSNick Hibma } 3709e2046dfSNick Hibma hid_end_parse(d); 371b425e319SNick Hibma 372b425e319SNick Hibma if (id != NO_REPORT_ID) 373b425e319SNick Hibma size += 8; /* add 8 bits for the report ID */ 374b425e319SNick Hibma 375b425e319SNick Hibma return ((size + 7) / 8); /* return size in bytes */ 3769e2046dfSNick Hibma } 3779e2046dfSNick Hibma 3789e2046dfSNick Hibma int 379b425e319SNick Hibma hid_locate(report_desc_t desc, unsigned int u, enum hid_kind k, hid_item_t *h) 3809e2046dfSNick Hibma { 3819e2046dfSNick Hibma hid_data_t d; 3829e2046dfSNick Hibma 3839e2046dfSNick Hibma for (d = hid_start_parse(desc, 1<<k); hid_get_item(d, h); ) { 3849e2046dfSNick Hibma if (h->kind == k && !(h->flags & HIO_CONST) && h->usage == u) { 3859e2046dfSNick Hibma hid_end_parse(d); 3869e2046dfSNick Hibma return (1); 3879e2046dfSNick Hibma } 3889e2046dfSNick Hibma } 3899e2046dfSNick Hibma hid_end_parse(d); 3909e2046dfSNick Hibma h->report_size = 0; 3919e2046dfSNick Hibma return (0); 3929e2046dfSNick Hibma } 393