VirtualBox

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

Last change on this file was 102299, checked in by vboxsync, 6 months ago

Additions/WINNT/Mouse: do not try to remove device from list if it is not in the list

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

© 2023 Oracle
ContactPrivacy policyTerms of Use