VirtualBox

source: vbox/trunk/src/VBox/Devices/Input/DrvKeyboardQueue.cpp@ 50962

Last change on this file since 50962 was 50962, checked in by vboxsync, 11 years ago

Send HID usage codes rather than XT scan codes to keyboard devices, with conversion in driver. See #6026.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.9 KB
Line 
1/* $Id: DrvKeyboardQueue.cpp 50962 2014-04-03 13:58:47Z vboxsync $ */
2/** @file
3 * VBox input devices: Keyboard queue driver
4 */
5
6/*
7 * Copyright (C) 2006-2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_KBD_QUEUE
23#include <VBox/vmm/pdmdrv.h>
24#include <iprt/assert.h>
25#include <iprt/uuid.h>
26
27#include "VBoxDD.h"
28
29
30
31/*******************************************************************************
32* Structures and Typedefs *
33*******************************************************************************/
34
35/** Scancode translator state. */
36typedef enum {
37 SS_IDLE, /**< Starting state. */
38 SS_EXT, /**< E0 byte was received. */
39 SS_EXT1 /**< E1 byte was received. */
40} scan_state_t;
41
42/**
43 * Keyboard queue driver instance data.
44 *
45 * @implements PDMIKEYBOARDCONNECTOR
46 * @implements PDMIKEYBOARDPORT
47 */
48typedef struct DRVKBDQUEUE
49{
50 /** Pointer to the driver instance structure. */
51 PPDMDRVINS pDrvIns;
52 /** Pointer to the keyboard port interface of the driver/device above us. */
53 PPDMIKEYBOARDPORT pUpPort;
54 /** Pointer to the keyboard port interface of the driver/device below us. */
55 PPDMIKEYBOARDCONNECTOR pDownConnector;
56 /** Our keyboard connector interface. */
57 PDMIKEYBOARDCONNECTOR IConnector;
58 /** Our keyboard port interface. */
59 PDMIKEYBOARDPORT IPort;
60 /** The queue handle. */
61 PPDMQUEUE pQueue;
62 /** State of the scancode translation. */
63 scan_state_t XlatState;
64 /** Discard input when this flag is set. */
65 bool fInactive;
66 /** When VM is suspended, queue full errors are not fatal. */
67 bool fSuspended;
68} DRVKBDQUEUE, *PDRVKBDQUEUE;
69
70
71/**
72 * Keyboard queue item.
73 */
74typedef struct DRVKBDQUEUEITEM
75{
76 /** The core part owned by the queue manager. */
77 PDMQUEUEITEMCORE Core;
78 /** The keycode. */
79 uint32_t u32UsageCode;
80} DRVKBDQUEUEITEM, *PDRVKBDQUEUEITEM;
81
82
83/*******************************************************************************
84* Global Variables *
85*******************************************************************************/
86
87/** Lookup table for converting PC/XT scan codes to USB HID usage codes. */
88static const uint8_t aScancode2Hid[] =
89{
90 0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, /* 00-07 */
91 0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b, /* 08-1F */
92 0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c, /* 10-17 */
93 0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16, /* 18-1F */
94 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33, /* 20-27 */
95 0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19, /* 28-2F */
96 0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55, /* 30-37 */
97 0xe2, 0x2c, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, /* 38-3F */
98 0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f, /* 40-47 */
99 0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59, /* 48-4F */
100 0x5a, 0x5b, 0x62, 0x63, 0x46, 0x00, 0x64, 0x44, /* 50-57 */
101 0x45, 0x67, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, /* 58-5F */
102 0x00, 0x00, 0x00, 0x00, 0x68, 0x69, 0x6a, 0x6b, /* 60-67 */
103 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x00, /* 68-6F */
104 0x88, 0x91, 0x90, 0x87, 0x00, 0x00, 0x00, 0x00, /* 70-77 */
105 0x00, 0x8a, 0x00, 0x8b, 0x00, 0x89, 0x85, 0x00 /* 78-7F */
106};
107
108/** Lookup table for extended scancodes (arrow keys etc.). */
109static const uint8_t aExtScan2Hid[] =
110{
111 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00-07 */
112 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 08-1F */
113 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10-17 */
114 0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00, /* 18-1F */
115 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 20-27 */
116 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28-2F */
117 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46, /* 30-37 */
118 /* Sun-specific keys. Most of the XT codes are made up */
119 0xe6, 0x00, 0x00, 0x75, 0x76, 0x77, 0xA3, 0x78, /* 38-3F */
120 0x80, 0x81, 0x82, 0x79, 0x00, 0x00, 0x48, 0x4a, /* 40-47 */
121 0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d, /* 48-4F */
122 0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00, /* 50-57 */
123 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x66, 0x00, /* 58-5F */
124 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 60-67 */
125 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 68-6F */
126 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 70-77 */
127 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* 78-7F */
128};
129
130/**
131 * Convert a PC scan code to a USB HID usage byte.
132 *
133 * @param state Current state of the translator (scan_state_t).
134 * @param scanCode Incoming scan code.
135 * @param pUsage Pointer to usage; high bit set for key up events. The
136 * contents are only valid if returned state is SS_IDLE.
137 *
138 * @return scan_state_t New state of the translator.
139 */
140static scan_state_t ScancodeToHidUsage(scan_state_t state, uint8_t scanCode, uint32_t *pUsage)
141{
142 uint32_t keyUp;
143 uint8_t usage;
144
145 Assert(pUsage);
146
147 /* Isolate the scan code and key break flag. */
148 keyUp = (scanCode & 0x80) << 24;
149
150 switch (state) {
151 case SS_IDLE:
152 if (scanCode == 0xE0) {
153 state = SS_EXT;
154 } else if (scanCode == 0xE1) {
155 state = SS_EXT1;
156 } else {
157 usage = aScancode2Hid[scanCode & 0x7F];
158 *pUsage = usage | keyUp;
159 /* Remain in SS_IDLE state. */
160 }
161 break;
162 case SS_EXT:
163 usage = aExtScan2Hid[scanCode & 0x7F];
164 *pUsage = usage | keyUp;
165 state = SS_IDLE;
166 break;
167 case SS_EXT1:
168 /* The sequence is E1 1D 45 E1 9D C5. We take the easy way out and remain
169 * in the SS_EXT1 state until 45 or C5 is received.
170 */
171 if ((scanCode & 0x7F) == 0x45) {
172 *pUsage = 0x48;
173 if (scanCode == 0xC5)
174 *pUsage |= keyUp;
175 state = SS_IDLE;
176 }
177 /* Else remain in SS_EXT1 state. */
178 break;
179 }
180 return state;
181}
182
183
184/* -=-=-=-=- IBase -=-=-=-=- */
185
186/**
187 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
188 */
189static DECLCALLBACK(void *) drvKbdQueueQueryInterface(PPDMIBASE pInterface, const char *pszIID)
190{
191 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
192 PDRVKBDQUEUE pThis = PDMINS_2_DATA(pDrvIns, PDRVKBDQUEUE);
193
194 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
195 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIKEYBOARDCONNECTOR, &pThis->IConnector);
196 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIKEYBOARDPORT, &pThis->IPort);
197 return NULL;
198}
199
200
201/* -=-=-=-=- IKeyboardPort -=-=-=-=- */
202
203/** Converts a pointer to DRVKBDQUEUE::IPort to a DRVKBDQUEUE pointer. */
204#define IKEYBOARDPORT_2_DRVKBDQUEUE(pInterface) ( (PDRVKBDQUEUE)((char *)(pInterface) - RT_OFFSETOF(DRVKBDQUEUE, IPort)) )
205
206
207/**
208 * Queues a keyboard event.
209 * Because of the event queueing the EMT context requirement is lifted.
210 *
211 * @returns VBox status code.
212 * @param pInterface Pointer to this interface structure.
213 * @param u8ScanCode The scan code to translate/queue.
214 * @thread Any thread.
215 */
216static DECLCALLBACK(int) drvKbdQueuePutEventScan(PPDMIKEYBOARDPORT pInterface, uint8_t u8ScanCode)
217{
218 PDRVKBDQUEUE pDrv = IKEYBOARDPORT_2_DRVKBDQUEUE(pInterface);
219 /* Ignore any attempt to send events if queue is inactive. */
220 if (pDrv->fInactive)
221 return VINF_SUCCESS;
222
223 uint32_t u32Usage = 0;
224 pDrv->XlatState = ScancodeToHidUsage(pDrv->XlatState, u8ScanCode, &u32Usage);
225
226 if (pDrv->XlatState == SS_IDLE) {
227 PDRVKBDQUEUEITEM pItem = (PDRVKBDQUEUEITEM)PDMQueueAlloc(pDrv->pQueue);
228 if (pItem)
229 {
230 pItem->u32UsageCode = u32Usage;
231 PDMQueueInsert(pDrv->pQueue, &pItem->Core);
232 return VINF_SUCCESS;
233 }
234 if (!pDrv->fSuspended)
235 AssertMsgFailed(("drvKbdQueuePutEventScan: Queue is full!!!!\n"));
236 return VERR_PDM_NO_QUEUE_ITEMS;
237 }
238 else
239 return VINF_SUCCESS;
240}
241
242
243/* -=-=-=-=- IConnector -=-=-=-=- */
244
245#define PPDMIKEYBOARDCONNECTOR_2_DRVKBDQUEUE(pInterface) ( (PDRVKBDQUEUE)((char *)(pInterface) - RT_OFFSETOF(DRVKBDQUEUE, IConnector)) )
246
247
248/**
249 * Pass LED status changes from the guest thru to the frontend driver.
250 *
251 * @param pInterface Pointer to the keyboard connector interface structure.
252 * @param enmLeds The new LED mask.
253 */
254static DECLCALLBACK(void) drvKbdPassThruLedsChange(PPDMIKEYBOARDCONNECTOR pInterface, PDMKEYBLEDS enmLeds)
255{
256 PDRVKBDQUEUE pDrv = PPDMIKEYBOARDCONNECTOR_2_DRVKBDQUEUE(pInterface);
257 pDrv->pDownConnector->pfnLedStatusChange(pDrv->pDownConnector, enmLeds);
258}
259
260/**
261 * Pass keyboard state changes from the guest thru to the frontend driver.
262 *
263 * @param pInterface Pointer to the keyboard connector interface structure.
264 * @param fActive The new active/inactive state.
265 */
266static DECLCALLBACK(void) drvKbdPassThruSetActive(PPDMIKEYBOARDCONNECTOR pInterface, bool fActive)
267{
268 PDRVKBDQUEUE pDrv = PPDMIKEYBOARDCONNECTOR_2_DRVKBDQUEUE(pInterface);
269
270 AssertPtr(pDrv->pDownConnector->pfnSetActive);
271 pDrv->pDownConnector->pfnSetActive(pDrv->pDownConnector, fActive);
272}
273
274
275/* -=-=-=-=- queue -=-=-=-=- */
276
277/**
278 * Queue callback for processing a queued item.
279 *
280 * @returns Success indicator.
281 * If false the item will not be removed and the flushing will stop.
282 * @param pDrvIns The driver instance.
283 * @param pItemCore Pointer to the queue item to process.
284 */
285static DECLCALLBACK(bool) drvKbdQueueConsumer(PPDMDRVINS pDrvIns, PPDMQUEUEITEMCORE pItemCore)
286{
287 PDRVKBDQUEUE pThis = PDMINS_2_DATA(pDrvIns, PDRVKBDQUEUE);
288 PDRVKBDQUEUEITEM pItem = (PDRVKBDQUEUEITEM)pItemCore;
289 int rc = pThis->pUpPort->pfnPutEventHid(pThis->pUpPort, pItem->u32UsageCode);
290 return RT_SUCCESS(rc);
291}
292
293
294/* -=-=-=-=- driver interface -=-=-=-=- */
295
296/**
297 * Power On notification.
298 *
299 * @returns VBox status.
300 * @param pDrvIns The drive instance data.
301 */
302static DECLCALLBACK(void) drvKbdQueuePowerOn(PPDMDRVINS pDrvIns)
303{
304 PDRVKBDQUEUE pThis = PDMINS_2_DATA(pDrvIns, PDRVKBDQUEUE);
305 pThis->fInactive = false;
306}
307
308
309/**
310 * Reset notification.
311 *
312 * @returns VBox status.
313 * @param pDrvIns The drive instance data.
314 */
315static DECLCALLBACK(void) drvKbdQueueReset(PPDMDRVINS pDrvIns)
316{
317 //PDRVKBDQUEUE pThis = PDMINS_2_DATA(pDrvIns, PDRVKBDQUEUE);
318 /** @todo purge the queue on reset. */
319}
320
321
322/**
323 * Suspend notification.
324 *
325 * @returns VBox status.
326 * @param pDrvIns The drive instance data.
327 */
328static DECLCALLBACK(void) drvKbdQueueSuspend(PPDMDRVINS pDrvIns)
329{
330 PDRVKBDQUEUE pThis = PDMINS_2_DATA(pDrvIns, PDRVKBDQUEUE);
331 pThis->fSuspended = true;
332}
333
334
335/**
336 * Resume notification.
337 *
338 * @returns VBox status.
339 * @param pDrvIns The drive instance data.
340 */
341static DECLCALLBACK(void) drvKbdQueueResume(PPDMDRVINS pDrvIns)
342{
343 PDRVKBDQUEUE pThis = PDMINS_2_DATA(pDrvIns, PDRVKBDQUEUE);
344 pThis->fInactive = false;
345 pThis->fSuspended = false;
346}
347
348
349/**
350 * Power Off notification.
351 *
352 * @param pDrvIns The drive instance data.
353 */
354static DECLCALLBACK(void) drvKbdQueuePowerOff(PPDMDRVINS pDrvIns)
355{
356 PDRVKBDQUEUE pThis = PDMINS_2_DATA(pDrvIns, PDRVKBDQUEUE);
357 pThis->fInactive = true;
358}
359
360
361/**
362 * Construct a keyboard driver instance.
363 *
364 * @copydoc FNPDMDRVCONSTRUCT
365 */
366static DECLCALLBACK(int) drvKbdQueueConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
367{
368 PDRVKBDQUEUE pDrv = PDMINS_2_DATA(pDrvIns, PDRVKBDQUEUE);
369 LogFlow(("drvKbdQueueConstruct: iInstance=%d\n", pDrvIns->iInstance));
370 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
371
372 /*
373 * Validate configuration.
374 */
375 if (!CFGMR3AreValuesValid(pCfg, "QueueSize\0Interval\0"))
376 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
377
378 /*
379 * Init basic data members and interfaces.
380 */
381 pDrv->fInactive = true;
382 pDrv->fSuspended = false;
383 pDrv->XlatState = SS_IDLE;
384 /* IBase. */
385 pDrvIns->IBase.pfnQueryInterface = drvKbdQueueQueryInterface;
386 /* IKeyboardConnector. */
387 pDrv->IConnector.pfnLedStatusChange = drvKbdPassThruLedsChange;
388 pDrv->IConnector.pfnSetActive = drvKbdPassThruSetActive;
389 /* IKeyboardPort. */
390 pDrv->IPort.pfnPutEventScan = drvKbdQueuePutEventScan;
391
392 /*
393 * Get the IKeyboardPort interface of the above driver/device.
394 */
395 pDrv->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIKEYBOARDPORT);
396 if (!pDrv->pUpPort)
397 {
398 AssertMsgFailed(("Configuration error: No keyboard port interface above!\n"));
399 return VERR_PDM_MISSING_INTERFACE_ABOVE;
400 }
401
402 /*
403 * Attach driver below and query it's connector interface.
404 */
405 PPDMIBASE pDownBase;
406 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pDownBase);
407 if (RT_FAILURE(rc))
408 {
409 AssertMsgFailed(("Failed to attach driver below us! rc=%Rra\n", rc));
410 return rc;
411 }
412 pDrv->pDownConnector = PDMIBASE_QUERY_INTERFACE(pDownBase, PDMIKEYBOARDCONNECTOR);
413 if (!pDrv->pDownConnector)
414 {
415 AssertMsgFailed(("Configuration error: No keyboard connector interface below!\n"));
416 return VERR_PDM_MISSING_INTERFACE_BELOW;
417 }
418
419 /*
420 * Create the queue.
421 */
422 uint32_t cMilliesInterval = 0;
423 rc = CFGMR3QueryU32(pCfg, "Interval", &cMilliesInterval);
424 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
425 cMilliesInterval = 0;
426 else if (RT_FAILURE(rc))
427 {
428 AssertMsgFailed(("Configuration error: 32-bit \"Interval\" -> rc=%Rrc\n", rc));
429 return rc;
430 }
431
432 uint32_t cItems = 0;
433 rc = CFGMR3QueryU32(pCfg, "QueueSize", &cItems);
434 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
435 cItems = 128;
436 else if (RT_FAILURE(rc))
437 {
438 AssertMsgFailed(("Configuration error: 32-bit \"QueueSize\" -> rc=%Rrc\n", rc));
439 return rc;
440 }
441
442 rc = PDMDrvHlpQueueCreate(pDrvIns, sizeof(DRVKBDQUEUEITEM), cItems, cMilliesInterval, drvKbdQueueConsumer, "Keyboard", &pDrv->pQueue);
443 if (RT_FAILURE(rc))
444 {
445 AssertMsgFailed(("Failed to create driver: cItems=%d cMilliesInterval=%d rc=%Rrc\n", cItems, cMilliesInterval, rc));
446 return rc;
447 }
448
449 return VINF_SUCCESS;
450}
451
452
453/**
454 * Keyboard queue driver registration record.
455 */
456const PDMDRVREG g_DrvKeyboardQueue =
457{
458 /* u32Version */
459 PDM_DRVREG_VERSION,
460 /* szName */
461 "KeyboardQueue",
462 /* szRCMod */
463 "",
464 /* szR0Mod */
465 "",
466 /* pszDescription */
467 "Keyboard queue driver to plug in between the key source and the device to do queueing and inter-thread transport.",
468 /* fFlags */
469 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
470 /* fClass. */
471 PDM_DRVREG_CLASS_KEYBOARD,
472 /* cMaxInstances */
473 ~0U,
474 /* cbInstance */
475 sizeof(DRVKBDQUEUE),
476 /* pfnConstruct */
477 drvKbdQueueConstruct,
478 /* pfnRelocate */
479 NULL,
480 /* pfnDestruct */
481 NULL,
482 /* pfnIOCtl */
483 NULL,
484 /* pfnPowerOn */
485 drvKbdQueuePowerOn,
486 /* pfnReset */
487 drvKbdQueueReset,
488 /* pfnSuspend */
489 drvKbdQueueSuspend,
490 /* pfnResume */
491 drvKbdQueueResume,
492 /* pfnAttach */
493 NULL,
494 /* pfnDetach */
495 NULL,
496 /* pfnPowerOff */
497 drvKbdQueuePowerOff,
498 /* pfnSoftReset */
499 NULL,
500 /* u32EndVersion */
501 PDM_DRVREG_VERSION
502};
503
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