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