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