VirtualBox

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

Last change on this file since 37221 was 37221, checked in by vboxsync, 14 years ago

VBoxMouse,VBoxGuest/win: NEW_PROTOCOL for mouse integration

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