VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/Graphics/Miniport/VBoxVideoHGSMI.cpp@ 33711

Last change on this file since 33711 was 33711, checked in by vboxsync, 14 years ago

Additions/WINNT/Graphics: more refactoring

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.8 KB
Line 
1/* $Id: VBoxVideoHGSMI.cpp 33711 2010-11-02 21:48:55Z vboxsync $ */
2/** @file
3 * VirtualBox Video miniport driver for NT/2k/XP - HGSMI related functions.
4 */
5
6/*
7 * Copyright (C) 2006-2009 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19#include "VBoxVideo.h"
20#include "Helper.h"
21
22#include <iprt/asm.h>
23#include <iprt/log.h>
24#include <iprt/thread.h>
25#include <VBox/VMMDev.h>
26#include <VBox/VBoxGuest.h>
27#include <VBox/VBoxVideo.h>
28
29#include <VBoxDisplay.h>
30
31#include "vboxioctl.h"
32
33#define MEM_TAG 'HVBV'
34
35void HGSMINotifyHostCmdComplete (PVBOXVIDEO_COMMON pCommon, HGSMIOFFSET offt)
36{
37 VBoxHGSMIHostWrite(pCommon, offt);
38}
39
40void HGSMIClearIrq (PVBOXVIDEO_COMMON pCommon)
41{
42 VBoxHGSMIHostWrite(pCommon, HGSMIOFFSET_VOID);
43}
44
45static void HGSMIHostCmdComplete (PVBOXVIDEO_COMMON pCommon, void * pvMem)
46{
47 HGSMIOFFSET offMem = HGSMIPointerToOffset (&pCommon->areaHostHeap, HGSMIBufferHeaderFromData (pvMem));
48 Assert(offMem != HGSMIOFFSET_VOID);
49 if(offMem != HGSMIOFFSET_VOID)
50 {
51 HGSMINotifyHostCmdComplete (pCommon, offMem);
52 }
53}
54
55static void hgsmiHostCmdProcess(PVBOXVIDEO_COMMON pCommon, HGSMIOFFSET offBuffer)
56{
57 int rc = HGSMIBufferProcess (&pCommon->areaHostHeap,
58 &pCommon->channels,
59 offBuffer);
60 Assert(!RT_FAILURE(rc));
61 if(RT_FAILURE(rc))
62 {
63 /* failure means the command was not submitted to the handler for some reason
64 * it's our responsibility to notify its completion in this case */
65 HGSMINotifyHostCmdComplete(pCommon, offBuffer);
66 }
67 /* if the cmd succeeded it's responsibility of the callback to complete it */
68}
69
70static HGSMIOFFSET hgsmiGetHostBuffer (PVBOXVIDEO_COMMON pCommon)
71{
72 return VBoxHGSMIHostRead(pCommon);
73}
74
75static void hgsmiHostCommandQueryProcess (PVBOXVIDEO_COMMON pCommon)
76{
77 HGSMIOFFSET offset = hgsmiGetHostBuffer (pCommon);
78 Assert(offset != HGSMIOFFSET_VOID);
79 if(offset != HGSMIOFFSET_VOID)
80 {
81 hgsmiHostCmdProcess(pCommon, offset);
82 }
83}
84
85void hgsmiProcessHostCommandQueue(PVBOXVIDEO_COMMON pCommon)
86{
87 while (pCommon->pHostFlags->u32HostFlags & HGSMIHOSTFLAGS_COMMANDS_PENDING)
88 {
89 if (!ASMAtomicCmpXchgBool(&pCommon->bHostCmdProcessing, true, false))
90 return;
91 hgsmiHostCommandQueryProcess(pCommon);
92 ASMAtomicWriteBool(&pCommon->bHostCmdProcessing, false);
93 }
94}
95
96/* Detect whether HGSMI is supported by the host. */
97BOOLEAN VBoxHGSMIIsSupported (void)
98{
99 USHORT DispiId;
100
101 VBoxVideoCmnPortWriteUshort((PUSHORT)VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ID);
102 VBoxVideoCmnPortWriteUshort((PUSHORT)VBE_DISPI_IOPORT_DATA, VBE_DISPI_ID_HGSMI);
103
104 DispiId = VBoxVideoCmnPortReadUshort((PUSHORT)VBE_DISPI_IOPORT_DATA);
105
106 return (DispiId == VBE_DISPI_ID_HGSMI);
107}
108
109typedef int FNHGSMICALLINIT (PVBOXVIDEO_COMMON pCommon, void *pvContext, void *pvData);
110typedef FNHGSMICALLINIT *PFNHGSMICALLINIT;
111
112typedef int FNHGSMICALLFINALIZE (PVBOXVIDEO_COMMON pCommon, void *pvContext, void *pvData);
113typedef FNHGSMICALLFINALIZE *PFNHGSMICALLFINALIZE;
114
115void* vboxHGSMIBufferAlloc(PVBOXVIDEO_COMMON pCommon,
116 HGSMISIZE cbData,
117 uint8_t u8Ch,
118 uint16_t u16Op)
119{
120#ifdef VBOX_WITH_WDDM
121 /* @todo: add synchronization */
122#endif
123 return HGSMIHeapAlloc (&pCommon->hgsmiAdapterHeap, cbData, u8Ch, u16Op);
124}
125
126void vboxHGSMIBufferFree (PVBOXVIDEO_COMMON pCommon, void *pvBuffer)
127{
128#ifdef VBOX_WITH_WDDM
129 /* @todo: add synchronization */
130#endif
131 HGSMIHeapFree (&pCommon->hgsmiAdapterHeap, pvBuffer);
132}
133
134int vboxHGSMIBufferSubmit (PVBOXVIDEO_COMMON pCommon, void *pvBuffer)
135{
136 /* Initialize the buffer and get the offset for port IO. */
137 HGSMIOFFSET offBuffer = HGSMIHeapBufferOffset (&pCommon->hgsmiAdapterHeap, pvBuffer);
138
139 Assert(offBuffer != HGSMIOFFSET_VOID);
140 if (offBuffer != HGSMIOFFSET_VOID)
141 {
142 /* Submit the buffer to the host. */
143 VBoxHGSMIGuestWrite(pCommon, offBuffer);
144 return VINF_SUCCESS;
145 }
146
147 return VERR_INVALID_PARAMETER;
148}
149
150static int vboxCallChannel (PVBOXVIDEO_COMMON pCommon,
151 uint8_t u8Ch,
152 uint16_t u16Op,
153 HGSMISIZE cbData,
154 PFNHGSMICALLINIT pfnInit,
155 PFNHGSMICALLFINALIZE pfnFinalize,
156 void *pvContext)
157{
158 int rc = VINF_SUCCESS;
159
160 /* Allocate the IO buffer. */
161 void *p = HGSMIHeapAlloc (&pCommon->hgsmiAdapterHeap, cbData, u8Ch, u16Op);
162
163 if (!p)
164 {
165 rc = VERR_NO_MEMORY;
166 }
167 else
168 {
169 /* Prepare data to be sent to the host. */
170 if (pfnInit)
171 {
172 rc = pfnInit (pCommon, pvContext, p);
173 }
174
175 if (RT_SUCCESS (rc))
176 {
177 /* Initialize the buffer and get the offset for port IO. */
178 HGSMIOFFSET offBuffer = HGSMIHeapBufferOffset (&pCommon->hgsmiAdapterHeap,
179 p);
180
181 /* Submit the buffer to the host. */
182 VBoxHGSMIGuestWrite(pCommon, offBuffer);
183
184 if (pfnFinalize)
185 {
186 rc = pfnFinalize (pCommon, pvContext, p);
187 }
188 }
189 else
190 {
191 AssertFailed ();
192 rc = VERR_INTERNAL_ERROR;
193 }
194
195 /* Free the IO buffer. */
196 HGSMIHeapFree (&pCommon->hgsmiAdapterHeap, p);
197 }
198
199 return rc;
200}
201
202static int vboxCallVBVA (PVBOXVIDEO_COMMON pCommon,
203 uint16_t u16Op,
204 HGSMISIZE cbData,
205 PFNHGSMICALLINIT pfnInit,
206 PFNHGSMICALLFINALIZE pfnFinalize,
207 void *pvContext)
208{
209 return vboxCallChannel (pCommon,
210 HGSMI_CH_VBVA,
211 u16Op,
212 cbData,
213 pfnInit,
214 pfnFinalize,
215 pvContext);
216}
217
218typedef struct _QUERYCONFCTX
219{
220 uint32_t u32Index;
221 ULONG *pulValue;
222} QUERYCONFCTX;
223
224static int vbvaInitQueryConf (PVBOXVIDEO_COMMON, void *pvContext, void *pvData)
225{
226 QUERYCONFCTX *pCtx = (QUERYCONFCTX *)pvContext;
227 VBVACONF32 *p = (VBVACONF32 *)pvData;
228
229 p->u32Index = pCtx->u32Index;
230 p->u32Value = 0;
231
232 return VINF_SUCCESS;
233}
234
235static int vbvaFinalizeQueryConf (PVBOXVIDEO_COMMON, void *pvContext, void *pvData)
236{
237 QUERYCONFCTX *pCtx = (QUERYCONFCTX *)pvContext;
238 VBVACONF32 *p = (VBVACONF32 *)pvData;
239
240 if (pCtx->pulValue)
241 {
242 *pCtx->pulValue = p->u32Value;
243 }
244
245 dprintf(("VBoxVideo::vboxQueryConf: u32Value = %d\n", p->u32Value));
246 return VINF_SUCCESS;
247}
248
249static int vboxQueryConfHGSMI (PVBOXVIDEO_COMMON pCommon, uint32_t u32Index, ULONG *pulValue)
250{
251 dprintf(("VBoxVideo::vboxQueryConf: u32Index = %d\n", u32Index));
252
253 QUERYCONFCTX context;
254
255 context.u32Index = u32Index;
256 context.pulValue = pulValue;
257
258 int rc = vboxCallVBVA (pCommon,
259 VBVA_QUERY_CONF32,
260 sizeof (VBVACONF32),
261 vbvaInitQueryConf,
262 vbvaFinalizeQueryConf,
263 &context);
264
265 dprintf(("VBoxVideo::vboxQueryConf: rc = %d\n", rc));
266
267 return rc;
268}
269
270int VBoxHGSMISendViewInfo(PVBOXVIDEO_COMMON pCommon, uint32_t u32Count, PFNHGSMIFILLVIEWINFO pfnFill, void *pvData)
271{
272 int rc;
273 /* Issue the screen info command. */
274 void *p = vboxHGSMIBufferAlloc (pCommon, sizeof(VBVAINFOVIEW) * u32Count,
275 HGSMI_CH_VBVA, VBVA_INFO_VIEW);
276 Assert(p);
277 if (p)
278 {
279 VBVAINFOVIEW *pInfo = (VBVAINFOVIEW *)p;
280 rc = pfnFill(pvData, pInfo);
281 if (RT_SUCCESS(rc))
282 vboxHGSMIBufferSubmit (pCommon, p);
283 vboxHGSMIBufferFree (pCommon, p);
284 }
285 else
286 rc = VERR_NO_MEMORY;
287 return rc;
288}
289
290
291int vbvaInitInfoCaps (PVBOXVIDEO_COMMON pCommon, void *pvContext, void *pvData)
292{
293 VBVACAPS *pCaps = (VBVACAPS*)pvData;
294 pCaps->rc = VERR_NOT_IMPLEMENTED;
295 pCaps->fCaps = pCommon->fCaps;
296 return VINF_SUCCESS;
297}
298
299
300int vbvaFinalizeInfoCaps (PVBOXVIDEO_COMMON, void *pvContext, void *pvData)
301{
302 VBVACAPS *pCaps = (VBVACAPS*)pvData;
303 AssertRC(pCaps->rc);
304 return pCaps->rc;
305}
306
307static int vbvaInitInfoHeap (PVBOXVIDEO_COMMON pCommon, void *pvContext, void *pvData)
308{
309 NOREF (pvContext);
310 VBVAINFOHEAP *p = (VBVAINFOHEAP *)pvData;
311
312 p->u32HeapOffset = pCommon->cbVRAM
313 - pCommon->cbMiniportHeap
314 - VBVA_ADAPTER_INFORMATION_SIZE;
315 p->u32HeapSize = pCommon->cbMiniportHeap;
316
317 return VINF_SUCCESS;
318}
319
320static int hgsmiInitFlagsLocation (PVBOXVIDEO_COMMON pCommon, void *pvContext, void *pvData)
321{
322 NOREF (pvContext);
323 HGSMIBUFFERLOCATION *p = (HGSMIBUFFERLOCATION *)pvData;
324
325 p->offLocation = pCommon->cbVRAM - sizeof (HGSMIHOSTFLAGS);
326 p->cbLocation = sizeof (HGSMIHOSTFLAGS);
327
328 return VINF_SUCCESS;
329}
330
331
332static int vboxSetupAdapterInfoHGSMI (PVBOXVIDEO_COMMON pCommon)
333{
334 dprintf(("VBoxVideo::vboxSetupAdapterInfo\n"));
335
336 /* setup the flags first to ensure they are initialized by the time the host heap is ready */
337 int rc = vboxCallChannel(pCommon,
338 HGSMI_CH_HGSMI,
339 HGSMI_CC_HOST_FLAGS_LOCATION,
340 sizeof (HGSMIBUFFERLOCATION),
341 hgsmiInitFlagsLocation,
342 NULL,
343 NULL);
344 AssertRC(rc);
345 if (RT_SUCCESS(rc) && pCommon->fCaps)
346 {
347 /* Inform about caps */
348 rc = vboxCallVBVA (pCommon,
349 VBVA_INFO_CAPS,
350 sizeof (VBVACAPS),
351 vbvaInitInfoCaps,
352 vbvaFinalizeInfoCaps,
353 NULL);
354 AssertRC(rc);
355 }
356 if (RT_SUCCESS (rc))
357 {
358 /* Report the host heap location. */
359 rc = vboxCallVBVA (pCommon,
360 VBVA_INFO_HEAP,
361 sizeof (VBVAINFOHEAP),
362 vbvaInitInfoHeap,
363 NULL,
364 NULL);
365 AssertRC(rc);
366 }
367
368
369 dprintf(("VBoxVideo::vboxSetupAdapterInfo finished rc = %d\n", rc));
370
371 return rc;
372}
373
374
375/**
376 * Helper function to register secondary displays (DualView). Note that this will not
377 * be available on pre-XP versions, and some editions on XP will fail because they are
378 * intentionally crippled.
379 *
380 * HGSMI variant is a bit different because it uses only HGSMI interface (VBVA channel)
381 * to talk to the host.
382 */
383VOID VBoxSetupDisplaysHGSMI(PDEVICE_EXTENSION PrimaryExtension,
384 ULONG AdapterMemorySize, uint32_t fCaps)
385{
386 VP_STATUS rc = NO_ERROR;
387
388 dprintf(("VBoxVideo::VBoxSetupDisplays: PrimaryExtension = %p\n",
389 PrimaryExtension));
390
391 memset(commonFromDeviceExt(PrimaryExtension), 0,
392 sizeof(*commonFromDeviceExt(PrimaryExtension)));
393 commonFromDeviceExt(PrimaryExtension)->cbVRAM = AdapterMemorySize;
394 commonFromDeviceExt(PrimaryExtension)->fCaps = fCaps;
395 commonFromDeviceExt(PrimaryExtension)->cDisplays = 1;
396 commonFromDeviceExt(PrimaryExtension)->bHGSMI = VBoxHGSMIIsSupported ();
397 /* Why does this use VBoxVideoCmnMemZero? The MSDN docs say that it should
398 * only be used on mapped display adapter memory. Done with memset above. */
399 // VBoxVideoCmnMemZero(&commonFromDeviceExt(PrimaryExtension)->areaHostHeap, sizeof(HGSMIAREA));
400 if (commonFromDeviceExt(PrimaryExtension)->bHGSMI)
401 {
402 /** @note (michael) moved this here as it is done unconditionally in both
403 * driver branches. Feel free to fix if that is ever changed. */
404 commonFromDeviceExt(PrimaryExtension)->IOPortHost = (RTIOPORT)VGA_PORT_HGSMI_HOST;
405 commonFromDeviceExt(PrimaryExtension)->IOPortGuest = (RTIOPORT)VGA_PORT_HGSMI_GUEST;
406
407 /* Map the adapter information. It will be needed for HGSMI IO. */
408 rc = VBoxMapAdapterMemory (commonFromDeviceExt(PrimaryExtension),
409 &commonFromDeviceExt(PrimaryExtension)->pvAdapterInformation,
410 AdapterMemorySize - VBVA_ADAPTER_INFORMATION_SIZE,
411 VBVA_ADAPTER_INFORMATION_SIZE
412 );
413 if (rc != NO_ERROR)
414 {
415 dprintf(("VBoxVideo::VBoxSetupDisplays: VBoxMapAdapterMemory pvAdapterInfoirrmation failed rc = %d\n",
416 rc));
417
418 commonFromDeviceExt(PrimaryExtension)->bHGSMI = FALSE;
419 }
420 else
421 {
422 /* Setup a HGSMI heap within the adapter information area. */
423 rc = HGSMIHeapSetup (&commonFromDeviceExt(PrimaryExtension)->hgsmiAdapterHeap,
424 commonFromDeviceExt(PrimaryExtension)->pvAdapterInformation,
425 VBVA_ADAPTER_INFORMATION_SIZE - sizeof(HGSMIHOSTFLAGS),
426 commonFromDeviceExt(PrimaryExtension)->cbVRAM - VBVA_ADAPTER_INFORMATION_SIZE,
427 false /*fOffsetBased*/);
428
429 if (RT_FAILURE (rc))
430 {
431 dprintf(("VBoxVideo::VBoxSetupDisplays: HGSMIHeapSetup failed rc = %d\n",
432 rc));
433
434 commonFromDeviceExt(PrimaryExtension)->bHGSMI = FALSE;
435 }
436 else
437 {
438 commonFromDeviceExt(PrimaryExtension)->pHostFlags = (HGSMIHOSTFLAGS*)(((uint8_t*)commonFromDeviceExt(PrimaryExtension)->pvAdapterInformation)
439 + VBVA_ADAPTER_INFORMATION_SIZE - sizeof(HGSMIHOSTFLAGS));
440 }
441 }
442 }
443
444 /* Setup the host heap and the adapter memory. */
445 if (commonFromDeviceExt(PrimaryExtension)->bHGSMI)
446 {
447 /* The miniport heap is used for the host buffers. */
448 ULONG cbMiniportHeap = 0;
449 vboxQueryConfHGSMI (commonFromDeviceExt(PrimaryExtension), VBOX_VBVA_CONF32_HOST_HEAP_SIZE, &cbMiniportHeap);
450
451 if (cbMiniportHeap != 0)
452 {
453 /* Do not allow too big heap. No more than 25% of VRAM is allowed. */
454 ULONG cbMiniportHeapMaxSize = AdapterMemorySize / 4;
455
456 if (cbMiniportHeapMaxSize >= VBVA_ADAPTER_INFORMATION_SIZE)
457 {
458 cbMiniportHeapMaxSize -= VBVA_ADAPTER_INFORMATION_SIZE;
459 }
460
461 if (cbMiniportHeap > cbMiniportHeapMaxSize)
462 {
463 cbMiniportHeap = cbMiniportHeapMaxSize;
464 }
465
466 /* Round up to 4096 bytes. */
467 commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap = (cbMiniportHeap + 0xFFF) & ~0xFFF;
468
469 dprintf(("VBoxVideo::VBoxSetupDisplays: cbMiniportHeap = 0x%08X, PrimaryExtension->u.primary.cbMiniportHeap = 0x%08X, cbMiniportHeapMaxSize = 0x%08X\n",
470 cbMiniportHeap, commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap, cbMiniportHeapMaxSize));
471
472 /* Map the heap region.
473 *
474 * Note: the heap will be used for the host buffers submitted to the guest.
475 * The miniport driver is responsible for reading FIFO and notifying
476 * display drivers.
477 */
478 rc = VBoxMapAdapterMemory (commonFromDeviceExt(PrimaryExtension),
479 &commonFromDeviceExt(PrimaryExtension)->pvMiniportHeap,
480 commonFromDeviceExt(PrimaryExtension)->cbVRAM
481 - VBVA_ADAPTER_INFORMATION_SIZE
482 - commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap,
483 commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap
484 );
485 if (rc != NO_ERROR)
486 {
487 commonFromDeviceExt(PrimaryExtension)->pvMiniportHeap = NULL;
488 commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap = 0;
489 commonFromDeviceExt(PrimaryExtension)->bHGSMI = FALSE;
490 }
491 else
492 {
493 HGSMIOFFSET offBase = commonFromDeviceExt(PrimaryExtension)->cbVRAM
494 - VBVA_ADAPTER_INFORMATION_SIZE
495 - commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap;
496
497 /* Init the host hap area. Buffers from the host will be placed there. */
498 HGSMIAreaInitialize (&commonFromDeviceExt(PrimaryExtension)->areaHostHeap,
499 commonFromDeviceExt(PrimaryExtension)->pvMiniportHeap,
500 commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap,
501 offBase);
502 }
503 }
504 else
505 {
506 /* Host has not requested a heap. */
507 commonFromDeviceExt(PrimaryExtension)->pvMiniportHeap = NULL;
508 commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap = 0;
509 }
510 }
511
512 /* Check whether the guest supports multimonitors. */
513 if (commonFromDeviceExt(PrimaryExtension)->bHGSMI)
514 {
515 /* Query the configured number of displays. */
516 ULONG cDisplays = 0;
517 vboxQueryConfHGSMI (commonFromDeviceExt(PrimaryExtension), VBOX_VBVA_CONF32_MONITOR_COUNT, &cDisplays);
518
519 dprintf(("VBoxVideo::VBoxSetupDisplays: cDisplays = %d\n",
520 cDisplays));
521
522 if (cDisplays == 0 || cDisplays > VBOX_VIDEO_MAX_SCREENS)
523 {
524 /* Host reported some bad value. Continue in the 1 screen mode. */
525 cDisplays = 1;
526 }
527 commonFromDeviceExt(PrimaryExtension)->cDisplays = cDisplays;
528 }
529
530 if (commonFromDeviceExt(PrimaryExtension)->bHGSMI)
531 {
532 /* Setup the information for the host. */
533 rc = vboxSetupAdapterInfoHGSMI (commonFromDeviceExt(PrimaryExtension));
534
535 if (RT_FAILURE (rc))
536 {
537 commonFromDeviceExt(PrimaryExtension)->bHGSMI = FALSE;
538 }
539 }
540
541 if (!commonFromDeviceExt(PrimaryExtension)->bHGSMI)
542 VBoxFreeDisplaysHGSMI(commonFromDeviceExt(PrimaryExtension));
543
544 dprintf(("VBoxVideo::VBoxSetupDisplays: finished\n"));
545}
546
547static bool VBoxUnmapAdpInfoCallback(PVOID pvCommon)
548{
549 PVBOXVIDEO_COMMON pCommon = (PVBOXVIDEO_COMMON)pvCommon;
550
551 pCommon->pHostFlags = NULL;
552 return TRUE;
553}
554
555void VBoxFreeDisplaysHGSMI(PVBOXVIDEO_COMMON pCommon)
556{
557 VBoxUnmapAdapterMemory(pCommon, &pCommon->pvMiniportHeap);
558 HGSMIHeapDestroy(&pCommon->hgsmiAdapterHeap);
559
560 /* Unmap the adapter information needed for HGSMI IO. */
561 VBoxSyncToVideoIRQ(pCommon, VBoxUnmapAdpInfoCallback, pCommon);
562 VBoxUnmapAdapterMemory(pCommon, &pCommon->pvAdapterInformation);
563}
564
565/*
566 * Send the pointer shape to the host.
567 */
568typedef struct _MOUSEPOINTERSHAPECTX
569{
570 VIDEO_POINTER_ATTRIBUTES *pPointerAttr;
571 uint32_t cbData;
572 int32_t i32Result;
573} MOUSEPOINTERSHAPECTX;
574
575static int vbvaInitMousePointerShape (PVBOXVIDEO_COMMON, void *pvContext, void *pvData)
576{
577 MOUSEPOINTERSHAPECTX *pCtx = (MOUSEPOINTERSHAPECTX *)pvContext;
578 VBVAMOUSEPOINTERSHAPE *p = (VBVAMOUSEPOINTERSHAPE *)pvData;
579
580 /* Will be updated by the host. */
581 p->i32Result = VINF_SUCCESS;
582
583 /* We have our custom flags in the field */
584 p->fu32Flags = pCtx->pPointerAttr->Enable & 0x0000FFFF;
585
586 p->u32HotX = (pCtx->pPointerAttr->Enable >> 16) & 0xFF;
587 p->u32HotY = (pCtx->pPointerAttr->Enable >> 24) & 0xFF;
588 p->u32Width = pCtx->pPointerAttr->Width;
589 p->u32Height = pCtx->pPointerAttr->Height;
590
591 if (p->fu32Flags & VBOX_MOUSE_POINTER_SHAPE)
592 {
593 /* If shape is supplied, then always create the pointer visible.
594 * See comments in 'vboxUpdatePointerShape'
595 */
596 p->fu32Flags |= VBOX_MOUSE_POINTER_VISIBLE;
597
598 /* Copy the actual pointer data. */
599 memcpy (p->au8Data, pCtx->pPointerAttr->Pixels, pCtx->cbData);
600 }
601
602 return VINF_SUCCESS;
603}
604
605static int vbvaFinalizeMousePointerShape (PVBOXVIDEO_COMMON, void *pvContext, void *pvData)
606{
607 MOUSEPOINTERSHAPECTX *pCtx = (MOUSEPOINTERSHAPECTX *)pvContext;
608 VBVAMOUSEPOINTERSHAPE *p = (VBVAMOUSEPOINTERSHAPE *)pvData;
609
610 pCtx->i32Result = p->i32Result;
611
612 return VINF_SUCCESS;
613}
614
615BOOLEAN vboxUpdatePointerShape (PDEVICE_EXTENSION DeviceExtension,
616 PVIDEO_POINTER_ATTRIBUTES pointerAttr,
617 uint32_t cbLength)
618{
619#ifndef VBOX_WITH_WDDM
620 PDEVICE_EXTENSION PrimaryExtension = DeviceExtension->pPrimary;
621
622 /* In multimonitor case the HW mouse pointer is the same on all screens,
623 * and Windows calls each display driver with the same pointer data: visible for
624 * the screen where the pointer is and invisible for other screens.
625 *
626 * This driver passes the shape to the host only from primary screen and makes
627 * the pointer always visible (in vbvaInitMousePointerShape).
628 *
629 * The simple solution makes it impossible to create the shape and keep the mouse
630 * pointer invisible. New shapes will be created visible.
631 * But:
632 * 1) VBox frontends actually ignore the visibility flag if VBOX_MOUSE_POINTER_SHAPE
633 * is set and always create new pointers visible.
634 * 2) Windows uses DrvMovePointer to hide the pointer, which will still work.
635 */
636
637 if (DeviceExtension->iDevice != PrimaryExtension->iDevice)
638 {
639 dprintf(("vboxUpdatePointerShape: ignore non primary device %d(%d)\n",
640 DeviceExtension->iDevice, PrimaryExtension->iDevice));
641 /* Success. */
642 return TRUE;
643 }
644#else
645 PDEVICE_EXTENSION PrimaryExtension = DeviceExtension;
646#endif
647
648 uint32_t cbData = 0;
649
650 if (!pointerAttr->Enable & VBOX_MOUSE_POINTER_SHAPE)
651 {
652 /* Size of the pointer data: sizeof (AND mask) + sizeof (XOR_MASK) */
653 cbData = ((((pointerAttr->Width + 7) / 8) * pointerAttr->Height + 3) & ~3)
654 + pointerAttr->Width * 4 * pointerAttr->Height;
655 }
656
657#ifndef DEBUG_misha
658 dprintf(("vboxUpdatePointerShape: cbData %d, %dx%d\n",
659 cbData, pointerAttr->Width, pointerAttr->Height));
660#endif
661
662 if (cbData > cbLength - sizeof(VIDEO_POINTER_ATTRIBUTES))
663 {
664 dprintf(("vboxUpdatePointerShape: calculated pointer data size is too big (%d bytes, limit %d)\n",
665 cbData, cbLength - sizeof(VIDEO_POINTER_ATTRIBUTES)));
666 return FALSE;
667 }
668
669 MOUSEPOINTERSHAPECTX ctx;
670
671 ctx.pPointerAttr = pointerAttr;
672 ctx.cbData = cbData;
673 ctx.i32Result = VERR_NOT_SUPPORTED;
674
675 int rc = vboxCallVBVA (commonFromDeviceExt(PrimaryExtension),
676 VBVA_MOUSE_POINTER_SHAPE,
677 sizeof (VBVAMOUSEPOINTERSHAPE) + cbData,
678 vbvaInitMousePointerShape,
679 vbvaFinalizeMousePointerShape,
680 &ctx);
681#ifndef DEBUG_misha
682 dprintf(("VBoxVideo::vboxMousePointerShape: rc %d, i32Result = %d\n", rc, ctx.i32Result));
683#endif
684
685 return RT_SUCCESS(rc) && RT_SUCCESS(ctx.i32Result);
686}
687
688#ifndef VBOX_WITH_WDDM
689typedef struct _VBVAMINIPORT_CHANNELCONTEXT
690{
691 PFNHGSMICHANNELHANDLER pfnChannelHandler;
692 void *pvChannelHandler;
693}VBVAMINIPORT_CHANNELCONTEXT;
694
695typedef struct _VBVADISP_CHANNELCONTEXT
696{
697 struct _VBVAHOSTCMD * pFirstCmd;
698 struct _VBVAHOSTCMD * pLastCmd;
699 VBOXVCMNSPIN_LOCK pSynchLock;
700#ifdef DEBUG
701 int cCmds;
702#endif
703 bool bValid;
704}VBVADISP_CHANNELCONTEXT;
705
706#ifdef DEBUG
707void dbgCheckListLocked(const VBVADISP_CHANNELCONTEXT *pList, struct _VBVAHOSTCMD * pCmd)
708{
709 int counter = 0;
710 for(struct _VBVAHOSTCMD * pCur = pList->pFirstCmd; pCur; pCur=pCur->u.pNext)
711 {
712 Assert(pCur != pCmd);
713 if(pCur == pList->pLastCmd)
714 {
715 Assert(pCur->u.pNext == NULL);
716 }
717 if(pCur->u.pNext == NULL)
718 {
719 Assert(pCur == pList->pLastCmd);
720 }
721 counter++;
722 }
723
724 Assert(counter == pList->cCmds);
725}
726
727void dbgCheckList(PDEVICE_EXTENSION PrimaryExtension, VBVADISP_CHANNELCONTEXT *pList, struct _VBVAHOSTCMD * pCmd)
728{
729 VBOXVCMNIRQL oldIrql;
730 VBoxVideoCmnSpinLockAcquire(PrimaryExtension, &pList->pSynchLock, &oldIrql);
731
732 dbgCheckListLocked(pList, pCmd);
733
734 VBoxVideoCmnSpinLockRelease(PrimaryExtension, &pList->pSynchLock, oldIrql);
735}
736
737#define DBG_CHECKLIST_LOCKED(_pl, pc) dbgCheckListLocked(_pl, pc)
738#define DBG_CHECKLIST(_pe, _pl, pc) dbgCheckList(_pe, _pl, pc)
739
740#else
741#define DBG_CHECKLIST_LOCKED(_pl, pc) do{}while(0)
742#define DBG_CHECKLIST(_pe, _pl, pc) do{}while(0)
743#endif
744
745
746typedef struct _VBVA_CHANNELCONTEXTS
747{
748 PDEVICE_EXTENSION PrimaryExtension;
749 uint32_t cUsed;
750 uint32_t cContexts;
751 VBVAMINIPORT_CHANNELCONTEXT mpContext;
752 VBVADISP_CHANNELCONTEXT aContexts[1];
753}VBVA_CHANNELCONTEXTS;
754
755static int vboxVBVADeleteChannelContexts(PDEVICE_EXTENSION PrimaryExtension, VBVA_CHANNELCONTEXTS * pContext)
756{
757 VBoxVideoCmnMemFree(PrimaryExtension,pContext);
758 return VINF_SUCCESS;
759}
760
761static int vboxVBVACreateChannelContexts(PDEVICE_EXTENSION PrimaryExtension, VBVA_CHANNELCONTEXTS ** ppContext)
762{
763 uint32_t cDisplays = (uint32_t)commonFromDeviceExt(PrimaryExtension)->cDisplays;
764 const size_t size = RT_OFFSETOF(VBVA_CHANNELCONTEXTS, aContexts[cDisplays]);
765 VBVA_CHANNELCONTEXTS * pContext = (VBVA_CHANNELCONTEXTS*)VBoxVideoCmnMemAllocNonPaged(PrimaryExtension, size, MEM_TAG);
766 if(pContext)
767 {
768 memset(pContext, 0, size);
769 pContext->cContexts = cDisplays;
770 pContext->PrimaryExtension = PrimaryExtension;
771 *ppContext = pContext;
772 return VINF_SUCCESS;
773 }
774 return VERR_GENERAL_FAILURE;
775}
776
777static VBVADISP_CHANNELCONTEXT* vboxVBVAFindHandlerInfo(VBVA_CHANNELCONTEXTS *pCallbacks, int iId)
778{
779 if(iId < 0)
780 {
781 return NULL;
782 }
783 else if(pCallbacks->cContexts > (uint32_t)iId)
784 {
785 return &pCallbacks->aContexts[iId];
786 }
787 return NULL;
788}
789
790DECLCALLBACK(void) hgsmiHostCmdComplete (HVBOXVIDEOHGSMI hHGSMI, struct _VBVAHOSTCMD * pCmd)
791{
792 PDEVICE_EXTENSION PrimaryExtension = ((PDEVICE_EXTENSION)hHGSMI)->pPrimary;
793 HGSMIHostCmdComplete (commonFromDeviceExt(PrimaryExtension), pCmd);
794}
795
796DECLCALLBACK(int) hgsmiHostCmdRequest (HVBOXVIDEOHGSMI hHGSMI, uint8_t u8Channel, struct _VBVAHOSTCMD ** ppCmd)
797{
798// if(display < 0)
799// return VERR_INVALID_PARAMETER;
800 if(!ppCmd)
801 return VERR_INVALID_PARAMETER;
802
803 PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)hHGSMI;
804 PDEVICE_EXTENSION PrimaryExtension = pDevExt->pPrimary;
805
806 /* pick up the host commands */
807 hgsmiProcessHostCommandQueue(commonFromDeviceExt(PrimaryExtension));
808
809 HGSMICHANNEL * pChannel = HGSMIChannelFindById (&commonFromDeviceExt(PrimaryExtension)->channels, u8Channel);
810 if(pChannel)
811 {
812 VBVA_CHANNELCONTEXTS * pContexts = (VBVA_CHANNELCONTEXTS *)pChannel->handler.pvHandler;
813 VBVADISP_CHANNELCONTEXT *pDispContext = vboxVBVAFindHandlerInfo(pContexts, pDevExt->iDevice);
814 Assert(pDispContext);
815 if(pDispContext)
816 {
817 UCHAR oldIrql;
818 VBoxVideoCmnSpinLockAcquire(PrimaryExtension, &pDispContext->pSynchLock, &oldIrql);
819
820 DBG_CHECKLIST_LOCKED(pDispContext, NULL);
821
822 *ppCmd = pDispContext->pFirstCmd;
823 pDispContext->pFirstCmd = NULL;
824 pDispContext->pLastCmd = NULL;
825#ifdef DEBUG
826 pDispContext->cCmds = 0;
827#endif
828 VBoxVideoCmnSpinLockRelease(PrimaryExtension, &pDispContext->pSynchLock, oldIrql);
829
830 DBG_CHECKLIST(PrimaryExtension, pDispContext, NULL);
831
832 return VINF_SUCCESS;
833 }
834 }
835
836 return VERR_INVALID_PARAMETER;
837}
838
839static DECLCALLBACK(int) vboxVBVAChannelGenericHandler(void *pvHandler, uint16_t u16ChannelInfo, void *pvBuffer, HGSMISIZE cbBuffer)
840{
841 VBVA_CHANNELCONTEXTS *pCallbacks = (VBVA_CHANNELCONTEXTS*)pvHandler;
842// Assert(0);
843 Assert(cbBuffer > VBVAHOSTCMD_HDRSIZE);
844 if(cbBuffer > VBVAHOSTCMD_HDRSIZE)
845 {
846 VBVAHOSTCMD *pHdr = (VBVAHOSTCMD*)pvBuffer;
847 Assert(pHdr->iDstID >= 0);
848 if(pHdr->iDstID >= 0)
849 {
850 VBVADISP_CHANNELCONTEXT* pHandler = vboxVBVAFindHandlerInfo(pCallbacks, pHdr->iDstID);
851 Assert(pHandler && pHandler->bValid);
852 if(pHandler && pHandler->bValid)
853 {
854 VBVAHOSTCMD *pFirst = NULL, *pLast = NULL;
855 for(VBVAHOSTCMD *pCur = pHdr; pCur; )
856 {
857 Assert(!pCur->u.Data);
858 Assert(!pFirst);
859 Assert(!pLast);
860
861 switch(u16ChannelInfo)
862 {
863 case VBVAHG_DISPLAY_CUSTOM:
864 {
865 if(pLast)
866 {
867 pLast->u.pNext = pCur;
868 pLast = pCur;
869 }
870 else
871 {
872 pFirst = pCur;
873 pLast = pCur;
874 }
875 Assert(!pCur->u.Data);
876 //TODO: use offset here
877 pCur = pCur->u.pNext;
878 Assert(!pCur);
879 Assert(pFirst);
880 Assert(pFirst == pLast);
881 break;
882 }
883 case VBVAHG_EVENT:
884 {
885 VBVAHOSTCMDEVENT *pEventCmd = VBVAHOSTCMD_BODY(pCur, VBVAHOSTCMDEVENT);
886#ifndef VBOX_WITH_WDDM
887 PEVENT pEvent = (PEVENT)pEventCmd->pEvent;
888 pCallbacks->PrimaryExtension->u.primary.VideoPortProcs.pfnSetEvent(
889 pCallbacks->PrimaryExtension,
890 pEvent);
891#else
892 PKEVENT pEvent = (PKEVENT)pEventCmd->pEvent;
893 KeSetEvent(pEvent, 0, FALSE);
894#endif
895 }
896 default:
897 {
898 DBG_CHECKLIST(pCallbacks->PrimaryExtension, pHandler, pCur);
899 Assert(u16ChannelInfo==VBVAHG_EVENT);
900 Assert(!pCur->u.Data);
901 //TODO: use offset here
902 if(pLast)
903 pLast->u.pNext = pCur->u.pNext;
904 VBVAHOSTCMD * pNext = pCur->u.pNext;
905 pCur->u.pNext = NULL;
906 HGSMIHostCmdComplete(commonFromDeviceExt(pCallbacks->PrimaryExtension), pCur);
907 pCur = pNext;
908 Assert(!pCur);
909 Assert(!pFirst);
910 Assert(pFirst == pLast);
911 break;
912 }
913 }
914 }
915
916 DBG_CHECKLIST(pCallbacks->PrimaryExtension, pHandler, pFirst);
917
918 /* we do not support lists currently */
919 Assert(pFirst == pLast);
920 if(pLast)
921 {
922 Assert(pLast->u.pNext == NULL);
923 }
924
925 if(pFirst)
926 {
927 Assert(pLast);
928 UCHAR oldIrql;
929 VBoxVideoCmnSpinLockAcquire(pCallbacks->PrimaryExtension,
930 &pHandler->pSynchLock,
931 &oldIrql);
932
933 DBG_CHECKLIST_LOCKED(pHandler, pFirst);
934
935 if(pHandler->pLastCmd)
936 {
937 pHandler->pLastCmd->u.pNext = pFirst;
938 Assert(pHandler->pFirstCmd);
939 }
940 else
941 {
942 Assert(!pHandler->pFirstCmd);
943 pHandler->pFirstCmd = pFirst;
944 }
945 pHandler->pLastCmd = pLast;
946#ifdef DEBUG
947 pHandler->cCmds++;
948#endif
949 DBG_CHECKLIST_LOCKED(pHandler, NULL);
950
951 VBoxVideoCmnSpinLockRelease(pCallbacks->PrimaryExtension,
952 &pHandler->pSynchLock,
953 oldIrql);
954 }
955 else
956 {
957 Assert(!pLast);
958 }
959 return VINF_SUCCESS;
960 }
961 }
962 else
963 {
964 //TODO: impl
965// HGSMIMINIPORT_CHANNELCONTEXT *pHandler = vboxVideoHGSMIFindHandler;
966// if(pHandler && pHandler->pfnChannelHandler)
967// {
968// pHandler->pfnChannelHandler(pHandler->pvChannelHandler, u16ChannelInfo, pHdr, cbBuffer);
969//
970// return VINF_SUCCESS;
971// }
972 }
973 }
974 /* no handlers were found, need to complete the command here */
975 HGSMIHostCmdComplete(commonFromDeviceExt(pCallbacks->PrimaryExtension), pvBuffer);
976 return VINF_SUCCESS;
977}
978
979static HGSMICHANNELHANDLER g_OldHandler;
980
981int vboxVBVAChannelDisplayEnable(PDEVICE_EXTENSION PrimaryExtension,
982 int iDisplay, /* negative would mean this is a miniport handler */
983 uint8_t u8Channel)
984{
985 VBVA_CHANNELCONTEXTS * pContexts;
986 HGSMICHANNEL * pChannel = HGSMIChannelFindById (&commonFromDeviceExt(PrimaryExtension)->channels, u8Channel);
987 if(!pChannel)
988 {
989 int rc = vboxVBVACreateChannelContexts(PrimaryExtension, &pContexts);
990 if(RT_FAILURE(rc))
991 {
992 return rc;
993 }
994 }
995 else
996 {
997 pContexts = (VBVA_CHANNELCONTEXTS *)pChannel->handler.pvHandler;
998 }
999
1000 VBVADISP_CHANNELCONTEXT *pDispContext = vboxVBVAFindHandlerInfo(pContexts, iDisplay);
1001 Assert(pDispContext);
1002 if(pDispContext)
1003 {
1004#ifdef DEBUGVHWASTRICT
1005 Assert(!pDispContext->bValid);
1006#endif
1007 Assert(!pDispContext->pFirstCmd);
1008 Assert(!pDispContext->pLastCmd);
1009 if(!pDispContext->bValid)
1010 {
1011 pDispContext->bValid = true;
1012 pDispContext->pFirstCmd = NULL;
1013 pDispContext->pLastCmd= NULL;
1014#ifdef DEBUG
1015 pDispContext->cCmds = 0;
1016#endif
1017
1018 VBoxVideoCmnSpinLockCreate(PrimaryExtension, &pDispContext->pSynchLock);
1019
1020 int rc = VINF_SUCCESS;
1021 if(!pChannel)
1022 {
1023 rc = HGSMIChannelRegister (&commonFromDeviceExt(PrimaryExtension)->channels,
1024 u8Channel,
1025 "VGA Miniport HGSMI channel",
1026 vboxVBVAChannelGenericHandler,
1027 pContexts,
1028 &g_OldHandler);
1029 }
1030
1031 if(RT_SUCCESS(rc))
1032 {
1033 pContexts->cUsed++;
1034 return VINF_SUCCESS;
1035 }
1036 }
1037 }
1038
1039 if(!pChannel)
1040 {
1041 vboxVBVADeleteChannelContexts(PrimaryExtension, pContexts);
1042 }
1043
1044 return VERR_GENERAL_FAILURE;
1045}
1046#endif /* !VBOX_WITH_WDDM */
1047
1048/** @todo Mouse pointer position to be read from VMMDev memory, address of the memory region
1049 * can be queried from VMMDev via an IOCTL. This VMMDev memory region will contain
1050 * host information which is needed by the guest.
1051 *
1052 * Reading will not cause a switch to the host.
1053 *
1054 * Have to take into account:
1055 * * synchronization: host must write to the memory only from EMT,
1056 * large structures must be read under flag, which tells the host
1057 * that the guest is currently reading the memory (OWNER flag?).
1058 * * guest writes: may be allocate a page for the host info and make
1059 * the page readonly for the guest.
1060 * * the information should be available only for additions drivers.
1061 * * VMMDev additions driver will inform the host which version of the info it expects,
1062 * host must support all versions.
1063 *
1064 */
1065
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette