VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/Mouse/NT5/VBoxMFInternal.cpp@ 41642

Last change on this file since 41642 was 41642, checked in by vboxsync, 12 years ago

VBoxGuest: Use the EventSpinlock when setting the mouse callback and made the ISR take down the callback info while owning that spinlock. Cleanups.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.3 KB
Line 
1/* $Id: VBoxMFInternal.cpp 41642 2012-06-11 10:30:54Z vboxsync $ */
2
3/** @file
4 * VBox Mouse filter internal functions
5 */
6
7/*
8 * Copyright (C) 2011 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include "VBoxMF.h"
20#include <VBox/VBoxGuestLib.h>
21#include <VBox/VBoxGuest.h>
22#include <iprt/assert.h>
23#include <iprt/asm.h>
24
25typedef struct VBOXGDC
26{
27 PDEVICE_OBJECT pDo;
28 PFILE_OBJECT pFo;
29} VBOXGDC, *PVBOXGDC;
30
31typedef struct _VBoxGlobalContext
32{
33 volatile LONG cDevicesStarted;
34 volatile LONG fVBGLInited;
35 volatile LONG fVBGLInitFailed;
36 volatile LONG fHostInformed;
37 volatile LONG fHostMouseFound;
38 VBOXGDC Gdc;
39 KSPIN_LOCK SyncLock;
40 volatile PVBOXMOUSE_DEVEXT pCurrentDevExt;
41 LIST_ENTRY DevExtList;
42 BOOLEAN fIsNewProtEnabled;
43 MOUSE_INPUT_DATA LastReportedData;
44} VBoxGlobalContext;
45
46static VBoxGlobalContext g_ctx = {};
47
48/* Guest Device Communication API */
49NTSTATUS VBoxGdcInit()
50{
51 UNICODE_STRING UniName;
52 RtlInitUnicodeString(&UniName, VBOXGUEST_DEVICE_NAME_NT);
53 NTSTATUS Status = IoGetDeviceObjectPointer(&UniName, FILE_ALL_ACCESS, &g_ctx.Gdc.pFo, &g_ctx.Gdc.pDo);
54 if (!NT_SUCCESS(Status))
55 {
56 WARN(("IoGetDeviceObjectPointer failed Status(0x%x)", Status));
57 memset(&g_ctx.Gdc, 0, sizeof (g_ctx.Gdc));
58 }
59 return Status;
60}
61
62BOOLEAN VBoxGdcIsInitialized()
63{
64 return !!g_ctx.Gdc.pDo;
65}
66
67NTSTATUS VBoxGdcTerm()
68{
69 if (!g_ctx.Gdc.pFo)
70 return STATUS_SUCCESS;
71 /* this will dereference device object as well */
72 ObDereferenceObject(g_ctx.Gdc.pFo);
73 return STATUS_SUCCESS;
74}
75
76static NTSTATUS vboxGdcSubmitAsync(ULONG uCtl, PVOID pvBuffer, SIZE_T cbBuffer, PKEVENT pEvent, PIO_STATUS_BLOCK pIoStatus)
77{
78 NTSTATUS Status;
79 PIRP pIrp;
80 KIRQL Irql = KeGetCurrentIrql();
81 Assert(Irql == PASSIVE_LEVEL);
82
83 pIrp = IoBuildDeviceIoControlRequest(uCtl, g_ctx.Gdc.pDo, NULL, 0, NULL, 0, TRUE, pEvent, pIoStatus);
84 if (!pIrp)
85 {
86 WARN(("IoBuildDeviceIoControlRequest failed!!\n"));
87 pIoStatus->Status = STATUS_INSUFFICIENT_RESOURCES;
88 pIoStatus->Information = 0;
89 return STATUS_INSUFFICIENT_RESOURCES;
90 }
91
92 PIO_STACK_LOCATION pSl = IoGetNextIrpStackLocation(pIrp);
93 pSl->Parameters.Others.Argument1 = (PVOID)pvBuffer;
94 pSl->Parameters.Others.Argument2 = (PVOID)cbBuffer;
95 Status = IoCallDriver(g_ctx.Gdc.pDo, pIrp);
96
97 return Status;
98}
99
100static NTSTATUS vboxGdcSubmit(ULONG uCtl, PVOID pvBuffer, SIZE_T cbBuffer)
101{
102 IO_STATUS_BLOCK IoStatus = {0};
103 KEVENT Event;
104 NTSTATUS Status;
105
106 KeInitializeEvent(&Event, NotificationEvent, FALSE);
107
108 Status = vboxGdcSubmitAsync(uCtl, pvBuffer, cbBuffer, &Event, &IoStatus);
109 if (Status == STATUS_PENDING)
110 {
111 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
112 Status = IoStatus.Status;
113 }
114
115 return Status;
116}
117
118static DECLCALLBACK(void) vboxNewProtMouseEventCb(void *pvContext)
119{
120 PVBOXMOUSE_DEVEXT pDevExt = (PVBOXMOUSE_DEVEXT)ASMAtomicUoReadPtr((void * volatile *)&g_ctx.pCurrentDevExt);
121 if (pDevExt)
122 {
123#define VBOXMOUSE_POLLERTAG 'PMBV'
124 NTSTATUS Status = IoAcquireRemoveLock(&pDevExt->RemoveLock, pDevExt);
125 if (NT_SUCCESS(Status))
126 {
127 ULONG InputDataConsumed = 0;
128 VBoxDrvNotifyServiceCB(pDevExt, &g_ctx.LastReportedData, &g_ctx.LastReportedData + 1, &InputDataConsumed);
129 IoReleaseRemoveLock(&pDevExt->RemoveLock, pDevExt);
130 }
131 else
132 {
133 WARN(("IoAcquireRemoveLock failed, Status (0x%x)", Status));
134 }
135 }
136 else
137 {
138 WARN(("no current pDevExt specified"));
139 }
140}
141
142static BOOLEAN vboxNewProtIsEnabled()
143{
144 return g_ctx.fIsNewProtEnabled;
145}
146
147static NTSTATUS vboxNewProtRegisterMouseEventCb(BOOLEAN fRegister)
148{
149 VBoxGuestMouseSetNotifyCallback CbInfo = {};
150 CbInfo.pfnNotify = fRegister ? vboxNewProtMouseEventCb : NULL;
151
152 NTSTATUS Status = vboxGdcSubmit(VBOXGUEST_IOCTL_SET_MOUSE_NOTIFY_CALLBACK, &CbInfo, sizeof (CbInfo));
153 if (!NT_SUCCESS(Status))
154 {
155 WARN(("vboxGdcSubmit failed Status(0x%x)", Status));
156 }
157 return Status;
158}
159
160
161NTSTATUS VBoxNewProtInit()
162{
163 NTSTATUS Status = VBoxGdcInit();
164 if (NT_SUCCESS(Status))
165 {
166 KeInitializeSpinLock(&g_ctx.SyncLock);
167 InitializeListHead(&g_ctx.DevExtList);
168 /* we assume the new prot data in g_ctx is zero-initialized (see g_ctx definition) */
169
170 Status = vboxNewProtRegisterMouseEventCb(TRUE);
171 if (NT_SUCCESS(Status))
172 {
173 g_ctx.fIsNewProtEnabled = TRUE;
174 return STATUS_SUCCESS;
175 }
176 VBoxGdcTerm();
177 }
178
179 return Status;
180}
181
182NTSTATUS VBoxNewProtTerm()
183{
184 Assert(IsListEmpty(&g_ctx.DevExtList));
185 if (!vboxNewProtIsEnabled())
186 {
187 WARN(("New Protocol is disabled"));
188 return STATUS_SUCCESS;
189 }
190
191 g_ctx.fIsNewProtEnabled = FALSE;
192
193 NTSTATUS Status = vboxNewProtRegisterMouseEventCb(FALSE);
194 if (!NT_SUCCESS(Status))
195 {
196 WARN(("KeWaitForSingleObject failed, Status (0x%x)", Status));
197 return Status;
198 }
199
200 VBoxGdcTerm();
201
202 return STATUS_SUCCESS;
203}
204
205static NTSTATUS vboxNewProtDeviceAdded(PVBOXMOUSE_DEVEXT pDevExt)
206{
207 if (!vboxNewProtIsEnabled())
208 {
209 WARN(("New Protocol is disabled"));
210 return STATUS_UNSUCCESSFUL;
211 }
212
213 NTSTATUS Status = STATUS_SUCCESS;
214 KIRQL Irql;
215 KeAcquireSpinLock(&g_ctx.SyncLock, &Irql);
216 InsertHeadList(&g_ctx.DevExtList, &pDevExt->ListEntry);
217 if (!g_ctx.pCurrentDevExt)
218 {
219 ASMAtomicWritePtr(&g_ctx.pCurrentDevExt, pDevExt);
220 /* ensure the object is not deleted while it is being used by a poller thread */
221 ObReferenceObject(pDevExt->pdoSelf);
222 }
223 KeReleaseSpinLock(&g_ctx.SyncLock, Irql);
224
225 return Status;
226}
227
228#define PVBOXMOUSE_DEVEXT_FROM_LE(_pLe) ( (PVBOXMOUSE_DEVEXT)(((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXMOUSE_DEVEXT, ListEntry)) )
229
230static NTSTATUS vboxNewProtDeviceRemoved(PVBOXMOUSE_DEVEXT pDevExt)
231{
232 if (!vboxNewProtIsEnabled())
233 {
234 WARN(("New Protocol is disabled"));
235 return STATUS_UNSUCCESSFUL;
236 }
237
238 KIRQL Irql;
239 NTSTATUS Status = STATUS_SUCCESS;
240 KeAcquireSpinLock(&g_ctx.SyncLock, &Irql);
241 RemoveEntryList(&pDevExt->ListEntry);
242 if (g_ctx.pCurrentDevExt == pDevExt)
243 {
244 ObDereferenceObject(pDevExt->pdoSelf);
245 g_ctx.pCurrentDevExt = NULL;
246 for (PLIST_ENTRY pCur = g_ctx.DevExtList.Flink; pCur != &g_ctx.DevExtList; pCur = pCur->Flink)
247 {
248 PVBOXMOUSE_DEVEXT pNewCurDevExt = PVBOXMOUSE_DEVEXT_FROM_LE(pCur);
249 ASMAtomicWritePtr(&g_ctx.pCurrentDevExt, pNewCurDevExt);
250 /* ensure the object is not deleted while it is being used by a poller thread */
251 ObReferenceObject(pNewCurDevExt->pdoSelf);
252 break;
253 }
254 }
255
256 KeReleaseSpinLock(&g_ctx.SyncLock, Irql);
257
258 return Status;
259}
260
261VOID VBoxDrvNotifyServiceCB(PVBOXMOUSE_DEVEXT pDevExt, PMOUSE_INPUT_DATA InputDataStart, PMOUSE_INPUT_DATA InputDataEnd, PULONG InputDataConsumed)
262{
263 KIRQL Irql;
264 /* we need to avoid concurrency between the poller thread and our ServiceCB.
265 * this is perhaps not the best way of doing things, but the most easiest to avoid concurrency
266 * and to ensure the pfnServiceCB is invoked at DISPATCH_LEVEL */
267 KeAcquireSpinLock(&g_ctx.SyncLock, &Irql);
268 if (pDevExt->pSCReq)
269 {
270 int rc = VbglGRPerform(&pDevExt->pSCReq->header);
271
272 if (RT_SUCCESS(rc))
273 {
274 if (pDevExt->pSCReq->mouseFeatures & VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE)
275 {
276 PMOUSE_INPUT_DATA pData = InputDataStart;
277 while (pData<InputDataEnd)
278 {
279 pData->LastX = pDevExt->pSCReq->pointerXPos;
280 pData->LastY = pDevExt->pSCReq->pointerYPos;
281 pData->Flags = MOUSE_MOVE_ABSOLUTE;
282 if (vboxNewProtIsEnabled())
283 pData->Flags |= MOUSE_VIRTUAL_DESKTOP;
284 pData++;
285 }
286
287 /* get the last data & cache it */
288 --pData;
289 g_ctx.LastReportedData.UnitId = pData->UnitId;
290 }
291 }
292 else
293 {
294 WARN(("VbglGRPerform failed with rc=%#x", rc));
295 }
296 }
297
298 /* Call original callback */
299 pDevExt->OriginalConnectData.pfnServiceCB(pDevExt->OriginalConnectData.pDO,
300 InputDataStart, InputDataEnd, InputDataConsumed);
301 KeReleaseSpinLock(&g_ctx.SyncLock, Irql);
302}
303
304static BOOLEAN vboxIsVBGLInited(void)
305{
306 return InterlockedCompareExchange(&g_ctx.fVBGLInited, TRUE, TRUE) == TRUE;
307}
308
309static BOOLEAN vboxIsVBGLInitFailed (void)
310{
311 return InterlockedCompareExchange(&g_ctx.fVBGLInitFailed, TRUE, TRUE) == TRUE;
312}
313
314static BOOLEAN vboxIsHostInformed(void)
315{
316 return InterlockedCompareExchange(&g_ctx.fHostInformed, TRUE, TRUE) == TRUE;
317}
318
319static BOOLEAN vboxIsHostMouseFound(void)
320{
321 return InterlockedCompareExchange(&g_ctx.fHostMouseFound, TRUE, TRUE) == TRUE;
322}
323
324VOID VBoxDeviceAdded(PVBOXMOUSE_DEVEXT pDevExt)
325{
326 LOGF_ENTER();
327 LONG callCnt = InterlockedIncrement(&g_ctx.cDevicesStarted);
328
329 /* One time Vbgl initialization */
330 if (callCnt == 1)
331 {
332 if (!vboxIsVBGLInited() && !vboxIsVBGLInitFailed())
333 {
334 int rc = VbglInit();
335
336 if (RT_SUCCESS(rc))
337 {
338 InterlockedExchange(&g_ctx.fVBGLInited, TRUE);
339 LOG(("VBGL init OK"));
340 }
341 else
342 {
343 InterlockedExchange (&g_ctx.fVBGLInitFailed, TRUE);
344 WARN(("VBGL init failed with rc=%#x", rc));
345 }
346 }
347 }
348
349 vboxNewProtDeviceAdded(pDevExt);
350
351 if (!vboxIsHostMouseFound())
352 {
353 NTSTATUS rc;
354 UCHAR buffer[512];
355 CM_RESOURCE_LIST *pResourceList = (CM_RESOURCE_LIST *)&buffer[0];
356 ULONG cbWritten=0;
357 BOOLEAN bDetected = FALSE;
358
359 rc = IoGetDeviceProperty(pDevExt->pdoMain, DevicePropertyBootConfiguration,
360 sizeof(buffer), &buffer[0], &cbWritten);
361 if (!NT_SUCCESS(rc))
362 {
363 WARN(("IoGetDeviceProperty failed with rc=%#x", rc));
364 return;
365 }
366
367 LOG(("Number of descriptors: %d", pResourceList->Count));
368
369 /* Check if device claims IO port 0x60 or int12 */
370 for (ULONG i=0; i<pResourceList->Count; ++i)
371 {
372 CM_FULL_RESOURCE_DESCRIPTOR *pFullDescriptor = &pResourceList->List[i];
373
374 LOG(("FullDescriptor[%i]: IfType %d, Bus %d, Ver %d, Rev %d, Count %d",
375 i, pFullDescriptor->InterfaceType, pFullDescriptor->BusNumber,
376 pFullDescriptor->PartialResourceList.Version, pFullDescriptor->PartialResourceList.Revision,
377 pFullDescriptor->PartialResourceList.Count));
378
379 for (ULONG j=0; j<pFullDescriptor->PartialResourceList.Count; ++j)
380 {
381 CM_PARTIAL_RESOURCE_DESCRIPTOR *pPartialDescriptor = &pFullDescriptor->PartialResourceList.PartialDescriptors[j];
382 LOG(("PartialDescriptor[%d]: type %d, ShareDisposition %d, Flags 0x%04X, Start 0x%llx, length 0x%x",
383 j, pPartialDescriptor->Type, pPartialDescriptor->ShareDisposition, pPartialDescriptor->Flags,
384 pPartialDescriptor->u.Generic.Start.QuadPart, pPartialDescriptor->u.Generic.Length));
385
386 switch(pPartialDescriptor->Type)
387 {
388 case CmResourceTypePort:
389 {
390 LOG(("CmResourceTypePort %#x", pPartialDescriptor->u.Port.Start.QuadPart));
391 if (pPartialDescriptor->u.Port.Start.QuadPart == 0x60)
392 {
393 bDetected = TRUE;
394 }
395 break;
396 }
397 case CmResourceTypeInterrupt:
398 {
399 LOG(("CmResourceTypeInterrupt %ld", pPartialDescriptor->u.Interrupt.Vector));
400 if (pPartialDescriptor->u.Interrupt.Vector == 0xC)
401 {
402 bDetected = TRUE;
403 }
404 break;
405 }
406 default:
407 {
408 break;
409 }
410 }
411 }
412 }
413
414 if (bDetected)
415 {
416 /* It's the emulated 8042 PS/2 mouse/kbd device, so mark it as the Host one.
417 * For this device the filter will query absolute mouse coords from the host.
418 */
419 InterlockedExchange(&g_ctx.fHostMouseFound, TRUE);
420
421 pDevExt->bHostMouse = TRUE;
422 LOG(("Host mouse found"));
423 }
424 }
425 LOGF_LEAVE();
426}
427
428VOID VBoxInformHost(PVBOXMOUSE_DEVEXT pDevExt)
429{
430 LOGF_ENTER();
431
432 if (!vboxIsVBGLInited())
433 {
434 WARN(("!vboxIsVBGLInited"));
435 return;
436 }
437
438 /* Inform host we support absolute coordinates */
439 if (pDevExt->bHostMouse && !vboxIsHostInformed())
440 {
441 VMMDevReqMouseStatus *req = NULL;
442 int rc = VbglGRAlloc((VMMDevRequestHeader **)&req, sizeof(VMMDevReqMouseStatus), VMMDevReq_SetMouseStatus);
443
444 if (RT_SUCCESS(rc))
445 {
446 req->mouseFeatures = VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE;
447 if (vboxNewProtIsEnabled())
448 req->mouseFeatures |= VMMDEV_MOUSE_NEW_PROTOCOL;
449
450 req->pointerXPos = 0;
451 req->pointerYPos = 0;
452
453 rc = VbglGRPerform(&req->header);
454
455 if (RT_SUCCESS(rc))
456 {
457 InterlockedExchange(&g_ctx.fHostInformed, TRUE);
458 }
459 else
460 {
461 WARN(("VbglGRPerform failed with rc=%#x", rc));
462 }
463
464 VbglGRFree(&req->header);
465 }
466 else
467 {
468 WARN(("VbglGRAlloc failed with rc=%#x", rc));
469 }
470 }
471
472 /* Preallocate request to be used in VBoxServiceCB*/
473 if (pDevExt->bHostMouse && !pDevExt->pSCReq)
474 {
475 VMMDevReqMouseStatus *req = NULL;
476
477 int rc = VbglGRAlloc((VMMDevRequestHeader **)&req, sizeof(VMMDevReqMouseStatus), VMMDevReq_GetMouseStatus);
478
479 if (RT_SUCCESS(rc))
480 {
481 InterlockedExchangePointer((PVOID volatile *)&pDevExt->pSCReq, req);
482 }
483 else
484 {
485 WARN(("VbglGRAlloc for service callback failed with rc=%#x", rc));
486 }
487 }
488
489 LOGF_LEAVE();
490}
491
492VOID VBoxDeviceRemoved(PVBOXMOUSE_DEVEXT pDevExt)
493{
494 LOGF_ENTER();
495
496 /* Save the allocated request pointer and clear the devExt. */
497 VMMDevReqMouseStatus *pSCReq = (VMMDevReqMouseStatus *) InterlockedExchangePointer((PVOID volatile *)&pDevExt->pSCReq, NULL);
498
499 if (pDevExt->bHostMouse && vboxIsHostInformed())
500 {
501 // tell the VMM that from now on we can't handle absolute coordinates anymore
502 VMMDevReqMouseStatus *req = NULL;
503
504 int rc = VbglGRAlloc((VMMDevRequestHeader **)&req, sizeof(VMMDevReqMouseStatus), VMMDevReq_SetMouseStatus);
505
506 if (RT_SUCCESS(rc))
507 {
508 req->mouseFeatures = 0;
509 req->pointerXPos = 0;
510 req->pointerYPos = 0;
511
512 rc = VbglGRPerform(&req->header);
513
514 if (RT_FAILURE(rc))
515 {
516 WARN(("VbglGRPerform failed with rc=%#x", rc));
517 }
518
519 VbglGRFree(&req->header);
520 }
521 else
522 {
523 WARN(("VbglGRAlloc failed with rc=%#x", rc));
524 }
525
526 InterlockedExchange(&g_ctx.fHostInformed, FALSE);
527 }
528
529 if (pSCReq)
530 {
531 VbglGRFree(&pSCReq->header);
532 }
533
534 LONG callCnt = InterlockedDecrement(&g_ctx.cDevicesStarted);
535
536 vboxNewProtDeviceRemoved(pDevExt);
537
538 if (callCnt == 0)
539 {
540 if (vboxIsVBGLInited())
541 {
542 /* Set the flag to prevent reinitializing of the VBGL. */
543 InterlockedExchange(&g_ctx.fVBGLInitFailed, TRUE);
544
545 VbglTerminate();
546
547 /* The VBGL is now in the not initialized state. */
548 InterlockedExchange(&g_ctx.fVBGLInited, FALSE);
549 InterlockedExchange(&g_ctx.fVBGLInitFailed, FALSE);
550 }
551 }
552
553 LOGF_LEAVE();
554}
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