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