19e2046dfSNick Hibma /* $NetBSD: parse.c,v 1.9 2000/03/17 18:09:17 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; 559e2046dfSNick Hibma }; 569e2046dfSNick Hibma 579e2046dfSNick Hibma static int min(int x, int y) { return x < y ? x : y; } 589e2046dfSNick Hibma 599e2046dfSNick Hibma static void 609e2046dfSNick Hibma hid_clear_local(hid_item_t *c) 619e2046dfSNick Hibma { 629e2046dfSNick Hibma 639e2046dfSNick Hibma _DIAGASSERT(c != NULL); 649e2046dfSNick Hibma 659e2046dfSNick Hibma c->usage = 0; 669e2046dfSNick Hibma c->usage_minimum = 0; 679e2046dfSNick Hibma c->usage_maximum = 0; 689e2046dfSNick Hibma c->designator_index = 0; 699e2046dfSNick Hibma c->designator_minimum = 0; 709e2046dfSNick Hibma c->designator_maximum = 0; 719e2046dfSNick Hibma c->string_index = 0; 729e2046dfSNick Hibma c->string_minimum = 0; 739e2046dfSNick Hibma c->string_maximum = 0; 749e2046dfSNick Hibma c->set_delimiter = 0; 759e2046dfSNick Hibma } 769e2046dfSNick Hibma 779e2046dfSNick Hibma hid_data_t 789e2046dfSNick Hibma hid_start_parse(report_desc_t d, int kindset) 799e2046dfSNick Hibma { 809e2046dfSNick Hibma struct hid_data *s; 819e2046dfSNick Hibma 829e2046dfSNick Hibma _DIAGASSERT(d != NULL); 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 969e2046dfSNick Hibma _DIAGASSERT(s != NULL); 979e2046dfSNick Hibma 989e2046dfSNick Hibma while (s->cur.next) { 999e2046dfSNick Hibma hid_item_t *hi = s->cur.next->next; 1009e2046dfSNick Hibma free(s->cur.next); 1019e2046dfSNick Hibma s->cur.next = hi; 1029e2046dfSNick Hibma } 1039e2046dfSNick Hibma free(s); 1049e2046dfSNick Hibma } 1059e2046dfSNick Hibma 1069e2046dfSNick Hibma int 1079e2046dfSNick Hibma hid_get_item(hid_data_t s, hid_item_t *h) 1089e2046dfSNick Hibma { 1099e2046dfSNick Hibma hid_item_t *c; 1109e2046dfSNick Hibma unsigned int bTag = 0, bType = 0, bSize, oldpos; 1119e2046dfSNick Hibma unsigned char *data; 1129e2046dfSNick Hibma int dval; 1139e2046dfSNick Hibma unsigned char *p; 1149e2046dfSNick Hibma hid_item_t *hi; 1159e2046dfSNick Hibma int i; 1169e2046dfSNick Hibma 1179e2046dfSNick Hibma _DIAGASSERT(s != NULL); 1189e2046dfSNick Hibma _DIAGASSERT(h != NULL); 1199e2046dfSNick Hibma 1209e2046dfSNick Hibma c = &s->cur; 1219e2046dfSNick Hibma 1229e2046dfSNick Hibma top: 1239e2046dfSNick Hibma if (s->multimax) { 1249e2046dfSNick Hibma if (s->multi < s->multimax) { 1259e2046dfSNick Hibma c->usage = s->usages[min(s->multi, s->nusage-1)]; 1269e2046dfSNick Hibma s->multi++; 1279e2046dfSNick Hibma *h = *c; 1289e2046dfSNick Hibma c->pos += c->report_size; 1299e2046dfSNick Hibma h->next = 0; 1309e2046dfSNick Hibma return (1); 1319e2046dfSNick Hibma } else { 1329e2046dfSNick Hibma c->report_count = s->multimax; 1339e2046dfSNick Hibma s->multimax = 0; 1349e2046dfSNick Hibma s->nusage = 0; 1359e2046dfSNick Hibma hid_clear_local(c); 1369e2046dfSNick Hibma } 1379e2046dfSNick Hibma } 1389e2046dfSNick Hibma for (;;) { 1399e2046dfSNick Hibma p = s->p; 1409e2046dfSNick Hibma if (p >= s->end) 1419e2046dfSNick Hibma return (0); 1429e2046dfSNick Hibma 1439e2046dfSNick Hibma bSize = *p++; 1449e2046dfSNick Hibma if (bSize == 0xfe) { 1459e2046dfSNick Hibma /* long item */ 1469e2046dfSNick Hibma bSize = *p++; 1479e2046dfSNick Hibma bSize |= *p++ << 8; 1489e2046dfSNick Hibma bTag = *p++; 1499e2046dfSNick Hibma data = p; 1509e2046dfSNick Hibma p += bSize; 1519e2046dfSNick Hibma } else { 1529e2046dfSNick Hibma /* short item */ 1539e2046dfSNick Hibma bTag = bSize >> 4; 1549e2046dfSNick Hibma bType = (bSize >> 2) & 3; 1559e2046dfSNick Hibma bSize &= 3; 1569e2046dfSNick Hibma if (bSize == 3) bSize = 4; 1579e2046dfSNick Hibma data = p; 1589e2046dfSNick Hibma p += bSize; 1599e2046dfSNick Hibma } 1609e2046dfSNick Hibma s->p = p; 1619e2046dfSNick Hibma /* 1629e2046dfSNick Hibma * The spec is unclear if the data is signed or unsigned. 1639e2046dfSNick Hibma */ 1649e2046dfSNick Hibma switch(bSize) { 1659e2046dfSNick Hibma case 0: 1669e2046dfSNick Hibma dval = 0; 1679e2046dfSNick Hibma break; 1689e2046dfSNick Hibma case 1: 1699e2046dfSNick Hibma dval = (int8_t)*data++; 1709e2046dfSNick Hibma break; 1719e2046dfSNick Hibma case 2: 1729e2046dfSNick Hibma dval = *data++; 1739e2046dfSNick Hibma dval |= *data++ << 8; 1749e2046dfSNick Hibma dval = (int16_t)dval; 1759e2046dfSNick Hibma break; 1769e2046dfSNick Hibma case 4: 1779e2046dfSNick Hibma dval = *data++; 1789e2046dfSNick Hibma dval |= *data++ << 8; 1799e2046dfSNick Hibma dval |= *data++ << 16; 1809e2046dfSNick Hibma dval |= *data++ << 24; 1819e2046dfSNick Hibma break; 1829e2046dfSNick Hibma default: 1839e2046dfSNick Hibma return (-1); 1849e2046dfSNick Hibma } 1859e2046dfSNick Hibma 1869e2046dfSNick Hibma switch (bType) { 1879e2046dfSNick Hibma case 0: /* Main */ 1889e2046dfSNick Hibma switch (bTag) { 1899e2046dfSNick Hibma case 8: /* Input */ 1909e2046dfSNick Hibma if (!(s->kindset & (1 << hid_input))) 1919e2046dfSNick Hibma continue; 1929e2046dfSNick Hibma c->kind = hid_input; 1939e2046dfSNick Hibma c->flags = dval; 1949e2046dfSNick Hibma ret: 1959e2046dfSNick Hibma if (c->flags & HIO_VARIABLE) { 1969e2046dfSNick Hibma s->multimax = c->report_count; 1979e2046dfSNick Hibma s->multi = 0; 1989e2046dfSNick Hibma c->report_count = 1; 1999e2046dfSNick Hibma if (s->minset) { 2009e2046dfSNick Hibma for (i = c->usage_minimum; 2019e2046dfSNick Hibma i <= c->usage_maximum; 2029e2046dfSNick Hibma i++) { 2039e2046dfSNick Hibma s->usages[s->nusage] = i; 2049e2046dfSNick Hibma if (s->nusage < MAXUSAGE-1) 2059e2046dfSNick Hibma s->nusage++; 2069e2046dfSNick Hibma } 2079e2046dfSNick Hibma s->minset = 0; 2089e2046dfSNick Hibma } 2099e2046dfSNick Hibma goto top; 2109e2046dfSNick Hibma } else { 2119e2046dfSNick Hibma if (s->minset) 2129e2046dfSNick Hibma c->usage = c->usage_minimum; 2139e2046dfSNick Hibma *h = *c; 2149e2046dfSNick Hibma h->next = 0; 2159e2046dfSNick Hibma c->pos += c->report_size * c->report_count; 2169e2046dfSNick Hibma hid_clear_local(c); 2179e2046dfSNick Hibma s->minset = 0; 2189e2046dfSNick Hibma return (1); 2199e2046dfSNick Hibma } 2209e2046dfSNick Hibma case 9: /* Output */ 2219e2046dfSNick Hibma if (!(s->kindset & (1 << hid_output))) 2229e2046dfSNick Hibma continue; 2239e2046dfSNick Hibma c->kind = hid_output; 2249e2046dfSNick Hibma c->flags = dval; 2259e2046dfSNick Hibma goto ret; 2269e2046dfSNick Hibma case 10: /* Collection */ 2279e2046dfSNick Hibma c->kind = hid_collection; 2289e2046dfSNick Hibma c->collection = dval; 2299e2046dfSNick Hibma c->collevel++; 2309e2046dfSNick Hibma *h = *c; 2319e2046dfSNick Hibma hid_clear_local(c); 2329e2046dfSNick Hibma c->report_ID = NO_REPORT_ID; 2339e2046dfSNick Hibma s->nusage = 0; 2349e2046dfSNick Hibma return (1); 2359e2046dfSNick Hibma case 11: /* Feature */ 2369e2046dfSNick Hibma if (!(s->kindset & (1 << hid_feature))) 2379e2046dfSNick Hibma continue; 2389e2046dfSNick Hibma c->kind = hid_feature; 2399e2046dfSNick Hibma c->flags = dval; 2409e2046dfSNick Hibma goto ret; 2419e2046dfSNick Hibma case 12: /* End collection */ 2429e2046dfSNick Hibma c->kind = hid_endcollection; 2439e2046dfSNick Hibma c->collevel--; 2449e2046dfSNick Hibma *h = *c; 2459e2046dfSNick Hibma /*hid_clear_local(c);*/ 2469e2046dfSNick Hibma s->nusage = 0; 2479e2046dfSNick Hibma return (1); 2489e2046dfSNick Hibma default: 2499e2046dfSNick Hibma return (-2); 2509e2046dfSNick Hibma } 2519e2046dfSNick Hibma 2529e2046dfSNick Hibma case 1: /* Global */ 2539e2046dfSNick Hibma switch (bTag) { 2549e2046dfSNick Hibma case 0: 2559e2046dfSNick Hibma c->_usage_page = dval << 16; 2569e2046dfSNick Hibma break; 2579e2046dfSNick Hibma case 1: 2589e2046dfSNick Hibma c->logical_minimum = dval; 2599e2046dfSNick Hibma break; 2609e2046dfSNick Hibma case 2: 2619e2046dfSNick Hibma c->logical_maximum = dval; 2629e2046dfSNick Hibma break; 2639e2046dfSNick Hibma case 3: 2649e2046dfSNick Hibma c->physical_maximum = dval; 2659e2046dfSNick Hibma break; 2669e2046dfSNick Hibma case 4: 2679e2046dfSNick Hibma c->physical_maximum = dval; 2689e2046dfSNick Hibma break; 2699e2046dfSNick Hibma case 5: 2709e2046dfSNick Hibma c->unit_exponent = dval; 2719e2046dfSNick Hibma break; 2729e2046dfSNick Hibma case 6: 2739e2046dfSNick Hibma c->unit = dval; 2749e2046dfSNick Hibma break; 2759e2046dfSNick Hibma case 7: 2769e2046dfSNick Hibma c->report_size = dval; 2779e2046dfSNick Hibma break; 2789e2046dfSNick Hibma case 8: 2799e2046dfSNick Hibma c->report_ID = dval; 2809e2046dfSNick Hibma break; 2819e2046dfSNick Hibma case 9: 2829e2046dfSNick Hibma c->report_count = dval; 2839e2046dfSNick Hibma break; 2849e2046dfSNick Hibma case 10: /* Push */ 2859e2046dfSNick Hibma hi = malloc(sizeof *hi); 2869e2046dfSNick Hibma *hi = s->cur; 2879e2046dfSNick Hibma c->next = hi; 2889e2046dfSNick Hibma break; 2899e2046dfSNick Hibma case 11: /* Pop */ 2909e2046dfSNick Hibma hi = c->next; 2919e2046dfSNick Hibma oldpos = c->pos; 2929e2046dfSNick Hibma s->cur = *hi; 2939e2046dfSNick Hibma c->pos = oldpos; 2949e2046dfSNick Hibma free(hi); 2959e2046dfSNick Hibma break; 2969e2046dfSNick Hibma default: 2979e2046dfSNick Hibma return (-3); 2989e2046dfSNick Hibma } 2999e2046dfSNick Hibma break; 3009e2046dfSNick Hibma case 2: /* Local */ 3019e2046dfSNick Hibma switch (bTag) { 3029e2046dfSNick Hibma case 0: 3039e2046dfSNick Hibma if (bSize == 1) 3049e2046dfSNick Hibma dval = c->_usage_page | (dval&0xff); 3059e2046dfSNick Hibma else if (bSize == 2) 3069e2046dfSNick Hibma dval = c->_usage_page | (dval&0xffff); 3079e2046dfSNick Hibma c->usage = dval; 3089e2046dfSNick Hibma if (s->nusage < MAXUSAGE) 3099e2046dfSNick Hibma s->usages[s->nusage++] = dval; 3109e2046dfSNick Hibma /* else XXX */ 3119e2046dfSNick Hibma break; 3129e2046dfSNick Hibma case 1: 3139e2046dfSNick Hibma s->minset = 1; 3149e2046dfSNick Hibma if (bSize == 1) 3159e2046dfSNick Hibma dval = c->_usage_page | (dval&0xff); 3169e2046dfSNick Hibma else if (bSize == 2) 3179e2046dfSNick Hibma dval = c->_usage_page | (dval&0xffff); 3189e2046dfSNick Hibma c->usage_minimum = dval; 3199e2046dfSNick Hibma break; 3209e2046dfSNick Hibma case 2: 3219e2046dfSNick Hibma if (bSize == 1) 3229e2046dfSNick Hibma dval = c->_usage_page | (dval&0xff); 3239e2046dfSNick Hibma else if (bSize == 2) 3249e2046dfSNick Hibma dval = c->_usage_page | (dval&0xffff); 3259e2046dfSNick Hibma c->usage_maximum = dval; 3269e2046dfSNick Hibma break; 3279e2046dfSNick Hibma case 3: 3289e2046dfSNick Hibma c->designator_index = dval; 3299e2046dfSNick Hibma break; 3309e2046dfSNick Hibma case 4: 3319e2046dfSNick Hibma c->designator_minimum = dval; 3329e2046dfSNick Hibma break; 3339e2046dfSNick Hibma case 5: 3349e2046dfSNick Hibma c->designator_maximum = dval; 3359e2046dfSNick Hibma break; 3369e2046dfSNick Hibma case 7: 3379e2046dfSNick Hibma c->string_index = dval; 3389e2046dfSNick Hibma break; 3399e2046dfSNick Hibma case 8: 3409e2046dfSNick Hibma c->string_minimum = dval; 3419e2046dfSNick Hibma break; 3429e2046dfSNick Hibma case 9: 3439e2046dfSNick Hibma c->string_maximum = dval; 3449e2046dfSNick Hibma break; 3459e2046dfSNick Hibma case 10: 3469e2046dfSNick Hibma c->set_delimiter = dval; 3479e2046dfSNick Hibma break; 3489e2046dfSNick Hibma default: 3499e2046dfSNick Hibma return (-4); 3509e2046dfSNick Hibma } 3519e2046dfSNick Hibma break; 3529e2046dfSNick Hibma default: 3539e2046dfSNick Hibma return (-5); 3549e2046dfSNick Hibma } 3559e2046dfSNick Hibma } 3569e2046dfSNick Hibma } 3579e2046dfSNick Hibma 3589e2046dfSNick Hibma int 3599e2046dfSNick Hibma hid_report_size(report_desc_t r, enum hid_kind k, int *idp) 3609e2046dfSNick Hibma { 3619e2046dfSNick Hibma struct hid_data *d; 3629e2046dfSNick Hibma hid_item_t h; 3639e2046dfSNick Hibma int size, id; 3649e2046dfSNick Hibma 3659e2046dfSNick Hibma _DIAGASSERT(r != NULL); 3669e2046dfSNick Hibma /* idp may be NULL */ 3679e2046dfSNick Hibma 3689e2046dfSNick Hibma id = 0; 3699e2046dfSNick Hibma if (idp) 3709e2046dfSNick Hibma *idp = 0; 3719e2046dfSNick Hibma memset(&h, 0, sizeof h); 3729e2046dfSNick Hibma for (d = hid_start_parse(r, 1<<k); hid_get_item(d, &h); ) { 3739e2046dfSNick Hibma if (h.report_ID != NO_REPORT_ID) { 3749e2046dfSNick Hibma if (idp) 3759e2046dfSNick Hibma *idp = h.report_ID; 3769e2046dfSNick Hibma id = 8; 3779e2046dfSNick Hibma } 3789e2046dfSNick Hibma } 3799e2046dfSNick Hibma hid_end_parse(d); 3809e2046dfSNick Hibma size = h.pos + id; 3819e2046dfSNick Hibma return ((size + 7) / 8); 3829e2046dfSNick Hibma } 3839e2046dfSNick Hibma 3849e2046dfSNick Hibma int 3859e2046dfSNick Hibma hid_locate(desc, u, k, h) 3869e2046dfSNick Hibma report_desc_t desc; 3879e2046dfSNick Hibma unsigned int u; 3889e2046dfSNick Hibma enum hid_kind k; 3899e2046dfSNick Hibma hid_item_t *h; 3909e2046dfSNick Hibma { 3919e2046dfSNick Hibma hid_data_t d; 3929e2046dfSNick Hibma 3939e2046dfSNick Hibma _DIAGASSERT(desc != NULL); 3949e2046dfSNick Hibma _DIAGASSERT(h != NULL); 3959e2046dfSNick Hibma 3969e2046dfSNick Hibma for (d = hid_start_parse(desc, 1<<k); hid_get_item(d, h); ) { 3979e2046dfSNick Hibma if (h->kind == k && !(h->flags & HIO_CONST) && h->usage == u) { 3989e2046dfSNick Hibma hid_end_parse(d); 3999e2046dfSNick Hibma return (1); 4009e2046dfSNick Hibma } 4019e2046dfSNick Hibma } 4029e2046dfSNick Hibma hid_end_parse(d); 4039e2046dfSNick Hibma h->report_size = 0; 4049e2046dfSNick Hibma return (0); 4059e2046dfSNick Hibma } 406