VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/Graphics/Display/vbox.c@ 17610

Last change on this file since 17610 was 17610, checked in by vboxsync, 16 years ago

HGSMI for the graphics device: the windows guest display driver prototype code.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.6 KB
Line 
1/** @file
2 *
3 * VBoxGuest -- VirtualBox Win 2000/XP guest display driver
4 *
5 * VBox support functions.
6 *
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22#include "driver.h"
23
24#include <VBox/VBoxGuest.h>
25#include <VBox/err.h>
26#include <iprt/asm.h>
27
28/*
29 * There is a hardware ring buffer in the VBox VMMDev PCI memory space.
30 * All graphics commands go there serialized by vboxHwBufferBeginUpdate.
31 * and vboxHwBufferEndUpdate.
32 *
33 * off32Free is writing position. off32Data is reading position.
34 * off32Free == off32Data means buffer is empty.
35 * There must be always gap between off32Data and off32Free when data
36 * are in the buffer.
37 * Guest only changes off32Free, host changes off32Data.
38 */
39
40/* Forward declarations of internal functions. */
41static void vboxHwBufferFlush (PPDEV ppdev);
42static void vboxHwBufferPlaceDataAt (PPDEV ppdev, void *p, uint32_t cb, uint32_t offset);
43static BOOL vboxHwBufferWrite (PPDEV ppdev, const void *p, uint32_t cb);
44
45#ifndef VBOX_WITH_HGSMI
46/*
47 * Public hardware buffer methods.
48 */
49BOOL vboxVbvaEnable (PPDEV ppdev)
50{
51 BOOL bRc = FALSE;
52
53 ULONG returnedDataLength;
54 ULONG ulEnable = TRUE;
55
56 DISPDBG((1, "VBoxDisp::vboxVbvaEnable called\n"));
57
58 if (!ghsemHwBuffer)
59 {
60 return FALSE;
61 }
62
63 if (EngDeviceIoControl(ppdev->hDriver,
64 IOCTL_VIDEO_VBVA_ENABLE,
65 &ulEnable,
66 sizeof (ulEnable),
67 &ppdev->vbva,
68 sizeof (ppdev->vbva),
69 &returnedDataLength) == 0)
70 {
71 DISPDBG((1, "VBoxDisp::vboxVbvaEnable: vbva: pVbvaMemory = %p, pfnFlush = %p, pvFlush = %p.\n",
72 ppdev->vbva.pVbvaMemory, ppdev->vbva.pfnFlush, ppdev->vbva.pvFlush));
73
74 if (ppdev->vbva.pVbvaMemory
75 && ppdev->vbva.pfnFlush
76 && ppdev->vbva.pvFlush)
77 {
78 ppdev->fHwBufferOverflow = FALSE;
79 ppdev->pRecord = NULL;
80
81 /* All have been initialized. */
82 bRc = TRUE;
83 }
84 }
85
86 if (!bRc)
87 {
88 vboxVbvaDisable (ppdev);
89 }
90
91 return bRc;
92}
93
94void vboxVbvaDisable (PPDEV ppdev)
95{
96 DISPDBG((1, "VBoxDisp::vbvaDisable called.\n"));
97
98 RtlZeroMemory (&ppdev->vbva, sizeof (ppdev->vbva));
99
100 ppdev->fHwBufferOverflow = FALSE;
101 ppdev->pRecord = NULL;
102
103 return;
104}
105
106BOOL vboxHwBufferBeginUpdate (PPDEV ppdev)
107{
108 BOOL bRc = FALSE;
109
110 VBVAMEMORY *pVbvaMemory = ppdev->vbva.pVbvaMemory;
111
112 DISPDBG((1, "VBoxDisp::vboxHwBufferBeginUpdate called flags = 0x%08X\n", pVbvaMemory? pVbvaMemory->fu32ModeFlags: -1));
113
114 if ( pVbvaMemory
115 && (pVbvaMemory->fu32ModeFlags & VBVA_F_MODE_ENABLED))
116 {
117 uint32_t indexRecordNext;
118
119 EngAcquireSemaphore (ghsemHwBuffer);
120
121 VBVA_ASSERT (!ppdev->fHwBufferOverflow);
122 VBVA_ASSERT (ppdev->pRecord == NULL);
123
124 indexRecordNext = (pVbvaMemory->indexRecordFree + 1) % VBVA_MAX_RECORDS;
125
126 if (indexRecordNext == pVbvaMemory->indexRecordFirst)
127 {
128 /* All slots in the records queue are used. */
129 vboxHwBufferFlush (ppdev);
130 }
131
132 if (indexRecordNext == pVbvaMemory->indexRecordFirst)
133 {
134 /* Even after flush there is no place. Fail the request. */
135 DISPDBG((1, "VBoxDisp::vboxHwBufferBeginUpdate no space in the queue of records!!! first %d, last %d\n",
136 pVbvaMemory->indexRecordFirst, pVbvaMemory->indexRecordFree));
137 EngReleaseSemaphore (ghsemHwBuffer);
138 }
139 else
140 {
141 /* Initialize the record. */
142 VBVARECORD *pRecord = &pVbvaMemory->aRecords[pVbvaMemory->indexRecordFree];
143
144 pRecord->cbRecord = VBVA_F_RECORD_PARTIAL;
145
146 pVbvaMemory->indexRecordFree = indexRecordNext;
147
148 DISPDBG((1, "VBoxDisp::vboxHwBufferBeginUpdate indexRecordNext = %d\n", indexRecordNext));
149
150 /* Remember which record we are using. */
151 ppdev->pRecord = pRecord;
152
153 bRc = TRUE;
154 }
155 }
156
157 return bRc;
158}
159
160void vboxHwBufferEndUpdate (PPDEV ppdev)
161{
162 VBVAMEMORY *pVbvaMemory;
163 VBVARECORD *pRecord;
164
165 DISPDBG((1, "VBoxDisp::vboxHwBufferEndUpdate called\n"));
166
167 pVbvaMemory = ppdev->vbva.pVbvaMemory;
168 VBVA_ASSERT(pVbvaMemory);
169
170 pRecord = ppdev->pRecord;
171 VBVA_ASSERT (pRecord && (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL));
172
173 /* Mark the record completed. */
174 pRecord->cbRecord &= ~VBVA_F_RECORD_PARTIAL;
175
176 ppdev->fHwBufferOverflow = FALSE;
177 ppdev->pRecord = NULL;
178
179 EngReleaseSemaphore (ghsemHwBuffer);
180
181 return;
182}
183
184/*
185 * Private operations.
186 */
187static uint32_t vboxHwBufferAvail (VBVAMEMORY *pVbvaMemory)
188{
189 int32_t i32Diff = pVbvaMemory->off32Data - pVbvaMemory->off32Free;
190
191 return i32Diff > 0? i32Diff: VBVA_RING_BUFFER_SIZE + i32Diff;
192}
193
194static void vboxHwBufferFlush (PPDEV ppdev)
195{
196 VBVAMEMORY *pVbvaMemory = ppdev->vbva.pVbvaMemory;
197
198 VBVA_ASSERT (pVbvaMemory);
199
200 ppdev->vbva.pfnFlush (ppdev->vbva.pvFlush);
201
202 return;
203}
204
205static void vboxHwBufferPlaceDataAt (PPDEV ppdev, void *p, uint32_t cb, uint32_t offset)
206{
207 VBVAMEMORY *pVbvaMemory = ppdev->vbva.pVbvaMemory;
208
209 uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - offset;
210 uint8_t *dst = &pVbvaMemory->au8RingBuffer[offset];
211 int32_t i32Diff = cb - u32BytesTillBoundary;
212
213 if (i32Diff <= 0)
214 {
215 /* Chunk will not cross buffer boundary. */
216 memcpy (dst, p, cb);
217 }
218 else
219 {
220 /* Chunk crosses buffer boundary. */
221 memcpy (dst, p, u32BytesTillBoundary);
222 memcpy (&pVbvaMemory->au8RingBuffer[0], (uint8_t *)p + u32BytesTillBoundary, i32Diff);
223 }
224
225 return;
226}
227
228static BOOL vboxHwBufferWrite (PPDEV ppdev, const void *p, uint32_t cb)
229{
230 VBVAMEMORY *pVbvaMemory;
231 VBVARECORD *pRecord;
232 uint32_t cbHwBufferAvail;
233
234 uint32_t cbWritten = 0;
235
236 VBVA_ASSERT(ppdev);
237
238 if (ppdev->fHwBufferOverflow)
239 {
240 return FALSE;
241 }
242
243 pVbvaMemory = ppdev->vbva.pVbvaMemory;
244 VBVA_ASSERT (pVbvaMemory->indexRecordFirst != pVbvaMemory->indexRecordFree);
245
246 pRecord = ppdev->pRecord;
247 VBVA_ASSERT (pRecord && (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL));
248
249 DISPDBG((1, "VW %d\n", cb));
250
251 cbHwBufferAvail = vboxHwBufferAvail (pVbvaMemory);
252
253 while (cb > 0)
254 {
255 uint32_t cbChunk = cb;
256
257// DISPDBG((1, "VBoxDisp::vboxHwBufferWrite pVbvaMemory->off32Free %d, pRecord->cbRecord 0x%08X, cbHwBufferAvail %d, cb %d, cbWritten %d\n", pVbvaMemory->off32Free, pRecord->cbRecord, cbHwBufferAvail, cb, cbWritten));
258
259 if (cbChunk >= cbHwBufferAvail)
260 {
261 DISPDBG((1, "VBoxDisp::vboxHwBufferWrite 1) avail %d, chunk %d\n", cbHwBufferAvail, cbChunk));
262
263 vboxHwBufferFlush (ppdev);
264
265 cbHwBufferAvail = vboxHwBufferAvail (pVbvaMemory);
266
267 if (cbChunk >= cbHwBufferAvail)
268 {
269 DISPDBG((1, "VBoxDisp::vboxHwBufferWrite: no place for %d bytes. Only %d bytes available after flush. Going to partial writes.\n", cb, cbHwBufferAvail));
270
271 if (cbHwBufferAvail <= VBVA_RING_BUFFER_THRESHOLD)
272 {
273 DISPDBG((1, "VBoxDisp::vboxHwBufferWrite: Buffer overflow!!!\n"));
274 ppdev->fHwBufferOverflow = TRUE;
275 VBVA_ASSERT(FALSE);
276 return FALSE;
277 }
278
279 cbChunk = cbHwBufferAvail - VBVA_RING_BUFFER_THRESHOLD;
280 }
281 }
282
283 VBVA_ASSERT(cbChunk <= cb);
284 VBVA_ASSERT(cbChunk <= vboxHwBufferAvail (pVbvaMemory));
285
286 vboxHwBufferPlaceDataAt (ppdev, (uint8_t *)p + cbWritten, cbChunk, pVbvaMemory->off32Free);
287
288 pVbvaMemory->off32Free = (pVbvaMemory->off32Free + cbChunk) % VBVA_RING_BUFFER_SIZE;
289 pRecord->cbRecord += cbChunk;
290 cbHwBufferAvail -= cbChunk;
291
292 cb -= cbChunk;
293 cbWritten += cbChunk;
294 }
295
296 return TRUE;
297}
298
299/*
300 * Public writer to hardware buffer.
301 */
302BOOL vboxWrite (PPDEV ppdev, const void *pv, uint32_t cb)
303{
304 return vboxHwBufferWrite (ppdev, pv, cb);
305}
306
307BOOL vboxOrderSupported (PPDEV ppdev, unsigned code)
308{
309 VBVAMEMORY *pVbvaMemory;
310
311 pVbvaMemory = ppdev->vbva.pVbvaMemory;
312
313 if (pVbvaMemory->fu32ModeFlags & VBVA_F_MODE_VRDP_ORDER_MASK)
314 {
315 /* Order masking enabled. */
316 if (pVbvaMemory->fu32SupportedOrders & (1 << code))
317 {
318 return TRUE;
319 }
320 }
321
322 return FALSE;
323}
324
325void VBoxProcessDisplayInfo(PPDEV ppdev)
326{
327 DWORD returnedDataLength;
328
329 DISPDBG((1, "Process: %d,%d\n", ppdev->ptlDevOrg.x, ppdev->ptlDevOrg.y));
330
331 EngDeviceIoControl(ppdev->hDriver,
332 IOCTL_VIDEO_INTERPRET_DISPLAY_MEMORY,
333 NULL,
334 0,
335 NULL,
336 0,
337 &returnedDataLength);
338}
339
340#else /* VBOX_WITH_HGSMI */
341
342/*
343 * Public hardware buffer methods.
344 */
345BOOL vboxVbvaEnable (PPDEV ppdev)
346{
347 BOOL bRc = FALSE;
348
349 DISPDBG((1, "VBoxDisp::vboxVbvaEnable called\n"));
350
351 if (ppdev->bHGSMISupported)
352 {
353 VBVABUFFER *pVBVA = (VBVABUFFER *)((uint8_t *)ppdev->pjScreen + ppdev->layout.offVBVABuffer);
354
355 pVBVA->u32HostEvents = 0;
356 pVBVA->u32SupportedOrders = 0;
357 pVBVA->off32Data;
358 pVBVA->off32Free;
359 RtlZeroMemory (pVBVA->aRecords, sizeof (pVBVA->aRecords));
360 pVBVA->indexRecordFirst;
361 pVBVA->indexRecordFree;
362 pVBVA->cbPartialWriteThreshold;
363 pVBVA->cbData = ppdev->layout.cbVBVABuffer - sizeof (VBVABUFFER) + sizeof (pVBVA->au8Data);
364
365 ppdev->fHwBufferOverflow = FALSE;
366 ppdev->pRecord = NULL;
367 ppdev->pVBVA = pVBVA;
368
369#if 0
370 /* @todo inform host that VBVA mode has been entered. */
371 bRC = vboxVBVAInformHost (ppdev);
372#else
373 /* All have been initialized. */
374 bRc = TRUE;
375#endif
376 }
377
378 if (!bRc)
379 {
380 vboxVbvaDisable (ppdev);
381 }
382
383 return bRc;
384}
385
386void vboxVbvaDisable (PPDEV ppdev)
387{
388 DISPDBG((1, "VBoxDisp::vbvaDisable called.\n"));
389
390 ppdev->fHwBufferOverflow = FALSE;
391 ppdev->pRecord = NULL;
392 ppdev->pVBVA = NULL;
393
394 return;
395}
396
397BOOL vboxHwBufferBeginUpdate (PPDEV ppdev)
398{
399 BOOL bRc = FALSE;
400
401 DISPDBG((1, "VBoxDisp::vboxHwBufferBeginUpdate called flags = 0x%08X\n",
402 ppdev->pVBVA? ppdev->pVBVA->u32HostEvents: -1));
403
404 if ( ppdev->pVBVA
405 && (ppdev->pVBVA->u32HostEvents & VBVA_F_MODE_ENABLED))
406 {
407 uint32_t indexRecordNext;
408
409 VBVA_ASSERT (!ppdev->fHwBufferOverflow);
410 VBVA_ASSERT (ppdev->pRecord == NULL);
411
412 indexRecordNext = (ppdev->pVBVA->indexRecordFree + 1) % VBVA_MAX_RECORDS;
413
414 if (indexRecordNext == ppdev->pVBVA->indexRecordFirst)
415 {
416 /* All slots in the records queue are used. */
417 vboxHwBufferFlush (ppdev);
418 }
419
420 if (indexRecordNext == ppdev->pVBVA->indexRecordFirst)
421 {
422 /* Even after flush there is no place. Fail the request. */
423 DISPDBG((1, "VBoxDisp::vboxHwBufferBeginUpdate no space in the queue of records!!! first %d, last %d\n",
424 ppdev->pVBVA->indexRecordFirst, ppdev->pVBVA->indexRecordFree));
425 }
426 else
427 {
428 /* Initialize the record. */
429 VBVARECORD *pRecord = &ppdev->pVBVA->aRecords[ppdev->pVBVA->indexRecordFree];
430
431 pRecord->cbRecord = VBVA_F_RECORD_PARTIAL;
432
433 ppdev->pVBVA->indexRecordFree = indexRecordNext;
434
435 DISPDBG((1, "VBoxDisp::vboxHwBufferBeginUpdate indexRecordNext = %d\n", indexRecordNext));
436
437 /* Remember which record we are using. */
438 ppdev->pRecord = pRecord;
439
440 bRc = TRUE;
441 }
442 }
443
444 return bRc;
445}
446
447void vboxHwBufferEndUpdate (PPDEV ppdev)
448{
449 VBVARECORD *pRecord;
450
451 DISPDBG((1, "VBoxDisp::vboxHwBufferEndUpdate called\n"));
452
453 VBVA_ASSERT(ppdev->pVBVA);
454
455 pRecord = ppdev->pRecord;
456 VBVA_ASSERT (pRecord && (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL));
457
458 /* Mark the record completed. */
459 pRecord->cbRecord &= ~VBVA_F_RECORD_PARTIAL;
460
461 ppdev->fHwBufferOverflow = FALSE;
462 ppdev->pRecord = NULL;
463
464 return;
465}
466
467/*
468 * Private operations.
469 */
470static uint32_t vboxHwBufferAvail (const VBVABUFFER *pVBVA)
471{
472 int32_t i32Diff = pVBVA->off32Data - pVBVA->off32Free;
473
474 return i32Diff > 0? i32Diff: pVBVA->cbData + i32Diff;
475}
476
477static void vboxHwBufferFlush (PPDEV ppdev)
478{
479 VBVA_ASSERT (ppdev->pVBVA);
480
481#if 0
482 /* @todo issue the flush command */
483 void *p = HGSMIHeapAlloc (&ppdev->hgsmiDisplayHeap,
484 sizeof (VBVA_FLUSH),
485 HGSMI_CH_VBVA,
486 HGSMI_CC_VBVA_FLUSH);
487........
488 if (!p)
489 {
490 DISPDBG((0, "VBoxDISP::vboxInitVBoxVideo: HGSMIHeapAlloc failed\n"));
491 rc = VERR_NO_MEMORY;
492 }
493 else
494 {
495 HGSMIOFFSET offBuffer = HGSMIHeapBufferOffset (&ppdev->hgsmiDisplayHeap,
496 p);
497
498 ((HGSMI_BUFFER_LOCATION *)p)->offLocation = ppdev->layout.offDisplayInformation;
499 ((HGSMI_BUFFER_LOCATION *)p)->cbLocation = sizeof (HGSMIHOSTFLAGS);
500
501 /* Submit the buffer to the host. */
502 ASMOutU16 (VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_VBVA_GUEST);
503 ASMOutU32 (VBE_DISPI_IOPORT_DATA, offBuffer);
504
505 HGSMIHeapFree (&ppdev->hgsmiDisplayHeap, p);
506 }
507#endif
508
509 return;
510}
511
512static void vboxHwBufferPlaceDataAt (PPDEV ppdev, const void *p, uint32_t cb, uint32_t offset)
513{
514 VBVABUFFER *pVBVA = ppdev->pVBVA;
515 uint32_t u32BytesTillBoundary = pVBVA->cbData - offset;
516 uint8_t *dst = &pVBVA->au8Data[offset];
517 int32_t i32Diff = cb - u32BytesTillBoundary;
518
519 if (i32Diff <= 0)
520 {
521 /* Chunk will not cross buffer boundary. */
522 memcpy (dst, p, cb);
523 }
524 else
525 {
526 /* Chunk crosses buffer boundary. */
527 memcpy (dst, p, u32BytesTillBoundary);
528 memcpy (&pVBVA->au8Data[0], (uint8_t *)p + u32BytesTillBoundary, i32Diff);
529 }
530
531 return;
532}
533
534static BOOL vboxHwBufferWrite (PPDEV ppdev, const void *p, uint32_t cb)
535{
536 VBVARECORD *pRecord;
537 uint32_t cbHwBufferAvail;
538
539 uint32_t cbWritten = 0;
540
541 VBVABUFFER *pVBVA = ppdev->pVBVA;
542 VBVA_ASSERT(pVBVA);
543
544 if (!pVBVA || ppdev->fHwBufferOverflow)
545 {
546 return FALSE;
547 }
548
549 VBVA_ASSERT (pVBVA->indexRecordFirst != pVBVA->indexRecordFree);
550
551 pRecord = ppdev->pRecord;
552 VBVA_ASSERT (pRecord && (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL));
553
554 DISPDBG((1, "VW %d\n", cb));
555
556 cbHwBufferAvail = vboxHwBufferAvail (pVBVA);
557
558 while (cb > 0)
559 {
560 uint32_t cbChunk = cb;
561
562 DISPDBG((1, "VBoxDisp::vboxHwBufferWrite pVBVA->off32Free %d, pRecord->cbRecord 0x%08X, cbHwBufferAvail %d, cb %d, cbWritten %d\n",
563 pVBVA->off32Free, pRecord->cbRecord, cbHwBufferAvail, cb, cbWritten));
564
565 if (cbChunk >= cbHwBufferAvail)
566 {
567 DISPDBG((1, "VBoxDisp::vboxHwBufferWrite 1) avail %d, chunk %d\n", cbHwBufferAvail, cbChunk));
568
569 vboxHwBufferFlush (ppdev);
570
571 cbHwBufferAvail = vboxHwBufferAvail (pVBVA);
572
573 if (cbChunk >= cbHwBufferAvail)
574 {
575 DISPDBG((1, "VBoxDisp::vboxHwBufferWrite: no place for %d bytes. Only %d bytes available after flush. Going to partial writes.\n",
576 cb, cbHwBufferAvail));
577
578 if (cbHwBufferAvail <= pVBVA->cbPartialWriteThreshold)
579 {
580 DISPDBG((1, "VBoxDisp::vboxHwBufferWrite: Buffer overflow!!!\n"));
581 ppdev->fHwBufferOverflow = TRUE;
582 VBVA_ASSERT(FALSE);
583 return FALSE;
584 }
585
586 cbChunk = cbHwBufferAvail - pVBVA->cbPartialWriteThreshold;
587 }
588 }
589
590 VBVA_ASSERT(cbChunk <= cb);
591 VBVA_ASSERT(cbChunk <= vboxHwBufferAvail (pVBVA));
592
593 vboxHwBufferPlaceDataAt (ppdev, (uint8_t *)p + cbWritten, cbChunk, pVBVA->off32Free);
594
595 pVBVA->off32Free = (pVBVA->off32Free + cbChunk) % pVBVA->cbData;
596 pRecord->cbRecord += cbChunk;
597 cbHwBufferAvail -= cbChunk;
598
599 cb -= cbChunk;
600 cbWritten += cbChunk;
601 }
602
603 return TRUE;
604}
605
606/*
607 * Public writer to the hardware buffer.
608 */
609BOOL vboxWrite (PPDEV ppdev, const void *pv, uint32_t cb)
610{
611 return vboxHwBufferWrite (ppdev, pv, cb);
612}
613
614BOOL vboxOrderSupported (PPDEV ppdev, unsigned code)
615{
616 VBVABUFFER *pVBVA = ppdev->pVBVA;
617
618 if (!pVBVA)
619 {
620 return FALSE;
621 }
622
623 if (pVBVA->u32SupportedOrders & (1 << code))
624 {
625 return TRUE;
626 }
627
628 return FALSE;
629}
630
631void VBoxProcessDisplayInfo(PPDEV ppdev)
632{
633#if 0
634 DWORD returnedDataLength;
635
636 DISPDBG((1, "Process: %d,%d\n", ppdev->ptlDevOrg.x, ppdev->ptlDevOrg.y));
637
638 EngDeviceIoControl(ppdev->hDriver,
639 IOCTL_VIDEO_INTERPRET_DISPLAY_MEMORY,
640 NULL,
641 0,
642 NULL,
643 0,
644 &returnedDataLength);
645#endif
646}
647#endif /* VBOX_WITH_HGSMI */
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