VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvAudio.cpp@ 60990

Last change on this file since 60990 was 60990, checked in by vboxsync, 9 years ago

DrvAudio.cpp: Replaced the generic VERR_NO_MORE_HANDLES with a dedicated one (VERR_AUDIO_NO_FREE_INPUT_STREAMS). This error occurs here with a default win8.1 config.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 67.1 KB
Line 
1/* $Id: DrvAudio.cpp 60990 2016-05-15 17:58:13Z vboxsync $ */
2/** @file
3 * Intermediate audio driver header.
4 *
5 * @remarks Intermediate audio driver for connecting the audio device emulation
6 * with the host backend.
7 */
8
9/*
10 * Copyright (C) 2006-2016 Oracle Corporation
11 *
12 * This file is part of VirtualBox Open Source Edition (OSE), as
13 * available from http://www.virtualbox.org. This file is free software;
14 * you can redistribute it and/or modify it under the terms of the GNU
15 * General Public License (GPL) as published by the Free Software
16 * Foundation, in version 2 as it comes in the "COPYING" file of the
17 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19 * --------------------------------------------------------------------
20 *
21 * This code is based on: audio.c from QEMU AUDIO subsystem.
22 *
23 * QEMU Audio subsystem
24 *
25 * Copyright (c) 2003-2005 Vassili Karpov (malc)
26 *
27 * Permission is hereby granted, free of charge, to any person obtaining a copy
28 * of this software and associated documentation files (the "Software"), to deal
29 * in the Software without restriction, including without limitation the rights
30 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
31 * copies of the Software, and to permit persons to whom the Software is
32 * furnished to do so, subject to the following conditions:
33 *
34 * The above copyright notice and this permission notice shall be included in
35 * all copies or substantial portions of the Software.
36 *
37 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
39 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
40 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
41 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
42 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
43 * THE SOFTWARE.
44 */
45#define LOG_GROUP LOG_GROUP_DRV_AUDIO
46#include <VBox/log.h>
47#include <VBox/vmm/pdm.h>
48#include <VBox/err.h>
49#include <VBox/vmm/mm.h>
50#include <VBox/vmm/pdmaudioifs.h>
51
52#include <iprt/alloc.h>
53#include <iprt/asm-math.h>
54#include <iprt/assert.h>
55#include <iprt/circbuf.h>
56#include <iprt/string.h>
57#include <iprt/uuid.h>
58
59#include "VBoxDD.h"
60
61#include <ctype.h>
62#include <stdlib.h>
63
64#include "DrvAudio.h"
65#include "AudioMixBuffer.h"
66
67static int drvAudioAllocHstIn(PDRVAUDIO pThis, PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOHSTSTRMIN *ppHstStrmIn);
68static int drvAudioAllocHstOut(PDRVAUDIO pThis, PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOHSTSTRMOUT *ppHstStrmOut);
69static int drvAudioCreateStreamPairIn(PDRVAUDIO pThis, PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOGSTSTRMIN *ppGstStrmIn);
70static int drvAudioDestroyHstIn(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMIN pHstStrmIn);
71static int drvAudioDestroyGstIn(PDRVAUDIO pThis, PPDMAUDIOGSTSTRMIN pGstStrmIn);
72static int drvAudioDestroyGstOut(PDRVAUDIO pThis, PPDMAUDIOGSTSTRMOUT pGstStrmOut);
73static PPDMAUDIOHSTSTRMIN drvAudioFindAnyHstIn(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMIN pHstStrmIn);
74static PPDMAUDIOHSTSTRMOUT drvAudioFindAnyHstOut(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMOUT pHstStrmOut);
75static PPDMAUDIOHSTSTRMOUT drvAudioFindSpecificOut(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg);
76
77static int drvAudioGstInInit(PPDMAUDIOGSTSTRMIN pGstStrmIn, PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg);
78
79static void drvAudioHstInFreeRes(PPDMAUDIOHSTSTRMIN pHstStrmIn);
80static void drvAudioHstOutFreeRes(PPDMAUDIOHSTSTRMOUT pHstStrmOut);
81
82
83int drvAudioAddHstOut(PDRVAUDIO pThis, PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOHSTSTRMOUT *ppHstStrmOut)
84{
85 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
86 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
87
88 int rc;
89
90 PPDMAUDIOHSTSTRMOUT pHstStrmOut = drvAudioFindSpecificOut(pThis, NULL, pCfg);
91 if (!pHstStrmOut)
92 {
93 rc = drvAudioAllocHstOut(pThis, pCfg, &pHstStrmOut);
94 if (RT_FAILURE(rc))
95 pHstStrmOut = drvAudioFindAnyHstOut(pThis, NULL /* pHstStrmOut */);
96 }
97
98 rc = pHstStrmOut ? VINF_SUCCESS : rc;
99
100 if (RT_SUCCESS(rc))
101 *ppHstStrmOut = pHstStrmOut;
102
103 return rc;
104}
105
106#ifndef VBOX_AUDIO_TESTCASE
107static PDMAUDIOFMT drvAudioGetConfFormat(PCFGMNODE pCfgHandle, const char *pszKey,
108 PDMAUDIOFMT enmDefault, bool *pfDefault)
109{
110 if ( pCfgHandle == NULL
111 || pszKey == NULL)
112 {
113 *pfDefault = true;
114 return enmDefault;
115 }
116
117 char *pszValue = NULL;
118 int rc = CFGMR3QueryStringAlloc(pCfgHandle, pszKey, &pszValue);
119 if (RT_FAILURE(rc))
120 {
121 *pfDefault = true;
122 return enmDefault;
123 }
124
125 PDMAUDIOFMT fmt = DrvAudioStrToAudFmt(pszValue);
126 if (fmt == PDMAUDIOFMT_INVALID)
127 {
128 *pfDefault = true;
129 return enmDefault;
130 }
131
132 *pfDefault = false;
133 return fmt;
134}
135
136static int drvAudioGetConfInt(PCFGMNODE pCfgHandle, const char *pszKey,
137 int iDefault, bool *pfDefault)
138{
139
140 if ( pCfgHandle == NULL
141 || pszKey == NULL)
142 {
143 *pfDefault = true;
144 return iDefault;
145 }
146
147 uint64_t u64Data = 0;
148 int rc = CFGMR3QueryInteger(pCfgHandle, pszKey, &u64Data);
149 if (RT_FAILURE(rc))
150 {
151 *pfDefault = true;
152 return iDefault;
153
154 }
155
156 *pfDefault = false;
157 return u64Data;
158}
159
160static const char *drvAudioGetConfStr(PCFGMNODE pCfgHandle, const char *pszKey,
161 const char *pszDefault, bool *pfDefault)
162{
163 if ( pCfgHandle == NULL
164 || pszKey == NULL)
165 {
166 *pfDefault = true;
167 return pszDefault;
168 }
169
170 char *pszValue = NULL;
171 int rc = CFGMR3QueryStringAlloc(pCfgHandle, pszKey, &pszValue);
172 if (RT_FAILURE(rc))
173 {
174 *pfDefault = true;
175 return pszDefault;
176 }
177
178 *pfDefault = false;
179 return pszValue;
180}
181
182static int drvAudioProcessOptions(PCFGMNODE pCfgHandle, const char *pszPrefix, audio_option *paOpts, size_t cOpts)
183{
184 AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
185 AssertPtrReturn(pszPrefix, VERR_INVALID_POINTER);
186 /* oaOpts and cOpts are optional. */
187
188 PCFGMNODE pCfgChildHandle = NULL;
189 PCFGMNODE pCfgChildChildHandle = NULL;
190
191 /* If pCfgHandle is NULL, let NULL be passed to get int and get string functions..
192 * The getter function will return default values.
193 */
194 if (pCfgHandle != NULL)
195 {
196 /* If its audio general setting, need to traverse to one child node.
197 * /Devices/ichac97/0/LUN#0/Config/Audio
198 */
199 if(!strncmp(pszPrefix, "AUDIO", 5)) /** @todo Use a \#define */
200 {
201 pCfgChildHandle = CFGMR3GetFirstChild(pCfgHandle);
202 if(pCfgChildHandle)
203 pCfgHandle = pCfgChildHandle;
204 }
205 else
206 {
207 /* If its driver specific configuration , then need to traverse two level deep child
208 * child nodes. for eg. in case of DirectSoundConfiguration item
209 * /Devices/ichac97/0/LUN#0/Config/Audio/DirectSoundConfig
210 */
211 pCfgChildHandle = CFGMR3GetFirstChild(pCfgHandle);
212 if (pCfgChildHandle)
213 {
214 pCfgChildChildHandle = CFGMR3GetFirstChild(pCfgChildHandle);
215 if (pCfgChildChildHandle)
216 pCfgHandle = pCfgChildChildHandle;
217 }
218 }
219 }
220
221 for (size_t i = 0; i < cOpts; i++)
222 {
223 audio_option *pOpt = &paOpts[i];
224 if (!pOpt->valp)
225 {
226 LogFlowFunc(("Option value pointer for `%s' is not set\n", pOpt->name));
227 continue;
228 }
229
230 bool fUseDefault;
231
232 switch (pOpt->tag)
233 {
234 case AUD_OPT_BOOL:
235 case AUD_OPT_INT:
236 {
237 int *intp = (int *)pOpt->valp;
238 *intp = drvAudioGetConfInt(pCfgHandle, pOpt->name, *intp, &fUseDefault);
239
240 break;
241 }
242
243 case AUD_OPT_FMT:
244 {
245 PDMAUDIOFMT *fmtp = (PDMAUDIOFMT *)pOpt->valp;
246 *fmtp = drvAudioGetConfFormat(pCfgHandle, pOpt->name, *fmtp, &fUseDefault);
247
248 break;
249 }
250
251 case AUD_OPT_STR:
252 {
253 const char **strp = (const char **)pOpt->valp;
254 *strp = drvAudioGetConfStr(pCfgHandle, pOpt->name, *strp, &fUseDefault);
255
256 break;
257 }
258
259 default:
260 LogFlowFunc(("Bad value tag for option `%s' - %d\n", pOpt->name, pOpt->tag));
261 fUseDefault = false;
262 break;
263 }
264
265 if (!pOpt->overridenp)
266 pOpt->overridenp = &pOpt->overriden;
267
268 *pOpt->overridenp = !fUseDefault;
269 }
270
271 return VINF_SUCCESS;
272}
273#endif /* !VBOX_AUDIO_TESTCASE */
274
275static bool drvAudioStreamCfgIsValid(PPDMAUDIOSTREAMCFG pCfg)
276{
277 bool fValid = ( pCfg->cChannels == 1
278 || pCfg->cChannels == 2); /* Either stereo (2) or mono (1), per stream. */
279
280 fValid |= ( pCfg->enmEndianness == PDMAUDIOENDIANNESS_LITTLE
281 || pCfg->enmEndianness == PDMAUDIOENDIANNESS_BIG);
282
283 if (fValid)
284 {
285 switch (pCfg->enmFormat)
286 {
287 case PDMAUDIOFMT_S8:
288 case PDMAUDIOFMT_U8:
289 case PDMAUDIOFMT_S16:
290 case PDMAUDIOFMT_U16:
291 case PDMAUDIOFMT_S32:
292 case PDMAUDIOFMT_U32:
293 break;
294 default:
295 fValid = false;
296 break;
297 }
298 }
299
300 /** @todo Check for defined frequencies supported. */
301 fValid |= pCfg->uHz > 0;
302
303 return fValid;
304}
305
306static int drvAudioControlHstIn(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMIN pHstStrmIn, PDMAUDIOSTREAMCMD enmStreamCmd)
307{
308 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
309 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
310
311 int rc = RTCritSectEnter(&pHstStrmIn->CritSect);
312 if (RT_FAILURE(rc))
313 return rc;
314
315 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
316
317 switch (enmStreamCmd)
318 {
319 case PDMAUDIOSTREAMCMD_ENABLE:
320 {
321 if (!(pHstStrmIn->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
322 {
323 rc = pThis->pHostDrvAudio->pfnControlIn(pThis->pHostDrvAudio, pHstStrmIn, PDMAUDIOSTREAMCMD_ENABLE);
324 if (RT_SUCCESS(rc))
325 pHstStrmIn->fStatus |= PDMAUDIOSTRMSTS_FLAG_ENABLED;
326 }
327 else
328 rc = VINF_SUCCESS;
329
330 break;
331 }
332
333 case PDMAUDIOSTREAMCMD_DISABLE:
334 {
335 if (pHstStrmIn->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED)
336 {
337 rc = pThis->pHostDrvAudio->pfnControlIn(pThis->pHostDrvAudio, pHstStrmIn, PDMAUDIOSTREAMCMD_DISABLE);
338 if (RT_SUCCESS(rc))
339 {
340 pHstStrmIn->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_ENABLED;
341 AudioMixBufClear(&pHstStrmIn->MixBuf);
342 }
343 }
344 else
345 rc = VINF_SUCCESS;
346
347 break;
348 }
349
350 case PDMAUDIOSTREAMCMD_PAUSE:
351 {
352 if (!(pHstStrmIn->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED))
353 {
354 rc = pThis->pHostDrvAudio->pfnControlIn(pThis->pHostDrvAudio, pHstStrmIn, PDMAUDIOSTREAMCMD_PAUSE);
355 if (RT_SUCCESS(rc))
356 pHstStrmIn->fStatus |= PDMAUDIOSTRMSTS_FLAG_PAUSED;
357 }
358 else
359 rc = VINF_SUCCESS;
360
361 break;
362 }
363
364 case PDMAUDIOSTREAMCMD_RESUME:
365 {
366 if (pHstStrmIn->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED)
367 {
368 rc = pThis->pHostDrvAudio->pfnControlIn(pThis->pHostDrvAudio, pHstStrmIn, PDMAUDIOSTREAMCMD_RESUME);
369 if (RT_SUCCESS(rc))
370 pHstStrmIn->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PAUSED;
371 }
372 else
373 rc = VINF_SUCCESS;
374
375 break;
376 }
377
378 default:
379 AssertMsgFailed(("Command %ld not implemented\n", enmStreamCmd));
380 rc = VERR_NOT_IMPLEMENTED;
381 break;
382 }
383
384 LogFunc(("[%s] Returned %Rrc\n", pHstStrmIn->MixBuf.pszName, rc));
385
386 int rc2 = RTCritSectLeave(&pHstStrmIn->CritSect);
387 if (RT_SUCCESS(rc))
388 rc = rc2;
389
390 return rc;
391}
392
393static int drvAudioControlHstOut(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMOUT pHstStrmOut, PDMAUDIOSTREAMCMD enmStreamCmd)
394{
395 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
396 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
397
398 int rc = RTCritSectEnter(&pHstStrmOut->CritSect);
399 if (RT_FAILURE(rc))
400 return rc;
401
402 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
403
404 switch (enmStreamCmd)
405 {
406 case PDMAUDIOSTREAMCMD_ENABLE:
407 {
408 if (!(pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
409 {
410 rc = pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut, PDMAUDIOSTREAMCMD_ENABLE);
411 if (RT_SUCCESS(rc))
412 {
413 Assert(!(pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE));
414 pHstStrmOut->fStatus |= PDMAUDIOSTRMSTS_FLAG_ENABLED;
415 }
416 }
417 else
418 rc = VINF_SUCCESS;
419
420 break;
421 }
422
423 case PDMAUDIOSTREAMCMD_DISABLE:
424 {
425 if (pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED)
426 {
427 rc = pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut, PDMAUDIOSTREAMCMD_DISABLE);
428 if (RT_SUCCESS(rc))
429 {
430 pHstStrmOut->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_ENABLED;
431 AudioMixBufClear(&pHstStrmOut->MixBuf);
432 }
433 }
434 else
435 rc = VINF_SUCCESS;
436
437 break;
438 }
439
440 case PDMAUDIOSTREAMCMD_PAUSE:
441 {
442 if (!(pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED))
443 {
444 rc = pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut, PDMAUDIOSTREAMCMD_PAUSE);
445 if (RT_SUCCESS(rc))
446 pHstStrmOut->fStatus |= PDMAUDIOSTRMSTS_FLAG_PAUSED;
447 }
448 else
449 rc = VINF_SUCCESS;
450
451 break;
452 }
453
454 case PDMAUDIOSTREAMCMD_RESUME:
455 {
456 if (pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED)
457 {
458 rc = pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut, PDMAUDIOSTREAMCMD_RESUME);
459 if (RT_SUCCESS(rc))
460 pHstStrmOut->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PAUSED;
461 }
462 else
463 rc = VINF_SUCCESS;
464
465 break;
466 }
467
468 default:
469 AssertMsgFailed(("Command %ld not implemented\n", enmStreamCmd));
470 rc = VERR_NOT_IMPLEMENTED;
471 break;
472 }
473
474 LogFunc(("[%s] Returned %Rrc\n", pHstStrmOut->MixBuf.pszName, rc));
475
476 int rc2 = RTCritSectLeave(&pHstStrmOut->CritSect);
477 if (RT_SUCCESS(rc))
478 rc = rc2;
479
480 return rc;
481}
482
483static int drvAudioDestroyHstOut(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
484{
485 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
486 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
487
488 LogFlowFunc(("%s\n", pHstStrmOut->MixBuf.pszName));
489
490 int rc = VINF_SUCCESS;
491
492 if (RTListIsEmpty(&pHstStrmOut->lstGstStrmOut))
493 {
494 drvAudioHstOutFreeRes(pHstStrmOut);
495
496 /* Remove from driver instance list. */
497 RTListNodeRemove(&pHstStrmOut->Node);
498
499 if (RTCritSectIsInitialized(&pHstStrmOut->CritSect))
500 {
501 int rc2 = RTCritSectDelete(&pHstStrmOut->CritSect);
502 AssertRC(rc2);
503 }
504
505 RTMemFree(pHstStrmOut);
506 pHstStrmOut = NULL;
507
508 pThis->cStreamsFreeOut++;
509 }
510 else
511 {
512 rc = VERR_WRONG_ORDER;
513 LogFlowFunc(("[%s] Still is being used, skipping\n", pHstStrmOut->MixBuf.pszName));
514 }
515
516 LogFlowFuncLeaveRC(rc);
517 return rc;
518}
519
520static int drvAudioDestroyGstOut(PDRVAUDIO pThis, PPDMAUDIOGSTSTRMOUT pGstStrmOut)
521{
522 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
523
524 if (!pGstStrmOut)
525 return VINF_SUCCESS;
526
527 LogFlowFunc(("%s\n", pGstStrmOut->MixBuf.pszName));
528
529 if (pGstStrmOut->State.cRefs > 1) /* Do other objects still have a reference to it? Bail out. */
530 return VERR_WRONG_ORDER;
531
532 AudioMixBufDestroy(&pGstStrmOut->MixBuf);
533
534 /* Is this guest stream associate to a host stream? */
535 if (pGstStrmOut->pHstStrmOut)
536 {
537 /* Unregister from parent first. */
538 RTListNodeRemove(&pGstStrmOut->Node);
539 }
540
541 RTMemFree(pGstStrmOut);
542 pGstStrmOut = NULL;
543
544 return VINF_SUCCESS;
545}
546
547static PPDMAUDIOHSTSTRMIN drvAudioFindAnyHstIn(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMIN pHstStrmIn)
548{
549 if (pHstStrmIn)
550 {
551 if (RTListNodeIsLast(&pThis->lstHstStrmIn, &pHstStrmIn->Node))
552 return NULL;
553
554 return RTListNodeGetNext(&pHstStrmIn->Node, PDMAUDIOHSTSTRMIN, Node);
555 }
556
557 return RTListGetFirst(&pThis->lstHstStrmIn, PDMAUDIOHSTSTRMIN, Node);
558}
559
560static int drvAudioHstInAdd(PDRVAUDIO pThis, PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOHSTSTRMIN *ppHstStrmIn)
561{
562 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
563 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
564 AssertPtrReturn(ppHstStrmIn, VERR_INVALID_POINTER);
565
566 PPDMAUDIOHSTSTRMIN pHstStrmIn;
567 int rc = drvAudioAllocHstIn(pThis, pCfg, &pHstStrmIn);
568 if (RT_SUCCESS(rc))
569 *ppHstStrmIn = pHstStrmIn;
570
571 LogFlowFuncLeaveRC(rc);
572 return rc;
573}
574
575static void drvAudioHstInFreeRes(PPDMAUDIOHSTSTRMIN pHstStrmIn)
576{
577 if (!pHstStrmIn)
578 return;
579
580 AudioMixBufDestroy(&pHstStrmIn->MixBuf);
581}
582
583static int drvAudioGstOutInit(PPDMAUDIOGSTSTRMOUT pGstStrmOut, PPDMAUDIOHSTSTRMOUT pHostStrmOut, PPDMAUDIOSTREAMCFG pCfg)
584{
585 AssertPtrReturn(pGstStrmOut, VERR_INVALID_POINTER);
586 AssertPtrReturn(pHostStrmOut, VERR_INVALID_POINTER);
587 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
588
589 int rc = DrvAudioStreamCfgToProps(pCfg, &pGstStrmOut->Props);
590 if (RT_SUCCESS(rc))
591 {
592 char *pszName;
593 if (RTStrAPrintf(&pszName, "%s (Guest)", pCfg->szName) <= 0)
594 return VERR_NO_MEMORY;
595
596 rc = AudioMixBufInit(&pGstStrmOut->MixBuf, pszName, &pGstStrmOut->Props, AudioMixBufSize(&pHostStrmOut->MixBuf));
597 if (RT_SUCCESS(rc))
598 rc = AudioMixBufLinkTo(&pGstStrmOut->MixBuf, &pHostStrmOut->MixBuf);
599
600 if (RT_SUCCESS(rc))
601 {
602 pGstStrmOut->State.cRefs = 1;
603 pGstStrmOut->State.fActive = false;
604 pGstStrmOut->State.fEmpty = true;
605
606 RTStrPrintf(pGstStrmOut->State.szName, RT_ELEMENTS(pGstStrmOut->State.szName),
607 "%s", pszName);
608
609 pGstStrmOut->pHstStrmOut = pHostStrmOut;
610 }
611
612 RTStrFree(pszName);
613 }
614
615 LogFlowFunc(("pszName=%s, rc=%Rrc\n", pCfg->szName, rc));
616 return rc;
617}
618
619static int drvAudioAllocHstOut(PDRVAUDIO pThis, PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOHSTSTRMOUT *ppHstStrmOut)
620{
621 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
622 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
623
624 if (!pThis->cStreamsFreeOut)
625 {
626 LogFlowFunc(("Maximum number of host output streams reached\n"));
627 return VERR_NO_MORE_HANDLES;
628 }
629
630 /* Validate backend configuration. */
631 if (!pThis->BackendCfg.cbStreamOut)
632 {
633 LogFlowFunc(("Backend output configuration not valid, bailing out\n"));
634 return VERR_INVALID_PARAMETER;
635 }
636
637 PPDMAUDIOHSTSTRMOUT pHstStrmOut = (PPDMAUDIOHSTSTRMOUT)RTMemAllocZ(pThis->BackendCfg.cbStreamOut);
638 if (!pHstStrmOut)
639 {
640 LogFlowFunc(("Error allocating host output stream with %zu bytes\n",
641 pThis->BackendCfg.cbStreamOut));
642 return VERR_NO_MEMORY;
643 }
644
645 int rc;
646
647 do
648 {
649 RTListInit(&pHstStrmOut->lstGstStrmOut);
650
651 uint32_t cSamples = 0;
652 rc = pThis->pHostDrvAudio->pfnInitOut
653 ? pThis->pHostDrvAudio->pfnInitOut(pThis->pHostDrvAudio, pHstStrmOut, pCfg, &cSamples) : VINF_SUCCESS;
654
655 if (RT_FAILURE(rc))
656 {
657 LogFlowFunc(("Initializing host backend failed with rc=%Rrc\n", rc));
658 break;
659 }
660
661 pHstStrmOut->fStatus |= PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
662
663 char *pszName;
664 if (RTStrAPrintf(&pszName, "%s (Host)", pCfg->szName) <= 0)
665 {
666 rc = VERR_NO_MEMORY;
667 break;
668 }
669
670 rc = AudioMixBufInit(&pHstStrmOut->MixBuf, pszName, &pHstStrmOut->Props, cSamples);
671 if (RT_SUCCESS(rc))
672 rc = RTCritSectInit(&pHstStrmOut->CritSect);
673
674 if (RT_SUCCESS(rc))
675 {
676 RTListPrepend(&pThis->lstHstStrmOut, &pHstStrmOut->Node);
677 pThis->cStreamsFreeOut--;
678 }
679
680 RTStrFree(pszName);
681
682 } while (0);
683
684 if (RT_FAILURE(rc))
685 {
686 if (pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
687 {
688 int rc2 = pThis->pHostDrvAudio->pfnFiniOut
689 ? pThis->pHostDrvAudio->pfnFiniOut(pThis->pHostDrvAudio, pHstStrmOut) : VINF_SUCCESS;
690 if (RT_SUCCESS(rc2))
691 pHstStrmOut->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
692 }
693
694 drvAudioHstOutFreeRes(pHstStrmOut);
695 RTMemFree(pHstStrmOut);
696 }
697 else
698 *ppHstStrmOut = pHstStrmOut;
699
700 LogFlowFuncLeaveRC(rc);
701 return rc;
702}
703
704static int drvAudioCreateStreamPairOut(PDRVAUDIO pThis, PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOGSTSTRMOUT *ppGstStrmOut)
705{
706 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
707 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
708
709 PPDMAUDIOGSTSTRMOUT pGstStrmOut =
710 (PPDMAUDIOGSTSTRMOUT)RTMemAllocZ(sizeof(PDMAUDIOGSTSTRMOUT));
711 if (!pGstStrmOut)
712 {
713 LogFlowFunc(("Failed to allocate memory for guest output stream \"%s\"\n", pCfg->szName));
714 return VERR_NO_MEMORY;
715 }
716
717 /*
718 * The host stream always will get the backend audio stream configuration.
719 */
720 PPDMAUDIOHSTSTRMOUT pHstStrmOut;
721 int rc = drvAudioAddHstOut(pThis, pCfg, &pHstStrmOut);
722 if (RT_FAILURE(rc))
723 {
724 LogFlowFunc(("Error adding host output stream \"%s\", rc=%Rrc\n", pCfg->szName, rc));
725
726 RTMemFree(pGstStrmOut);
727 return rc;
728 }
729
730 /*
731 * The guest stream always will get the audio stream configuration told
732 * by the device emulation (which in turn was/could be set by the guest OS).
733 */
734 rc = drvAudioGstOutInit(pGstStrmOut, pHstStrmOut, pCfg);
735 if (RT_SUCCESS(rc))
736 {
737 RTListPrepend(&pHstStrmOut->lstGstStrmOut, &pGstStrmOut->Node);
738
739 if (ppGstStrmOut)
740 *ppGstStrmOut = pGstStrmOut;
741 }
742
743 if (RT_FAILURE(rc))
744 drvAudioDestroyGstOut(pThis, pGstStrmOut);
745
746 LogFlowFuncLeaveRC(rc);
747 return rc;
748}
749
750static int drvAudioCreateStreamPairIn(PDRVAUDIO pThis, PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOGSTSTRMIN *ppGstStrmIn)
751{
752 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
753 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
754
755 PPDMAUDIOGSTSTRMIN pGstStrmIn = (PPDMAUDIOGSTSTRMIN)RTMemAllocZ(sizeof(PDMAUDIOGSTSTRMIN));
756 if (!pGstStrmIn)
757 return VERR_NO_MEMORY;
758
759 /*
760 * The host stream always will get the backend audio stream configuration.
761 */
762 PPDMAUDIOHSTSTRMIN pHstStrmIn;
763 int rc = drvAudioHstInAdd(pThis, pCfg, &pHstStrmIn);
764 if (RT_FAILURE(rc))
765 {
766 LogFunc(("Failed to add host audio input stream \"%s\", rc=%Rrc\n", pCfg->szName, rc));
767
768 RTMemFree(pGstStrmIn);
769 return rc;
770 }
771
772 /*
773 * The guest stream always will get the audio stream configuration told
774 * by the device emulation (which in turn was/could be set by the guest OS).
775 */
776 rc = drvAudioGstInInit(pGstStrmIn, pHstStrmIn, pCfg);
777 if (RT_SUCCESS(rc))
778 {
779 pHstStrmIn->pGstStrmIn = pGstStrmIn;
780
781 if (ppGstStrmIn)
782 *ppGstStrmIn = pGstStrmIn;
783 }
784 else
785 drvAudioDestroyGstIn(pThis, pGstStrmIn);
786
787 LogFlowFuncLeaveRC(rc);
788 return rc;
789}
790
791/**
792 * Initializes a guest input stream.
793 *
794 * @return IPRT status code.
795 * @param pGstStrmIn Pointer to guest stream to initialize.
796 * @param pHstStrmIn Pointer to host input stream to associate this guest
797 * stream with.
798 * @param pCfg Pointer to stream configuration to use.
799 */
800static int drvAudioGstInInit(PPDMAUDIOGSTSTRMIN pGstStrmIn, PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg)
801{
802 AssertPtrReturn(pGstStrmIn, VERR_INVALID_POINTER);
803 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
804 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
805
806 int rc = DrvAudioStreamCfgToProps(pCfg, &pGstStrmIn->Props);
807 if (RT_SUCCESS(rc))
808 {
809 char *pszName;
810 if (RTStrAPrintf(&pszName, "%s (Guest)",
811 strlen(pCfg->szName) ? pCfg->szName : "<Untitled>") <= 0)
812 {
813 return VERR_NO_MEMORY;
814 }
815
816 rc = AudioMixBufInit(&pGstStrmIn->MixBuf, pszName, &pGstStrmIn->Props, AudioMixBufSize(&pHstStrmIn->MixBuf));
817 if (RT_SUCCESS(rc))
818 rc = AudioMixBufLinkTo(&pHstStrmIn->MixBuf, &pGstStrmIn->MixBuf);
819
820 if (RT_SUCCESS(rc))
821 {
822#ifdef DEBUG
823 DrvAudioStreamCfgPrint(pCfg);
824#endif
825 pGstStrmIn->State.cRefs = 1;
826 pGstStrmIn->State.fActive = false;
827 pGstStrmIn->State.fEmpty = true;
828
829 RTStrPrintf(pGstStrmIn->State.szName, RT_ELEMENTS(pGstStrmIn->State.szName),
830 "%s", pszName);
831
832 pGstStrmIn->pHstStrmIn = pHstStrmIn;
833 }
834
835 RTStrFree(pszName);
836 }
837
838 LogFlowFunc(("pszName=%s, rc=%Rrc\n", pCfg->szName, rc));
839 return rc;
840}
841
842static int drvAudioAllocHstIn(PDRVAUDIO pThis, PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOHSTSTRMIN *ppHstStrmIn)
843{
844 if (!pThis->cStreamsFreeIn)
845 {
846 LogFlowFunc(("No more input streams free to use, bailing out\n"));
847 return VERR_AUDIO_NO_FREE_INPUT_STREAMS;
848 }
849
850 /* Validate backend configuration. */
851 if (!pThis->BackendCfg.cbStreamIn)
852 {
853 LogFlowFunc(("Backend input configuration not valid, bailing out\n"));
854 return VERR_INVALID_PARAMETER;
855 }
856
857 PPDMAUDIOHSTSTRMIN pHstStrmIn =
858 (PPDMAUDIOHSTSTRMIN)RTMemAllocZ(pThis->BackendCfg.cbStreamIn);
859 if (!pHstStrmIn)
860 {
861 LogFlowFunc(("Error allocating host innput stream with %RU32 bytes\n",
862 pThis->BackendCfg.cbStreamOut));
863 return VERR_NO_MEMORY;
864 }
865
866 int rc;
867
868 do /* goto avoidance */
869 {
870 uint32_t cSamples = 0;
871 rc = pThis->pHostDrvAudio->pfnInitIn
872 ? pThis->pHostDrvAudio->pfnInitIn(pThis->pHostDrvAudio, pHstStrmIn,
873 pCfg, pCfg->DestSource.Source, &cSamples)
874 : VINF_SUCCESS;
875 if (RT_FAILURE(rc))
876 {
877 LogFlowFunc(("Initializing host backend failed with rc=%Rrc\n", rc));
878 break;
879 }
880
881 pHstStrmIn->fStatus |= PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
882
883 char *pszName;
884 if (RTStrAPrintf(&pszName, "%s (Host)", pCfg->szName) <= 0)
885 {
886 rc = VERR_NO_MEMORY;
887 break;
888 }
889
890 rc = AudioMixBufInit(&pHstStrmIn->MixBuf, pszName, &pHstStrmIn->Props, cSamples);
891 if (RT_SUCCESS(rc))
892 rc = RTCritSectInit(&pHstStrmIn->CritSect);
893
894 if (RT_SUCCESS(rc))
895 {
896 RTListPrepend(&pThis->lstHstStrmIn, &pHstStrmIn->Node);
897 pThis->cStreamsFreeIn--;
898 }
899
900 RTStrFree(pszName);
901
902 } while (0);
903
904 if (RT_FAILURE(rc))
905 {
906 if (pHstStrmIn->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
907 {
908 int rc2 = pThis->pHostDrvAudio->pfnFiniIn
909 ? pThis->pHostDrvAudio->pfnFiniIn(pThis->pHostDrvAudio, pHstStrmIn) : VINF_SUCCESS;
910 if (RT_SUCCESS(rc2))
911 pHstStrmIn->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
912 }
913
914 drvAudioHstInFreeRes(pHstStrmIn);
915 RTMemFree(pHstStrmIn);
916 }
917 else
918 *ppHstStrmIn = pHstStrmIn;
919
920 LogFlowFuncLeaveRC(rc);
921 return rc;
922}
923
924/**
925 * Writes VM audio output data from the guest stream into the host stream.
926 * The attached host driver backend then will play out the audio in a
927 * later step then.
928 *
929 * @return IPRT status code.
930 * @return int
931 * @param pThis
932 * @param pGstStrmOut
933 * @param pvBuf
934 * @param cbBuf
935 * @param pcbWritten
936 */
937static DECLCALLBACK(int) drvAudioWrite(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut,
938 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
939{
940 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
941 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
942
943 AssertPtrReturn(pGstStrmOut, VERR_INVALID_POINTER);
944 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
945 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
946 /* pcbWritten is optional. */
947
948 int rc = RTCritSectEnter(&pThis->CritSect);
949 if (RT_FAILURE(rc))
950 return rc;
951
952 if (!pThis->pHostDrvAudio->pfnIsEnabled(pThis->pHostDrvAudio, PDMAUDIODIR_OUT))
953 {
954 rc = RTCritSectLeave(&pThis->CritSect);
955 AssertRC(rc);
956
957 return VERR_NOT_AVAILABLE;
958 }
959
960 PPDMAUDIOHSTSTRMOUT pHstStrmOut = pGstStrmOut->pHstStrmOut;
961 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
962
963 AssertMsg(pGstStrmOut->pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED,
964 ("Writing to disabled host output stream \"%s\" not possible\n",
965 pHstStrmOut->MixBuf.pszName));
966
967 if (!AudioMixBufFreeBytes(&pGstStrmOut->MixBuf))
968 {
969 if (pcbWritten)
970 *pcbWritten = 0;
971
972 return RTCritSectLeave(&pThis->CritSect);
973 }
974
975 /*
976 * First, write data from the device emulation into our
977 * guest mixing buffer.
978 */
979 uint32_t cWritten;
980 rc = AudioMixBufWriteAt(&pGstStrmOut->MixBuf, 0 /* Offset in samples */, pvBuf, cbBuf, &cWritten);
981
982 /*
983 * Second, mix the guest mixing buffer with the host mixing
984 * buffer so that the host backend can play the data lateron.
985 */
986 uint32_t cMixed;
987 if ( RT_SUCCESS(rc)
988 && cWritten)
989 {
990 rc = AudioMixBufMixToParent(&pGstStrmOut->MixBuf, cWritten, &cMixed);
991 }
992 else
993 cMixed = 0;
994
995 if (RT_SUCCESS(rc))
996 {
997 /*
998 * Return the number of samples which actually have been mixed
999 * down to the parent, regardless how much samples were written
1000 * into the children buffer.
1001 */
1002 if (pcbWritten)
1003 *pcbWritten = AUDIOMIXBUF_S2B(&pGstStrmOut->MixBuf, cMixed);
1004 }
1005
1006 LogFlowFunc(("%s -> %s: cbBuf=%RU32, cWritten=%RU32 (%RU32 bytes), cMixed=%RU32, rc=%Rrc\n",
1007 pGstStrmOut->MixBuf.pszName, pHstStrmOut->MixBuf.pszName, cbBuf, cWritten,
1008 AUDIOMIXBUF_S2B(&pGstStrmOut->MixBuf, cWritten), cMixed, rc));
1009
1010 int rc2 = RTCritSectLeave(&pThis->CritSect);
1011 if (RT_SUCCESS(rc))
1012 rc = rc2;
1013
1014 return rc;
1015}
1016
1017static PPDMAUDIOHSTSTRMOUT drvAudioFindAnyHstOut(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
1018{
1019 if (pHstStrmOut)
1020 {
1021 if (RTListNodeIsLast(&pThis->lstHstStrmOut, &pHstStrmOut->Node))
1022 return NULL;
1023
1024 return RTListNodeGetNext(&pHstStrmOut->Node, PDMAUDIOHSTSTRMOUT, Node);
1025 }
1026
1027 return RTListGetFirst(&pThis->lstHstStrmOut, PDMAUDIOHSTSTRMOUT, Node);
1028}
1029
1030static PPDMAUDIOHSTSTRMOUT drvAudioHstFindAnyEnabledOut(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMOUT pHostStrmOut)
1031{
1032 while ((pHostStrmOut = drvAudioFindAnyHstOut(pThis, pHostStrmOut)))
1033 {
1034 if (pHostStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED)
1035 return pHostStrmOut;
1036 }
1037
1038 return NULL;
1039}
1040
1041static PPDMAUDIOHSTSTRMOUT drvAudioFindSpecificOut(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
1042 PPDMAUDIOSTREAMCFG pCfg)
1043{
1044 while ((pHstStrmOut = drvAudioFindAnyHstOut(pThis, pHstStrmOut)))
1045 {
1046 if (DrvAudioPCMPropsAreEqual(&pHstStrmOut->Props, pCfg))
1047 return pHstStrmOut;
1048 }
1049
1050 return NULL;
1051}
1052
1053int drvAudioDestroyHstIn(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMIN pHstStrmIn)
1054{
1055 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1056 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
1057
1058 LogFlowFunc(("%s\n", pHstStrmIn->MixBuf.pszName));
1059
1060 int rc = VINF_SUCCESS;
1061
1062 if (!pHstStrmIn->pGstStrmIn)
1063 {
1064 drvAudioHstInFreeRes(pHstStrmIn);
1065
1066 if (RTCritSectIsInitialized(&pHstStrmIn->CritSect))
1067 {
1068 int rc2 = RTCritSectDelete(&pHstStrmIn->CritSect);
1069 AssertRC(rc2);
1070 }
1071
1072 /* Remove from driver instance list. */
1073 RTListNodeRemove(&pHstStrmIn->Node);
1074
1075 RTMemFree(pHstStrmIn);
1076 pThis->cStreamsFreeIn++;
1077 }
1078 else
1079 {
1080 rc = VERR_WRONG_ORDER;
1081 LogFlowFunc(("[%s] Still is being used, skipping\n", pHstStrmIn->MixBuf.pszName));
1082 }
1083
1084 LogFlowFuncLeaveRC(rc);
1085 return rc;
1086}
1087
1088static int drvAudioDestroyGstIn(PDRVAUDIO pThis, PPDMAUDIOGSTSTRMIN pGstStrmIn)
1089{
1090 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1091
1092 if (!pGstStrmIn)
1093 return VINF_SUCCESS;
1094
1095 LogFlowFunc(("%s\n", pGstStrmIn->MixBuf.pszName));
1096
1097 if (pGstStrmIn->State.cRefs > 1) /* Do other objects still have a reference to it? Bail out. */
1098 return VERR_WRONG_ORDER;
1099
1100 AudioMixBufDestroy(&pGstStrmIn->MixBuf);
1101
1102 /* Is this guest stream associate to a host stream? */
1103 if (pGstStrmIn->pHstStrmIn)
1104 {
1105 /* Unlink child. */
1106 pGstStrmIn->pHstStrmIn->pGstStrmIn = NULL;
1107 }
1108
1109 RTMemFree(pGstStrmIn);
1110 pGstStrmIn = NULL;
1111
1112 return VINF_SUCCESS;
1113}
1114
1115static DECLCALLBACK(uint32_t) drvAudioAddRefIn(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrm)
1116{
1117 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1118 AssertPtrReturn(pGstStrm, VERR_INVALID_POINTER);
1119
1120 NOREF(pInterface);
1121
1122 return ++pGstStrm->State.cRefs;
1123}
1124
1125static DECLCALLBACK(uint32_t) drvAudioAddRefOut(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrm)
1126{
1127 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1128 AssertPtrReturn(pGstStrm, VERR_INVALID_POINTER);
1129
1130 NOREF(pInterface);
1131
1132 return ++pGstStrm->State.cRefs;
1133}
1134
1135static DECLCALLBACK(uint32_t) drvAudioReleaseIn(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrm)
1136{
1137 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1138 AssertPtrReturn(pGstStrm, VERR_INVALID_POINTER);
1139
1140 NOREF(pInterface);
1141
1142 Assert(pGstStrm->State.cRefs);
1143 if (pGstStrm->State.cRefs)
1144 pGstStrm->State.cRefs--;
1145
1146 return pGstStrm->State.cRefs;
1147}
1148
1149static DECLCALLBACK(uint32_t) drvAudioReleaseOut(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrm)
1150{
1151 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1152 AssertPtrReturn(pGstStrm, VERR_INVALID_POINTER);
1153
1154 NOREF(pInterface);
1155
1156 Assert(pGstStrm->State.cRefs);
1157 if (pGstStrm->State.cRefs)
1158 pGstStrm->State.cRefs--;
1159
1160 return pGstStrm->State.cRefs;
1161}
1162
1163static DECLCALLBACK(int) drvAudioQueryStatus(PPDMIAUDIOCONNECTOR pInterface,
1164 uint32_t *pcbAvailIn, uint32_t *pcbFreeOut,
1165 uint32_t *pcSamplesLive)
1166{
1167 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1168 /* pcbAvailIn is optional. */
1169 /* pcbFreeOut is optional. */
1170 /* pcSamplesLive is optional. */
1171
1172 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1173
1174 int rc = RTCritSectEnter(&pThis->CritSect);
1175 if (RT_FAILURE(rc))
1176 return rc;
1177
1178 /*
1179 * Playback.
1180 */
1181 uint32_t cSamplesLive = 0;
1182 uint32_t cbFreeOut = UINT32_MAX;
1183
1184 PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
1185 while ((pHstStrmOut = drvAudioHstFindAnyEnabledOut(pThis, pHstStrmOut)))
1186 {
1187 cSamplesLive = AudioMixBufAvail(&pHstStrmOut->MixBuf);
1188
1189 /* Has this stream marked as disabled but there still were guest streams relying
1190 * on it? Check if this stream now can be closed and do so, if possible. */
1191 if ( (pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE)
1192 && !cSamplesLive)
1193 {
1194 /* Stop playing the current (pending) stream. */
1195 int rc2 = drvAudioControlHstOut(pThis, pHstStrmOut, PDMAUDIOSTREAMCMD_DISABLE);
1196 if (RT_SUCCESS(rc2))
1197 {
1198 pHstStrmOut->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
1199
1200 LogFunc(("[%s] Disabling stream\n", pHstStrmOut->MixBuf.pszName));
1201 }
1202 else
1203 LogFunc(("[%s] Backend vetoed against closing output stream, rc=%Rrc\n", pHstStrmOut->MixBuf.pszName, rc2));
1204
1205 continue;
1206 }
1207
1208 LogFlowFunc(("[%s] cSamplesLive=%RU32\n", pHstStrmOut->MixBuf.pszName, cSamplesLive));
1209
1210 /*
1211 * No live samples to play at the moment?
1212 *
1213 * Tell the device emulation for each connected guest stream how many
1214 * bytes are free so that the device emulation can continue writing data to
1215 * these streams.
1216 */
1217 PPDMAUDIOGSTSTRMOUT pGstStrmOut;
1218 uint32_t cbFree2 = UINT32_MAX;
1219 RTListForEach(&pHstStrmOut->lstGstStrmOut, pGstStrmOut, PDMAUDIOGSTSTRMOUT, Node)
1220 {
1221 if (pGstStrmOut->State.fActive)
1222 {
1223 /* Tell the sound device emulation how many samples are free
1224 * so that it can start writing PCM data to us. */
1225 cbFree2 = RT_MIN(cbFree2, AUDIOMIXBUF_S2B_RATIO(&pGstStrmOut->MixBuf,
1226 AudioMixBufFree(&pGstStrmOut->MixBuf)));
1227#ifdef DEBUG_andy
1228 LogFlowFunc(("\t[%s] cbFreeOut=%RU32\n", pGstStrmOut->MixBuf.pszName, cbFree2));
1229#endif
1230 }
1231 }
1232
1233 cbFreeOut = RT_MIN(cbFreeOut, cbFree2);
1234 }
1235
1236 /*
1237 * Recording.
1238 */
1239 uint32_t cbAvailIn = 0;
1240
1241 PPDMAUDIOHSTSTRMIN pHstStrmIn = NULL;
1242 while ((pHstStrmIn = drvAudioFindAnyHstIn(pThis, pHstStrmIn)))
1243 {
1244 /* Disabled? Skip it! */
1245 if (!(pHstStrmIn->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
1246 continue;
1247
1248 /* Call the host backend to capture the audio input data. */
1249 uint32_t cSamplesCaptured;
1250 int rc2 = pThis->pHostDrvAudio->pfnCaptureIn(pThis->pHostDrvAudio, pHstStrmIn,
1251 &cSamplesCaptured);
1252 if (RT_FAILURE(rc2))
1253 continue;
1254
1255 PPDMAUDIOGSTSTRMIN pGstStrmIn = pHstStrmIn->pGstStrmIn;
1256 AssertPtrBreak(pGstStrmIn);
1257
1258 if (pGstStrmIn->State.fActive)
1259 {
1260 cbAvailIn = RT_MAX(cbAvailIn, AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf,
1261 AudioMixBufMixed(&pHstStrmIn->MixBuf)));
1262#ifdef DEBUG_andy
1263 LogFlowFunc(("\t[%s] cbAvailIn=%RU32\n", pHstStrmIn->MixBuf.pszName, cbAvailIn));
1264#endif
1265 }
1266 }
1267
1268 if (RT_SUCCESS(rc))
1269 {
1270 if (cbFreeOut == UINT32_MAX)
1271 cbFreeOut = 0;
1272
1273 if (pcbAvailIn)
1274 *pcbAvailIn = cbAvailIn;
1275
1276 if (pcbFreeOut)
1277 *pcbFreeOut = cbFreeOut;
1278
1279 if (pcSamplesLive)
1280 *pcSamplesLive = cSamplesLive;
1281 }
1282
1283 int rc2 = RTCritSectLeave(&pThis->CritSect);
1284 if (RT_SUCCESS(rc))
1285 rc = rc2;
1286
1287 if (RT_FAILURE(rc))
1288 LogFlowFuncLeaveRC(rc);
1289
1290 return rc;
1291}
1292
1293static DECLCALLBACK(int) drvAudioPlayOut(PPDMIAUDIOCONNECTOR pInterface, uint32_t *pcSamplesPlayed)
1294{
1295 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1296 /* pcSamplesPlayed is optional. */
1297
1298 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1299
1300 int rc = RTCritSectEnter(&pThis->CritSect);
1301 if (RT_FAILURE(rc))
1302 return rc;
1303
1304 /* Backend output (temporarily) disabled / unavailable? */
1305 if (!pThis->pHostDrvAudio->pfnIsEnabled(pThis->pHostDrvAudio, PDMAUDIODIR_OUT))
1306 {
1307 rc = pThis->pHostDrvAudio->pfnGetConf(pThis->pHostDrvAudio, &pThis->BackendCfg);
1308 AssertRC(rc);
1309
1310 if ( !pThis->BackendCfg.cSinks
1311 || !pThis->BackendCfg.cMaxStreamsOut)
1312 {
1313 int rc2 = RTCritSectLeave(&pThis->CritSect);
1314 AssertRC(rc2);
1315
1316 return VERR_NOT_AVAILABLE;
1317 }
1318 }
1319
1320 /*
1321 * Process all enabled host output streams.
1322 */
1323 uint32_t cSamplesPlayedMax = 0;
1324 PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
1325 while ((pHstStrmOut = drvAudioHstFindAnyEnabledOut(pThis, pHstStrmOut)))
1326 {
1327#if 0
1328 uint32_t cStreamsLive;
1329 uint32_t cSamplesLive = drvAudioHstOutSamplesLive(pHstStrmOut, &cStreamsLive);
1330 if (!cStreamsLive)
1331 cSamplesLive = 0;
1332
1333 /* Has this stream marked as disabled but there still were guest streams relying
1334 * on it? Check if this stream now can be closed and do so, if possible. */
1335 if ( pHstStrmOut->fPendingDisable
1336 && !cStreamsLive)
1337 {
1338 /* Stop playing the current (pending) stream. */
1339 int rc2 = pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut,
1340 PDMAUDIOSTREAMCMD_DISABLE);
1341 if (RT_SUCCESS(rc2))
1342 {
1343 pHstStrmOut->fEnabled = false;
1344 pHstStrmOut->fPendingDisable = false;
1345
1346 LogFunc(("\t%p: Disabling stream\n", pHstStrmOut));
1347 }
1348 else
1349 LogFunc(("\t%p: Backend vetoed against closing output stream, rc=%Rrc\n",
1350 pHstStrmOut, rc2));
1351
1352 continue;
1353 }
1354#endif
1355
1356 uint32_t cSamplesPlayed = 0;
1357 int rc2 = pThis->pHostDrvAudio->pfnPlayOut(pThis->pHostDrvAudio, pHstStrmOut, &cSamplesPlayed);
1358 if (RT_FAILURE(rc2))
1359 {
1360 int rc3 = pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut, PDMAUDIOSTREAMCMD_DISABLE);
1361 AssertRC(rc3);
1362 }
1363 else
1364 cSamplesPlayedMax = RT_MAX(cSamplesPlayed, cSamplesPlayedMax);
1365
1366 LogFlowFunc(("\t[%s] cSamplesPlayed=%RU32, cSamplesPlayedMax=%RU32, rc=%Rrc\n",
1367 pHstStrmOut->MixBuf.pszName, cSamplesPlayed, cSamplesPlayedMax, rc2));
1368
1369 bool fNeedsCleanup = false;
1370
1371 PPDMAUDIOGSTSTRMOUT pGstStrmOut;
1372 RTListForEach(&pHstStrmOut->lstGstStrmOut, pGstStrmOut, PDMAUDIOGSTSTRMOUT, Node)
1373 {
1374 if ( !pGstStrmOut->State.fActive
1375 && pGstStrmOut->State.fEmpty)
1376 continue;
1377
1378 if (AudioMixBufIsEmpty(&pGstStrmOut->MixBuf))
1379 {
1380 pGstStrmOut->State.fEmpty = true;
1381 fNeedsCleanup |= !pGstStrmOut->State.fActive;
1382 }
1383 }
1384
1385 if (fNeedsCleanup)
1386 {
1387 RTListForEach(&pHstStrmOut->lstGstStrmOut, pGstStrmOut, PDMAUDIOGSTSTRMOUT, Node)
1388 {
1389 if (!pGstStrmOut->State.fActive)
1390 drvAudioDestroyGstOut(pThis, pGstStrmOut);
1391 }
1392 }
1393 }
1394
1395 if (RT_SUCCESS(rc))
1396 {
1397 if (pcSamplesPlayed)
1398 *pcSamplesPlayed = cSamplesPlayedMax;
1399 }
1400
1401 int rc2 = RTCritSectLeave(&pThis->CritSect);
1402 if (RT_SUCCESS(rc))
1403 rc = rc2;
1404
1405 if (RT_FAILURE(rc))
1406 LogFlowFuncLeaveRC(rc);
1407
1408 return rc;
1409}
1410
1411#ifdef VBOX_WITH_AUDIO_CALLBACKS
1412static PPDMAUDIOCALLBACK drvAudioCallbackDuplicate(PPDMAUDIOCALLBACK pCB)
1413{
1414 PPDMAUDIOCALLBACK pCBCopy = (PPDMAUDIOCALLBACK)RTMemDup((void *)pCB, sizeof(PDMAUDIOCALLBACK));
1415 if (!pCBCopy)
1416 return NULL;
1417
1418 if (pCB->pvCtx)
1419 {
1420 pCBCopy->pvCtx = RTMemDup(pCB->pvCtx, pCB->cbCtx);
1421 if (!pCBCopy->pvCtx)
1422 {
1423 RTMemFree(pCBCopy);
1424 return NULL;
1425 }
1426
1427 pCBCopy->cbCtx = pCB->cbCtx;
1428 }
1429
1430 return pCBCopy;
1431}
1432
1433static void drvAudioCallbackDestroy(PPDMAUDIOCALLBACK pCB)
1434{
1435 if (!pCB)
1436 return;
1437
1438 RTListNodeRemove(&pCB->Node);
1439 if (pCB->pvCtx)
1440 {
1441 Assert(pCB->cbCtx);
1442 RTMemFree(pCB->pvCtx);
1443 }
1444 RTMemFree(pCB);
1445}
1446
1447static DECLCALLBACK(int) drvAudioRegisterCallbacks(PPDMIAUDIOCONNECTOR pInterface,
1448 PPDMAUDIOCALLBACK paCallbacks, size_t cCallbacks)
1449{
1450 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1451 AssertPtrReturn(paCallbacks, VERR_INVALID_POINTER);
1452 AssertReturn(cCallbacks, VERR_INVALID_PARAMETER);
1453
1454 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1455
1456 int rc = RTCritSectEnter(&pThis->CritSect);
1457 if (RT_FAILURE(rc))
1458 return rc;
1459
1460 for (size_t i = 0; i < cCallbacks; i++)
1461 {
1462 PPDMAUDIOCALLBACK pCB = drvAudioCallbackDuplicate(&paCallbacks[i]);
1463 if (!pCB)
1464 {
1465 rc = VERR_NO_MEMORY;
1466 break;
1467 }
1468
1469 switch (pCB->enmType)
1470 {
1471 case PDMAUDIOCALLBACKTYPE_INPUT:
1472 RTListAppend(&pThis->lstCBIn, &pCB->Node);
1473 break;
1474
1475 case PDMAUDIOCALLBACKTYPE_OUTPUT:
1476 RTListAppend(&pThis->lstCBOut, &pCB->Node);
1477 break;
1478
1479 default:
1480 AssertMsgFailed(("Not supported\n"));
1481 break;
1482 }
1483 }
1484
1485 /** @todo Undo allocations on error. */
1486
1487 int rc2 = RTCritSectLeave(&pThis->CritSect);
1488 if (RT_SUCCESS(rc))
1489 rc = rc2;
1490
1491 return rc;
1492}
1493
1494static DECLCALLBACK(int) drvAudioCallback(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIOCALLBACKTYPE enmType,
1495 void *pvUser, size_t cbUser)
1496{
1497 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1498 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
1499 AssertReturn(cbUser, VERR_INVALID_PARAMETER);
1500
1501 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1502 PRTLISTANCHOR pListAnchor = NULL;
1503
1504 switch (enmType)
1505 {
1506 case PDMAUDIOCALLBACKTYPE_INPUT:
1507 pListAnchor = &pThis->lstCBIn;
1508 break;
1509
1510 case PDMAUDIOCALLBACKTYPE_OUTPUT:
1511 pListAnchor = &pThis->lstCBOut;
1512 break;
1513
1514 default:
1515 AssertMsgFailed(("Not supported\n"));
1516 break;
1517 }
1518
1519 if (pListAnchor)
1520 {
1521 PPDMAUDIOCALLBACK pCB;
1522 RTListForEach(pListAnchor, pCB, PDMAUDIOCALLBACK, Node)
1523 {
1524 Assert(pCB->enmType == enmType);
1525 pCB->pfnCallback(enmType, pCB->pvCtx, pCB->cbCtx, pvUser, cbUser);
1526 }
1527 }
1528
1529 return VINF_SUCCESS;
1530}
1531#endif
1532
1533static int drvAudioHostInit(PCFGMNODE pCfgHandle, PDRVAUDIO pThis)
1534{
1535 /* pCfgHandle is optional. */
1536 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1537
1538 NOREF(pCfgHandle);
1539
1540 LogFlowFuncEnter();
1541
1542 AssertPtr(pThis->pHostDrvAudio);
1543 int rc = pThis->pHostDrvAudio->pfnInit(pThis->pHostDrvAudio);
1544 if (RT_FAILURE(rc))
1545 {
1546 LogFlowFunc(("Initialization of lower driver failed with rc=%Rrc\n", rc));
1547 return rc;
1548 }
1549
1550 /* Get the configuration data from backend. */
1551 rc = pThis->pHostDrvAudio->pfnGetConf(pThis->pHostDrvAudio, &pThis->BackendCfg);
1552 if (RT_FAILURE(rc))
1553 {
1554 LogFlowFunc(("Getting backend configuration failed with rc=%Rrc\n", rc));
1555 return rc;
1556 }
1557
1558 pThis->cStreamsFreeIn = 0;
1559 pThis->cStreamsFreeOut = 0;
1560
1561 if (pThis->BackendCfg.cSinks)
1562 {
1563 Assert(pThis->BackendCfg.cbStreamOut);
1564
1565 pThis->cStreamsFreeOut = pThis->BackendCfg.cMaxStreamsOut;
1566 }
1567
1568 if (pThis->BackendCfg.cSources)
1569 {
1570 Assert(pThis->BackendCfg.cbStreamIn);
1571
1572 pThis->cStreamsFreeIn = pThis->BackendCfg.cMaxStreamsIn;
1573 }
1574
1575 LogFlowFunc(("cStreamsFreeIn=%RU8, cStreamsFreeOut=%RU8\n", pThis->cStreamsFreeIn, pThis->cStreamsFreeOut));
1576
1577 LogRel2(("Audio: Host audio backend supports %RU32 input streams and %RU32 output streams at once\n",
1578 /* Clamp for logging. Unlimited streams are defined by UINT32_MAX. */
1579 RT_MIN(64, pThis->cStreamsFreeIn), RT_MIN(64, pThis->cStreamsFreeOut)));
1580
1581 LogFlowFuncLeave();
1582 return VINF_SUCCESS;
1583}
1584
1585static void drvAudioStateHandler(PPDMDRVINS pDrvIns, PDMAUDIOSTREAMCMD enmCmd)
1586{
1587 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1588 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1589
1590 LogFlowFunc(("enmCmd=%ld\n", enmCmd));
1591
1592 if (!pThis->pHostDrvAudio)
1593 return;
1594
1595 PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
1596 while ((pHstStrmOut = drvAudioFindAnyHstOut(pThis, pHstStrmOut)))
1597 drvAudioControlHstOut(pThis, pHstStrmOut, enmCmd);
1598
1599 PPDMAUDIOHSTSTRMIN pHstStrmIn = NULL;
1600 while ((pHstStrmIn = drvAudioFindAnyHstIn(pThis, pHstStrmIn)))
1601 drvAudioControlHstIn(pThis, pHstStrmIn, enmCmd);
1602}
1603
1604static DECLCALLBACK(int) drvAudioInit(PCFGMNODE pCfgHandle, PPDMDRVINS pDrvIns)
1605{
1606 AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
1607 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
1608
1609 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1610 LogFlowFunc(("pThis=%p, pDrvIns=%p\n", pThis, pDrvIns));
1611
1612 RTListInit(&pThis->lstHstStrmIn);
1613 RTListInit(&pThis->lstHstStrmOut);
1614#ifdef VBOX_WITH_AUDIO_CALLBACKS
1615 RTListInit(&pThis->lstCBIn);
1616 RTListInit(&pThis->lstCBOut);
1617#endif
1618
1619 int rc = RTCritSectInit(&pThis->CritSect);
1620
1621 /** @todo Add audio driver options. */
1622
1623 /*
1624 * If everything went well, initialize the lower driver.
1625 */
1626 if (RT_SUCCESS(rc))
1627 rc = drvAudioHostInit(pCfgHandle, pThis);
1628
1629 LogFlowFuncLeaveRC(rc);
1630 return rc;
1631}
1632
1633static DECLCALLBACK(int) drvAudioRead(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrmIn,
1634 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1635{
1636 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1637 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1638
1639 if (!pGstStrmIn)
1640 return VERR_NOT_AVAILABLE;
1641
1642 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1643 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1644 /* pcbWritten is optional. */
1645
1646 int rc = RTCritSectEnter(&pThis->CritSect);
1647 if (RT_FAILURE(rc))
1648 return rc;
1649
1650 if (!pThis->pHostDrvAudio->pfnIsEnabled(pThis->pHostDrvAudio, PDMAUDIODIR_IN))
1651 {
1652 if (pcbRead)
1653 *pcbRead = 0;
1654
1655 return RTCritSectLeave(&pThis->CritSect);
1656 }
1657
1658 PPDMAUDIOHSTSTRMIN pHstStrmIn = pGstStrmIn->pHstStrmIn;
1659 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
1660
1661 AssertMsg(pGstStrmIn->pHstStrmIn->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED,
1662 ("Reading from disabled host input stream \"%s\" not possible\n", pGstStrmIn->MixBuf.pszName));
1663
1664 /*
1665 * Read from the parent buffer (that is, the guest buffer) which
1666 * should have the audio data in the format the guest needs.
1667 */
1668 uint32_t cRead;
1669 rc = AudioMixBufReadCirc(&pGstStrmIn->MixBuf, pvBuf, cbBuf, &cRead);
1670 if (RT_SUCCESS(rc))
1671 {
1672 AudioMixBufFinish(&pGstStrmIn->MixBuf, cRead);
1673
1674 if (pcbRead)
1675 *pcbRead = AUDIOMIXBUF_S2B(&pGstStrmIn->MixBuf, cRead);
1676 }
1677
1678 LogFlowFunc(("cRead=%RU32 (%RU32 bytes), rc=%Rrc\n",
1679 cRead, AUDIOMIXBUF_S2B(&pGstStrmIn->MixBuf, cRead), rc));
1680
1681 int rc2 = RTCritSectLeave(&pThis->CritSect);
1682 if (RT_SUCCESS(rc))
1683 rc = rc2;
1684
1685 return rc;
1686}
1687
1688static DECLCALLBACK(int) drvAudioEnableOut(PPDMIAUDIOCONNECTOR pInterface,
1689 PPDMAUDIOGSTSTRMOUT pGstStrmOut, bool fEnable)
1690{
1691 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1692
1693 if (!pGstStrmOut)
1694 return VERR_NOT_AVAILABLE;
1695
1696 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1697
1698 int rc = VINF_SUCCESS;
1699
1700 PPDMAUDIOHSTSTRMOUT pHstStrmOut = pGstStrmOut->pHstStrmOut;
1701 AssertPtr(pHstStrmOut);
1702
1703 if (fEnable)
1704 {
1705 /* Is a pending disable outstanding? Then disable first. */
1706 if (pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE)
1707 {
1708 rc = drvAudioControlHstOut(pThis, pHstStrmOut, PDMAUDIOSTREAMCMD_DISABLE);
1709 if (RT_SUCCESS(rc))
1710 pHstStrmOut->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
1711 }
1712
1713 if (RT_SUCCESS(rc))
1714 rc = drvAudioControlHstOut(pThis, pHstStrmOut, PDMAUDIOSTREAMCMD_ENABLE);
1715 }
1716 else /* Disable */
1717 {
1718 if (pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED)
1719 {
1720 uint32_t cGstStrmsActive = 0;
1721
1722 /*
1723 * Check if there are any active guest streams assigned
1724 * to this host stream which still are being marked as active.
1725 *
1726 * In that case we have to defer closing the host stream and
1727 * wait until all guest streams have been finished.
1728 */
1729 PPDMAUDIOGSTSTRMOUT pIter;
1730 RTListForEach(&pHstStrmOut->lstGstStrmOut, pIter, PDMAUDIOGSTSTRMOUT, Node)
1731 {
1732 if (pIter->State.fActive)
1733 {
1734 cGstStrmsActive++;
1735 break; /* At least one assigned & active guest stream is enough. */
1736 }
1737 }
1738
1739 /* Do we need to defer closing the host stream? */
1740 if (cGstStrmsActive >= 1)
1741 pHstStrmOut->fStatus |= PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
1742
1743 /* Can we close the host stream now instead of deferring it? */
1744 if (!(pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE))
1745 rc = drvAudioControlHstOut(pThis, pHstStrmOut, PDMAUDIOSTREAMCMD_DISABLE);
1746 }
1747 }
1748
1749 if (RT_SUCCESS(rc))
1750 pGstStrmOut->State.fActive = fEnable;
1751
1752 LogFlowFunc(("%s: fEnable=%RTbool, fStatus=0x%x, rc=%Rrc\n",
1753 pGstStrmOut->MixBuf.pszName, fEnable, pHstStrmOut->fStatus, rc));
1754
1755 return rc;
1756}
1757
1758static DECLCALLBACK(int) drvAudioEnableIn(PPDMIAUDIOCONNECTOR pInterface,
1759 PPDMAUDIOGSTSTRMIN pGstStrmIn, bool fEnable)
1760{
1761 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1762
1763 if (!pGstStrmIn)
1764 return VERR_NOT_AVAILABLE;
1765
1766 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1767
1768 int rc = VINF_SUCCESS;
1769
1770 PPDMAUDIOHSTSTRMIN pHstStrmIn = pGstStrmIn->pHstStrmIn;
1771 AssertPtr(pHstStrmIn);
1772
1773 rc = drvAudioControlHstIn(pThis, pHstStrmIn,
1774 fEnable ? PDMAUDIOSTREAMCMD_ENABLE : PDMAUDIOSTREAMCMD_DISABLE);
1775 if (RT_SUCCESS(rc))
1776 pGstStrmIn->State.fActive = fEnable;
1777
1778 LogFlowFunc(("%s: fEnable=%RTbool, rc=%Rrc\n", pGstStrmIn->MixBuf.pszName, fEnable, rc));
1779
1780 return rc;
1781}
1782
1783static DECLCALLBACK(bool) drvAudioIsValidIn(PPDMIAUDIOCONNECTOR pInterface,
1784 PPDMAUDIOGSTSTRMIN pGstStrmIn)
1785{
1786 return (pGstStrmIn != NULL);
1787}
1788
1789static DECLCALLBACK(bool) drvAudioIsValidOut(PPDMIAUDIOCONNECTOR pInterface,
1790 PPDMAUDIOGSTSTRMOUT pGstStrmOut)
1791{
1792 return (pGstStrmOut != NULL);
1793}
1794
1795static DECLCALLBACK(int) drvAudioCreateIn(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAMCFG pCfg,
1796 PPDMAUDIOGSTSTRMIN *ppGstStrmIn)
1797{
1798 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1799 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1800 AssertPtrReturn(ppGstStrmIn, VERR_INVALID_POINTER);
1801
1802 Assert(pCfg->enmDir == PDMAUDIODIR_IN);
1803
1804 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1805
1806 int rc = RTCritSectEnter(&pThis->CritSect);
1807 if (RT_FAILURE(rc))
1808 return rc;
1809
1810 LogFlowFunc(("szName=%s\n", pCfg->szName));
1811
1812 if (!drvAudioStreamCfgIsValid(pCfg))
1813 {
1814 LogFunc(("Input stream configuration is not valid, bailing out\n"));
1815 rc = VERR_INVALID_PARAMETER;
1816 }
1817
1818 if (RT_SUCCESS(rc))
1819 {
1820 PPDMAUDIOGSTSTRMIN pGstStrmIn;
1821 rc = drvAudioCreateStreamPairIn(pThis, pCfg, &pGstStrmIn);
1822 if (RT_SUCCESS(rc))
1823 *ppGstStrmIn = pGstStrmIn;
1824 }
1825
1826 int rc2 = RTCritSectLeave(&pThis->CritSect);
1827 if (RT_SUCCESS(rc))
1828 rc = rc2;
1829
1830 LogFlowFuncLeaveRC(rc);
1831 return rc;
1832}
1833
1834static DECLCALLBACK(int) drvAudioCreateOut(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAMCFG pCfg,
1835 PPDMAUDIOGSTSTRMOUT *ppGstStrmOut)
1836{
1837 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1838 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1839 AssertPtrReturn(ppGstStrmOut, VERR_INVALID_POINTER);
1840
1841 Assert(pCfg->enmDir == PDMAUDIODIR_OUT);
1842
1843 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1844
1845 int rc = RTCritSectEnter(&pThis->CritSect);
1846 if (RT_FAILURE(rc))
1847 return rc;
1848
1849 LogFlowFunc(("pszName=%s\n", pCfg->szName));
1850
1851 if (!drvAudioStreamCfgIsValid(pCfg))
1852 {
1853 LogFunc(("Output stream configuration is not valid, bailing out\n"));
1854 rc = VERR_INVALID_PARAMETER;
1855 }
1856
1857 if (RT_SUCCESS(rc))
1858 {
1859 PPDMAUDIOGSTSTRMOUT pGstStrmOut;
1860 rc = drvAudioCreateStreamPairOut(pThis, pCfg, &pGstStrmOut);
1861 if (RT_SUCCESS(rc))
1862 *ppGstStrmOut = pGstStrmOut;
1863 }
1864
1865 int rc2 = RTCritSectLeave(&pThis->CritSect);
1866 if (RT_SUCCESS(rc))
1867 rc = rc2;
1868
1869 LogFlowFuncLeaveRC(rc);
1870 return rc;
1871}
1872
1873static DECLCALLBACK(int) drvAudioGetConfig(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOBACKENDCFG pCfg)
1874{
1875 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1876 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1877
1878 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1879
1880 int rc = RTCritSectEnter(&pThis->CritSect);
1881 if (RT_FAILURE(rc))
1882 return rc;
1883
1884 rc = pThis->pHostDrvAudio->pfnGetConf(pThis->pHostDrvAudio, pCfg);
1885
1886 int rc2 = RTCritSectLeave(&pThis->CritSect);
1887 if (RT_SUCCESS(rc))
1888 rc = rc2;
1889
1890 LogFlowFuncLeaveRC(rc);
1891 return rc;
1892}
1893
1894static DECLCALLBACK(bool) drvAudioIsActiveIn(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrmIn)
1895{
1896 AssertPtrReturn(pInterface, false);
1897 /* pGstStrmIn is optional. */
1898
1899 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1900
1901 int rc2 = RTCritSectEnter(&pThis->CritSect);
1902 AssertRC(rc2);
1903
1904 bool fRet = pGstStrmIn ? pGstStrmIn->State.fActive : false;
1905
1906 rc2 = RTCritSectLeave(&pThis->CritSect);
1907 AssertRC(rc2);
1908
1909 return fRet;
1910}
1911
1912static DECLCALLBACK(bool) drvAudioIsActiveOut(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut)
1913{
1914 AssertPtrReturn(pInterface, false);
1915 /* pGstStrmOut is optional. */
1916
1917 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1918
1919 int rc2 = RTCritSectEnter(&pThis->CritSect);
1920 AssertRC(rc2);
1921
1922 bool fRet = pGstStrmOut ? pGstStrmOut->State.fActive : false;
1923
1924 rc2 = RTCritSectLeave(&pThis->CritSect);
1925 AssertRC(rc2);
1926
1927 return fRet;
1928}
1929
1930static DECLCALLBACK(void) drvAudioDestroyIn(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrmIn)
1931{
1932 AssertPtrReturnVoid(pInterface);
1933 /* pGstStrmIn is optional. */
1934
1935 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1936
1937 if (!pGstStrmIn)
1938 return;
1939
1940 int rc2 = RTCritSectEnter(&pThis->CritSect);
1941 AssertRC(rc2);
1942
1943 rc2 = drvAudioDestroyGstIn(pThis, pGstStrmIn);
1944 AssertRC(rc2);
1945
1946 rc2 = RTCritSectLeave(&pThis->CritSect);
1947 AssertRC(rc2);
1948}
1949
1950static DECLCALLBACK(void) drvAudioDestroyOut(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut)
1951{
1952 AssertPtrReturnVoid(pInterface);
1953 /* pGstStrmOut is optional. */
1954
1955 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1956
1957 if (!pGstStrmOut)
1958 return;
1959
1960 int rc2 = RTCritSectEnter(&pThis->CritSect);
1961 AssertRC(rc2);
1962
1963 rc2 = drvAudioDestroyGstOut(pThis, pGstStrmOut);
1964 AssertRC(rc2);
1965
1966 rc2 = RTCritSectLeave(&pThis->CritSect);
1967 AssertRC(rc2);
1968}
1969
1970void drvAudioHstOutFreeRes(PPDMAUDIOHSTSTRMOUT pHstStrmOut)
1971{
1972 AssertPtrReturnVoid(pHstStrmOut);
1973 AudioMixBufDestroy(&pHstStrmOut->MixBuf);
1974}
1975
1976/********************************************************************/
1977
1978/**
1979 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1980 */
1981static DECLCALLBACK(void *) drvAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1982{
1983 LogFlowFunc(("pInterface=%p, pszIID=%s\n", pInterface, pszIID));
1984
1985 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1986 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1987
1988 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1989 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIAUDIOCONNECTOR, &pThis->IAudioConnector);
1990
1991 return NULL;
1992}
1993
1994/**
1995 * Power Off notification.
1996 *
1997 * @param pDrvIns The driver instance data.
1998 */
1999static DECLCALLBACK(void) drvAudioPowerOff(PPDMDRVINS pDrvIns)
2000{
2001 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2002
2003 LogFlowFuncEnter();
2004
2005 /* First, disable all streams. */
2006 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_DISABLE);
2007
2008 int rc2;
2009
2010 PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
2011 while ((pHstStrmOut = drvAudioFindAnyHstOut(pThis, pHstStrmOut)))
2012 {
2013 if (pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
2014 {
2015 rc2 = pThis->pHostDrvAudio->pfnFiniOut
2016 ? pThis->pHostDrvAudio->pfnFiniOut(pThis->pHostDrvAudio, pHstStrmOut) : VINF_SUCCESS;
2017 if (RT_SUCCESS(rc2))
2018 pHstStrmOut->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
2019 }
2020 else
2021 rc2 = VINF_SUCCESS;
2022
2023 AssertMsgRC(rc2, ("Host output stream %p failed to uninit: %Rrc\n", pHstStrmOut, rc2));
2024 }
2025
2026 PPDMAUDIOHSTSTRMIN pHstStrmIn = NULL;
2027 while ((pHstStrmIn = drvAudioFindAnyHstIn(pThis, pHstStrmIn)))
2028 {
2029 if (pHstStrmIn->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
2030 {
2031 rc2 = pThis->pHostDrvAudio->pfnFiniIn
2032 ? pThis->pHostDrvAudio->pfnFiniIn(pThis->pHostDrvAudio, pHstStrmIn) : VINF_SUCCESS;
2033 if (RT_SUCCESS(rc2))
2034 pHstStrmIn->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
2035 }
2036 else
2037 rc2 = VINF_SUCCESS;
2038
2039 AssertMsgRC(rc2, ("Host input stream %p failed to uninit: %Rrc\n", pHstStrmIn, rc2));
2040 }
2041
2042 /*
2043 * Last call for the driver below us.
2044 * Let it know that we reached end of life.
2045 */
2046 if (pThis->pHostDrvAudio->pfnShutdown)
2047 pThis->pHostDrvAudio->pfnShutdown(pThis->pHostDrvAudio);
2048
2049 pThis->pHostDrvAudio = NULL;
2050
2051 LogFlowFuncLeave();
2052}
2053
2054/**
2055 * Constructs an audio driver instance.
2056 *
2057 * @copydoc FNPDMDRVCONSTRUCT
2058 */
2059static DECLCALLBACK(int) drvAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
2060{
2061 LogFlowFunc(("pDrvIns=%#p, pCfgHandle=%#p, fFlags=%x\n", pDrvIns, pCfgHandle, fFlags));
2062
2063 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2064 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
2065
2066 /*
2067 * Init the static parts.
2068 */
2069 pThis->pDrvIns = pDrvIns;
2070 /* IBase. */
2071 pDrvIns->IBase.pfnQueryInterface = drvAudioQueryInterface;
2072 /* IAudioConnector. */
2073 pThis->IAudioConnector.pfnQueryStatus = drvAudioQueryStatus;
2074 pThis->IAudioConnector.pfnAddRefIn = drvAudioAddRefIn;
2075 pThis->IAudioConnector.pfnAddRefOut = drvAudioAddRefOut;
2076 pThis->IAudioConnector.pfnReleaseIn = drvAudioReleaseIn;
2077 pThis->IAudioConnector.pfnReleaseOut = drvAudioReleaseOut;
2078 pThis->IAudioConnector.pfnRead = drvAudioRead;
2079 pThis->IAudioConnector.pfnWrite = drvAudioWrite;
2080 pThis->IAudioConnector.pfnGetConfig = drvAudioGetConfig;
2081 pThis->IAudioConnector.pfnIsActiveIn = drvAudioIsActiveIn;
2082 pThis->IAudioConnector.pfnIsActiveOut = drvAudioIsActiveOut;
2083 pThis->IAudioConnector.pfnIsValidIn = drvAudioIsValidIn;
2084 pThis->IAudioConnector.pfnIsValidOut = drvAudioIsValidOut;
2085 pThis->IAudioConnector.pfnEnableOut = drvAudioEnableOut;
2086 pThis->IAudioConnector.pfnEnableIn = drvAudioEnableIn;
2087 pThis->IAudioConnector.pfnDestroyIn = drvAudioDestroyIn;
2088 pThis->IAudioConnector.pfnDestroyOut = drvAudioDestroyOut;
2089 pThis->IAudioConnector.pfnCreateIn = drvAudioCreateIn;
2090 pThis->IAudioConnector.pfnCreateOut = drvAudioCreateOut;
2091 pThis->IAudioConnector.pfnPlayOut = drvAudioPlayOut;
2092#ifdef VBOX_WITH_AUDIO_CALLBACKS
2093 pThis->IAudioConnector.pfnRegisterCallbacks = drvAudioRegisterCallbacks;
2094 pThis->IAudioConnector.pfnCallback = drvAudioCallback;
2095#endif
2096
2097 /*
2098 * Attach driver below and query its connector interface.
2099 */
2100 PPDMIBASE pDownBase;
2101 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pDownBase);
2102 if (RT_FAILURE(rc))
2103 {
2104 LogRel(("Audio: Failed to attach to driver %p below (flags=0x%x), rc=%Rrc\n",
2105 pDrvIns, fFlags, rc));
2106 return rc;
2107 }
2108
2109 pThis->pHostDrvAudio = PDMIBASE_QUERY_INTERFACE(pDownBase, PDMIHOSTAUDIO);
2110 if (!pThis->pHostDrvAudio)
2111 {
2112 LogRel(("Audio: Failed to query interface for underlying host driver\n"));
2113 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
2114 N_("Host audio backend missing or invalid"));
2115 }
2116
2117 rc = drvAudioInit(pCfgHandle, pDrvIns);
2118 if (RT_SUCCESS(rc))
2119 {
2120 pThis->fTerminate = false;
2121 pThis->pDrvIns = pDrvIns;
2122 }
2123
2124 LogFlowFuncLeaveRC(rc);
2125 return rc;
2126}
2127
2128/**
2129 * Destructs an audio driver instance.
2130 *
2131 * @copydoc FNPDMDRVDESTRUCT
2132 */
2133static DECLCALLBACK(void) drvAudioDestruct(PPDMDRVINS pDrvIns)
2134{
2135 LogFlowFuncEnter();
2136
2137 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
2138 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2139
2140 int rc2 = RTCritSectEnter(&pThis->CritSect);
2141 AssertRC(rc2);
2142
2143 /*
2144 * Note: No calls here to the driver below us anymore,
2145 * as PDM already has destroyed it.
2146 * If you need to call something from the host driver,
2147 * do this in drvAudioPowerOff() instead.
2148 */
2149
2150 /*
2151 * Destroy all host input streams.
2152 */
2153 PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
2154 while ((pHstStrmOut = drvAudioFindAnyHstOut(pThis, pHstStrmOut)))
2155 {
2156 rc2 = drvAudioDestroyHstOut(pThis, pHstStrmOut);
2157 AssertRC(rc2);
2158
2159 /* Sanity. */
2160 Assert(RTListIsEmpty(&pHstStrmOut->lstGstStrmOut));
2161
2162 pHstStrmOut = NULL;
2163 }
2164
2165 /*
2166 * Destroy all host input streams.
2167 */
2168 PPDMAUDIOHSTSTRMIN pHstStrmIn = NULL;
2169 while ((pHstStrmIn = drvAudioFindAnyHstIn(pThis, pHstStrmIn)))
2170 {
2171 rc2 = drvAudioDestroyHstIn(pThis, pHstStrmIn);
2172 AssertRC(rc2);
2173
2174 /* Sanity. */
2175 Assert(pHstStrmIn->pGstStrmIn == NULL);
2176
2177 pHstStrmIn = NULL;
2178 }
2179
2180 /* Sanity. */
2181 Assert(RTListIsEmpty(&pThis->lstHstStrmIn));
2182 Assert(RTListIsEmpty(&pThis->lstHstStrmOut));
2183
2184#ifdef VBOX_WITH_AUDIO_CALLBACKS
2185 /*
2186 * Destroy callbacks, if any.
2187 */
2188 PPDMAUDIOCALLBACK pCB, pCBNext;
2189 RTListForEachSafe(&pThis->lstCBIn, pCB, pCBNext, PDMAUDIOCALLBACK, Node)
2190 drvAudioCallbackDestroy(pCB);
2191
2192 RTListForEachSafe(&pThis->lstCBOut, pCB, pCBNext, PDMAUDIOCALLBACK, Node)
2193 drvAudioCallbackDestroy(pCB);
2194#endif
2195
2196 rc2 = RTCritSectLeave(&pThis->CritSect);
2197 AssertRC(rc2);
2198
2199 rc2 = RTCritSectDelete(&pThis->CritSect);
2200 AssertRC(rc2);
2201
2202 LogFlowFuncLeave();
2203}
2204
2205/**
2206 * Suspend notification.
2207 *
2208 * @param pDrvIns The driver instance data.
2209 */
2210static DECLCALLBACK(void) drvAudioSuspend(PPDMDRVINS pDrvIns)
2211{
2212 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_PAUSE);
2213}
2214
2215/**
2216 * Resume notification.
2217 *
2218 * @param pDrvIns The driver instance data.
2219 */
2220static DECLCALLBACK(void) drvAudioResume(PPDMDRVINS pDrvIns)
2221{
2222 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_RESUME);
2223}
2224
2225/**
2226 * Audio driver registration record.
2227 */
2228const PDMDRVREG g_DrvAUDIO =
2229{
2230 /* u32Version */
2231 PDM_DRVREG_VERSION,
2232 /* szName */
2233 "AUDIO",
2234 /* szRCMod */
2235 "",
2236 /* szR0Mod */
2237 "",
2238 /* pszDescription */
2239 "Audio connector driver",
2240 /* fFlags */
2241 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2242 /* fClass */
2243 PDM_DRVREG_CLASS_AUDIO,
2244 /* cMaxInstances */
2245 2,
2246 /* cbInstance */
2247 sizeof(DRVAUDIO),
2248 /* pfnConstruct */
2249 drvAudioConstruct,
2250 /* pfnDestruct */
2251 drvAudioDestruct,
2252 /* pfnRelocate */
2253 NULL,
2254 /* pfnIOCtl */
2255 NULL,
2256 /* pfnPowerOn */
2257 NULL,
2258 /* pfnReset */
2259 NULL,
2260 /* pfnSuspend */
2261 drvAudioSuspend,
2262 /* pfnResume */
2263 drvAudioResume,
2264 /* pfnAttach */
2265 NULL,
2266 /* pfnDetach */
2267 NULL,
2268 /* pfnPowerOff */
2269 drvAudioPowerOff,
2270 /* pfnSoftReset */
2271 NULL,
2272 /* u32EndVersion */
2273 PDM_DRVREG_VERSION
2274};
2275
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