1/* 2 * Copyright 2009-2011, Michael Lotz, mmlr@mlotz.ch. 3 * Distributed under the terms of the MIT License. 4 */ 5 6#ifndef USERLAND_HID 7#include "Driver.h" 8#else 9#include "UserlandHID.h" 10#endif 11 12#include "HIDCollection.h" 13#include "HIDParser.h" 14#include "HIDReport.h" 15 16#include <new> 17#include <stdlib.h> 18#include <string.h> 19 20 21static uint8 sItemSize[4] = { 0, 1, 2, 4 }; 22static int8 sUnitExponent[16] = { 23 // really just a 4 bit signed value 24 0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1 25}; 26 27 28HIDParser::HIDParser(HIDDevice *device) 29 : fDevice(device), 30 fUsesReportIDs(false), 31 fReportCount(0), 32 fReports(NULL), 33 fRootCollection(NULL) 34{ 35} 36 37 38HIDParser::~HIDParser() 39{ 40 _Reset(); 41} 42 43 44status_t 45HIDParser::ParseReportDescriptor(const uint8 *reportDescriptor, 46 size_t descriptorLength) 47{ 48 _Reset(); 49 50 global_item_state globalState; 51 memset(&globalState, 0, sizeof(global_item_state)); 52 53 local_item_state localState; 54 memset(&localState, 0, sizeof(local_item_state)); 55 56 uint32 usageStackUsed = 0; 57 uint32 usageStackSize = 10; 58 usage_value *usageStack = (usage_value *)malloc(usageStackSize 59 * sizeof(usage_value)); 60 if (usageStack == NULL) { 61 TRACE_ALWAYS("no memory to allocate usage stack\n"); 62 return B_NO_MEMORY; 63 } 64 65 fRootCollection = new(std::nothrow) HIDCollection(NULL, COLLECTION_LOGICAL, 66 localState); 67 if (fRootCollection == NULL) { 68 TRACE_ALWAYS("no memory to allocate root collection\n"); 69 return B_NO_MEMORY; 70 } 71 72 HIDCollection *collection = fRootCollection; 73 const uint8 *pointer = reportDescriptor; 74 const uint8 *end = pointer + descriptorLength; 75 76 while (pointer < end) { 77 const item_prefix *item = (item_prefix *)pointer; 78 size_t itemSize = sItemSize[item->size]; 79 uint32 data = 0; 80 81 if (item->type == ITEM_TYPE_LONG) { 82 long_item *longItem = (long_item *)item; 83 itemSize += longItem->data_size; 84 } else { 85 short_item *shortItem = (short_item *)item; 86 switch (itemSize) { 87 case 1: 88 data = shortItem->data.as_uint8[0]; 89 break; 90 91 case 2: 92 data = shortItem->data.as_uint16[0]; 93 break; 94 95 case 4: 96 data = shortItem->data.as_uint32; 97 break; 98 99 default: 100 break; 101 } 102 } 103 104 TRACE("got item: type: %s; size: %lu; tag: %u; data: %lu\n", 105 item->type == ITEM_TYPE_MAIN ? "main" 106 : item->type == ITEM_TYPE_GLOBAL ? "global" 107 : item->type == ITEM_TYPE_LOCAL ? "local" : "long", 108 itemSize, item->tag, data); 109 110 switch (item->type) { 111 case ITEM_TYPE_MAIN: 112 { 113 // preprocess the local state if relevant (usages for 114 // collections and report items) 115 if (item->tag != ITEM_TAG_MAIN_END_COLLECTION) { 116 // make all usages extended for easier later processing 117 for (uint32 i = 0; i < usageStackUsed; i++) { 118 if (usageStack[i].is_extended) 119 continue; 120 usageStack[i].u.s.usage_page = globalState.usage_page; 121 usageStack[i].is_extended = true; 122 } 123 124 if (!localState.usage_minimum.is_extended) { 125 // the specs say if one of them is extended they must 126 // both be extended, so if the minimum isn't, the 127 // maximum mustn't either. 128 localState.usage_minimum.u.s.usage_page 129 = localState.usage_maximum.u.s.usage_page 130 = globalState.usage_page; 131 localState.usage_minimum.is_extended 132 = localState.usage_maximum.is_extended = true; 133 } 134 135 localState.usage_stack = usageStack; 136 localState.usage_stack_used = usageStackUsed; 137 } 138 139 if (item->tag == ITEM_TAG_MAIN_COLLECTION) { 140 HIDCollection *newCollection 141 = new(std::nothrow) HIDCollection(collection, 142 (uint8)data, localState); 143 if (newCollection == NULL) { 144 TRACE_ALWAYS("no memory to allocate new collection\n"); 145 break; 146 } 147 148 collection->AddChild(newCollection); 149 collection = newCollection; 150 } else if (item->tag == ITEM_TAG_MAIN_END_COLLECTION) { 151 if (collection == fRootCollection) { 152 TRACE_ALWAYS("end collection with no open one\n"); 153 break; 154 } 155 156 collection = collection->Parent(); 157 } else { 158 uint8 reportType = HID_REPORT_TYPE_ANY; 159 switch (item->tag) { 160 case ITEM_TAG_MAIN_INPUT: 161 reportType = HID_REPORT_TYPE_INPUT; 162 break; 163 164 case ITEM_TAG_MAIN_OUTPUT: 165 reportType = HID_REPORT_TYPE_OUTPUT; 166 break; 167 168 case ITEM_TAG_MAIN_FEATURE: 169 reportType = HID_REPORT_TYPE_FEATURE; 170 break; 171 172 default: 173 TRACE_ALWAYS("unknown main item tag: 0x%02x\n", 174 item->tag); 175 break; 176 } 177 178 if (reportType == HID_REPORT_TYPE_ANY) 179 break; 180 181 HIDReport *target = _FindOrCreateReport(reportType, 182 globalState.report_id); 183 if (target == NULL) 184 break; 185 186 // fill in a sensible default if the index isn't set 187 if (!localState.designator_index_set) { 188 localState.designator_index 189 = localState.designator_minimum; 190 } 191 192 if (!localState.string_index_set) 193 localState.string_index = localState.string_minimum; 194 195 main_item_data *mainData = (main_item_data *)&data; 196 target->AddMainItem(globalState, localState, *mainData, 197 collection); 198 } 199 200 // reset the local item state 201 memset(&localState, 0, sizeof(local_item_state)); 202 usageStackUsed = 0; 203 break; 204 } 205 206 case ITEM_TYPE_GLOBAL: 207 { 208 switch (item->tag) { 209 case ITEM_TAG_GLOBAL_USAGE_PAGE: 210 globalState.usage_page = data; 211 break; 212 213 case ITEM_TAG_GLOBAL_LOGICAL_MINIMUM: 214 globalState.logical_minimum = data; 215 break; 216 217 case ITEM_TAG_GLOBAL_LOGICAL_MAXIMUM: 218 globalState.logical_maximum = data; 219 break; 220 221 case ITEM_TAG_GLOBAL_PHYSICAL_MINIMUM: 222 globalState.physical_minimum = data; 223 break; 224 225 case ITEM_TAG_GLOBAL_PHYSICAL_MAXIMUM: 226 globalState.physical_maximum = data; 227 break; 228 229 case ITEM_TAG_GLOBAL_UNIT_EXPONENT: 230 globalState.unit_exponent = data; 231 break; 232 233 case ITEM_TAG_GLOBAL_UNIT: 234 globalState.unit = data; 235 break; 236 237 case ITEM_TAG_GLOBAL_REPORT_SIZE: 238 globalState.report_size = data; 239 break; 240 241 case ITEM_TAG_GLOBAL_REPORT_ID: 242 globalState.report_id = data; 243 fUsesReportIDs = true; 244 break; 245 246 case ITEM_TAG_GLOBAL_REPORT_COUNT: 247 globalState.report_count = data; 248 break; 249 250 case ITEM_TAG_GLOBAL_PUSH: 251 { 252 global_item_state *copy = (global_item_state *)malloc( 253 sizeof(global_item_state)); 254 if (copy == NULL) { 255 TRACE_ALWAYS("out of memory for global push\n"); 256 break; 257 } 258 259 memcpy(copy, &globalState, sizeof(global_item_state)); 260 globalState.link = copy; 261 break; 262 } 263 264 case ITEM_TAG_GLOBAL_POP: 265 { 266 if (globalState.link == NULL) { 267 TRACE_ALWAYS("global pop without item on stack\n"); 268 break; 269 } 270 271 global_item_state *link = globalState.link; 272 memcpy(&globalState, link, sizeof(global_item_state)); 273 free(link); 274 break; 275 } 276 277 default: 278 TRACE_ALWAYS("unknown global item tag: 0x%02x\n", 279 item->tag); 280 break; 281 } 282 283 break; 284 } 285 286 case ITEM_TYPE_LOCAL: 287 { 288 switch (item->tag) { 289 case ITEM_TAG_LOCAL_USAGE: 290 { 291 if (usageStackUsed >= usageStackSize) { 292 usageStackSize += 10; 293 usage_value *newUsageStack 294 = (usage_value *)realloc(usageStack, 295 usageStackSize * sizeof(usage_value)); 296 if (newUsageStack == NULL) { 297 TRACE_ALWAYS("no memory when growing usages\n"); 298 usageStackSize -= 10; 299 break; 300 } 301 302 usageStack = newUsageStack; 303 } 304 305 usage_value *value = &usageStack[usageStackUsed]; 306 value->is_extended = itemSize == sizeof(uint32); 307 value->u.extended = data; 308 usageStackUsed++; 309 break; 310 } 311 312 case ITEM_TAG_LOCAL_USAGE_MINIMUM: 313 localState.usage_minimum.u.extended = data; 314 localState.usage_minimum.is_extended 315 = itemSize == sizeof(uint32); 316 localState.usage_minimum_set = true; 317 break; 318 319 case ITEM_TAG_LOCAL_USAGE_MAXIMUM: 320 localState.usage_maximum.u.extended = data; 321 localState.usage_maximum.is_extended 322 = itemSize == sizeof(uint32); 323 localState.usage_maximum_set = true; 324 break; 325 326 case ITEM_TAG_LOCAL_DESIGNATOR_INDEX: 327 localState.designator_index = data; 328 localState.designator_index_set = true; 329 break; 330 331 case ITEM_TAG_LOCAL_DESIGNATOR_MINIMUM: 332 localState.designator_minimum = data; 333 break; 334 335 case ITEM_TAG_LOCAL_DESIGNATOR_MAXIMUM: 336 localState.designator_maximum = data; 337 break; 338 339 case ITEM_TAG_LOCAL_STRING_INDEX: 340 localState.string_index = data; 341 localState.string_index_set = true; 342 break; 343 344 case ITEM_TAG_LOCAL_STRING_MINIMUM: 345 localState.string_minimum = data; 346 break; 347 348 case ITEM_TAG_LOCAL_STRING_MAXIMUM: 349 localState.string_maximum = data; 350 break; 351 352 default: 353 TRACE_ALWAYS("unknown local item tag: 0x%02x\n", 354 item->tag); 355 break; 356 } 357 358 break; 359 } 360 361 case ITEM_TYPE_LONG: 362 { 363 long_item *longItem = (long_item *)item; 364 365 // no long items are defined yet 366 switch (longItem->long_item_tag) { 367 default: 368 TRACE_ALWAYS("unknown long item tag: 0x%02x\n", 369 longItem->long_item_tag); 370 break; 371 } 372 373 break; 374 } 375 } 376 377 pointer += itemSize + sizeof(item_prefix); 378 } 379 380 global_item_state *state = globalState.link; 381 while (state != NULL) { 382 global_item_state *next = state->link; 383 free(state); 384 state = next; 385 } 386 387 free(usageStack); 388 return B_OK; 389} 390 391 392HIDReport * 393HIDParser::FindReport(uint8 type, uint8 id) 394{ 395 for (uint8 i = 0; i < fReportCount; i++) { 396 HIDReport *report = fReports[i]; 397 if (report == NULL) 398 continue; 399 400 if ((report->Type() & type) != 0 && report->ID() == id) 401 return report; 402 } 403 404 return NULL; 405} 406 407 408uint8 409HIDParser::CountReports(uint8 type) 410{ 411 uint8 count = 0; 412 for (uint8 i = 0; i < fReportCount; i++) { 413 HIDReport *report = fReports[i]; 414 if (report == NULL) 415 continue; 416 417 if (report->Type() & type) 418 count++; 419 } 420 421 return count; 422} 423 424 425HIDReport * 426HIDParser::ReportAt(uint8 type, uint8 index) 427{ 428 for (uint8 i = 0; i < fReportCount; i++) { 429 HIDReport *report = fReports[i]; 430 if (report == NULL || (report->Type() & type) == 0) 431 continue; 432 433 if (index-- == 0) 434 return report; 435 } 436 437 return NULL; 438} 439 440 441size_t 442HIDParser::MaxReportSize() 443{ 444 size_t maxSize = 0; 445 for (uint32 i = 0; i < fReportCount; i++) { 446 HIDReport *report = fReports[i]; 447 if (report == NULL) 448 continue; 449 450 if (report->ReportSize() > maxSize) 451 maxSize = report->ReportSize(); 452 } 453 454 if (fUsesReportIDs) 455 maxSize++; 456 457 return maxSize; 458} 459 460 461void 462HIDParser::SetReport(status_t status, uint8 *report, size_t length) 463{ 464 if (status != B_OK || length == 0) { 465 if (status == B_OK) 466 status = B_ERROR; 467 468 report = NULL; 469 length = 0; 470 } 471 472 uint8 targetID = 0; 473 if (fUsesReportIDs && status == B_OK) { 474 targetID = report[0]; 475 report++; 476 length--; 477 } 478 479 // We need to notify all input reports, as we don't know who has waiting 480 // listeners. Anyone other than the target report also waiting for a 481 // transfer to happen needs to reschedule one now. 482 for (uint32 i = 0; i < fReportCount; i++) { 483 if (fReports[i] == NULL 484 || fReports[i]->Type() != HID_REPORT_TYPE_INPUT) 485 continue; 486 487 if (fReports[i]->ID() == targetID) 488 fReports[i]->SetReport(status, report, length); 489 else 490 fReports[i]->SetReport(B_INTERRUPTED, NULL, 0); 491 } 492} 493 494 495void 496HIDParser::PrintToStream() 497{ 498 for (uint8 i = 0; i < fReportCount; i++) { 499 HIDReport *report = fReports[i]; 500 if (report == NULL) 501 continue; 502 503 report->PrintToStream(); 504 } 505 506 fRootCollection->PrintToStream(); 507} 508 509 510HIDReport * 511HIDParser::_FindOrCreateReport(uint8 type, uint8 id) 512{ 513 HIDReport *report = FindReport(type, id); 514 if (report != NULL) 515 return report; 516 517 report = new(std::nothrow) HIDReport(this, type, id); 518 if (report == NULL) { 519 TRACE_ALWAYS("no memory when allocating report\n"); 520 return NULL; 521 } 522 523 HIDReport **newReports = (HIDReport **)realloc(fReports, 524 (fReportCount + 1) * sizeof(HIDReport *)); 525 if (newReports == NULL) { 526 TRACE_ALWAYS("no memory when growing report list\n"); 527 delete report; 528 return NULL; 529 } 530 531 fReports = newReports; 532 fReports[fReportCount++] = report; 533 return report; 534} 535 536 537float 538HIDParser::_CalculateResolution(global_item_state *state) 539{ 540 int64 physicalMinimum = state->physical_minimum; 541 int64 physicalMaximum = state->physical_maximum; 542 if (physicalMinimum == 0 && physicalMaximum == 0) { 543 physicalMinimum = state->logical_minimum; 544 physicalMaximum = state->logical_maximum; 545 } 546 547 int8 unitExponent = sUnitExponent[state->unit_exponent]; 548 549 float power = 1; 550 if (unitExponent < 0) { 551 while (unitExponent++ < 0) 552 power /= 10; 553 } else { 554 while (unitExponent-- > 0) 555 power *= 10; 556 } 557 558 float divisor = (physicalMaximum - physicalMinimum) * power; 559 if (divisor == 0.0) 560 return 0.0; 561 562 return (state->logical_maximum - state->logical_minimum) / divisor; 563} 564 565 566void 567HIDParser::_Reset() 568{ 569 for (uint8 i = 0; i < fReportCount; i++) 570 delete fReports[i]; 571 572 delete fRootCollection; 573 free(fReports); 574 575 fUsesReportIDs = false; 576 fReportCount = 0; 577 fReports = NULL; 578 fRootCollection = NULL; 579} 580