VirtualBox

source: vbox/trunk/src/VBox/Main/KeyboardImpl.cpp@ 29436

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

Route input to PS/2 keyboard unless USB keyboard was explicitly activated.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.9 KB
Line 
1/* $Id: KeyboardImpl.cpp 28909 2010-04-29 16:34:17Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2008 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#include "KeyboardImpl.h"
19#include "ConsoleImpl.h"
20
21#include "AutoCaller.h"
22#include "Logging.h"
23
24#include <VBox/com/array.h>
25#include <VBox/pdmdrv.h>
26#include <iprt/asm.h>
27
28// defines
29////////////////////////////////////////////////////////////////////////////////
30
31// globals
32////////////////////////////////////////////////////////////////////////////////
33
34/** @name Keyboard device capabilities bitfield
35 * @{ */
36enum
37{
38 /** The keyboard device does not wish to receive keystrokes. */
39 KEYBOARD_DEVCAP_DISABLED = 0,
40 /** The keyboard device does wishes to receive keystrokes. */
41 KEYBOARD_DEVCAP_ENABLED = 1
42};
43
44/**
45 * Keyboard driver instance data.
46 */
47typedef struct DRVMAINKEYBOARD
48{
49 /** Pointer to the keyboard object. */
50 Keyboard *pKeyboard;
51 /** Pointer to the driver instance structure. */
52 PPDMDRVINS pDrvIns;
53 /** Pointer to the keyboard port interface of the driver/device above us. */
54 PPDMIKEYBOARDPORT pUpPort;
55 /** Our keyboard connector interface. */
56 PDMIKEYBOARDCONNECTOR IConnector;
57 /** The capabilities of this device. */
58 uint32_t u32DevCaps;
59} DRVMAINKEYBOARD, *PDRVMAINKEYBOARD;
60
61/** Converts PDMIVMMDEVCONNECTOR pointer to a DRVMAINVMMDEV pointer. */
62#define PPDMIKEYBOARDCONNECTOR_2_MAINKEYBOARD(pInterface) ( (PDRVMAINKEYBOARD) ((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINKEYBOARD, IConnector)) )
63
64
65// constructor / destructor
66////////////////////////////////////////////////////////////////////////////////
67
68Keyboard::Keyboard()
69 : mParent(NULL)
70{
71}
72
73Keyboard::~Keyboard()
74{
75}
76
77HRESULT Keyboard::FinalConstruct()
78{
79 RT_ZERO(mpDrv);
80 mpVMMDev = NULL;
81 mfVMMDevInited = false;
82 return S_OK;
83}
84
85void Keyboard::FinalRelease()
86{
87 uninit();
88}
89
90// public methods
91////////////////////////////////////////////////////////////////////////////////
92
93/**
94 * Initializes the keyboard object.
95 *
96 * @returns COM result indicator
97 * @param parent handle of our parent object
98 */
99HRESULT Keyboard::init (Console *aParent)
100{
101 LogFlowThisFunc(("aParent=%p\n", aParent));
102
103 ComAssertRet(aParent, E_INVALIDARG);
104
105 /* Enclose the state transition NotReady->InInit->Ready */
106 AutoInitSpan autoInitSpan(this);
107 AssertReturn(autoInitSpan.isOk(), E_FAIL);
108
109 unconst(mParent) = aParent;
110
111 /* Confirm a successful initialization */
112 autoInitSpan.setSucceeded();
113
114 return S_OK;
115}
116
117/**
118 * Uninitializes the instance and sets the ready flag to FALSE.
119 * Called either from FinalRelease() or by the parent when it gets destroyed.
120 */
121void Keyboard::uninit()
122{
123 LogFlowThisFunc(("\n"));
124
125 /* Enclose the state transition Ready->InUninit->NotReady */
126 AutoUninitSpan autoUninitSpan(this);
127 if (autoUninitSpan.uninitDone())
128 return;
129
130 for (unsigned i = 0; i < KEYBOARD_MAX_DEVICES; ++i)
131 {
132 if (mpDrv[i])
133 mpDrv[i]->pKeyboard = NULL;
134 mpDrv[i] = NULL;
135 }
136
137 mpVMMDev = NULL;
138 mfVMMDevInited = true;
139
140 unconst(mParent) = NULL;
141}
142
143/**
144 * Sends a scancode to the keyboard.
145 *
146 * @returns COM status code
147 * @param scancode The scancode to send
148 */
149STDMETHODIMP Keyboard::PutScancode (LONG scancode)
150{
151 HRESULT rc = S_OK;
152
153 AutoCaller autoCaller(this);
154 if (FAILED(autoCaller.rc())) return autoCaller.rc();
155
156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
157
158 CHECK_CONSOLE_DRV(mpDrv[0]);
159
160 PPDMIKEYBOARDPORT pUpPort = NULL;
161 for (int i = KEYBOARD_MAX_DEVICES - 1; i >= 0 ; --i)
162 {
163 if (mpDrv[i] && (mpDrv[i]->u32DevCaps & KEYBOARD_DEVCAP_ENABLED))
164 {
165 pUpPort = mpDrv[i]->pUpPort;
166 break;
167 }
168 }
169 /* No enabled keyboard - throw the input away. */
170 if (!pUpPort)
171 return rc;
172
173 int vrc = pUpPort->pfnPutEvent(pUpPort, (uint8_t)scancode);
174
175 if (RT_FAILURE(vrc))
176 rc = setError(VBOX_E_IPRT_ERROR,
177 tr("Could not send scan code 0x%08X to the virtual keyboard (%Rrc)"),
178 scancode, vrc);
179
180 return rc;
181}
182
183/**
184 * Sends a list of scancodes to the keyboard.
185 *
186 * @returns COM status code
187 * @param scancodes Pointer to the first scancode
188 * @param count Number of scancodes
189 * @param codesStored Address of variable to store the number
190 * of scancodes that were sent to the keyboard.
191 This value can be NULL.
192 */
193STDMETHODIMP Keyboard::PutScancodes (ComSafeArrayIn (LONG, scancodes),
194 ULONG *codesStored)
195{
196 HRESULT rc = S_OK;
197
198 if (ComSafeArrayInIsNull (scancodes))
199 return E_INVALIDARG;
200
201 AutoCaller autoCaller(this);
202 if (FAILED(autoCaller.rc())) return autoCaller.rc();
203
204 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
205
206 CHECK_CONSOLE_DRV(mpDrv[0]);
207
208 /* Send input to the last enabled device. Relies on the fact that
209 * the USB keyboard is always initialized after the PS/2 keyboard.
210 */
211 PPDMIKEYBOARDPORT pUpPort = NULL;
212 for (int i = KEYBOARD_MAX_DEVICES - 1; i >= 0 ; --i)
213 {
214 if (mpDrv[i] && (mpDrv[i]->u32DevCaps & KEYBOARD_DEVCAP_ENABLED))
215 {
216 pUpPort = mpDrv[i]->pUpPort;
217 break;
218 }
219 }
220 /* No enabled keyboard - throw the input away. */
221 if (!pUpPort)
222 return rc;
223
224 com::SafeArray<LONG> keys (ComSafeArrayInArg (scancodes));
225 int vrc = VINF_SUCCESS;
226
227 for (uint32_t i = 0; (i < keys.size()) && RT_SUCCESS(vrc); i++)
228 vrc = pUpPort->pfnPutEvent(pUpPort, (uint8_t)keys[i]);
229
230 if (RT_FAILURE(vrc))
231 return setError(VBOX_E_IPRT_ERROR,
232 tr("Could not send all scan codes to the virtual keyboard (%Rrc)"),
233 vrc);
234
235 /// @todo is it actually possible that not all scancodes can be transmitted?
236 if (codesStored)
237 *codesStored = (uint32_t)keys.size();
238
239 return rc;
240}
241
242/**
243 * Sends Control-Alt-Delete to the keyboard. This could be done otherwise
244 * but it's so common that we'll be nice and supply a convenience API.
245 *
246 * @returns COM status code
247 *
248 */
249STDMETHODIMP Keyboard::PutCAD()
250{
251 static com::SafeArray<LONG> cadSequence(6);
252
253 cadSequence[0] = 0x1d; // Ctrl down
254 cadSequence[1] = 0x38; // Alt down
255 cadSequence[2] = 0x53; // Del down
256 cadSequence[3] = 0xd3; // Del up
257 cadSequence[4] = 0xb8; // Alt up
258 cadSequence[5] = 0x9d; // Ctrl up
259
260 return PutScancodes(ComSafeArrayAsInParam(cadSequence), NULL);
261}
262
263//
264// private methods
265//
266
267/**
268 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
269 */
270DECLCALLBACK(void *) Keyboard::drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
271{
272 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
273 PDRVMAINKEYBOARD pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
274
275 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
276 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIKEYBOARDCONNECTOR, &pDrv->IConnector);
277 return NULL;
278}
279
280
281/**
282 * Destruct a keyboard driver instance.
283 *
284 * @returns VBox status.
285 * @param pDrvIns The driver instance data.
286 */
287DECLCALLBACK(void) Keyboard::drvDestruct (PPDMDRVINS pDrvIns)
288{
289 PDRVMAINKEYBOARD pData = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
290 LogFlow(("Keyboard::drvDestruct: iInstance=%d\n", pDrvIns->iInstance));
291 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
292
293 if (pData->pKeyboard)
294 {
295 AutoWriteLock kbdLock(pData->pKeyboard COMMA_LOCKVAL_SRC_POS);
296 for (unsigned cDev = 0; cDev < KEYBOARD_MAX_DEVICES; ++cDev)
297 if (pData->pKeyboard->mpDrv[cDev] == pData)
298 {
299 pData->pKeyboard->mpDrv[cDev] = NULL;
300 break;
301 }
302 pData->pKeyboard->mpVMMDev = NULL;
303 }
304}
305
306DECLCALLBACK(void) keyboardLedStatusChange (PPDMIKEYBOARDCONNECTOR pInterface, PDMKEYBLEDS enmLeds)
307{
308 PDRVMAINKEYBOARD pDrv = PPDMIKEYBOARDCONNECTOR_2_MAINKEYBOARD (pInterface);
309 pDrv->pKeyboard->getParent()->onKeyboardLedsChange (!!(enmLeds & PDMKEYBLEDS_NUMLOCK),
310 !!(enmLeds & PDMKEYBLEDS_CAPSLOCK),
311 !!(enmLeds & PDMKEYBLEDS_SCROLLLOCK));
312}
313
314/**
315 * @interface_method_impl{PDMIKEYBOARDCONNECTOR,pfnSetActive}
316 */
317DECLCALLBACK(void) Keyboard::keyboardSetActive(PPDMIKEYBOARDCONNECTOR pInterface, bool fActive)
318{
319 PDRVMAINKEYBOARD pDrv = PPDMIKEYBOARDCONNECTOR_2_MAINKEYBOARD(pInterface);
320 if (fActive)
321 pDrv->u32DevCaps |= KEYBOARD_DEVCAP_ENABLED;
322 else
323 pDrv->u32DevCaps &= ~KEYBOARD_DEVCAP_ENABLED;
324}
325
326/**
327 * Construct a keyboard driver instance.
328 *
329 * @copydoc FNPDMDRVCONSTRUCT
330 */
331DECLCALLBACK(int) Keyboard::drvConstruct (PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
332{
333 PDRVMAINKEYBOARD pData = PDMINS_2_DATA (pDrvIns, PDRVMAINKEYBOARD);
334 LogFlow(("Keyboard::drvConstruct: iInstance=%d\n", pDrvIns->iInstance));
335 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
336
337 /*
338 * Validate configuration.
339 */
340 if (!CFGMR3AreValuesValid (pCfg, "Object\0"))
341 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
342 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
343 ("Configuration error: Not possible to attach anything to this driver!\n"),
344 VERR_PDM_DRVINS_NO_ATTACH);
345
346 /*
347 * IBase.
348 */
349 pDrvIns->IBase.pfnQueryInterface = Keyboard::drvQueryInterface;
350
351 pData->IConnector.pfnLedStatusChange = keyboardLedStatusChange;
352 pData->IConnector.pfnSetActive = keyboardSetActive;
353
354 /*
355 * Get the IKeyboardPort interface of the above driver/device.
356 */
357 pData->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIKEYBOARDPORT);
358 if (!pData->pUpPort)
359 {
360 AssertMsgFailed (("Configuration error: No keyboard port interface above!\n"));
361 return VERR_PDM_MISSING_INTERFACE_ABOVE;
362 }
363
364 /*
365 * Get the Keyboard object pointer and update the mpDrv member.
366 */
367 void *pv;
368 int rc = CFGMR3QueryPtr (pCfg, "Object", &pv);
369 if (RT_FAILURE(rc))
370 {
371 AssertMsgFailed (("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
372 return rc;
373 }
374 pData->pKeyboard = (Keyboard *)pv; /** @todo Check this cast! */
375 unsigned cDev;
376 for (cDev = 0; cDev < KEYBOARD_MAX_DEVICES; ++cDev)
377 if (!pData->pKeyboard->mpDrv[cDev])
378 {
379 pData->pKeyboard->mpDrv[cDev] = pData;
380 break;
381 }
382 if (cDev == KEYBOARD_MAX_DEVICES)
383 return VERR_NO_MORE_HANDLES;
384
385 return VINF_SUCCESS;
386}
387
388
389/**
390 * Keyboard driver registration record.
391 */
392const PDMDRVREG Keyboard::DrvReg =
393{
394 /* u32Version */
395 PDM_DRVREG_VERSION,
396 /* szName */
397 "MainKeyboard",
398 /* szRCMod */
399 "",
400 /* szR0Mod */
401 "",
402 /* pszDescription */
403 "Main keyboard driver (Main as in the API).",
404 /* fFlags */
405 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
406 /* fClass. */
407 PDM_DRVREG_CLASS_KEYBOARD,
408 /* cMaxInstances */
409 ~0,
410 /* cbInstance */
411 sizeof(DRVMAINKEYBOARD),
412 /* pfnConstruct */
413 Keyboard::drvConstruct,
414 /* pfnDestruct */
415 Keyboard::drvDestruct,
416 /* pfnRelocate */
417 NULL,
418 /* pfnIOCtl */
419 NULL,
420 /* pfnPowerOn */
421 NULL,
422 /* pfnReset */
423 NULL,
424 /* pfnSuspend */
425 NULL,
426 /* pfnResume */
427 NULL,
428 /* pfnAttach */
429 NULL,
430 /* pfnDetach */
431 NULL,
432 /* pfnPowerOff */
433 NULL,
434 /* pfnSoftReset */
435 NULL,
436 /* u32EndVersion */
437 PDM_DRVREG_VERSION
438};
439/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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