VirtualBox

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

Last change on this file since 58178 was 58178, checked in by vboxsync, 9 years ago

VbglInit -> VbglInitPrimary & VbglInitClient. VBoxGuestLib/Init.cpp cleanups and positive thinking.

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