1 /* $NetBSD: parse.c,v 1.9 2000/03/17 18:09:17 augustss Exp $ */ 2 3 /* 4 * Copyright (c) 1999 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 * $FreeBSD$ 29 * 30 */ 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 "libusb.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 multi; 53 int multimax; 54 int kindset; 55 }; 56 57 static int min(int x, int y) { return x < y ? x : y; } 58 59 static void 60 hid_clear_local(hid_item_t *c) 61 { 62 c->usage = 0; 63 c->usage_minimum = 0; 64 c->usage_maximum = 0; 65 c->designator_index = 0; 66 c->designator_minimum = 0; 67 c->designator_maximum = 0; 68 c->string_index = 0; 69 c->string_minimum = 0; 70 c->string_maximum = 0; 71 c->set_delimiter = 0; 72 } 73 74 hid_data_t 75 hid_start_parse(report_desc_t d, int kindset) 76 { 77 struct hid_data *s; 78 79 s = malloc(sizeof *s); 80 memset(s, 0, sizeof *s); 81 s->start = s->p = d->data; 82 s->end = d->data + d->size; 83 s->kindset = kindset; 84 return (s); 85 } 86 87 void 88 hid_end_parse(hid_data_t s) 89 { 90 while (s->cur.next) { 91 hid_item_t *hi = s->cur.next->next; 92 free(s->cur.next); 93 s->cur.next = hi; 94 } 95 free(s); 96 } 97 98 int 99 hid_get_item(hid_data_t s, hid_item_t *h) 100 { 101 hid_item_t *c; 102 unsigned int bTag = 0, bType = 0, bSize, oldpos; 103 unsigned char *data; 104 int dval; 105 unsigned char *p; 106 hid_item_t *hi; 107 int i; 108 109 c = &s->cur; 110 111 top: 112 if (s->multimax) { 113 if (s->multi < s->multimax) { 114 c->usage = s->usages[min(s->multi, s->nusage-1)]; 115 s->multi++; 116 *h = *c; 117 c->pos += c->report_size; 118 h->next = 0; 119 return (1); 120 } else { 121 c->report_count = s->multimax; 122 s->multimax = 0; 123 s->nusage = 0; 124 hid_clear_local(c); 125 } 126 } 127 for (;;) { 128 p = s->p; 129 if (p >= s->end) 130 return (0); 131 132 bSize = *p++; 133 if (bSize == 0xfe) { 134 /* long item */ 135 bSize = *p++; 136 bSize |= *p++ << 8; 137 bTag = *p++; 138 data = p; 139 p += bSize; 140 } else { 141 /* short item */ 142 bTag = bSize >> 4; 143 bType = (bSize >> 2) & 3; 144 bSize &= 3; 145 if (bSize == 3) bSize = 4; 146 data = p; 147 p += bSize; 148 } 149 s->p = p; 150 /* 151 * The spec is unclear if the data is signed or unsigned. 152 */ 153 switch(bSize) { 154 case 0: 155 dval = 0; 156 break; 157 case 1: 158 dval = (int8_t)*data++; 159 break; 160 case 2: 161 dval = *data++; 162 dval |= *data++ << 8; 163 dval = (int16_t)dval; 164 break; 165 case 4: 166 dval = *data++; 167 dval |= *data++ << 8; 168 dval |= *data++ << 16; 169 dval |= *data++ << 24; 170 break; 171 default: 172 return (-1); 173 } 174 175 switch (bType) { 176 case 0: /* Main */ 177 switch (bTag) { 178 case 8: /* Input */ 179 if (!(s->kindset & (1 << hid_input))) 180 continue; 181 c->kind = hid_input; 182 c->flags = dval; 183 ret: 184 if (c->flags & HIO_VARIABLE) { 185 s->multimax = c->report_count; 186 s->multi = 0; 187 c->report_count = 1; 188 if (s->minset) { 189 for (i = c->usage_minimum; 190 i <= c->usage_maximum; 191 i++) { 192 s->usages[s->nusage] = i; 193 if (s->nusage < MAXUSAGE-1) 194 s->nusage++; 195 } 196 s->minset = 0; 197 } 198 goto top; 199 } else { 200 if (s->minset) 201 c->usage = c->usage_minimum; 202 *h = *c; 203 h->next = 0; 204 c->pos += c->report_size * c->report_count; 205 hid_clear_local(c); 206 s->minset = 0; 207 return (1); 208 } 209 case 9: /* Output */ 210 if (!(s->kindset & (1 << hid_output))) 211 continue; 212 c->kind = hid_output; 213 c->flags = dval; 214 goto ret; 215 case 10: /* Collection */ 216 c->kind = hid_collection; 217 c->collection = dval; 218 c->collevel++; 219 *h = *c; 220 hid_clear_local(c); 221 c->report_ID = NO_REPORT_ID; 222 s->nusage = 0; 223 return (1); 224 case 11: /* Feature */ 225 if (!(s->kindset & (1 << hid_feature))) 226 continue; 227 c->kind = hid_feature; 228 c->flags = dval; 229 goto ret; 230 case 12: /* End collection */ 231 c->kind = hid_endcollection; 232 c->collevel--; 233 *h = *c; 234 /*hid_clear_local(c);*/ 235 s->nusage = 0; 236 return (1); 237 default: 238 return (-2); 239 } 240 241 case 1: /* Global */ 242 switch (bTag) { 243 case 0: 244 c->_usage_page = dval << 16; 245 break; 246 case 1: 247 c->logical_minimum = dval; 248 break; 249 case 2: 250 c->logical_maximum = dval; 251 break; 252 case 3: 253 c->physical_maximum = dval; 254 break; 255 case 4: 256 c->physical_maximum = dval; 257 break; 258 case 5: 259 c->unit_exponent = dval; 260 break; 261 case 6: 262 c->unit = dval; 263 break; 264 case 7: 265 c->report_size = dval; 266 break; 267 case 8: 268 c->report_ID = dval; 269 break; 270 case 9: 271 c->report_count = dval; 272 break; 273 case 10: /* Push */ 274 hi = malloc(sizeof *hi); 275 *hi = s->cur; 276 c->next = hi; 277 break; 278 case 11: /* Pop */ 279 hi = c->next; 280 oldpos = c->pos; 281 s->cur = *hi; 282 c->pos = oldpos; 283 free(hi); 284 break; 285 default: 286 return (-3); 287 } 288 break; 289 case 2: /* Local */ 290 switch (bTag) { 291 case 0: 292 if (bSize == 1) 293 dval = c->_usage_page | (dval&0xff); 294 else if (bSize == 2) 295 dval = c->_usage_page | (dval&0xffff); 296 c->usage = dval; 297 if (s->nusage < MAXUSAGE) 298 s->usages[s->nusage++] = dval; 299 /* else XXX */ 300 break; 301 case 1: 302 s->minset = 1; 303 if (bSize == 1) 304 dval = c->_usage_page | (dval&0xff); 305 else if (bSize == 2) 306 dval = c->_usage_page | (dval&0xffff); 307 c->usage_minimum = dval; 308 break; 309 case 2: 310 if (bSize == 1) 311 dval = c->_usage_page | (dval&0xff); 312 else if (bSize == 2) 313 dval = c->_usage_page | (dval&0xffff); 314 c->usage_maximum = dval; 315 break; 316 case 3: 317 c->designator_index = dval; 318 break; 319 case 4: 320 c->designator_minimum = dval; 321 break; 322 case 5: 323 c->designator_maximum = dval; 324 break; 325 case 7: 326 c->string_index = dval; 327 break; 328 case 8: 329 c->string_minimum = dval; 330 break; 331 case 9: 332 c->string_maximum = dval; 333 break; 334 case 10: 335 c->set_delimiter = dval; 336 break; 337 default: 338 return (-4); 339 } 340 break; 341 default: 342 return (-5); 343 } 344 } 345 } 346 347 int 348 hid_report_size(report_desc_t r, enum hid_kind k, int *idp) 349 { 350 struct hid_data *d; 351 hid_item_t h; 352 int size, id; 353 354 id = 0; 355 if (idp) 356 *idp = 0; 357 memset(&h, 0, sizeof h); 358 for (d = hid_start_parse(r, 1<<k); hid_get_item(d, &h); ) { 359 if (h.report_ID != NO_REPORT_ID) { 360 if (idp) 361 *idp = h.report_ID; 362 id = 8; 363 } 364 } 365 hid_end_parse(d); 366 size = h.pos + id; 367 return ((size + 7) / 8); 368 } 369 370 int 371 hid_locate(desc, u, k, h) 372 report_desc_t desc; 373 unsigned int u; 374 enum hid_kind k; 375 hid_item_t *h; 376 { 377 hid_data_t d; 378 379 for (d = hid_start_parse(desc, 1<<k); hid_get_item(d, h); ) { 380 if (h->kind == k && !(h->flags & HIO_CONST) && h->usage == u) { 381 hid_end_parse(d); 382 return (1); 383 } 384 } 385 hid_end_parse(d); 386 h->report_size = 0; 387 return (0); 388 } 389