Changeset 26867 in vbox for trunk/src/VBox/Devices/Input
- Timestamp:
- Feb 26, 2010 4:40:20 PM (15 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Input/UsbKbd.cpp
r26669 r26867 113 113 } USBHIDK_REPORT, *PUSBHIDK_REPORT; 114 114 115 /* Scancode translator state. */ 116 typedef enum { 117 SS_IDLE, /* Starting state. */ 118 SS_EXT, /* E0 byte was received. */ 119 SS_EXT1, /* E1 byte was received. */ 120 } scan_state_t; 121 115 122 /** 116 123 * The USB HID instance data. … … 134 141 USBHIDREQSTATE enmState; 135 142 143 /** State of the scancode translation. */ 144 scan_state_t XlatState; 145 136 146 /** HID report reflecting the current keyboard state. */ 137 147 USBHIDK_REPORT Report; … … 169 179 R3PTRTYPE(PPDMIKEYBOARDCONNECTOR) pDrv; 170 180 } Lun0; 171 172 181 } USBHID; 173 182 /** Pointer to the USB HID instance data. */ … … 323 332 324 333 325 /* ******************************************************************************326 * Internal Functions * 327 *******************************************************************************/ 328 329 /** 330 * Initializes an URB queue.334 /* 335 * Because of historical reasons and poor design, VirtualBox internally uses BIOS 336 * PC/XT style scan codes to represent keyboard events. Each key press and release is 337 * represented as a stream of bytes, typically only one byte but up to four-byte 338 * sequences are possible. In the typical case, the GUI front end generates the stream 339 * of scan codes which we need to translate back to a single up/down event. 331 340 * 332 * @param pQueue The URB queue. 333 */ 334 static void usbHidQueueInit(PUSBHIDURBQUEUE pQueue) 335 { 336 pQueue->pHead = NULL; 337 pQueue->ppTail = &pQueue->pHead; 338 } 339 340 341 342 /** 343 * Inserts an URB at the end of the queue. 344 * 345 * @param pQueue The URB queue. 346 * @param pUrb The URB to insert. 347 */ 348 DECLINLINE(void) usbHidQueueAddTail(PUSBHIDURBQUEUE pQueue, PVUSBURB pUrb) 349 { 350 pUrb->Dev.pNext = NULL; 351 *pQueue->ppTail = pUrb; 352 pQueue->ppTail = &pUrb->Dev.pNext; 353 } 354 355 356 /** 357 * Unlinks the head of the queue and returns it. 358 * 359 * @returns The head entry. 360 * @param pQueue The URB queue. 361 */ 362 DECLINLINE(PVUSBURB) usbHidQueueRemoveHead(PUSBHIDURBQUEUE pQueue) 363 { 364 PVUSBURB pUrb = pQueue->pHead; 365 if (pUrb) 366 { 367 PVUSBURB pNext = pUrb->Dev.pNext; 368 pQueue->pHead = pNext; 369 if (!pNext) 370 pQueue->ppTail = &pQueue->pHead; 371 else 372 pUrb->Dev.pNext = NULL; 373 } 374 return pUrb; 375 } 376 377 378 /** 379 * Removes an URB from anywhere in the queue. 380 * 381 * @returns true if found, false if not. 382 * @param pQueue The URB queue. 383 * @param pUrb The URB to remove. 384 */ 385 DECLINLINE(bool) usbHidQueueRemove(PUSBHIDURBQUEUE pQueue, PVUSBURB pUrb) 386 { 387 PVUSBURB pCur = pQueue->pHead; 388 if (pCur == pUrb) 389 pQueue->pHead = pUrb->Dev.pNext; 390 else 391 { 392 while (pCur) 393 { 394 if (pCur->Dev.pNext == pUrb) 395 { 396 pCur->Dev.pNext = pUrb->Dev.pNext; 397 break; 398 } 399 pCur = pCur->Dev.pNext; 400 } 401 if (!pCur) 402 return false; 403 } 404 if (!pUrb->Dev.pNext) 405 pQueue->ppTail = &pQueue->pHead; 406 return true; 407 } 408 409 410 /** 411 * Checks if the queue is empty or not. 412 * 413 * @returns true if it is, false if it isn't. 414 * @param pQueue The URB queue. 415 */ 416 DECLINLINE(bool) usbHidQueueIsEmpty(PCUSBHIDURBQUEUE pQueue) 417 { 418 return pQueue->pHead == NULL; 419 } 420 421 422 /** 423 * Links an URB into the done queue. 424 * 425 * @param pThis The HID instance. 426 * @param pUrb The URB. 427 */ 428 static void usbHidLinkDone(PUSBHID pThis, PVUSBURB pUrb) 429 { 430 usbHidQueueAddTail(&pThis->DoneQueue, pUrb); 431 432 if (pThis->fHaveDoneQueueWaiter) 433 { 434 int rc = RTSemEventSignal(pThis->hEvtDoneQueue); 435 AssertRC(rc); 436 } 437 } 438 439 440 441 /** 442 * Completes the URB with a stalled state, halting the pipe. 443 */ 444 static int usbHidCompleteStall(PUSBHID pThis, PUSBHIDEP pEp, PVUSBURB pUrb, const char *pszWhy) 445 { 446 Log(("usbHidCompleteStall/#%u: pUrb=%p:%s: %s\n", pThis->pUsbIns->iInstance, pUrb, pUrb->pszDesc, pszWhy)); 447 448 pUrb->enmStatus = VUSBSTATUS_STALL; 449 450 /** @todo figure out if the stall is global or pipe-specific or both. */ 451 if (pEp) 452 pEp->fHalted = true; 453 else 454 { 455 pThis->aEps[1].fHalted = true; 456 pThis->aEps[2].fHalted = true; 457 } 458 459 usbHidLinkDone(pThis, pUrb); 460 return VINF_SUCCESS; 461 } 462 463 464 /** 465 * Completes the URB with a OK state. 466 */ 467 static int usbHidCompleteOk(PUSBHID pThis, PVUSBURB pUrb, size_t cbData) 468 { 469 Log(("usbHidCompleteOk/#%u: pUrb=%p:%s cbData=%#zx\n", pThis->pUsbIns->iInstance, pUrb, pUrb->pszDesc, cbData)); 470 471 pUrb->enmStatus = VUSBSTATUS_OK; 472 pUrb->cbData = cbData; 473 474 usbHidLinkDone(pThis, pUrb); 475 return VINF_SUCCESS; 476 } 477 478 479 /** 480 * Reset worker for usbHidUsbReset, usbHidUsbSetConfiguration and 481 * usbHidUrbHandleDefaultPipe. 482 * 483 * @returns VBox status code. 484 * @param pThis The HID instance. 485 * @param pUrb Set when usbHidUrbHandleDefaultPipe is the 486 * caller. 487 * @param fSetConfig Set when usbHidUsbSetConfiguration is the 488 * caller. 489 */ 490 static int usbHidResetWorker(PUSBHID pThis, PVUSBURB pUrb, bool fSetConfig) 491 { 492 /* 493 * Reset the device state. 494 */ 495 pThis->enmState = USBHIDREQSTATE_READY; 496 pThis->bIdle = 0; 497 memset(&pThis->Report, 0, sizeof(pThis->Report)); 498 499 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aEps); i++) 500 pThis->aEps[i].fHalted = false; 501 502 if (!pUrb && !fSetConfig) /* (only device reset) */ 503 pThis->bConfigurationValue = 0; /* default */ 504 505 /* 506 * Ditch all pending URBs. 507 */ 508 PVUSBURB pCurUrb; 509 while ((pCurUrb = usbHidQueueRemoveHead(&pThis->ToHostQueue)) != NULL) 510 { 511 pCurUrb->enmStatus = VUSBSTATUS_CRC; 512 usbHidLinkDone(pThis, pCurUrb); 513 } 514 515 if (pUrb) 516 return usbHidCompleteOk(pThis, pUrb, 0); 517 return VINF_SUCCESS; 518 } 519 520 521 /** 522 * Sends a state report to the host if there is a pending URB. 523 */ 524 static int usbHidSendReport(PUSBHID pThis) 525 { 526 PVUSBURB pUrb = usbHidQueueRemoveHead(&pThis->ToHostQueue); 527 if (pUrb) 528 { 529 PUSBHIDK_REPORT pReport = &pThis->Report; 530 size_t cbCopy; 531 532 cbCopy = sizeof(*pReport); 533 memcpy(&pUrb->abData[0], pReport, cbCopy); 534 // LogRel(("Sent report: %x : %x %x, size %d\n", pReport->ShiftState, pReport->aKeys[0], pReport->aKeys[1], cbCopy)); 535 return usbHidCompleteOk(pThis, pUrb, cbCopy); 536 } 537 return VINF_EOF; 538 } 539 540 /** 541 * @interface_method_impl{PDMIBASE,pfnQueryInterface} 542 */ 543 static DECLCALLBACK(void *) usbHidKeyboardQueryInterface(PPDMIBASE pInterface, const char *pszIID) 544 { 545 PUSBHID pThis = RT_FROM_MEMBER(pInterface, USBHID, Lun0.IBase); 546 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->Lun0.IBase); 547 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIKEYBOARDPORT, &pThis->Lun0.IPort); 548 return NULL; 549 } 550 551 static int8_t clamp_i8(int32_t val) 552 { 553 if (val > 127) 554 val = 127; 555 else if (val < -127) 556 val = -127; 557 return val; 558 } 559 560 static uint8_t aKeycode2Hid[] = 341 * This function could possibly live somewhere else. 342 */ 343 344 /* Lookup table for converting PC/XT scan codes to USB HID usage codes. */ 345 static uint8_t aScancode2Hid[] = 561 346 { 562 347 0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, … … 575 360 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, 576 361 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 577 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 578 }; 362 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65 363 }; 364 365 /* Lookup table for extended scancodes (arrow keys etc.). */ 366 static uint8_t aExtScan2Hid[] = 367 { 368 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 369 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 370 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 371 0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00, 372 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 373 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 374 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46, 375 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 376 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a, 377 0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d, 378 0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00, 379 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 380 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 381 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 382 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 383 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 384 }; 385 386 /** 387 * Convert a PC scan code to a USB HID usage byte. 388 * 389 * @param state Current state of the translator (scan_state_t). 390 * @param scanCode Incoming scan code. 391 * @param pUsage Pointer to usage; high bit set for key up events. The 392 * contents are only valid if returned state is SS_IDLE. 393 * 394 * @return scan_state_t New state of the translator. 395 */ 396 static scan_state_t ScancodeToHidUsage(scan_state_t state, uint8_t scanCode, uint32_t *pUsage) 397 { 398 uint32_t keyUp; 399 uint8_t usage; 400 401 Assert(pUsage); 402 403 /* Isolate the scan code and key break flag. */ 404 keyUp = (scanCode & 0x80) << 24; 405 406 switch (state) { 407 case SS_IDLE: 408 if (scanCode == 0xE0) { 409 state = SS_EXT; 410 } else if (scanCode == 0xE1) { 411 state = SS_EXT1; 412 } else { 413 usage = aScancode2Hid[scanCode & 0x7F]; 414 *pUsage = usage | keyUp; 415 } 416 break; 417 case SS_EXT: 418 usage = aExtScan2Hid[scanCode & 0x7F]; 419 *pUsage = usage | keyUp; 420 state = SS_IDLE; 421 break; 422 } 423 return state; 424 } 425 426 /******************************************************************************* 427 * Internal Functions * 428 *******************************************************************************/ 429 430 /** 431 * Initializes an URB queue. 432 * 433 * @param pQueue The URB queue. 434 */ 435 static void usbHidQueueInit(PUSBHIDURBQUEUE pQueue) 436 { 437 pQueue->pHead = NULL; 438 pQueue->ppTail = &pQueue->pHead; 439 } 440 441 442 443 /** 444 * Inserts an URB at the end of the queue. 445 * 446 * @param pQueue The URB queue. 447 * @param pUrb The URB to insert. 448 */ 449 DECLINLINE(void) usbHidQueueAddTail(PUSBHIDURBQUEUE pQueue, PVUSBURB pUrb) 450 { 451 pUrb->Dev.pNext = NULL; 452 *pQueue->ppTail = pUrb; 453 pQueue->ppTail = &pUrb->Dev.pNext; 454 } 455 456 457 /** 458 * Unlinks the head of the queue and returns it. 459 * 460 * @returns The head entry. 461 * @param pQueue The URB queue. 462 */ 463 DECLINLINE(PVUSBURB) usbHidQueueRemoveHead(PUSBHIDURBQUEUE pQueue) 464 { 465 PVUSBURB pUrb = pQueue->pHead; 466 if (pUrb) 467 { 468 PVUSBURB pNext = pUrb->Dev.pNext; 469 pQueue->pHead = pNext; 470 if (!pNext) 471 pQueue->ppTail = &pQueue->pHead; 472 else 473 pUrb->Dev.pNext = NULL; 474 } 475 return pUrb; 476 } 477 478 479 /** 480 * Removes an URB from anywhere in the queue. 481 * 482 * @returns true if found, false if not. 483 * @param pQueue The URB queue. 484 * @param pUrb The URB to remove. 485 */ 486 DECLINLINE(bool) usbHidQueueRemove(PUSBHIDURBQUEUE pQueue, PVUSBURB pUrb) 487 { 488 PVUSBURB pCur = pQueue->pHead; 489 if (pCur == pUrb) 490 pQueue->pHead = pUrb->Dev.pNext; 491 else 492 { 493 while (pCur) 494 { 495 if (pCur->Dev.pNext == pUrb) 496 { 497 pCur->Dev.pNext = pUrb->Dev.pNext; 498 break; 499 } 500 pCur = pCur->Dev.pNext; 501 } 502 if (!pCur) 503 return false; 504 } 505 if (!pUrb->Dev.pNext) 506 pQueue->ppTail = &pQueue->pHead; 507 return true; 508 } 509 510 511 /** 512 * Checks if the queue is empty or not. 513 * 514 * @returns true if it is, false if it isn't. 515 * @param pQueue The URB queue. 516 */ 517 DECLINLINE(bool) usbHidQueueIsEmpty(PCUSBHIDURBQUEUE pQueue) 518 { 519 return pQueue->pHead == NULL; 520 } 521 522 523 /** 524 * Links an URB into the done queue. 525 * 526 * @param pThis The HID instance. 527 * @param pUrb The URB. 528 */ 529 static void usbHidLinkDone(PUSBHID pThis, PVUSBURB pUrb) 530 { 531 usbHidQueueAddTail(&pThis->DoneQueue, pUrb); 532 533 if (pThis->fHaveDoneQueueWaiter) 534 { 535 int rc = RTSemEventSignal(pThis->hEvtDoneQueue); 536 AssertRC(rc); 537 } 538 } 539 540 541 542 /** 543 * Completes the URB with a stalled state, halting the pipe. 544 */ 545 static int usbHidCompleteStall(PUSBHID pThis, PUSBHIDEP pEp, PVUSBURB pUrb, const char *pszWhy) 546 { 547 Log(("usbHidCompleteStall/#%u: pUrb=%p:%s: %s\n", pThis->pUsbIns->iInstance, pUrb, pUrb->pszDesc, pszWhy)); 548 549 pUrb->enmStatus = VUSBSTATUS_STALL; 550 551 /** @todo figure out if the stall is global or pipe-specific or both. */ 552 if (pEp) 553 pEp->fHalted = true; 554 else 555 { 556 pThis->aEps[1].fHalted = true; 557 pThis->aEps[2].fHalted = true; 558 } 559 560 usbHidLinkDone(pThis, pUrb); 561 return VINF_SUCCESS; 562 } 563 564 565 /** 566 * Completes the URB with a OK state. 567 */ 568 static int usbHidCompleteOk(PUSBHID pThis, PVUSBURB pUrb, size_t cbData) 569 { 570 Log(("usbHidCompleteOk/#%u: pUrb=%p:%s cbData=%#zx\n", pThis->pUsbIns->iInstance, pUrb, pUrb->pszDesc, cbData)); 571 572 pUrb->enmStatus = VUSBSTATUS_OK; 573 pUrb->cbData = cbData; 574 575 usbHidLinkDone(pThis, pUrb); 576 return VINF_SUCCESS; 577 } 578 579 580 /** 581 * Reset worker for usbHidUsbReset, usbHidUsbSetConfiguration and 582 * usbHidUrbHandleDefaultPipe. 583 * 584 * @returns VBox status code. 585 * @param pThis The HID instance. 586 * @param pUrb Set when usbHidUrbHandleDefaultPipe is the 587 * caller. 588 * @param fSetConfig Set when usbHidUsbSetConfiguration is the 589 * caller. 590 */ 591 static int usbHidResetWorker(PUSBHID pThis, PVUSBURB pUrb, bool fSetConfig) 592 { 593 /* 594 * Reset the device state. 595 */ 596 pThis->enmState = USBHIDREQSTATE_READY; 597 pThis->bIdle = 0; 598 memset(&pThis->Report, 0, sizeof(pThis->Report)); 599 600 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aEps); i++) 601 pThis->aEps[i].fHalted = false; 602 603 if (!pUrb && !fSetConfig) /* (only device reset) */ 604 pThis->bConfigurationValue = 0; /* default */ 605 606 /* 607 * Ditch all pending URBs. 608 */ 609 PVUSBURB pCurUrb; 610 while ((pCurUrb = usbHidQueueRemoveHead(&pThis->ToHostQueue)) != NULL) 611 { 612 pCurUrb->enmStatus = VUSBSTATUS_CRC; 613 usbHidLinkDone(pThis, pCurUrb); 614 } 615 616 if (pUrb) 617 return usbHidCompleteOk(pThis, pUrb, 0); 618 return VINF_SUCCESS; 619 } 620 621 622 /** 623 * Sends a state report to the host if there is a pending URB. 624 */ 625 static int usbHidSendReport(PUSBHID pThis) 626 { 627 PVUSBURB pUrb = usbHidQueueRemoveHead(&pThis->ToHostQueue); 628 if (pUrb) 629 { 630 PUSBHIDK_REPORT pReport = &pThis->Report; 631 size_t cbCopy; 632 633 cbCopy = sizeof(*pReport); 634 memcpy(&pUrb->abData[0], pReport, cbCopy); 635 // LogRel(("Sent report: %x : %x %x, size %d\n", pReport->ShiftState, pReport->aKeys[0], pReport->aKeys[1], cbCopy)); 636 return usbHidCompleteOk(pThis, pUrb, cbCopy); 637 } 638 return VINF_EOF; 639 } 640 641 /** 642 * @interface_method_impl{PDMIBASE,pfnQueryInterface} 643 */ 644 static DECLCALLBACK(void *) usbHidKeyboardQueryInterface(PPDMIBASE pInterface, const char *pszIID) 645 { 646 PUSBHID pThis = RT_FROM_MEMBER(pInterface, USBHID, Lun0.IBase); 647 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->Lun0.IBase); 648 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIKEYBOARDPORT, &pThis->Lun0.IPort); 649 return NULL; 650 } 579 651 580 652 /** … … 589 661 PUSBHID pThis = RT_FROM_MEMBER(pInterface, USBHID, Lun0.IPort); 590 662 PUSBHIDK_REPORT pReport = &pThis->Report; 591 uint8_t u8HidCode; 592 int fKeyDown; 593 unsigned i; 663 uint32_t u32Usage; 664 uint8_t u8HidCode; 665 int fKeyDown; 666 unsigned i; 594 667 595 668 // int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY); 596 669 // AssertReleaseRC(rc); 597 670 598 /* Extract the key event. */ 599 u8HidCode = aKeycode2Hid[u8KeyCode & 0x7f]; 600 fKeyDown = !(u8KeyCode & 0x80); 601 602 if (fKeyDown) 603 { 604 for (i = 0; i < RT_ELEMENTS(pReport->aKeys); ++i) 671 pThis->XlatState = ScancodeToHidUsage(pThis->XlatState, u8KeyCode, &u32Usage); 672 673 if (pThis->XlatState == SS_IDLE) 674 { 675 /* The usage code is valid. */ 676 fKeyDown = !(u32Usage & 0x80000000); 677 u8HidCode = u32Usage & 0xFF; 678 679 if (fKeyDown) 605 680 { 606 if (pReport->aKeys[i] == u8HidCode) 607 break; /* Skip repeat events. */ 608 if (pReport->aKeys[i] == 0) 681 for (i = 0; i < RT_ELEMENTS(pReport->aKeys); ++i) 609 682 { 610 pReport->aKeys[i] = u8HidCode; /* Report key down. */ 611 break; 683 if (pReport->aKeys[i] == u8HidCode) 684 break; /* Skip repeat events. */ 685 if (pReport->aKeys[i] == 0) 686 { 687 pReport->aKeys[i] = u8HidCode; /* Report key down. */ 688 break; 689 } 690 } 691 if (i == RT_ELEMENTS(pReport->aKeys)) 692 { 693 /* We ran out of room. Report error. */ 694 // @todo!! 612 695 } 613 696 } 614 if (i == RT_ELEMENTS(pReport->aKeys))697 else 615 698 { 616 /* We ran out of room. Report error. */ 617 // @todo!! 618 } 619 } 620 else 621 { 622 for (i = 0; i < RT_ELEMENTS(pReport->aKeys); ++i) 623 { 624 if (pReport->aKeys[i] == u8HidCode) 699 for (i = 0; i < RT_ELEMENTS(pReport->aKeys); ++i) 625 700 { 626 pReport->aKeys[i] = 0; 627 break; /* Remove key down. */ 701 if (pReport->aKeys[i] == u8HidCode) 702 { 703 pReport->aKeys[i] = 0; 704 break; /* Remove key down. */ 705 } 706 } 707 if (i == RT_ELEMENTS(pReport->aKeys)) 708 { 709 // AssertMsgFailed(("Key is up but was never down!?")); 628 710 } 629 711 } 630 if (i == RT_ELEMENTS(pReport->aKeys)) 631 { 632 // AssertMsgFailed(("Key is up but was never down!?")); 633 } 634 } 635 636 /* Send a report if the host is already waiting for it. */ 637 usbHidSendReport(pThis); 712 713 /* Send a report if the host is already waiting for it. */ 714 usbHidSendReport(pThis); 715 } 638 716 639 717 // PDMCritSectLeave(&pThis->CritSect); … … 1022 1100 pThis->pUsbIns = pUsbIns; 1023 1101 pThis->hEvtDoneQueue = NIL_RTSEMEVENT; 1102 pThis->XlatState = SS_IDLE; 1024 1103 usbHidQueueInit(&pThis->ToHostQueue); 1025 1104 usbHidQueueInit(&pThis->DoneQueue);
Note:
See TracChangeset
for help on using the changeset viewer.