VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/Graphics/Video/mp/xpdm/VBoxMPInternal.cpp@ 40566

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

Ran the source code massager (scm).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.9 KB
Line 
1/* $Id: VBoxMPInternal.cpp 37423 2011-06-12 18:37:56Z vboxsync $ */
2
3/** @file
4 * VBox XPDM Miniport internal functions
5 */
6
7/*
8 * Copyright (C) 2011 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include "VBoxMPInternal.h"
20#include <VBox/VBoxVideo.h>
21#include <VBox/VBoxGuestLib.h>
22#include <iprt/asm.h>
23
24typedef struct _VBVAMINIPORT_CHANNELCONTEXT
25{
26 PFNHGSMICHANNELHANDLER pfnChannelHandler;
27 void *pvChannelHandler;
28} VBVAMINIPORT_CHANNELCONTEXT;
29
30typedef struct _VBVADISP_CHANNELCONTEXT
31{
32 /** The generic command handler builds up a list of commands - in reverse
33 * order! - here */
34 VBVAHOSTCMD *pCmd;
35 bool bValid;
36} VBVADISP_CHANNELCONTEXT;
37
38typedef struct _VBVA_CHANNELCONTEXTS
39{
40 PVBOXMP_COMMON pCommon;
41 uint32_t cUsed;
42 uint32_t cContexts;
43 VBVAMINIPORT_CHANNELCONTEXT mpContext;
44 VBVADISP_CHANNELCONTEXT aContexts[1];
45} VBVA_CHANNELCONTEXTS;
46
47/* Computes the size of a framebuffer. DualView has a few framebuffers of the computed size. */
48static void VBoxComputeFrameBufferSizes(PVBOXMP_DEVEXT pPrimaryExt)
49{
50 PVBOXMP_COMMON pCommon = VBoxCommonFromDeviceExt(pPrimaryExt);
51
52 ULONG ulAvailable = pCommon->cbVRAM - pCommon->cbMiniportHeap - VBVA_ADAPTER_INFORMATION_SIZE;
53 /* Size of a framebuffer. */
54 ULONG ulSize = ulAvailable / pCommon->cDisplays;
55 /* Align down to 4096 bytes. */
56 ulSize &= ~0xFFF;
57
58 LOG(("cbVRAM = 0x%08X, cDisplays = %d, ulSize = 0x%08X, ulSize * cDisplays = 0x%08X, slack = 0x%08X",
59 pCommon->cbVRAM, pCommon->cDisplays,
60 ulSize, ulSize * pCommon->cDisplays,
61 ulAvailable - ulSize * pCommon->cDisplays));
62
63 /* Update the primary info. */
64 pPrimaryExt->u.primary.ulMaxFrameBufferSize = ulSize;
65
66 /* Update the per extension info. */
67 PVBOXMP_DEVEXT pExt = pPrimaryExt;
68 ULONG ulFrameBufferOffset = 0;
69 while (pExt)
70 {
71 pExt->ulFrameBufferOffset = ulFrameBufferOffset;
72 /* That is assigned when a video mode is set. */
73 pExt->ulFrameBufferSize = 0;
74
75 LOG(("[%d] ulFrameBufferOffset 0x%08X", pExt->iDevice, ulFrameBufferOffset));
76
77 ulFrameBufferOffset += pPrimaryExt->u.primary.ulMaxFrameBufferSize;
78
79 pExt = pExt->pNext;
80 }
81}
82
83DECLCALLBACK(int) VBoxVbvaInitInfoDisplayCB(void *pvData, struct VBVAINFOVIEW *p, uint32_t cViews)
84{
85 PVBOXMP_DEVEXT pExt, pPrimaryExt = (PVBOXMP_DEVEXT) pvData;
86 unsigned i;
87
88 for (i=0, pExt=pPrimaryExt; i<cViews && pExt; i++, pExt=pExt->pNext)
89 {
90 p[i].u32ViewIndex = pExt->iDevice;
91 p[i].u32ViewOffset = pExt->ulFrameBufferOffset;
92 p[i].u32ViewSize = pPrimaryExt->u.primary.ulMaxFrameBufferSize;
93
94 /* How much VRAM should be reserved for the guest drivers to use VBVA. */
95 const uint32_t cbReservedVRAM = VBVA_DISPLAY_INFORMATION_SIZE + VBVA_MIN_BUFFER_SIZE;
96
97 p[i].u32MaxScreenSize = p[i].u32ViewSize > cbReservedVRAM?
98 p[i].u32ViewSize - cbReservedVRAM:
99 0;
100 }
101
102 if (i == VBoxCommonFromDeviceExt(pPrimaryExt)->cDisplays && pExt == NULL)
103 {
104 return VINF_SUCCESS;
105 }
106
107 AssertFailed ();
108 return VERR_INTERNAL_ERROR;
109}
110
111void VBoxCreateDisplays(PVBOXMP_DEVEXT pExt, PVIDEO_PORT_CONFIG_INFO pConfigInfo)
112{
113 LOGF_ENTER();
114
115 PVBOXMP_COMMON pCommon = VBoxCommonFromDeviceExt(pExt);
116 VBOXVIDEOPORTPROCS *pAPI = &pExt->u.primary.VideoPortProcs;
117
118 if (pCommon->bHGSMI)
119 {
120 if (pAPI->fSupportedTypes & VBOXVIDEOPORTPROCS_CSD)
121 {
122 PVBOXMP_DEVEXT pPrev = pExt;
123 ULONG iDisplay, cDisplays;
124
125 cDisplays = pCommon->cDisplays;
126 pCommon->cDisplays = 1;
127
128 for (iDisplay=1; iDisplay<cDisplays; ++iDisplay)
129 {
130 PVBOXMP_DEVEXT pSExt = NULL;
131 VP_STATUS rc;
132
133 rc = pAPI->pfnCreateSecondaryDisplay(pExt, (PVOID*)&pSExt, VIDEO_DUALVIEW_REMOVABLE);
134 VBOXMP_WARN_VPS(rc);
135
136 if (rc != NO_ERROR)
137 {
138 break;
139 }
140 LOG(("created secondary device %p", pSExt));
141
142 pSExt->pNext = NULL;
143 pSExt->pPrimary = pExt;
144 pSExt->iDevice = iDisplay;
145 pSExt->ulFrameBufferOffset = 0;
146 pSExt->ulFrameBufferSize = 0;
147 pSExt->u.secondary.bEnabled = FALSE;
148
149 /* Update the list pointers */
150 pPrev->pNext = pSExt;
151 pPrev = pSExt;
152
153 /* Take the successfully created display into account. */
154 pCommon->cDisplays++;
155 }
156 }
157 else
158 {
159 /* Even though VM could be configured to have multiply monitors,
160 * we can't support it on this windows version.
161 */
162 pCommon->cDisplays = 1;
163 }
164 }
165
166 /* Now when the number of monitors is known and extensions are created,
167 * calculate the layout of framebuffers.
168 */
169 VBoxComputeFrameBufferSizes(pExt);
170
171 /*Report our screen configuration to host*/
172 if (pCommon->bHGSMI)
173 {
174 int rc;
175 rc = VBoxHGSMISendViewInfo(&pCommon->guestCtx, pCommon->cDisplays, VBoxVbvaInitInfoDisplayCB, (void *) pExt);
176
177 if (RT_FAILURE (rc))
178 {
179 WARN(("VBoxHGSMISendViewInfo failed with rc=%#x, HGSMI disabled", rc));
180 pCommon->bHGSMI = FALSE;
181 }
182 }
183
184 LOGF_LEAVE();
185}
186
187static DECLCALLBACK(void) VBoxVbvaFlush(void *pvFlush)
188{
189 LOGF_ENTER();
190
191 PVBOXMP_DEVEXT pExt = (PVBOXMP_DEVEXT)pvFlush;
192 PVBOXMP_DEVEXT pPrimary = pExt? pExt->pPrimary: NULL;
193
194 if (pPrimary)
195 {
196 VMMDevVideoAccelFlush *req = (VMMDevVideoAccelFlush *)pPrimary->u.primary.pvReqFlush;
197
198 if (req)
199 {
200 int rc = VbglGRPerform (&req->header);
201
202 if (RT_FAILURE(rc))
203 {
204 WARN(("rc = %#xrc!", rc));
205 }
206 }
207 }
208 LOGF_LEAVE();
209}
210
211int VBoxVbvaEnable(PVBOXMP_DEVEXT pExt, BOOLEAN bEnable, VBVAENABLERESULT *pResult)
212{
213 int rc = VINF_SUCCESS;
214 LOGF_ENTER();
215
216 VMMDevMemory *pVMMDevMemory = NULL;
217
218 rc = VbglQueryVMMDevMemory (&pVMMDevMemory);
219 if (RT_FAILURE(rc))
220 {
221 WARN(("VbglQueryVMMDevMemory rc = %#xrc", rc));
222 LOGF_LEAVE();
223 return rc;
224 }
225
226 if (pExt->iDevice>0)
227 {
228 PVBOXMP_DEVEXT pPrimary = pExt->pPrimary;
229 LOGF(("skipping non-primary display %d", pExt->iDevice));
230
231 if (bEnable && pPrimary->u.primary.ulVbvaEnabled && pVMMDevMemory)
232 {
233 pResult->pVbvaMemory = &pVMMDevMemory->vbvaMemory;
234 pResult->pfnFlush = VBoxVbvaFlush;
235 pResult->pvFlush = pExt;
236 }
237 else
238 {
239 VideoPortZeroMemory(&pResult, sizeof(VBVAENABLERESULT));
240 }
241
242 LOGF_LEAVE();
243 return rc;
244 }
245
246 /* Allocate the memory block for VMMDevReq_VideoAccelFlush request. */
247 if (pExt->u.primary.pvReqFlush == NULL)
248 {
249 VMMDevVideoAccelFlush *req = NULL;
250
251 rc = VbglGRAlloc((VMMDevRequestHeader **)&req, sizeof(VMMDevVideoAccelFlush), VMMDevReq_VideoAccelFlush);
252
253 if (RT_SUCCESS(rc))
254 {
255 pExt->u.primary.pvReqFlush = req;
256 }
257 else
258 {
259 WARN(("VbglGRAlloc(VMMDevVideoAccelFlush) rc = %#xrc", rc));
260 LOGF_LEAVE();
261 return rc;
262 }
263 }
264
265 ULONG ulEnabled = 0;
266
267 VMMDevVideoAccelEnable *req = NULL;
268 rc = VbglGRAlloc((VMMDevRequestHeader **)&req, sizeof(VMMDevVideoAccelEnable), VMMDevReq_VideoAccelEnable);
269
270 if (RT_SUCCESS(rc))
271 {
272 req->u32Enable = bEnable;
273 req->cbRingBuffer = VBVA_RING_BUFFER_SIZE;
274 req->fu32Status = 0;
275
276 rc = VbglGRPerform(&req->header);
277 if (RT_SUCCESS(rc))
278 {
279 if (req->fu32Status & VBVA_F_STATUS_ACCEPTED)
280 {
281 LOG(("accepted"));
282
283 /* Initialize the result information and VBVA memory. */
284 if (req->fu32Status & VBVA_F_STATUS_ENABLED)
285 {
286 pResult->pVbvaMemory = &pVMMDevMemory->vbvaMemory;
287 pResult->pfnFlush = VBoxVbvaFlush;
288 pResult->pvFlush = pExt;
289 ulEnabled = 1;
290 }
291 else
292 {
293 VideoPortZeroMemory(&pResult, sizeof(VBVAENABLERESULT));
294 }
295 }
296 else
297 {
298 LOG(("rejected"));
299
300 /* Disable VBVA for old hosts. */
301 req->u32Enable = 0;
302 req->cbRingBuffer = VBVA_RING_BUFFER_SIZE;
303 req->fu32Status = 0;
304
305 VbglGRPerform(&req->header);
306
307 rc = VERR_NOT_SUPPORTED;
308 }
309 }
310 else
311 {
312 WARN(("rc = %#xrc", rc));
313 }
314
315 VbglGRFree(&req->header);
316 }
317 else
318 {
319 WARN(("VbglGRAlloc(VMMDevVideoAccelEnable) rc = %#xrc", rc));
320 }
321
322 pExt->u.primary.ulVbvaEnabled = ulEnabled;
323
324 LOGF_LEAVE();
325 return rc;
326}
327
328static VBVADISP_CHANNELCONTEXT* VBoxVbvaFindHandlerInfo(VBVA_CHANNELCONTEXTS *pCallbacks, int iId)
329{
330 if (iId < 0)
331 {
332 return NULL;
333 }
334 else if(pCallbacks->cContexts > (uint32_t)iId)
335 {
336 return &pCallbacks->aContexts[iId];
337 }
338 return NULL;
339}
340
341/* Reverses a NULL-terminated linked list of VBVAHOSTCMD structures. */
342static VBVAHOSTCMD *VBoxVbvaReverseList(VBVAHOSTCMD *pList)
343{
344 VBVAHOSTCMD *pFirst = NULL;
345 while (pList)
346 {
347 VBVAHOSTCMD *pNext = pList;
348 pList = pList->u.pNext;
349 pNext->u.pNext = pFirst;
350 pFirst = pNext;
351 }
352 return pFirst;
353}
354
355DECLCALLBACK(void) VBoxMPHGSMIHostCmdCompleteCB(HVBOXVIDEOHGSMI hHGSMI, struct VBVAHOSTCMD *pCmd)
356{
357 PHGSMIHOSTCOMMANDCONTEXT pCtx = &((PVBOXMP_COMMON)hHGSMI)->hostCtx;
358 VBoxHGSMIHostCmdComplete(pCtx, pCmd);
359}
360
361DECLCALLBACK(int)
362VBoxMPHGSMIHostCmdRequestCB(HVBOXVIDEOHGSMI hHGSMI, uint8_t u8Channel, uint32_t iDisplay, struct VBVAHOSTCMD **ppCmd)
363{
364 LOGF_ENTER();
365
366 if (!ppCmd)
367 {
368 LOGF_LEAVE();
369 return VERR_INVALID_PARAMETER;
370 }
371
372 PHGSMIHOSTCOMMANDCONTEXT pCtx = &((PVBOXMP_COMMON)hHGSMI)->hostCtx;
373
374 /* pick up the host commands */
375 VBoxHGSMIProcessHostQueue(pCtx);
376
377 HGSMICHANNEL *pChannel = HGSMIChannelFindById(&pCtx->channels, u8Channel);
378 if(pChannel)
379 {
380 VBVA_CHANNELCONTEXTS * pContexts = (VBVA_CHANNELCONTEXTS *)pChannel->handler.pvHandler;
381 VBVADISP_CHANNELCONTEXT *pDispContext = VBoxVbvaFindHandlerInfo(pContexts, iDisplay);
382
383 if(pDispContext)
384 {
385 VBVAHOSTCMD *pCmd;
386 do
387 {
388 pCmd = ASMAtomicReadPtrT(&pDispContext->pCmd, VBVAHOSTCMD *);
389 } while (!ASMAtomicCmpXchgPtr(&pDispContext->pCmd, NULL, pCmd));
390 *ppCmd = VBoxVbvaReverseList(pCmd);
391
392 LOGF_LEAVE();
393 return VINF_SUCCESS;
394 }
395 else
396 {
397 WARN(("!pDispContext for display %d", iDisplay));
398 }
399 }
400
401 LOGF_LEAVE();
402 return VERR_INVALID_PARAMETER;
403}
404
405#define MEM_TAG 'HVBV'
406static void* VBoxMPMemAllocDriver(PVBOXMP_COMMON pCommon, const size_t size)
407{
408 ULONG Tag = MEM_TAG;
409 PVBOXMP_DEVEXT pExt = VBoxCommonToPrimaryExt(pCommon);
410 return pExt->u.primary.VideoPortProcs.pfnAllocatePool(pExt, (VBOXVP_POOL_TYPE)VpNonPagedPool, size, Tag);
411}
412
413static void VBoxMPMemFreeDriver(PVBOXMP_COMMON pCommon, void *pv)
414{
415 PVBOXMP_DEVEXT pExt = VBoxCommonToPrimaryExt(pCommon);
416 pExt->u.primary.VideoPortProcs.pfnFreePool(pExt, pv);
417}
418
419static int VBoxVbvaCreateChannelContexts(PVBOXMP_COMMON pCommon, VBVA_CHANNELCONTEXTS **ppContext)
420{
421 uint32_t cDisplays = (uint32_t)pCommon->cDisplays;
422 const size_t size = RT_OFFSETOF(VBVA_CHANNELCONTEXTS, aContexts[cDisplays]);
423 VBVA_CHANNELCONTEXTS *pContext = (VBVA_CHANNELCONTEXTS*) VBoxMPMemAllocDriver(pCommon, size);
424 if (pContext)
425 {
426 VideoPortZeroMemory(pContext, size);
427 pContext->cContexts = cDisplays;
428 pContext->pCommon = pCommon;
429 *ppContext = pContext;
430 return VINF_SUCCESS;
431 }
432
433 WARN(("Failed to allocate %d bytes", size));
434 return VERR_GENERAL_FAILURE;
435}
436
437static int VBoxVbvaDeleteChannelContexts(PVBOXMP_COMMON pCommon, VBVA_CHANNELCONTEXTS * pContext)
438{
439 VBoxMPMemFreeDriver(pCommon, pContext);
440 return VINF_SUCCESS;
441}
442
443static void VBoxMPSignalEvent(PVBOXMP_COMMON pCommon, uint64_t pvEvent)
444{
445 PVBOXMP_DEVEXT pExt = VBoxCommonToPrimaryExt(pCommon);
446 PEVENT pEvent = (PEVENT)pvEvent;
447 pExt->u.primary.VideoPortProcs.pfnSetEvent(pExt, pEvent);
448}
449
450static DECLCALLBACK(int)
451VBoxVbvaChannelGenericHandlerCB(void *pvHandler, uint16_t u16ChannelInfo, void *pvBuffer, HGSMISIZE cbBuffer)
452{
453 VBVA_CHANNELCONTEXTS *pCallbacks = (VBVA_CHANNELCONTEXTS*)pvHandler;
454 LOGF_ENTER();
455
456 Assert(cbBuffer > VBVAHOSTCMD_HDRSIZE);
457
458 if (cbBuffer > VBVAHOSTCMD_HDRSIZE)
459 {
460 VBVAHOSTCMD *pHdr = (VBVAHOSTCMD*)pvBuffer;
461 Assert(pHdr->iDstID >= 0);
462
463 if(pHdr->iDstID >= 0)
464 {
465 VBVADISP_CHANNELCONTEXT* pHandler = VBoxVbvaFindHandlerInfo(pCallbacks, pHdr->iDstID);
466 Assert(pHandler && pHandler->bValid);
467
468 if(pHandler && pHandler->bValid)
469 {
470 VBVAHOSTCMD *pFirst=NULL, *pLast=NULL, *pCur=pHdr;
471
472 while (pCur)
473 {
474 /*@todo: */
475 Assert(!pCur->u.Data);
476 Assert(!pFirst);
477 Assert(!pLast);
478
479 switch (u16ChannelInfo)
480 {
481 case VBVAHG_DISPLAY_CUSTOM:
482 {
483#if 0 /* Never taken */
484 if(pLast)
485 {
486 pLast->u.pNext = pCur;
487 pLast = pCur;
488 }
489 else
490#endif
491 {
492 pFirst = pCur;
493 pLast = pCur;
494 }
495 Assert(!pCur->u.Data);
496#if 0 /* Who is supposed to set pNext? */
497 //TODO: use offset here
498 pCur = pCur->u.pNext;
499 Assert(!pCur);
500#else
501 Assert(!pCur->u.pNext);
502 pCur = NULL;
503#endif
504 Assert(pFirst);
505 Assert(pFirst == pLast);
506 break;
507 }
508 case VBVAHG_EVENT:
509 {
510 VBVAHOSTCMDEVENT *pEventCmd = VBVAHOSTCMD_BODY(pCur, VBVAHOSTCMDEVENT);
511 VBoxMPSignalEvent(pCallbacks->pCommon, pEventCmd->pEvent);
512 }
513 default:
514 {
515 Assert(u16ChannelInfo==VBVAHG_EVENT);
516 Assert(!pCur->u.Data);
517#if 0 /* pLast has been asserted to be NULL, and who should set pNext? */
518 //TODO: use offset here
519 if(pLast)
520 pLast->u.pNext = pCur->u.pNext;
521 VBVAHOSTCMD * pNext = pCur->u.pNext;
522 pCur->u.pNext = NULL;
523#else
524 Assert(!pCur->u.pNext);
525#endif
526 VBoxHGSMIHostCmdComplete(&pCallbacks->pCommon->hostCtx, pCur);
527#if 0 /* pNext is NULL, and the other things have already been asserted */
528 pCur = pNext;
529 Assert(!pCur);
530 Assert(!pFirst);
531 Assert(pFirst == pLast);
532#else
533 pCur = NULL;
534#endif
535 }
536 }
537 }
538
539 /* we do not support lists currently */
540 Assert(pFirst == pLast);
541 if(pLast)
542 {
543 Assert(pLast->u.pNext == NULL);
544 }
545 if(pFirst)
546 {
547 Assert(pLast);
548 VBVAHOSTCMD *pCmd;
549 do
550 {
551 pCmd = ASMAtomicReadPtrT(&pHandler->pCmd, VBVAHOSTCMD *);
552 pFirst->u.pNext = pCmd;
553 }
554 while (!ASMAtomicCmpXchgPtr(&pHandler->pCmd, pFirst, pCmd));
555 }
556 else
557 {
558 Assert(!pLast);
559 }
560 LOGF_LEAVE();
561 return VINF_SUCCESS;
562 }
563 }
564 else
565 {
566 /*@todo*/
567 }
568 }
569
570 LOGF_LEAVE();
571
572 /* no handlers were found, need to complete the command here */
573 VBoxHGSMIHostCmdComplete(&pCallbacks->pCommon->hostCtx, pvBuffer);
574 return VINF_SUCCESS;
575}
576
577/* Note: negative iDisplay would mean this is a miniport handler */
578int VBoxVbvaChannelDisplayEnable(PVBOXMP_COMMON pCommon, int iDisplay, uint8_t u8Channel)
579{
580 static HGSMICHANNELHANDLER s_OldHandler;
581
582 LOGF_ENTER();
583
584 VBVA_CHANNELCONTEXTS * pContexts;
585 HGSMICHANNEL * pChannel = HGSMIChannelFindById(&pCommon->hostCtx.channels, u8Channel);
586
587 if (!pChannel)
588 {
589 int rc = VBoxVbvaCreateChannelContexts(pCommon, &pContexts);
590 if (RT_FAILURE(rc))
591 {
592 WARN(("VBoxVbvaCreateChannelContexts failed with rc=%#x", rc));
593 LOGF_LEAVE();
594 return rc;
595 }
596 }
597 else
598 {
599 pContexts = (VBVA_CHANNELCONTEXTS *)pChannel->handler.pvHandler;
600 }
601
602 VBVADISP_CHANNELCONTEXT *pDispContext = VBoxVbvaFindHandlerInfo(pContexts, iDisplay);
603 if (!pDispContext)
604 {
605 WARN(("!pDispContext"));
606 LOGF_LEAVE();
607 return VERR_GENERAL_FAILURE;
608 }
609
610#ifdef DEBUGVHWASTRICT
611 Assert(!pDispContext->bValid);
612#endif
613 Assert(!pDispContext->pCmd);
614
615 if (!pDispContext->bValid)
616 {
617 pDispContext->bValid = true;
618 pDispContext->pCmd = NULL;
619
620 int rc = VINF_SUCCESS;
621 if (!pChannel)
622 {
623 rc = HGSMIChannelRegister(&pCommon->hostCtx.channels, u8Channel,
624 "VGA Miniport HGSMI channel", VBoxVbvaChannelGenericHandlerCB,
625 pContexts, &s_OldHandler);
626 }
627
628 if (RT_SUCCESS(rc))
629 {
630 pContexts->cUsed++;
631 LOGF_LEAVE();
632 return VINF_SUCCESS;
633 }
634 else
635 {
636 WARN(("HGSMIChannelRegister failed with rc=%#x", rc));
637 }
638 }
639
640 if(!pChannel)
641 {
642 VBoxVbvaDeleteChannelContexts(pCommon, pContexts);
643 }
644
645 LOGF_LEAVE();
646 return VERR_GENERAL_FAILURE;
647}
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