VirtualBox

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

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

VBoxGuestR0Lib: Function prefix and DECL macro cleanups.

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