VirtualBox

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

Last change on this file since 85959 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.5 KB
Line 
1/* $Id: VBoxMFInternal.cpp 82968 2020-02-04 10:35:17Z 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-2020 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/*********************************************************************************************************************************
22* Header Files *
23*********************************************************************************************************************************/
24#undef WIN9X_COMPAT_SPINLOCK
25#define WIN9X_COMPAT_SPINLOCK /* Avoid duplicate _KeInitializeSpinLock@4 error on x86. */
26#include <iprt/asm.h>
27#include "VBoxMF.h"
28#include <VBox/VBoxGuestLib.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 = VbglR0SetMouseNotifyCallback(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(("VbglR0SetMouseNotifyCallback 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 = VbglR0SetMouseNotifyCallback(NULL, NULL);
137 if (RT_FAILURE(rc))
138 WARN(("VbglR0SetMouseNotifyCallback 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 = VbglR0GRPerform(&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(("VbglR0GRPerform 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 if (rc == STATUS_OBJECT_NAME_NOT_FOUND) /* This happen when loading on a running system, don't want the assertion. */
298 LOG(("IoGetDeviceProperty failed with STATUS_OBJECT_NAME_NOT_FOUND"));
299 else
300 WARN(("IoGetDeviceProperty failed with rc=%#x", rc));
301 return;
302 }
303
304 LOG(("Number of descriptors: %d", pResourceList->Count));
305
306 /* Check if device claims IO port 0x60 or int12 */
307 for (ULONG i=0; i<pResourceList->Count; ++i)
308 {
309 CM_FULL_RESOURCE_DESCRIPTOR *pFullDescriptor = &pResourceList->List[i];
310
311 LOG(("FullDescriptor[%i]: IfType %d, Bus %d, Ver %d, Rev %d, Count %d",
312 i, pFullDescriptor->InterfaceType, pFullDescriptor->BusNumber,
313 pFullDescriptor->PartialResourceList.Version, pFullDescriptor->PartialResourceList.Revision,
314 pFullDescriptor->PartialResourceList.Count));
315
316 for (ULONG j=0; j<pFullDescriptor->PartialResourceList.Count; ++j)
317 {
318 CM_PARTIAL_RESOURCE_DESCRIPTOR *pPartialDescriptor = &pFullDescriptor->PartialResourceList.PartialDescriptors[j];
319 LOG(("PartialDescriptor[%d]: type %d, ShareDisposition %d, Flags 0x%04X, Start 0x%llx, length 0x%x",
320 j, pPartialDescriptor->Type, pPartialDescriptor->ShareDisposition, pPartialDescriptor->Flags,
321 pPartialDescriptor->u.Generic.Start.QuadPart, pPartialDescriptor->u.Generic.Length));
322
323 switch(pPartialDescriptor->Type)
324 {
325 case CmResourceTypePort:
326 {
327 LOG(("CmResourceTypePort %#x", pPartialDescriptor->u.Port.Start.QuadPart));
328 if (pPartialDescriptor->u.Port.Start.QuadPart == 0x60)
329 {
330 bDetected = TRUE;
331 }
332 break;
333 }
334 case CmResourceTypeInterrupt:
335 {
336 LOG(("CmResourceTypeInterrupt %ld", pPartialDescriptor->u.Interrupt.Vector));
337 if (pPartialDescriptor->u.Interrupt.Vector == 0xC)
338 {
339 bDetected = TRUE;
340 }
341 break;
342 }
343 default:
344 {
345 break;
346 }
347 }
348 }
349 }
350
351 if (bDetected)
352 {
353 /* It's the emulated 8042 PS/2 mouse/kbd device, so mark it as the Host one.
354 * For this device the filter will query absolute mouse coords from the host.
355 */
356 /** @todo r=bird: The g_ctx.fHostMouseFound needs to be cleared
357 * when the device is removed... */
358 InterlockedExchange(&g_ctx.fHostMouseFound, TRUE);
359
360 pDevExt->bHostMouse = TRUE;
361 LOG(("Host mouse found"));
362 }
363 }
364
365 /* Finally call the handler, which needs a correct pDevExt->bHostMouse value. */
366 vboxNewProtDeviceAdded(pDevExt);
367
368 LOGF_LEAVE();
369}
370
371void VBoxInformHost(PVBOXMOUSE_DEVEXT pDevExt)
372{
373 LOGF_ENTER();
374 if (vboxIsVBGLInited())
375 {
376 /* Do lazy callback installation. */
377 vboxNewProtLazyRegister();
378
379 /* Inform host we support absolute coordinates */
380 if (pDevExt->bHostMouse && !vboxIsHostInformed())
381 {
382 VMMDevReqMouseStatus *req = NULL;
383 int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&req, sizeof(VMMDevReqMouseStatus), VMMDevReq_SetMouseStatus);
384 if (RT_SUCCESS(rc))
385 {
386 req->mouseFeatures = VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE;
387 if (g_ctx.fIsNewProtEnabled)
388 req->mouseFeatures |= VMMDEV_MOUSE_NEW_PROTOCOL;
389
390 req->pointerXPos = 0;
391 req->pointerYPos = 0;
392
393 rc = VbglR0GRPerform(&req->header);
394 if (RT_SUCCESS(rc))
395 InterlockedExchange(&g_ctx.fHostInformed, TRUE);
396 else
397 WARN(("VbglR0GRPerform failed with rc=%Rrc", rc));
398
399 VbglR0GRFree(&req->header);
400 }
401 else
402 WARN(("VbglR0GRAlloc failed with rc=%Rrc", rc));
403 }
404
405 /* Preallocate request to be used in VBoxServiceCB*/
406 if (pDevExt->bHostMouse && !pDevExt->pSCReq)
407 {
408 VMMDevReqMouseStatus *req = NULL;
409 int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&req, sizeof(VMMDevReqMouseStatus), VMMDevReq_GetMouseStatus);
410 if (RT_SUCCESS(rc))
411 InterlockedExchangePointer((PVOID volatile *)&pDevExt->pSCReq, req);
412 else
413 {
414 WARN(("VbglR0GRAlloc for service callback failed with rc=%Rrc", rc));
415 }
416 }
417 }
418 else
419 WARN(("!vboxIsVBGLInited"));
420 LOGF_LEAVE();
421}
422
423VOID VBoxDeviceRemoved(PVBOXMOUSE_DEVEXT pDevExt)
424{
425 LOGF_ENTER();
426
427 /*
428 * Tell the host that from now on we can't handle absolute coordinates anymore.
429 */
430 if (pDevExt->bHostMouse && vboxIsHostInformed())
431 {
432 VMMDevReqMouseStatus *req = NULL;
433 int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&req, sizeof(VMMDevReqMouseStatus), VMMDevReq_SetMouseStatus);
434 if (RT_SUCCESS(rc))
435 {
436 req->mouseFeatures = 0;
437 req->pointerXPos = 0;
438 req->pointerYPos = 0;
439
440 rc = VbglR0GRPerform(&req->header);
441 if (RT_FAILURE(rc))
442 WARN(("VbglR0GRPerform failed with rc=%Rrc", rc));
443
444 VbglR0GRFree(&req->header);
445 }
446 else
447 WARN(("VbglR0GRAlloc failed with rc=%Rrc", rc));
448
449 InterlockedExchange(&g_ctx.fHostInformed, FALSE);
450 }
451
452 /*
453 * Remove the device from the list so we won't get callouts any more.
454 */
455 vboxNewProtDeviceRemoved(pDevExt);
456
457 /*
458 * Free the preallocated request.
459 * Note! This could benefit from merging with vboxNewProtDeviceRemoved to
460 * avoid taking the spinlock twice in a row.
461 */
462 KIRQL Irql;
463 KeAcquireSpinLock(&g_ctx.SyncLock, &Irql);
464 VMMDevReqMouseStatus *pSCReq = ASMAtomicXchgPtrT(&pDevExt->pSCReq, NULL, VMMDevReqMouseStatus *);
465 KeReleaseSpinLock(&g_ctx.SyncLock, Irql);
466 if (pSCReq)
467 VbglR0GRFree(&pSCReq->header);
468
469 /*
470 * Do init ref count handling.
471 * Note! This sequence could potentially be racing VBoxDeviceAdded, depending
472 * on what the OS allows to run in parallel...
473 */
474 LONG cCalls = InterlockedDecrement(&g_ctx.cDevicesStarted);
475 if (cCalls == 0)
476 {
477 if (vboxIsVBGLInited())
478 {
479 /* Set the flag to prevent reinitializing of the VBGL. */
480 InterlockedExchange(&g_ctx.fVBGLInitFailed, TRUE);
481
482 vboxNewProtTerm();
483 VbglR0TerminateClient();
484
485 /* The VBGL is now in the not initialized state. */
486 InterlockedExchange(&g_ctx.fVBGLInited, FALSE);
487 InterlockedExchange(&g_ctx.fVBGLInitFailed, FALSE);
488 }
489 }
490
491 LOGF_LEAVE();
492}
493
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