VirtualBox

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

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

Additions/WINNT/Graphics: fix to r67612

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