VirtualBox

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

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

Do not block input on keyboard queue level when VM is paused.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.2 KB
Line 
1/* $Id: DrvKeyboardQueue.cpp 49423 2013-11-08 16:25:35Z 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 * Keyboard queue driver instance data.
36 *
37 * @implements PDMIKEYBOARDCONNECTOR
38 * @implements PDMIKEYBOARDPORT
39 */
40typedef struct DRVKBDQUEUE
41{
42 /** Pointer to the driver instance structure. */
43 PPDMDRVINS pDrvIns;
44 /** Pointer to the keyboard port interface of the driver/device above us. */
45 PPDMIKEYBOARDPORT pUpPort;
46 /** Pointer to the keyboard port interface of the driver/device below us. */
47 PPDMIKEYBOARDCONNECTOR pDownConnector;
48 /** Our keyboard connector interface. */
49 PDMIKEYBOARDCONNECTOR IConnector;
50 /** Our keyboard port interface. */
51 PDMIKEYBOARDPORT IPort;
52 /** The queue handle. */
53 PPDMQUEUE pQueue;
54 /** Discard input when this flag is set. */
55 bool fInactive;
56 /** When VM is suspended, queue full errors are not fatal. */
57 bool fSuspended;
58} DRVKBDQUEUE, *PDRVKBDQUEUE;
59
60
61/**
62 * Keyboard queue item.
63 */
64typedef struct DRVKBDQUEUEITEM
65{
66 /** The core part owned by the queue manager. */
67 PDMQUEUEITEMCORE Core;
68 /** The keycode. */
69 uint8_t u8KeyCode;
70} DRVKBDQUEUEITEM, *PDRVKBDQUEUEITEM;
71
72
73
74/* -=-=-=-=- IBase -=-=-=-=- */
75
76/**
77 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
78 */
79static DECLCALLBACK(void *) drvKbdQueueQueryInterface(PPDMIBASE pInterface, const char *pszIID)
80{
81 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
82 PDRVKBDQUEUE pThis = PDMINS_2_DATA(pDrvIns, PDRVKBDQUEUE);
83
84 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
85 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIKEYBOARDCONNECTOR, &pThis->IConnector);
86 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIKEYBOARDPORT, &pThis->IPort);
87 return NULL;
88}
89
90
91/* -=-=-=-=- IKeyboardPort -=-=-=-=- */
92
93/** Converts a pointer to DRVKBDQUEUE::IPort to a DRVKBDQUEUE pointer. */
94#define IKEYBOARDPORT_2_DRVKBDQUEUE(pInterface) ( (PDRVKBDQUEUE)((char *)(pInterface) - RT_OFFSETOF(DRVKBDQUEUE, IPort)) )
95
96
97/**
98 * Queues a keyboard event.
99 * Because of the event queueing the EMT context requirement is lifted.
100 *
101 * @returns VBox status code.
102 * @param pInterface Pointer to this interface structure.
103 * @param u8KeyCode The keycode to queue.
104 * @thread Any thread.
105 */
106static DECLCALLBACK(int) drvKbdQueuePutEvent(PPDMIKEYBOARDPORT pInterface, uint8_t u8KeyCode)
107{
108 PDRVKBDQUEUE pDrv = IKEYBOARDPORT_2_DRVKBDQUEUE(pInterface);
109 /* Ignore any attempt to send events if queue is inactive. */
110 if (pDrv->fInactive)
111 return VINF_SUCCESS;
112
113 PDRVKBDQUEUEITEM pItem = (PDRVKBDQUEUEITEM)PDMQueueAlloc(pDrv->pQueue);
114 if (pItem)
115 {
116 pItem->u8KeyCode = u8KeyCode;
117 PDMQueueInsert(pDrv->pQueue, &pItem->Core);
118 return VINF_SUCCESS;
119 }
120 if (!pDrv->fSuspended)
121 AssertMsgFailed(("drvKbdQueuePutEvent: Queue is full!!!!\n"));
122 return VERR_PDM_NO_QUEUE_ITEMS;
123}
124
125
126/* -=-=-=-=- IConnector -=-=-=-=- */
127
128#define PPDMIKEYBOARDCONNECTOR_2_DRVKBDQUEUE(pInterface) ( (PDRVKBDQUEUE)((char *)(pInterface) - RT_OFFSETOF(DRVKBDQUEUE, IConnector)) )
129
130
131/**
132 * Pass LED status changes from the guest thru to the frontend driver.
133 *
134 * @param pInterface Pointer to the keyboard connector interface structure.
135 * @param enmLeds The new LED mask.
136 */
137static DECLCALLBACK(void) drvKbdPassThruLedsChange(PPDMIKEYBOARDCONNECTOR pInterface, PDMKEYBLEDS enmLeds)
138{
139 PDRVKBDQUEUE pDrv = PPDMIKEYBOARDCONNECTOR_2_DRVKBDQUEUE(pInterface);
140 pDrv->pDownConnector->pfnLedStatusChange(pDrv->pDownConnector, enmLeds);
141}
142
143/**
144 * Pass keyboard state changes from the guest thru to the frontend driver.
145 *
146 * @param pInterface Pointer to the keyboard connector interface structure.
147 * @param fActive The new active/inactive state.
148 */
149static DECLCALLBACK(void) drvKbdPassThruSetActive(PPDMIKEYBOARDCONNECTOR pInterface, bool fActive)
150{
151 PDRVKBDQUEUE pDrv = PPDMIKEYBOARDCONNECTOR_2_DRVKBDQUEUE(pInterface);
152
153 AssertPtr(pDrv->pDownConnector->pfnSetActive);
154 pDrv->pDownConnector->pfnSetActive(pDrv->pDownConnector, fActive);
155}
156
157
158/* -=-=-=-=- queue -=-=-=-=- */
159
160/**
161 * Queue callback for processing a queued item.
162 *
163 * @returns Success indicator.
164 * If false the item will not be removed and the flushing will stop.
165 * @param pDrvIns The driver instance.
166 * @param pItemCore Pointer to the queue item to process.
167 */
168static DECLCALLBACK(bool) drvKbdQueueConsumer(PPDMDRVINS pDrvIns, PPDMQUEUEITEMCORE pItemCore)
169{
170 PDRVKBDQUEUE pThis = PDMINS_2_DATA(pDrvIns, PDRVKBDQUEUE);
171 PDRVKBDQUEUEITEM pItem = (PDRVKBDQUEUEITEM)pItemCore;
172 int rc = pThis->pUpPort->pfnPutEvent(pThis->pUpPort, pItem->u8KeyCode);
173 return RT_SUCCESS(rc);
174}
175
176
177/* -=-=-=-=- driver interface -=-=-=-=- */
178
179/**
180 * Power On notification.
181 *
182 * @returns VBox status.
183 * @param pDrvIns The drive instance data.
184 */
185static DECLCALLBACK(void) drvKbdQueuePowerOn(PPDMDRVINS pDrvIns)
186{
187 PDRVKBDQUEUE pThis = PDMINS_2_DATA(pDrvIns, PDRVKBDQUEUE);
188 pThis->fInactive = false;
189}
190
191
192/**
193 * Reset notification.
194 *
195 * @returns VBox status.
196 * @param pDrvIns The drive instance data.
197 */
198static DECLCALLBACK(void) drvKbdQueueReset(PPDMDRVINS pDrvIns)
199{
200 //PDRVKBDQUEUE pThis = PDMINS_2_DATA(pDrvIns, PDRVKBDQUEUE);
201 /** @todo purge the queue on reset. */
202}
203
204
205/**
206 * Suspend notification.
207 *
208 * @returns VBox status.
209 * @param pDrvIns The drive instance data.
210 */
211static DECLCALLBACK(void) drvKbdQueueSuspend(PPDMDRVINS pDrvIns)
212{
213 PDRVKBDQUEUE pThis = PDMINS_2_DATA(pDrvIns, PDRVKBDQUEUE);
214 pThis->fSuspended = true;
215}
216
217
218/**
219 * Resume notification.
220 *
221 * @returns VBox status.
222 * @param pDrvIns The drive instance data.
223 */
224static DECLCALLBACK(void) drvKbdQueueResume(PPDMDRVINS pDrvIns)
225{
226 PDRVKBDQUEUE pThis = PDMINS_2_DATA(pDrvIns, PDRVKBDQUEUE);
227 pThis->fSuspended = false;
228}
229
230
231/**
232 * Power Off notification.
233 *
234 * @param pDrvIns The drive instance data.
235 */
236static DECLCALLBACK(void) drvKbdQueuePowerOff(PPDMDRVINS pDrvIns)
237{
238 PDRVKBDQUEUE pThis = PDMINS_2_DATA(pDrvIns, PDRVKBDQUEUE);
239 pThis->fInactive = true;
240}
241
242
243/**
244 * Construct a keyboard driver instance.
245 *
246 * @copydoc FNPDMDRVCONSTRUCT
247 */
248static DECLCALLBACK(int) drvKbdQueueConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
249{
250 PDRVKBDQUEUE pDrv = PDMINS_2_DATA(pDrvIns, PDRVKBDQUEUE);
251 LogFlow(("drvKbdQueueConstruct: iInstance=%d\n", pDrvIns->iInstance));
252 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
253
254 /*
255 * Validate configuration.
256 */
257 if (!CFGMR3AreValuesValid(pCfg, "QueueSize\0Interval\0"))
258 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
259
260 /*
261 * Init basic data members and interfaces.
262 */
263 pDrv->fInactive = true;
264 pDrv->fSuspended = false;
265 /* IBase. */
266 pDrvIns->IBase.pfnQueryInterface = drvKbdQueueQueryInterface;
267 /* IKeyboardConnector. */
268 pDrv->IConnector.pfnLedStatusChange = drvKbdPassThruLedsChange;
269 pDrv->IConnector.pfnSetActive = drvKbdPassThruSetActive;
270 /* IKeyboardPort. */
271 pDrv->IPort.pfnPutEvent = drvKbdQueuePutEvent;
272
273 /*
274 * Get the IKeyboardPort interface of the above driver/device.
275 */
276 pDrv->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIKEYBOARDPORT);
277 if (!pDrv->pUpPort)
278 {
279 AssertMsgFailed(("Configuration error: No keyboard port interface above!\n"));
280 return VERR_PDM_MISSING_INTERFACE_ABOVE;
281 }
282
283 /*
284 * Attach driver below and query it's connector interface.
285 */
286 PPDMIBASE pDownBase;
287 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pDownBase);
288 if (RT_FAILURE(rc))
289 {
290 AssertMsgFailed(("Failed to attach driver below us! rc=%Rra\n", rc));
291 return rc;
292 }
293 pDrv->pDownConnector = PDMIBASE_QUERY_INTERFACE(pDownBase, PDMIKEYBOARDCONNECTOR);
294 if (!pDrv->pDownConnector)
295 {
296 AssertMsgFailed(("Configuration error: No keyboard connector interface below!\n"));
297 return VERR_PDM_MISSING_INTERFACE_BELOW;
298 }
299
300 /*
301 * Create the queue.
302 */
303 uint32_t cMilliesInterval = 0;
304 rc = CFGMR3QueryU32(pCfg, "Interval", &cMilliesInterval);
305 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
306 cMilliesInterval = 0;
307 else if (RT_FAILURE(rc))
308 {
309 AssertMsgFailed(("Configuration error: 32-bit \"Interval\" -> rc=%Rrc\n", rc));
310 return rc;
311 }
312
313 uint32_t cItems = 0;
314 rc = CFGMR3QueryU32(pCfg, "QueueSize", &cItems);
315 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
316 cItems = 128;
317 else if (RT_FAILURE(rc))
318 {
319 AssertMsgFailed(("Configuration error: 32-bit \"QueueSize\" -> rc=%Rrc\n", rc));
320 return rc;
321 }
322
323 rc = PDMDrvHlpQueueCreate(pDrvIns, sizeof(DRVKBDQUEUEITEM), cItems, cMilliesInterval, drvKbdQueueConsumer, "Keyboard", &pDrv->pQueue);
324 if (RT_FAILURE(rc))
325 {
326 AssertMsgFailed(("Failed to create driver: cItems=%d cMilliesInterval=%d rc=%Rrc\n", cItems, cMilliesInterval, rc));
327 return rc;
328 }
329
330 return VINF_SUCCESS;
331}
332
333
334/**
335 * Keyboard queue driver registration record.
336 */
337const PDMDRVREG g_DrvKeyboardQueue =
338{
339 /* u32Version */
340 PDM_DRVREG_VERSION,
341 /* szName */
342 "KeyboardQueue",
343 /* szRCMod */
344 "",
345 /* szR0Mod */
346 "",
347 /* pszDescription */
348 "Keyboard queue driver to plug in between the key source and the device to do queueing and inter-thread transport.",
349 /* fFlags */
350 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
351 /* fClass. */
352 PDM_DRVREG_CLASS_KEYBOARD,
353 /* cMaxInstances */
354 ~0U,
355 /* cbInstance */
356 sizeof(DRVKBDQUEUE),
357 /* pfnConstruct */
358 drvKbdQueueConstruct,
359 /* pfnRelocate */
360 NULL,
361 /* pfnDestruct */
362 NULL,
363 /* pfnIOCtl */
364 NULL,
365 /* pfnPowerOn */
366 drvKbdQueuePowerOn,
367 /* pfnReset */
368 drvKbdQueueReset,
369 /* pfnSuspend */
370 drvKbdQueueSuspend,
371 /* pfnResume */
372 drvKbdQueueResume,
373 /* pfnAttach */
374 NULL,
375 /* pfnDetach */
376 NULL,
377 /* pfnPowerOff */
378 drvKbdQueuePowerOff,
379 /* pfnSoftReset */
380 NULL,
381 /* u32EndVersion */
382 PDM_DRVREG_VERSION
383};
384
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