VirtualBox

source: vbox/trunk/src/VBox/Devices/Input/UsbKbd.cpp@ 26867

Last change on this file since 26867 was 26867, checked in by vboxsync, 15 years ago

UsbKbd: Improved scancode to HID usage translation.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.5 KB
Line 
1/** @file
2 * UsbKbd - USB Human Interface Device Emulation (Keyboard).
3 */
4
5/*
6 * Copyright (C) 2007-2010 Sun Microsystems, Inc.
7 *
8 * Sun Microsystems, Inc. confidential
9 * All rights reserved
10 */
11
12/*******************************************************************************
13* Header Files *
14*******************************************************************************/
15#define LOG_GROUP LOG_GROUP_USB_MSD
16#include <VBox/pdmusb.h>
17#include <VBox/log.h>
18#include <VBox/err.h>
19#include <iprt/assert.h>
20#include <iprt/critsect.h>
21#include <iprt/mem.h>
22#include <iprt/semaphore.h>
23#include <iprt/string.h>
24#include <iprt/uuid.h>
25#include "../Builtins.h"
26
27
28/*******************************************************************************
29* Defined Constants And Macros *
30*******************************************************************************/
31/** @name USB HID string IDs
32 * @{ */
33#define USBHID_STR_ID_MANUFACTURER 1
34#define USBHID_STR_ID_PRODUCT 2
35/** @} */
36
37/** @name USB HID specific descriptor types
38 * @{ */
39#define DT_IF_HID_REPORT 0x22
40/** @} */
41
42/** @name USB HID vendor and product IDs
43 * @{ */
44#define VBOX_USB_VENDOR 0x80EE
45#define USBHID_PID_KEYBOARD 0x0010
46/** @} */
47
48/** @name USB HID class specific requests
49 * @{ */
50#define HID_REQ_GET_REPORT 0x01
51#define HID_REQ_GET_IDLE 0x02
52#define HID_REQ_SET_REPORT 0x09
53#define HID_REQ_SET_IDLE 0x0A
54/** @} */
55
56/*******************************************************************************
57* Structures and Typedefs *
58*******************************************************************************/
59
60/**
61 * The USB HID request state.
62 */
63typedef enum USBHIDREQSTATE
64{
65 /** Invalid status. */
66 USBHIDREQSTATE_INVALID = 0,
67 /** Ready to receive a new read request. */
68 USBHIDREQSTATE_READY,
69 /** Have (more) data for the host. */
70 USBHIDREQSTATE_DATA_TO_HOST,
71 /** Waiting to supply status information to the host. */
72 USBHIDREQSTATE_STATUS,
73 /** The end of the valid states. */
74 USBHIDREQSTATE_END
75} USBHIDREQSTATE;
76
77
78/**
79 * Endpoint status data.
80 */
81typedef struct USBHIDEP
82{
83 bool fHalted;
84} USBHIDEP;
85/** Pointer to the endpoint status. */
86typedef USBHIDEP *PUSBHIDEP;
87
88
89/**
90 * A URB queue.
91 */
92typedef struct USBHIDURBQUEUE
93{
94 /** The head pointer. */
95 PVUSBURB pHead;
96 /** Where to insert the next entry. */
97 PVUSBURB *ppTail;
98} USBHIDURBQUEUE;
99/** Pointer to a URB queue. */
100typedef USBHIDURBQUEUE *PUSBHIDURBQUEUE;
101/** Pointer to a const URB queue. */
102typedef USBHIDURBQUEUE const *PCUSBHIDURBQUEUE;
103
104
105/**
106 * The USB HID report structure for regular keys.
107 */
108typedef struct USBHIDK_REPORT
109{
110 uint8_t ShiftState; /* Modifier keys bitfield */
111 uint8_t Reserved; /* Currently unused */
112 uint8_t aKeys[6]; /* Normal keys */
113} USBHIDK_REPORT, *PUSBHIDK_REPORT;
114
115/* Scancode translator state. */
116typedef 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
122/**
123 * The USB HID instance data.
124 */
125typedef struct USBHID
126{
127 /** Pointer back to the PDM USB Device instance structure. */
128 PPDMUSBINS pUsbIns;
129 /** Critical section protecting the device state. */
130 RTCRITSECT CritSect;
131
132 /** The current configuration.
133 * (0 - default, 1 - the one supported configuration, i.e configured.) */
134 uint8_t bConfigurationValue;
135 /** USB HID Idle value..
136 * (0 - only report state change, !=0 - report in bIdle * 4ms intervals.) */
137 uint8_t bIdle;
138 /** Endpoint 0 is the default control pipe, 1 is the dev->host interrupt one. */
139 USBHIDEP aEps[2];
140 /** The state of the HID (state machine).*/
141 USBHIDREQSTATE enmState;
142
143 /** State of the scancode translation. */
144 scan_state_t XlatState;
145
146 /** HID report reflecting the current keyboard state. */
147 USBHIDK_REPORT Report;
148
149 /** Pending to-host queue.
150 * The URBs waiting here are waiting for data to become available.
151 */
152 USBHIDURBQUEUE ToHostQueue;
153
154 /** Done queue
155 * The URBs stashed here are waiting to be reaped. */
156 USBHIDURBQUEUE DoneQueue;
157 /** Signalled when adding an URB to the done queue and fHaveDoneQueueWaiter
158 * is set. */
159 RTSEMEVENT hEvtDoneQueue;
160 /** Someone is waiting on the done queue. */
161 bool fHaveDoneQueueWaiter;
162
163 /**
164 * Keyboard port - LUN#0.
165 *
166 * @implements PDMIBASE
167 * @implements PDMIKEYBOARDPORT
168 */
169 struct
170 {
171 /** The base interface for the keyboard port. */
172 PDMIBASE IBase;
173 /** The keyboard port base interface. */
174 PDMIKEYBOARDPORT IPort;
175
176 /** The base interface of the attached keyboard driver. */
177 R3PTRTYPE(PPDMIBASE) pDrvBase;
178 /** The keyboard interface of the attached keyboard driver. */
179 R3PTRTYPE(PPDMIKEYBOARDCONNECTOR) pDrv;
180 } Lun0;
181} USBHID;
182/** Pointer to the USB HID instance data. */
183typedef USBHID *PUSBHID;
184
185/*******************************************************************************
186* Global Variables *
187*******************************************************************************/
188static const PDMUSBDESCCACHESTRING g_aUsbHidStrings_en_US[] =
189{
190 { USBHID_STR_ID_MANUFACTURER, "VirtualBox" },
191 { USBHID_STR_ID_PRODUCT, "USB Keyboard" },
192};
193
194static const PDMUSBDESCCACHELANG g_aUsbHidLanguages[] =
195{
196 { 0x0409, RT_ELEMENTS(g_aUsbHidStrings_en_US), g_aUsbHidStrings_en_US }
197};
198
199static const VUSBDESCENDPOINTEX g_aUsbHidEndpointDescs[] =
200{
201 {
202 {
203 /* .bLength = */ sizeof(VUSBDESCENDPOINT),
204 /* .bDescriptorType = */ VUSB_DT_ENDPOINT,
205 /* .bEndpointAddress = */ 0x81 /* ep=1, in */,
206 /* .bmAttributes = */ 3 /* interrupt */,
207 /* .wMaxPacketSize = */ 8,
208 /* .bInterval = */ 10,
209 },
210 /* .pvMore = */ NULL,
211 /* .pvClass = */ NULL,
212 /* .cbClass = */ 0
213 },
214};
215
216/* HID report descriptor. */
217static const uint8_t g_UsbHidReportDesc[] =
218{
219 /* Usage Page */ 0x05, 0x01, /* Generic Desktop */
220 /* Usage */ 0x09, 0x06, /* Keyboard */
221 /* Collection */ 0xA1, 0x01, /* Application */
222 /* Usage Page */ 0x05, 0x07, /* Keyboard */
223 /* Usage Minimum */ 0x19, 0xE0, /* Left Ctrl Key */
224 /* Usage Maximum */ 0x29, 0xE7, /* Right GUI Key */
225 /* Logical Minimum */ 0x15, 0x00, /* 0 */
226 /* Logical Maximum */ 0x25, 0x01, /* 1 */
227 /* Report Count */ 0x95, 0x08, /* 8 */
228 /* Report Size */ 0x75, 0x01, /* 1 */
229 /* Input */ 0x81, 0x02, /* Data, Value, Absolute, Bit field */
230 /* Report Count */ 0x95, 0x01, /* 1 */
231 /* Report Size */ 0x75, 0x08, /* 8 (padding bits) */
232 /* Input */ 0x81, 0x01, /* Constant, Array, Absolute, Bit field */
233 /* Report Count */ 0x95, 0x05, /* 5 */
234 /* Report Size */ 0x75, 0x01, /* 1 */
235 /* Usage Page */ 0x05, 0x08, /* LEDs */
236 /* Usage Minimum */ 0x19, 0x01, /* Num Lock */
237 /* Usage Maximum */ 0x29, 0x05, /* Kana */
238 /* Output */ 0x91, 0x02, /* Data, Value, Absolute, Non-volatile,Bit field */
239 /* Report Count */ 0x95, 0x01, /* 1 */
240 /* Report Size */ 0x75, 0x03, /* 3 */
241 /* Output */ 0x91, 0x01, /* Constant, Value, Absolute, Non-volatile, Bit field */
242 /* Report Count */ 0x95, 0x06, /* 6 */
243 /* Report Size */ 0x75, 0x08, /* 8 */
244 /* Logical Minimum */ 0x15, 0x00, /* 0 */
245 /* Logical Maximum */ 0x26, 0xFF,0x00,/* 255 */
246 /* Usage Page */ 0x05, 0x07, /* Keyboard */
247 /* Usage Minimum */ 0x19, 0x00, /* 0 */
248 /* Usage Maximum */ 0x29, 0xFF,0x00,/* 255 */
249 /* Input */ 0x81, 0x00, /* Data, Array, Absolute, Bit field */
250 /* End Collection */ 0xC0,
251};
252
253/* Additional HID class interface descriptor. */
254static const uint8_t g_UsbHidIfHidDesc[] =
255{
256 /* .bLength = */ 0x09,
257 /* .bDescriptorType = */ 0x21, /* HID */
258 /* .bcdHID = */ 0x10, 0x01, /* 1.1 */
259 /* .bCountryCode = */ 0x0D, /* International (ISO) */
260 /* .bNumDescriptors = */ 1,
261 /* .bDescriptorType = */ 0x22, /* Report */
262 /* .wDescriptorLength = */ sizeof(g_UsbHidReportDesc), 0x00
263};
264
265static const VUSBDESCINTERFACEEX g_UsbHidInterfaceDesc =
266{
267 {
268 /* .bLength = */ sizeof(VUSBDESCINTERFACE),
269 /* .bDescriptorType = */ VUSB_DT_INTERFACE,
270 /* .bInterfaceNumber = */ 0,
271 /* .bAlternateSetting = */ 0,
272 /* .bNumEndpoints = */ 1,
273 /* .bInterfaceClass = */ 3 /* HID */,
274 /* .bInterfaceSubClass = */ 1 /* Boot Interface */,
275 /* .bInterfaceProtocol = */ 1 /* Keyboard */,
276 /* .iInterface = */ 0
277 },
278 /* .pvMore = */ NULL,
279 /* .pvClass = */ &g_UsbHidIfHidDesc,
280 /* .cbClass = */ sizeof(g_UsbHidIfHidDesc),
281 &g_aUsbHidEndpointDescs[0]
282};
283
284static const VUSBINTERFACE g_aUsbHidInterfaces[] =
285{
286 { &g_UsbHidInterfaceDesc, /* .cSettings = */ 1 },
287};
288
289static const VUSBDESCCONFIGEX g_UsbHidConfigDesc =
290{
291 {
292 /* .bLength = */ sizeof(VUSBDESCCONFIG),
293 /* .bDescriptorType = */ VUSB_DT_CONFIG,
294 /* .wTotalLength = */ 0 /* recalculated on read */,
295 /* .bNumInterfaces = */ RT_ELEMENTS(g_aUsbHidInterfaces),
296 /* .bConfigurationValue =*/ 1,
297 /* .iConfiguration = */ 0,
298 /* .bmAttributes = */ RT_BIT(7),
299 /* .MaxPower = */ 50 /* 100mA */
300 },
301 NULL,
302 &g_aUsbHidInterfaces[0]
303};
304
305static const VUSBDESCDEVICE g_UsbHidDeviceDesc =
306{
307 /* .bLength = */ sizeof(g_UsbHidDeviceDesc),
308 /* .bDescriptorType = */ VUSB_DT_DEVICE,
309 /* .bcdUsb = */ 0x110, /* 1.1 */
310 /* .bDeviceClass = */ 0 /* Class specified in the interface desc. */,
311 /* .bDeviceSubClass = */ 0 /* Subclass specified in the interface desc. */,
312 /* .bDeviceProtocol = */ 0 /* Protocol specified in the interface desc. */,
313 /* .bMaxPacketSize0 = */ 8,
314 /* .idVendor = */ VBOX_USB_VENDOR,
315 /* .idProduct = */ USBHID_PID_KEYBOARD,
316 /* .bcdDevice = */ 0x0100, /* 1.0 */
317 /* .iManufacturer = */ USBHID_STR_ID_MANUFACTURER,
318 /* .iProduct = */ USBHID_STR_ID_PRODUCT,
319 /* .iSerialNumber = */ 0,
320 /* .bNumConfigurations = */ 1
321};
322
323static const PDMUSBDESCCACHE g_UsbHidDescCache =
324{
325 /* .pDevice = */ &g_UsbHidDeviceDesc,
326 /* .paConfigs = */ &g_UsbHidConfigDesc,
327 /* .paLanguages = */ g_aUsbHidLanguages,
328 /* .cLanguages = */ RT_ELEMENTS(g_aUsbHidLanguages),
329 /* .fUseCachedDescriptors = */ true,
330 /* .fUseCachedStringsDescriptors = */ true
331};
332
333
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.
340 *
341 * This function could possibly live somewhere else.
342 */
343
344/* Lookup table for converting PC/XT scan codes to USB HID usage codes. */
345static uint8_t aScancode2Hid[] =
346{
347 0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
348 0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b,
349 0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c,
350 0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16,
351 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33,
352 0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19,
353 0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55,
354 0xe2, 0x2c, 0x32, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
355 0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f,
356 0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59,
357 0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x00, 0x44,
358 0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
359 0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00,
360 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00,
361 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
362 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65
363};
364
365/* Lookup table for extended scancodes (arrow keys etc.). */
366static 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 */
396static 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 */
435static 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 */
449DECLINLINE(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 */
463DECLINLINE(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 */
486DECLINLINE(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 */
517DECLINLINE(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 */
529static 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 */
545static 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 */
568static 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 */
591static 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 */
625static 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 */
644static 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}
651
652/**
653 * Keyboard event handler.
654 *
655 * @returns VBox status code.
656 * @param pInterface Pointer to the keyboard port interface (KBDState::Keyboard.IPort).
657 * @param u8KeyCode The keycode.
658 */
659static DECLCALLBACK(int) usbHidKeyboardPutEvent(PPDMIKEYBOARDPORT pInterface, uint8_t u8KeyCode)
660{
661 PUSBHID pThis = RT_FROM_MEMBER(pInterface, USBHID, Lun0.IPort);
662 PUSBHIDK_REPORT pReport = &pThis->Report;
663 uint32_t u32Usage;
664 uint8_t u8HidCode;
665 int fKeyDown;
666 unsigned i;
667
668// int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
669// AssertReleaseRC(rc);
670
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)
680 {
681 for (i = 0; i < RT_ELEMENTS(pReport->aKeys); ++i)
682 {
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!!
695 }
696 }
697 else
698 {
699 for (i = 0; i < RT_ELEMENTS(pReport->aKeys); ++i)
700 {
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!?"));
710 }
711 }
712
713 /* Send a report if the host is already waiting for it. */
714 usbHidSendReport(pThis);
715 }
716
717// PDMCritSectLeave(&pThis->CritSect);
718 return VINF_SUCCESS;
719}
720
721/**
722 * @copydoc PDMUSBREG::pfnUrbReap
723 */
724static DECLCALLBACK(PVUSBURB) usbHidUrbReap(PPDMUSBINS pUsbIns, RTMSINTERVAL cMillies)
725{
726 PUSBHID pThis = PDMINS_2_DATA(pUsbIns, PUSBHID);
727 LogFlow(("usbHidUrbReap/#%u: cMillies=%u\n", pUsbIns->iInstance, cMillies));
728
729 RTCritSectEnter(&pThis->CritSect);
730
731 PVUSBURB pUrb = usbHidQueueRemoveHead(&pThis->DoneQueue);
732 if (!pUrb && cMillies)
733 {
734 /* Wait */
735 pThis->fHaveDoneQueueWaiter = true;
736 RTCritSectLeave(&pThis->CritSect);
737
738 RTSemEventWait(pThis->hEvtDoneQueue, cMillies);
739
740 RTCritSectEnter(&pThis->CritSect);
741 pThis->fHaveDoneQueueWaiter = false;
742
743 pUrb = usbHidQueueRemoveHead(&pThis->DoneQueue);
744 }
745
746 RTCritSectLeave(&pThis->CritSect);
747
748 if (pUrb)
749 Log(("usbHidUrbReap/#%u: pUrb=%p:%s\n", pUsbIns->iInstance, pUrb, pUrb->pszDesc));
750 return pUrb;
751}
752
753
754/**
755 * @copydoc PDMUSBREG::pfnUrbCancel
756 */
757static DECLCALLBACK(int) usbHidUrbCancel(PPDMUSBINS pUsbIns, PVUSBURB pUrb)
758{
759 PUSBHID pThis = PDMINS_2_DATA(pUsbIns, PUSBHID);
760 LogFlow(("usbHidUrbCancel/#%u: pUrb=%p:%s\n", pUsbIns->iInstance, pUrb, pUrb->pszDesc));
761 RTCritSectEnter(&pThis->CritSect);
762
763 /*
764 * Remove the URB from the to-host queue and move it onto the done queue.
765 */
766 if (usbHidQueueRemove(&pThis->ToHostQueue, pUrb))
767 usbHidLinkDone(pThis, pUrb);
768
769 RTCritSectLeave(&pThis->CritSect);
770 return VINF_SUCCESS;
771}
772
773
774/**
775 * Handles request sent to the inbound (device to host) interrupt pipe. This is
776 * rather different from bulk requests because an interrupt read URB may complete
777 * after arbitrarily long time.
778 */
779static int usbHidHandleIntrDevToHost(PUSBHID pThis, PUSBHIDEP pEp, PVUSBURB pUrb)
780{
781 /*
782 * Stall the request if the pipe is halted.
783 */
784 if (RT_UNLIKELY(pEp->fHalted))
785 return usbHidCompleteStall(pThis, NULL, pUrb, "Halted pipe");
786
787 /*
788 * Deal with the URB according to the state.
789 */
790 switch (pThis->enmState)
791 {
792 /*
793 * We've data left to transfer to the host.
794 */
795 case USBHIDREQSTATE_DATA_TO_HOST:
796 {
797 AssertFailed();
798 Log(("usbHidHandleIntrDevToHost: Entering STATUS\n"));
799 return usbHidCompleteOk(pThis, pUrb, 0);
800 }
801
802 /*
803 * Status transfer.
804 */
805 case USBHIDREQSTATE_STATUS:
806 {
807 AssertFailed();
808 Log(("usbHidHandleIntrDevToHost: Entering READY\n"));
809 pThis->enmState = USBHIDREQSTATE_READY;
810 return usbHidCompleteOk(pThis, pUrb, 0);
811 }
812
813 case USBHIDREQSTATE_READY:
814 usbHidQueueAddTail(&pThis->ToHostQueue, pUrb);
815 /* If device was not set idle, sent the current report right away. */
816 if (pThis->bIdle != 0)
817 usbHidSendReport(pThis);
818 LogFlow(("usbHidHandleIntrDevToHost: Sent report via %p:%s\n", pUrb, pUrb->pszDesc));
819 return VINF_SUCCESS;
820
821 /*
822 * Bad states, stall.
823 */
824 default:
825 Log(("usbHidHandleIntrDevToHost: enmState=%d cbData=%#x\n", pThis->enmState, pUrb->cbData));
826 return usbHidCompleteStall(pThis, NULL, pUrb, "Really bad state (D2H)!");
827 }
828}
829
830
831/**
832 * Handles request sent to the default control pipe.
833 */
834static int usbHidHandleDefaultPipe(PUSBHID pThis, PUSBHIDEP pEp, PVUSBURB pUrb)
835{
836 PVUSBSETUP pSetup = (PVUSBSETUP)&pUrb->abData[0];
837 AssertReturn(pUrb->cbData >= sizeof(*pSetup), VERR_VUSB_FAILED_TO_QUEUE_URB);
838
839 if ((pSetup->bmRequestType & VUSB_REQ_MASK) == VUSB_REQ_STANDARD)
840 {
841 switch (pSetup->bRequest)
842 {
843 case VUSB_REQ_GET_DESCRIPTOR:
844 {
845 switch (pSetup->bmRequestType)
846 {
847 case VUSB_TO_DEVICE | VUSB_REQ_STANDARD | VUSB_DIR_TO_HOST:
848 {
849 switch (pSetup->wValue >> 8)
850 {
851 case VUSB_DT_STRING:
852 Log(("usbHid: GET_DESCRIPTOR DT_STRING wValue=%#x wIndex=%#x\n", pSetup->wValue, pSetup->wIndex));
853 break;
854 default:
855 Log(("usbHid: GET_DESCRIPTOR, huh? wValue=%#x wIndex=%#x\n", pSetup->wValue, pSetup->wIndex));
856 break;
857 }
858 break;
859 }
860
861 case VUSB_TO_INTERFACE | VUSB_REQ_STANDARD | VUSB_DIR_TO_HOST:
862 {
863 switch (pSetup->wValue >> 8)
864 {
865 case DT_IF_HID_REPORT:
866 uint32_t cbCopy;
867
868 /* Returned data is written after the setup message. */
869 cbCopy = pUrb->cbData - sizeof(*pSetup);
870 cbCopy = RT_MIN(cbCopy, sizeof(g_UsbHidReportDesc));
871 Log(("usbHid: GET_DESCRIPTOR DT_IF_HID_REPORT wValue=%#x wIndex=%#x cbCopy=%#x\n", pSetup->wValue, pSetup->wIndex, cbCopy));
872 memcpy(&pUrb->abData[sizeof(*pSetup)], &g_UsbHidReportDesc, cbCopy);
873 return usbHidCompleteOk(pThis, pUrb, cbCopy + sizeof(*pSetup));
874 default:
875 Log(("usbHid: GET_DESCRIPTOR, huh? wValue=%#x wIndex=%#x\n", pSetup->wValue, pSetup->wIndex));
876 break;
877 }
878 break;
879 }
880
881 default:
882 Log(("usbHid: Bad GET_DESCRIPTOR req: bmRequestType=%#x\n", pSetup->bmRequestType));
883 return usbHidCompleteStall(pThis, pEp, pUrb, "Bad GET_DESCRIPTOR");
884 }
885 break;
886 }
887
888 case VUSB_REQ_CLEAR_FEATURE:
889 break;
890 }
891
892 /** @todo implement this. */
893 Log(("usbHid: Implement standard request: bmRequestType=%#x bRequest=%#x wValue=%#x wIndex=%#x wLength=%#x\n",
894 pSetup->bmRequestType, pSetup->bRequest, pSetup->wValue, pSetup->wIndex, pSetup->wLength));
895
896 usbHidCompleteStall(pThis, pEp, pUrb, "TODO: standard request stuff");
897 }
898 else if ((pSetup->bmRequestType & VUSB_REQ_MASK) == VUSB_REQ_CLASS)
899 {
900 switch (pSetup->bRequest)
901 {
902 case HID_REQ_SET_IDLE:
903 {
904 switch (pSetup->bmRequestType)
905 {
906 case VUSB_TO_INTERFACE | VUSB_REQ_CLASS | VUSB_DIR_TO_DEVICE:
907 {
908 Log(("usbHid: SET_IDLE wValue=%#x wIndex=%#x\n", pSetup->wValue, pSetup->wIndex));
909 pThis->bIdle = pSetup->wValue >> 8;
910 /* Consider 24ms to mean zero for keyboards (see IOUSBHIDDriver) */
911 if (pThis->bIdle == 6) pThis->bIdle = 0;
912 return usbHidCompleteOk(pThis, pUrb, 0);
913 }
914 break;
915 }
916 break;
917 }
918 case HID_REQ_GET_IDLE:
919 {
920 switch (pSetup->bmRequestType)
921 {
922 case VUSB_TO_INTERFACE | VUSB_REQ_CLASS | VUSB_DIR_TO_HOST:
923 {
924 Log(("usbHid: GET_IDLE wValue=%#x wIndex=%#x, returning %#x\n", pSetup->wValue, pSetup->wIndex, pThis->bIdle));
925 pUrb->abData[sizeof(*pSetup)] = pThis->bIdle;
926 return usbHidCompleteOk(pThis, pUrb, 1);
927 }
928 break;
929 }
930 break;
931 }
932 }
933 Log(("usbHid: Unimplemented class request: bmRequestType=%#x bRequest=%#x wValue=%#x wIndex=%#x wLength=%#x\n",
934 pSetup->bmRequestType, pSetup->bRequest, pSetup->wValue, pSetup->wIndex, pSetup->wLength));
935
936 usbHidCompleteStall(pThis, pEp, pUrb, "TODO: class request stuff");
937 }
938 else
939 {
940 Log(("usbHid: Unknown control msg: bmRequestType=%#x bRequest=%#x wValue=%#x wIndex=%#x wLength=%#x\n",
941 pSetup->bmRequestType, pSetup->bRequest, pSetup->wValue, pSetup->wIndex, pSetup->wLength));
942 return usbHidCompleteStall(pThis, pEp, pUrb, "Unknown control msg");
943 }
944
945 return VINF_SUCCESS;
946}
947
948
949/**
950 * @copydoc PDMUSBREG::pfnQueue
951 */
952static DECLCALLBACK(int) usbHidQueue(PPDMUSBINS pUsbIns, PVUSBURB pUrb)
953{
954 PUSBHID pThis = PDMINS_2_DATA(pUsbIns, PUSBHID);
955 LogFlow(("usbHidQueue/#%u: pUrb=%p:%s EndPt=%#x\n", pUsbIns->iInstance, pUrb, pUrb->pszDesc, pUrb->EndPt));
956 RTCritSectEnter(&pThis->CritSect);
957
958 /*
959 * Parse on a per end-point basis.
960 */
961 int rc;
962 switch (pUrb->EndPt)
963 {
964 case 0:
965 rc = usbHidHandleDefaultPipe(pThis, &pThis->aEps[0], pUrb);
966 break;
967
968 case 0x81:
969 AssertFailed();
970 case 0x01:
971 rc = usbHidHandleIntrDevToHost(pThis, &pThis->aEps[1], pUrb);
972 break;
973
974 default:
975 AssertMsgFailed(("EndPt=%d\n", pUrb->EndPt));
976 rc = VERR_VUSB_FAILED_TO_QUEUE_URB;
977 break;
978 }
979
980 RTCritSectLeave(&pThis->CritSect);
981 return rc;
982}
983
984
985/**
986 * @copydoc PDMUSBREG::pfnUsbClearHaltedEndpoint
987 */
988static DECLCALLBACK(int) usbHidUsbClearHaltedEndpoint(PPDMUSBINS pUsbIns, unsigned uEndpoint)
989{
990 PUSBHID pThis = PDMINS_2_DATA(pUsbIns, PUSBHID);
991 LogFlow(("usbHidUsbClearHaltedEndpoint/#%u: uEndpoint=%#x\n", pUsbIns->iInstance, uEndpoint));
992
993 if ((uEndpoint & ~0x80) < RT_ELEMENTS(pThis->aEps))
994 {
995 RTCritSectEnter(&pThis->CritSect);
996 pThis->aEps[(uEndpoint & ~0x80)].fHalted = false;
997 RTCritSectLeave(&pThis->CritSect);
998 }
999
1000 return VINF_SUCCESS;
1001}
1002
1003
1004/**
1005 * @copydoc PDMUSBREG::pfnUsbSetInterface
1006 */
1007static DECLCALLBACK(int) usbHidUsbSetInterface(PPDMUSBINS pUsbIns, uint8_t bInterfaceNumber, uint8_t bAlternateSetting)
1008{
1009 LogFlow(("usbHidUsbSetInterface/#%u: bInterfaceNumber=%u bAlternateSetting=%u\n", pUsbIns->iInstance, bInterfaceNumber, bAlternateSetting));
1010 Assert(bAlternateSetting == 0);
1011 return VINF_SUCCESS;
1012}
1013
1014
1015/**
1016 * @copydoc PDMUSBREG::pfnUsbSetConfiguration
1017 */
1018static DECLCALLBACK(int) usbHidUsbSetConfiguration(PPDMUSBINS pUsbIns, uint8_t bConfigurationValue,
1019 const void *pvOldCfgDesc, const void *pvOldIfState, const void *pvNewCfgDesc)
1020{
1021 PUSBHID pThis = PDMINS_2_DATA(pUsbIns, PUSBHID);
1022 LogFlow(("usbHidUsbSetConfiguration/#%u: bConfigurationValue=%u\n", pUsbIns->iInstance, bConfigurationValue));
1023 Assert(bConfigurationValue == 1);
1024 RTCritSectEnter(&pThis->CritSect);
1025
1026 /*
1027 * If the same config is applied more than once, it's a kind of reset.
1028 */
1029 if (pThis->bConfigurationValue == bConfigurationValue)
1030 usbHidResetWorker(pThis, NULL, true /*fSetConfig*/); /** @todo figure out the exact difference */
1031 pThis->bConfigurationValue = bConfigurationValue;
1032
1033 RTCritSectLeave(&pThis->CritSect);
1034 return VINF_SUCCESS;
1035}
1036
1037
1038/**
1039 * @copydoc PDMUSBREG::pfnUsbGetDescriptorCache
1040 */
1041static DECLCALLBACK(PCPDMUSBDESCCACHE) usbHidUsbGetDescriptorCache(PPDMUSBINS pUsbIns)
1042{
1043 PUSBHID pThis = PDMINS_2_DATA(pUsbIns, PUSBHID);
1044 LogFlow(("usbHidUsbGetDescriptorCache/#%u:\n", pUsbIns->iInstance));
1045 return &g_UsbHidDescCache;
1046}
1047
1048
1049/**
1050 * @copydoc PDMUSBREG::pfnUsbReset
1051 */
1052static DECLCALLBACK(int) usbHidUsbReset(PPDMUSBINS pUsbIns, bool fResetOnLinux)
1053{
1054 PUSBHID pThis = PDMINS_2_DATA(pUsbIns, PUSBHID);
1055 LogFlow(("usbHidUsbReset/#%u:\n", pUsbIns->iInstance));
1056 RTCritSectEnter(&pThis->CritSect);
1057
1058 int rc = usbHidResetWorker(pThis, NULL, false /*fSetConfig*/);
1059
1060 RTCritSectLeave(&pThis->CritSect);
1061 return rc;
1062}
1063
1064
1065/**
1066 * @copydoc PDMUSBREG::pfnDestruct
1067 */
1068static void usbHidDestruct(PPDMUSBINS pUsbIns)
1069{
1070 PUSBHID pThis = PDMINS_2_DATA(pUsbIns, PUSBHID);
1071 LogFlow(("usbHidDestruct/#%u:\n", pUsbIns->iInstance));
1072
1073 if (RTCritSectIsInitialized(&pThis->CritSect))
1074 {
1075 RTCritSectEnter(&pThis->CritSect);
1076 RTCritSectLeave(&pThis->CritSect);
1077 RTCritSectDelete(&pThis->CritSect);
1078 }
1079
1080 if (pThis->hEvtDoneQueue != NIL_RTSEMEVENT)
1081 {
1082 RTSemEventDestroy(pThis->hEvtDoneQueue);
1083 pThis->hEvtDoneQueue = NIL_RTSEMEVENT;
1084 }
1085}
1086
1087
1088/**
1089 * @copydoc PDMUSBREG::pfnConstruct
1090 */
1091static DECLCALLBACK(int) usbHidConstruct(PPDMUSBINS pUsbIns, int iInstance, PCFGMNODE pCfg, PCFGMNODE pCfgGlobal)
1092{
1093 PUSBHID pThis = PDMINS_2_DATA(pUsbIns, PUSBHID);
1094 Log(("usbHidConstruct/#%u:\n", iInstance));
1095
1096 /*
1097 * Perform the basic structure initialization first so the destructor
1098 * will not misbehave.
1099 */
1100 pThis->pUsbIns = pUsbIns;
1101 pThis->hEvtDoneQueue = NIL_RTSEMEVENT;
1102 pThis->XlatState = SS_IDLE;
1103 usbHidQueueInit(&pThis->ToHostQueue);
1104 usbHidQueueInit(&pThis->DoneQueue);
1105
1106 int rc = RTCritSectInit(&pThis->CritSect);
1107 AssertRCReturn(rc, rc);
1108
1109 rc = RTSemEventCreate(&pThis->hEvtDoneQueue);
1110 AssertRCReturn(rc, rc);
1111
1112 /*
1113 * Validate and read the configuration.
1114 */
1115 rc = CFGMR3ValidateConfig(pCfg, "/", "", "", "UsbHid", iInstance);
1116 if (RT_FAILURE(rc))
1117 return rc;
1118
1119 pThis->Lun0.IBase.pfnQueryInterface = usbHidKeyboardQueryInterface;
1120 pThis->Lun0.IPort.pfnPutEvent = usbHidKeyboardPutEvent;
1121
1122 /*
1123 * Attach the keyboard driver.
1124 */
1125 rc = pUsbIns->pHlpR3->pfnDriverAttach(pUsbIns, 0 /*iLun*/, &pThis->Lun0.IBase, &pThis->Lun0.pDrvBase, "Keyboard Port");
1126 if (RT_FAILURE(rc))
1127 return PDMUsbHlpVMSetError(pUsbIns, rc, RT_SRC_POS, N_("HID failed to attach keyboard driver"));
1128
1129 return VINF_SUCCESS;
1130}
1131
1132
1133/**
1134 * The USB Human Interface Device (HID) Keyboard registration record.
1135 */
1136const PDMUSBREG g_UsbHidKbd =
1137{
1138 /* u32Version */
1139 PDM_USBREG_VERSION,
1140 /* szName */
1141 "HidKeyboard",
1142 /* pszDescription */
1143 "USB HID Keyboard.",
1144 /* fFlags */
1145 0,
1146 /* cMaxInstances */
1147 ~0,
1148 /* cbInstance */
1149 sizeof(USBHID),
1150 /* pfnConstruct */
1151 usbHidConstruct,
1152 /* pfnDestruct */
1153 usbHidDestruct,
1154 /* pfnVMInitComplete */
1155 NULL,
1156 /* pfnVMPowerOn */
1157 NULL,
1158 /* pfnVMReset */
1159 NULL,
1160 /* pfnVMSuspend */
1161 NULL,
1162 /* pfnVMResume */
1163 NULL,
1164 /* pfnVMPowerOff */
1165 NULL,
1166 /* pfnHotPlugged */
1167 NULL,
1168 /* pfnHotUnplugged */
1169 NULL,
1170 /* pfnDriverAttach */
1171 NULL,
1172 /* pfnDriverDetach */
1173 NULL,
1174 /* pfnQueryInterface */
1175 NULL,
1176 /* pfnUsbReset */
1177 usbHidUsbReset,
1178 /* pfnUsbGetCachedDescriptors */
1179 usbHidUsbGetDescriptorCache,
1180 /* pfnUsbSetConfiguration */
1181 usbHidUsbSetConfiguration,
1182 /* pfnUsbSetInterface */
1183 usbHidUsbSetInterface,
1184 /* pfnUsbClearHaltedEndpoint */
1185 usbHidUsbClearHaltedEndpoint,
1186 /* pfnUrbNew */
1187 NULL/*usbHidUrbNew*/,
1188 /* pfnQueue */
1189 usbHidQueue,
1190 /* pfnUrbCancel */
1191 usbHidUrbCancel,
1192 /* pfnUrbReap */
1193 usbHidUrbReap,
1194 /* u32TheEnd */
1195 PDM_USBREG_VERSION
1196};
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette