VirtualBox

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

Last change on this file since 63047 was 63047, checked in by vboxsync, 8 years ago

GA/NT/Graphics: warnings

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