VirtualBox

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

Last change on this file since 68556 was 68556, checked in by vboxsync, 7 years ago

merging vbglioc r117707: Mouse/NT5: Use always Vbgl APIs to talking to the driver. Must do lazy registration of the event callback as we don't know when VBoxGuest will show up.

  • 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 68556 2017-08-31 12:09:59Z vboxsync $ */
2/** @file
3 * VBox Mouse Filter Driver - Internal functions.
4 *
5 * @todo r=bird: Would be better to merge this file into VBoxMFDriver.cpp...
6 */
7
8/*
9 * Copyright (C) 2011-2016 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20/*********************************************************************************************************************************
21* Header Files *
22*********************************************************************************************************************************/
23#undef WIN9X_COMPAT_SPINLOCK
24#define WIN9X_COMPAT_SPINLOCK /* Avoid duplicate _KeInitializeSpinLock@4 error on x86. */
25#include <iprt/asm.h>
26#include "VBoxMF.h"
27#include <VBox/VBoxGuestLib.h>
28#include <VBox/VBoxGuest.h>
29#include <iprt/assert.h>
30#include <iprt/string.h>
31
32
33/*********************************************************************************************************************************
34* Structures and Typedefs *
35*********************************************************************************************************************************/
36typedef struct _VBoxGlobalContext
37{
38 volatile LONG cDevicesStarted;
39 volatile LONG fVBGLInited;
40 volatile LONG fVBGLInitFailed;
41 volatile LONG fHostInformed;
42 volatile LONG fHostMouseFound;
43 VBGLIDCHANDLE IdcHandle;
44 KSPIN_LOCK SyncLock;
45 volatile PVBOXMOUSE_DEVEXT pCurrentDevExt;
46 LIST_ENTRY DevExtList;
47 bool fIsNewProtEnabled;
48 MOUSE_INPUT_DATA LastReportedData;
49} VBoxGlobalContext;
50
51
52/*********************************************************************************************************************************
53* Global Variables *
54*********************************************************************************************************************************/
55static VBoxGlobalContext g_ctx = {};
56
57
58/**
59 * Called from DriverEntry to initialize g_ctx.
60 */
61void VBoxMouFltInitGlobals(void)
62{
63 RT_ZERO(g_ctx);
64 KeInitializeSpinLock(&g_ctx.SyncLock);
65 InitializeListHead(&g_ctx.DevExtList);
66}
67
68
69/**
70 * Called on driver unload to clean up g_ctx.
71 */
72void VBoxMouFltDeleteGlobals(void)
73{
74 Assert(IsListEmpty(&g_ctx.DevExtList));
75}
76
77
78/**
79 * @callback_method_impl{FNVBOXGUESTMOUSENOTIFY}
80 */
81static DECLCALLBACK(void) vboxNewProtMouseEventCb(void *pvUser)
82{
83 RT_NOREF(pvUser);
84 PVBOXMOUSE_DEVEXT pDevExt = (PVBOXMOUSE_DEVEXT)ASMAtomicUoReadPtr((void * volatile *)&g_ctx.pCurrentDevExt);
85 if (pDevExt)
86 {
87 NTSTATUS Status = IoAcquireRemoveLock(&pDevExt->RemoveLock, pDevExt);
88 if (NT_SUCCESS(Status))
89 {
90 ULONG InputDataConsumed = 0;
91 VBoxDrvNotifyServiceCB(pDevExt, &g_ctx.LastReportedData, &g_ctx.LastReportedData + 1, &InputDataConsumed);
92 IoReleaseRemoveLock(&pDevExt->RemoveLock, pDevExt);
93 }
94 else
95 WARN(("IoAcquireRemoveLock failed, Status (0x%x)", Status));
96 }
97 else
98 WARN(("no current pDevExt specified"));
99}
100
101/**
102 * Lazy init callback.
103 *
104 * We don't have control over when VBoxGuest.sys is loaded and therefore cannot
105 * be sure it is already around when we are started or our devices instantiated.
106 * So, we try lazily attaching to the device when we have a chance.
107 *
108 * @returns true on success, false on failure.
109 */
110static bool vboxNewProtLazyRegister(void)
111{
112 if (g_ctx.fIsNewProtEnabled)
113 return true;
114 int rc = VbglSetMouseNotifyCallback(vboxNewProtMouseEventCb, NULL);
115 if (RT_SUCCESS(rc))
116 {
117 g_ctx.fIsNewProtEnabled = true;
118 LOG(("Successfully register mouse event callback with VBoxGuest."));
119 return true;
120 }
121 WARN(("VbglSetMouseNotifyCallback failed: %Rrc", rc));
122 return false;
123}
124
125/**
126 * This is called when the last device instance is destroyed.
127 *
128 * @returns NT status.
129 */
130static void vboxNewProtTerm(void)
131{
132 Assert(IsListEmpty(&g_ctx.DevExtList));
133 if (g_ctx.fIsNewProtEnabled)
134 {
135 g_ctx.fIsNewProtEnabled = false;
136 int rc = VbglSetMouseNotifyCallback(NULL, NULL);
137 if (RT_FAILURE(rc))
138 WARN(("VbglSetMouseNotifyCallback failed: %Rrc", rc));
139 }
140}
141
142/**
143 * Worker for VBoxDeviceAdded that enables callback processing of pDevExt.
144 *
145 * @param pDevExt The device instance that was added.
146 */
147static void vboxNewProtDeviceAdded(PVBOXMOUSE_DEVEXT pDevExt)
148{
149 /*
150 * Always add the device to the list.
151 */
152 KIRQL Irql;
153 KeAcquireSpinLock(&g_ctx.SyncLock, &Irql);
154
155 InsertHeadList(&g_ctx.DevExtList, &pDevExt->ListEntry);
156
157 /* g_ctx.pCurrentDevExt must be associated with the i8042prt device. */
158 if ( pDevExt->bHostMouse
159 && ASMAtomicCmpXchgPtr(&g_ctx.pCurrentDevExt, pDevExt, NULL))
160 {
161 /* ensure the object is not deleted while it is being used by a poller thread */
162 ObReferenceObject(pDevExt->pdoSelf);
163 }
164
165 KeReleaseSpinLock(&g_ctx.SyncLock, Irql);
166
167 /*
168 * Do lazy callback registration.
169 */
170 vboxNewProtLazyRegister();
171}
172
173/**
174 * Worker for VBoxDeviceRemoved that disables callback processing of pDevExt.
175 *
176 * @param pDevExt The device instance that is being removed.
177 */
178static void vboxNewProtDeviceRemoved(PVBOXMOUSE_DEVEXT pDevExt)
179{
180 /*
181 * Remove the device from the list.
182 */
183 KIRQL Irql;
184 KeAcquireSpinLock(&g_ctx.SyncLock, &Irql);
185
186 RemoveEntryList(&pDevExt->ListEntry);
187
188 /* Check if the PS/2 mouse is being removed. Usually never happens. */
189 if (ASMAtomicCmpXchgPtr(&g_ctx.pCurrentDevExt, NULL, pDevExt))
190 ObDereferenceObject(pDevExt->pdoSelf);
191
192 KeReleaseSpinLock(&g_ctx.SyncLock, Irql);
193}
194
195VOID VBoxDrvNotifyServiceCB(PVBOXMOUSE_DEVEXT pDevExt, PMOUSE_INPUT_DATA InputDataStart, PMOUSE_INPUT_DATA InputDataEnd,
196 PULONG InputDataConsumed)
197{
198 /* we need to avoid concurrency between the poller thread and our ServiceCB.
199 * this is perhaps not the best way of doing things, but the most easiest to avoid concurrency
200 * and to ensure the pfnServiceCB is invoked at DISPATCH_LEVEL */
201 KIRQL Irql;
202 KeAcquireSpinLock(&g_ctx.SyncLock, &Irql);
203 if (pDevExt->pSCReq)
204 {
205 int rc = VbglGRPerform(&pDevExt->pSCReq->header);
206 if (RT_SUCCESS(rc))
207 {
208 if (pDevExt->pSCReq->mouseFeatures & VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE)
209 {
210 PMOUSE_INPUT_DATA pData = InputDataStart;
211 while (pData < InputDataEnd)
212 {
213 pData->LastX = pDevExt->pSCReq->pointerXPos;
214 pData->LastY = pDevExt->pSCReq->pointerYPos;
215 pData->Flags = MOUSE_MOVE_ABSOLUTE;
216 if (g_ctx.fIsNewProtEnabled)
217 pData->Flags |= MOUSE_VIRTUAL_DESKTOP;
218 pData++;
219 }
220
221 /* get the last data & cache it */
222 --pData;
223 g_ctx.LastReportedData.UnitId = pData->UnitId;
224 }
225 }
226 else
227 {
228 WARN(("VbglGRPerform failed with rc=%Rrc", rc));
229 }
230 }
231
232 /* Call original callback */
233 pDevExt->OriginalConnectData.pfnServiceCB(pDevExt->OriginalConnectData.pDO, InputDataStart, InputDataEnd, InputDataConsumed);
234 KeReleaseSpinLock(&g_ctx.SyncLock, Irql);
235}
236
237static BOOLEAN vboxIsVBGLInited(void)
238{
239 return InterlockedCompareExchange(&g_ctx.fVBGLInited, TRUE, TRUE) == TRUE;
240}
241
242static BOOLEAN vboxIsVBGLInitFailed(void)
243{
244 return InterlockedCompareExchange(&g_ctx.fVBGLInitFailed, TRUE, TRUE) == TRUE;
245}
246
247static BOOLEAN vboxIsHostInformed(void)
248{
249 return InterlockedCompareExchange(&g_ctx.fHostInformed, TRUE, TRUE) == TRUE;
250}
251
252static BOOLEAN vboxIsHostMouseFound(void)
253{
254 return InterlockedCompareExchange(&g_ctx.fHostMouseFound, TRUE, TRUE) == TRUE;
255}
256
257void VBoxDeviceAdded(PVBOXMOUSE_DEVEXT pDevExt)
258{
259 LOGF_ENTER();
260 LONG cCalls = InterlockedIncrement(&g_ctx.cDevicesStarted);
261
262 /* One time Vbgl initialization */
263 if (cCalls == 1)
264 {
265 KeInitializeSpinLock(&g_ctx.SyncLock);
266 InitializeListHead(&g_ctx.DevExtList);
267
268 if (!vboxIsVBGLInited() && !vboxIsVBGLInitFailed())
269 {
270 int rc = VbglR0InitClient();
271 if (RT_SUCCESS(rc))
272 {
273 InterlockedExchange(&g_ctx.fVBGLInited, TRUE);
274 LOG(("VBGL init OK"));
275 vboxNewProtLazyRegister();
276 }
277 else
278 {
279 InterlockedExchange(&g_ctx.fVBGLInitFailed, TRUE);
280 WARN(("VBGL init failed with rc=%Rrc", rc));
281 }
282 }
283 }
284
285 if (!vboxIsHostMouseFound())
286 {
287 NTSTATUS rc;
288 UCHAR buffer[512];
289 CM_RESOURCE_LIST *pResourceList = (CM_RESOURCE_LIST *)&buffer[0];
290 ULONG cbWritten=0;
291 BOOLEAN bDetected = FALSE;
292
293 rc = IoGetDeviceProperty(pDevExt->pdoMain, DevicePropertyBootConfiguration,
294 sizeof(buffer), &buffer[0], &cbWritten);
295 if (!NT_SUCCESS(rc))
296 {
297 WARN(("IoGetDeviceProperty failed with rc=%#x", rc));
298 return;
299 }
300
301 LOG(("Number of descriptors: %d", pResourceList->Count));
302
303 /* Check if device claims IO port 0x60 or int12 */
304 for (ULONG i=0; i<pResourceList->Count; ++i)
305 {
306 CM_FULL_RESOURCE_DESCRIPTOR *pFullDescriptor = &pResourceList->List[i];
307
308 LOG(("FullDescriptor[%i]: IfType %d, Bus %d, Ver %d, Rev %d, Count %d",
309 i, pFullDescriptor->InterfaceType, pFullDescriptor->BusNumber,
310 pFullDescriptor->PartialResourceList.Version, pFullDescriptor->PartialResourceList.Revision,
311 pFullDescriptor->PartialResourceList.Count));
312
313 for (ULONG j=0; j<pFullDescriptor->PartialResourceList.Count; ++j)
314 {
315 CM_PARTIAL_RESOURCE_DESCRIPTOR *pPartialDescriptor = &pFullDescriptor->PartialResourceList.PartialDescriptors[j];
316 LOG(("PartialDescriptor[%d]: type %d, ShareDisposition %d, Flags 0x%04X, Start 0x%llx, length 0x%x",
317 j, pPartialDescriptor->Type, pPartialDescriptor->ShareDisposition, pPartialDescriptor->Flags,
318 pPartialDescriptor->u.Generic.Start.QuadPart, pPartialDescriptor->u.Generic.Length));
319
320 switch(pPartialDescriptor->Type)
321 {
322 case CmResourceTypePort:
323 {
324 LOG(("CmResourceTypePort %#x", pPartialDescriptor->u.Port.Start.QuadPart));
325 if (pPartialDescriptor->u.Port.Start.QuadPart == 0x60)
326 {
327 bDetected = TRUE;
328 }
329 break;
330 }
331 case CmResourceTypeInterrupt:
332 {
333 LOG(("CmResourceTypeInterrupt %ld", pPartialDescriptor->u.Interrupt.Vector));
334 if (pPartialDescriptor->u.Interrupt.Vector == 0xC)
335 {
336 bDetected = TRUE;
337 }
338 break;
339 }
340 default:
341 {
342 break;
343 }
344 }
345 }
346 }
347
348 if (bDetected)
349 {
350 /* It's the emulated 8042 PS/2 mouse/kbd device, so mark it as the Host one.
351 * For this device the filter will query absolute mouse coords from the host.
352 */
353 /** @todo r=bird: The g_ctx.fHostMouseFound needs to be cleared
354 * when the device is removed... */
355 InterlockedExchange(&g_ctx.fHostMouseFound, TRUE);
356
357 pDevExt->bHostMouse = TRUE;
358 LOG(("Host mouse found"));
359 }
360 }
361
362 /* Finally call the handler, which needs a correct pDevExt->bHostMouse value. */
363 vboxNewProtDeviceAdded(pDevExt);
364
365 LOGF_LEAVE();
366}
367
368void VBoxInformHost(PVBOXMOUSE_DEVEXT pDevExt)
369{
370 LOGF_ENTER();
371 if (vboxIsVBGLInited())
372 {
373 /* Do lazy callback installation. */
374 vboxNewProtLazyRegister();
375
376 /* Inform host we support absolute coordinates */
377 if (pDevExt->bHostMouse && !vboxIsHostInformed())
378 {
379 VMMDevReqMouseStatus *req = NULL;
380 int rc = VbglGRAlloc((VMMDevRequestHeader **)&req, sizeof(VMMDevReqMouseStatus), VMMDevReq_SetMouseStatus);
381 if (RT_SUCCESS(rc))
382 {
383 req->mouseFeatures = VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE;
384 if (g_ctx.fIsNewProtEnabled)
385 req->mouseFeatures |= VMMDEV_MOUSE_NEW_PROTOCOL;
386
387 req->pointerXPos = 0;
388 req->pointerYPos = 0;
389
390 rc = VbglGRPerform(&req->header);
391 if (RT_SUCCESS(rc))
392 InterlockedExchange(&g_ctx.fHostInformed, TRUE);
393 else
394 WARN(("VbglGRPerform failed with rc=%Rrc", rc));
395
396 VbglGRFree(&req->header);
397 }
398 else
399 WARN(("VbglGRAlloc failed with rc=%Rrc", rc));
400 }
401
402 /* Preallocate request to be used in VBoxServiceCB*/
403 if (pDevExt->bHostMouse && !pDevExt->pSCReq)
404 {
405 VMMDevReqMouseStatus *req = NULL;
406 int rc = VbglGRAlloc((VMMDevRequestHeader **)&req, sizeof(VMMDevReqMouseStatus), VMMDevReq_GetMouseStatus);
407 if (RT_SUCCESS(rc))
408 InterlockedExchangePointer((PVOID volatile *)&pDevExt->pSCReq, req);
409 else
410 {
411 WARN(("VbglGRAlloc for service callback failed with rc=%Rrc", rc));
412 }
413 }
414 }
415 else
416 WARN(("!vboxIsVBGLInited"));
417 LOGF_LEAVE();
418}
419
420VOID VBoxDeviceRemoved(PVBOXMOUSE_DEVEXT pDevExt)
421{
422 LOGF_ENTER();
423
424 /*
425 * Tell the host that from now on we can't handle absolute coordinates anymore.
426 */
427 if (pDevExt->bHostMouse && vboxIsHostInformed())
428 {
429 VMMDevReqMouseStatus *req = NULL;
430 int rc = VbglGRAlloc((VMMDevRequestHeader **)&req, sizeof(VMMDevReqMouseStatus), VMMDevReq_SetMouseStatus);
431 if (RT_SUCCESS(rc))
432 {
433 req->mouseFeatures = 0;
434 req->pointerXPos = 0;
435 req->pointerYPos = 0;
436
437 rc = VbglGRPerform(&req->header);
438 if (RT_FAILURE(rc))
439 WARN(("VbglGRPerform failed with rc=%Rrc", rc));
440
441 VbglGRFree(&req->header);
442 }
443 else
444 WARN(("VbglGRAlloc failed with rc=%Rrc", rc));
445
446 InterlockedExchange(&g_ctx.fHostInformed, FALSE);
447 }
448
449 /*
450 * Remove the device from the list so we won't get callouts any more.
451 */
452 vboxNewProtDeviceRemoved(pDevExt);
453
454 /*
455 * Free the preallocated request.
456 * Note! This could benefit from merging with vboxNewProtDeviceRemoved to
457 * avoid taking the spinlock twice in a row.
458 */
459 KIRQL Irql;
460 KeAcquireSpinLock(&g_ctx.SyncLock, &Irql);
461 VMMDevReqMouseStatus *pSCReq = ASMAtomicXchgPtrT(&pDevExt->pSCReq, NULL, VMMDevReqMouseStatus *);
462 KeReleaseSpinLock(&g_ctx.SyncLock, Irql);
463 if (pSCReq)
464 VbglGRFree(&pSCReq->header);
465
466 /*
467 * Do init ref count handling.
468 * Note! This sequence could potentially be racing VBoxDeviceAdded, depending
469 * on what the OS allows to run in parallel...
470 */
471 LONG cCalls = InterlockedDecrement(&g_ctx.cDevicesStarted);
472 if (cCalls == 0)
473 {
474 if (vboxIsVBGLInited())
475 {
476 /* Set the flag to prevent reinitializing of the VBGL. */
477 InterlockedExchange(&g_ctx.fVBGLInitFailed, TRUE);
478
479 vboxNewProtTerm();
480 VbglR0TerminateClient();
481
482 /* The VBGL is now in the not initialized state. */
483 InterlockedExchange(&g_ctx.fVBGLInited, FALSE);
484 InterlockedExchange(&g_ctx.fVBGLInitFailed, FALSE);
485 }
486 }
487
488 LOGF_LEAVE();
489}
490
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