VirtualBox

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

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

Audio: Update on infrastructure:

  • More work on HDA stream interleaving + surround support
  • The mixer can now (optionally) act as a supplemental layer between audio connector interface and device emulation (where applicable)
  • Multiple LUN streams can be bound to a certain sink, which in turn then can be treated as separate input/output channels
  • Unified more code which was duplicated between different audio device emulations
  • Tiny bit of documentation

Work in progress.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 67.0 KB
Line 
1/* $Id: DrvAudio.cpp 60925 2016-05-10 13:27:44Z 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_NO_MORE_HANDLES;
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
869 {
870 uint32_t cSamples = 0;
871 rc = pThis->pHostDrvAudio->pfnInitIn
872 ? pThis->pHostDrvAudio->pfnInitIn(pThis->pHostDrvAudio, pHstStrmIn,
873 pCfg, pCfg->DestSource.Source, &cSamples) : VINF_SUCCESS;
874 if (RT_FAILURE(rc))
875 {
876 LogFlowFunc(("Initializing host backend failed with rc=%Rrc\n", rc));
877 break;
878 }
879
880 pHstStrmIn->fStatus |= PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
881
882 char *pszName;
883 if (RTStrAPrintf(&pszName, "%s (Host)", pCfg->szName) <= 0)
884 {
885 rc = VERR_NO_MEMORY;
886 break;
887 }
888
889 rc = AudioMixBufInit(&pHstStrmIn->MixBuf, pszName, &pHstStrmIn->Props, cSamples);
890 if (RT_SUCCESS(rc))
891 rc = RTCritSectInit(&pHstStrmIn->CritSect);
892
893 if (RT_SUCCESS(rc))
894 {
895 RTListPrepend(&pThis->lstHstStrmIn, &pHstStrmIn->Node);
896 pThis->cStreamsFreeIn--;
897 }
898
899 RTStrFree(pszName);
900
901 } while (0);
902
903 if (RT_FAILURE(rc))
904 {
905 if (pHstStrmIn->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
906 {
907 int rc2 = pThis->pHostDrvAudio->pfnFiniIn
908 ? pThis->pHostDrvAudio->pfnFiniIn(pThis->pHostDrvAudio, pHstStrmIn) : VINF_SUCCESS;
909 if (RT_SUCCESS(rc2))
910 pHstStrmIn->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
911 }
912
913 drvAudioHstInFreeRes(pHstStrmIn);
914 RTMemFree(pHstStrmIn);
915 }
916 else
917 *ppHstStrmIn = pHstStrmIn;
918
919 LogFlowFuncLeaveRC(rc);
920 return rc;
921}
922
923/**
924 * Writes VM audio output data from the guest stream into the host stream.
925 * The attached host driver backend then will play out the audio in a
926 * later step then.
927 *
928 * @return IPRT status code.
929 * @return int
930 * @param pThis
931 * @param pGstStrmOut
932 * @param pvBuf
933 * @param cbBuf
934 * @param pcbWritten
935 */
936static DECLCALLBACK(int) drvAudioWrite(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut,
937 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
938{
939 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
940 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
941
942 AssertPtrReturn(pGstStrmOut, VERR_INVALID_POINTER);
943 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
944 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
945 /* pcbWritten is optional. */
946
947 int rc = RTCritSectEnter(&pThis->CritSect);
948 if (RT_FAILURE(rc))
949 return rc;
950
951 if (!pThis->pHostDrvAudio->pfnIsEnabled(pThis->pHostDrvAudio, PDMAUDIODIR_OUT))
952 {
953 rc = RTCritSectLeave(&pThis->CritSect);
954 AssertRC(rc);
955
956 return VERR_NOT_AVAILABLE;
957 }
958
959 PPDMAUDIOHSTSTRMOUT pHstStrmOut = pGstStrmOut->pHstStrmOut;
960 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
961
962 AssertMsg(pGstStrmOut->pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED,
963 ("Writing to disabled host output stream \"%s\" not possible\n",
964 pHstStrmOut->MixBuf.pszName));
965
966 if (!AudioMixBufFreeBytes(&pGstStrmOut->MixBuf))
967 {
968 if (pcbWritten)
969 *pcbWritten = 0;
970
971 return RTCritSectLeave(&pThis->CritSect);
972 }
973
974 /*
975 * First, write data from the device emulation into our
976 * guest mixing buffer.
977 */
978 uint32_t cWritten;
979 rc = AudioMixBufWriteAt(&pGstStrmOut->MixBuf, 0 /* Offset in samples */, pvBuf, cbBuf, &cWritten);
980
981 /*
982 * Second, mix the guest mixing buffer with the host mixing
983 * buffer so that the host backend can play the data lateron.
984 */
985 uint32_t cMixed;
986 if ( RT_SUCCESS(rc)
987 && cWritten)
988 {
989 rc = AudioMixBufMixToParent(&pGstStrmOut->MixBuf, cWritten, &cMixed);
990 }
991 else
992 cMixed = 0;
993
994 if (RT_SUCCESS(rc))
995 {
996 /*
997 * Return the number of samples which actually have been mixed
998 * down to the parent, regardless how much samples were written
999 * into the children buffer.
1000 */
1001 if (pcbWritten)
1002 *pcbWritten = AUDIOMIXBUF_S2B(&pGstStrmOut->MixBuf, cMixed);
1003 }
1004
1005 LogFlowFunc(("%s -> %s: cbBuf=%RU32, cWritten=%RU32 (%RU32 bytes), cMixed=%RU32, rc=%Rrc\n",
1006 pGstStrmOut->MixBuf.pszName, pHstStrmOut->MixBuf.pszName, cbBuf, cWritten,
1007 AUDIOMIXBUF_S2B(&pGstStrmOut->MixBuf, cWritten), cMixed, rc));
1008
1009 int rc2 = RTCritSectLeave(&pThis->CritSect);
1010 if (RT_SUCCESS(rc))
1011 rc = rc2;
1012
1013 return rc;
1014}
1015
1016static PPDMAUDIOHSTSTRMOUT drvAudioFindAnyHstOut(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
1017{
1018 if (pHstStrmOut)
1019 {
1020 if (RTListNodeIsLast(&pThis->lstHstStrmOut, &pHstStrmOut->Node))
1021 return NULL;
1022
1023 return RTListNodeGetNext(&pHstStrmOut->Node, PDMAUDIOHSTSTRMOUT, Node);
1024 }
1025
1026 return RTListGetFirst(&pThis->lstHstStrmOut, PDMAUDIOHSTSTRMOUT, Node);
1027}
1028
1029static PPDMAUDIOHSTSTRMOUT drvAudioHstFindAnyEnabledOut(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMOUT pHostStrmOut)
1030{
1031 while ((pHostStrmOut = drvAudioFindAnyHstOut(pThis, pHostStrmOut)))
1032 {
1033 if (pHostStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED)
1034 return pHostStrmOut;
1035 }
1036
1037 return NULL;
1038}
1039
1040static PPDMAUDIOHSTSTRMOUT drvAudioFindSpecificOut(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
1041 PPDMAUDIOSTREAMCFG pCfg)
1042{
1043 while ((pHstStrmOut = drvAudioFindAnyHstOut(pThis, pHstStrmOut)))
1044 {
1045 if (DrvAudioPCMPropsAreEqual(&pHstStrmOut->Props, pCfg))
1046 return pHstStrmOut;
1047 }
1048
1049 return NULL;
1050}
1051
1052int drvAudioDestroyHstIn(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMIN pHstStrmIn)
1053{
1054 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1055 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
1056
1057 LogFlowFunc(("%s\n", pHstStrmIn->MixBuf.pszName));
1058
1059 int rc = VINF_SUCCESS;
1060
1061 if (!pHstStrmIn->pGstStrmIn)
1062 {
1063 drvAudioHstInFreeRes(pHstStrmIn);
1064
1065 if (RTCritSectIsInitialized(&pHstStrmIn->CritSect))
1066 {
1067 int rc2 = RTCritSectDelete(&pHstStrmIn->CritSect);
1068 AssertRC(rc2);
1069 }
1070
1071 /* Remove from driver instance list. */
1072 RTListNodeRemove(&pHstStrmIn->Node);
1073
1074 RTMemFree(pHstStrmIn);
1075 pThis->cStreamsFreeIn++;
1076 }
1077 else
1078 {
1079 rc = VERR_WRONG_ORDER;
1080 LogFlowFunc(("[%s] Still is being used, skipping\n", pHstStrmIn->MixBuf.pszName));
1081 }
1082
1083 LogFlowFuncLeaveRC(rc);
1084 return rc;
1085}
1086
1087static int drvAudioDestroyGstIn(PDRVAUDIO pThis, PPDMAUDIOGSTSTRMIN pGstStrmIn)
1088{
1089 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1090
1091 if (!pGstStrmIn)
1092 return VINF_SUCCESS;
1093
1094 LogFlowFunc(("%s\n", pGstStrmIn->MixBuf.pszName));
1095
1096 if (pGstStrmIn->State.cRefs > 1) /* Do other objects still have a reference to it? Bail out. */
1097 return VERR_WRONG_ORDER;
1098
1099 AudioMixBufDestroy(&pGstStrmIn->MixBuf);
1100
1101 /* Is this guest stream associate to a host stream? */
1102 if (pGstStrmIn->pHstStrmIn)
1103 {
1104 /* Unlink child. */
1105 pGstStrmIn->pHstStrmIn->pGstStrmIn = NULL;
1106 }
1107
1108 RTMemFree(pGstStrmIn);
1109 pGstStrmIn = NULL;
1110
1111 return VINF_SUCCESS;
1112}
1113
1114static DECLCALLBACK(uint32_t) drvAudioAddRefIn(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrm)
1115{
1116 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1117 AssertPtrReturn(pGstStrm, VERR_INVALID_POINTER);
1118
1119 NOREF(pInterface);
1120
1121 return ++pGstStrm->State.cRefs;
1122}
1123
1124static DECLCALLBACK(uint32_t) drvAudioAddRefOut(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrm)
1125{
1126 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1127 AssertPtrReturn(pGstStrm, VERR_INVALID_POINTER);
1128
1129 NOREF(pInterface);
1130
1131 return ++pGstStrm->State.cRefs;
1132}
1133
1134static DECLCALLBACK(uint32_t) drvAudioReleaseIn(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrm)
1135{
1136 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1137 AssertPtrReturn(pGstStrm, VERR_INVALID_POINTER);
1138
1139 NOREF(pInterface);
1140
1141 Assert(pGstStrm->State.cRefs);
1142 if (pGstStrm->State.cRefs)
1143 pGstStrm->State.cRefs--;
1144
1145 return pGstStrm->State.cRefs;
1146}
1147
1148static DECLCALLBACK(uint32_t) drvAudioReleaseOut(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrm)
1149{
1150 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1151 AssertPtrReturn(pGstStrm, VERR_INVALID_POINTER);
1152
1153 NOREF(pInterface);
1154
1155 Assert(pGstStrm->State.cRefs);
1156 if (pGstStrm->State.cRefs)
1157 pGstStrm->State.cRefs--;
1158
1159 return pGstStrm->State.cRefs;
1160}
1161
1162static DECLCALLBACK(int) drvAudioQueryStatus(PPDMIAUDIOCONNECTOR pInterface,
1163 uint32_t *pcbAvailIn, uint32_t *pcbFreeOut,
1164 uint32_t *pcSamplesLive)
1165{
1166 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1167 /* pcbAvailIn is optional. */
1168 /* pcbFreeOut is optional. */
1169 /* pcSamplesLive is optional. */
1170
1171 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1172
1173 int rc = RTCritSectEnter(&pThis->CritSect);
1174 if (RT_FAILURE(rc))
1175 return rc;
1176
1177 /*
1178 * Playback.
1179 */
1180 uint32_t cSamplesLive = 0;
1181 uint32_t cbFreeOut = UINT32_MAX;
1182
1183 PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
1184 while ((pHstStrmOut = drvAudioHstFindAnyEnabledOut(pThis, pHstStrmOut)))
1185 {
1186 cSamplesLive = AudioMixBufAvail(&pHstStrmOut->MixBuf);
1187
1188 /* Has this stream marked as disabled but there still were guest streams relying
1189 * on it? Check if this stream now can be closed and do so, if possible. */
1190 if ( (pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE)
1191 && !cSamplesLive)
1192 {
1193 /* Stop playing the current (pending) stream. */
1194 int rc2 = drvAudioControlHstOut(pThis, pHstStrmOut, PDMAUDIOSTREAMCMD_DISABLE);
1195 if (RT_SUCCESS(rc2))
1196 {
1197 pHstStrmOut->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
1198
1199 LogFunc(("[%s] Disabling stream\n", pHstStrmOut->MixBuf.pszName));
1200 }
1201 else
1202 LogFunc(("[%s] Backend vetoed against closing output stream, rc=%Rrc\n", pHstStrmOut->MixBuf.pszName, rc2));
1203
1204 continue;
1205 }
1206
1207 LogFlowFunc(("[%s] cSamplesLive=%RU32\n", pHstStrmOut->MixBuf.pszName, cSamplesLive));
1208
1209 /*
1210 * No live samples to play at the moment?
1211 *
1212 * Tell the device emulation for each connected guest stream how many
1213 * bytes are free so that the device emulation can continue writing data to
1214 * these streams.
1215 */
1216 PPDMAUDIOGSTSTRMOUT pGstStrmOut;
1217 uint32_t cbFree2 = UINT32_MAX;
1218 RTListForEach(&pHstStrmOut->lstGstStrmOut, pGstStrmOut, PDMAUDIOGSTSTRMOUT, Node)
1219 {
1220 if (pGstStrmOut->State.fActive)
1221 {
1222 /* Tell the sound device emulation how many samples are free
1223 * so that it can start writing PCM data to us. */
1224 cbFree2 = RT_MIN(cbFree2, AUDIOMIXBUF_S2B_RATIO(&pGstStrmOut->MixBuf,
1225 AudioMixBufFree(&pGstStrmOut->MixBuf)));
1226#ifdef DEBUG_andy
1227 LogFlowFunc(("\t[%s] cbFreeOut=%RU32\n", pGstStrmOut->MixBuf.pszName, cbFree2));
1228#endif
1229 }
1230 }
1231
1232 cbFreeOut = RT_MIN(cbFreeOut, cbFree2);
1233 }
1234
1235 /*
1236 * Recording.
1237 */
1238 uint32_t cbAvailIn = 0;
1239
1240 PPDMAUDIOHSTSTRMIN pHstStrmIn = NULL;
1241 while ((pHstStrmIn = drvAudioFindAnyHstIn(pThis, pHstStrmIn)))
1242 {
1243 /* Disabled? Skip it! */
1244 if (!(pHstStrmIn->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
1245 continue;
1246
1247 /* Call the host backend to capture the audio input data. */
1248 uint32_t cSamplesCaptured;
1249 int rc2 = pThis->pHostDrvAudio->pfnCaptureIn(pThis->pHostDrvAudio, pHstStrmIn,
1250 &cSamplesCaptured);
1251 if (RT_FAILURE(rc2))
1252 continue;
1253
1254 PPDMAUDIOGSTSTRMIN pGstStrmIn = pHstStrmIn->pGstStrmIn;
1255 AssertPtrBreak(pGstStrmIn);
1256
1257 if (pGstStrmIn->State.fActive)
1258 {
1259 cbAvailIn = RT_MAX(cbAvailIn, AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf,
1260 AudioMixBufMixed(&pHstStrmIn->MixBuf)));
1261#ifdef DEBUG_andy
1262 LogFlowFunc(("\t[%s] cbAvailIn=%RU32\n", pHstStrmIn->MixBuf.pszName, cbAvailIn));
1263#endif
1264 }
1265 }
1266
1267 if (RT_SUCCESS(rc))
1268 {
1269 if (cbFreeOut == UINT32_MAX)
1270 cbFreeOut = 0;
1271
1272 if (pcbAvailIn)
1273 *pcbAvailIn = cbAvailIn;
1274
1275 if (pcbFreeOut)
1276 *pcbFreeOut = cbFreeOut;
1277
1278 if (pcSamplesLive)
1279 *pcSamplesLive = cSamplesLive;
1280 }
1281
1282 int rc2 = RTCritSectLeave(&pThis->CritSect);
1283 if (RT_SUCCESS(rc))
1284 rc = rc2;
1285
1286 if (RT_FAILURE(rc))
1287 LogFlowFuncLeaveRC(rc);
1288
1289 return rc;
1290}
1291
1292static DECLCALLBACK(int) drvAudioPlayOut(PPDMIAUDIOCONNECTOR pInterface, uint32_t *pcSamplesPlayed)
1293{
1294 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1295 /* pcSamplesPlayed is optional. */
1296
1297 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1298
1299 int rc = RTCritSectEnter(&pThis->CritSect);
1300 if (RT_FAILURE(rc))
1301 return rc;
1302
1303 /* Backend output (temporarily) disabled / unavailable? */
1304 if (!pThis->pHostDrvAudio->pfnIsEnabled(pThis->pHostDrvAudio, PDMAUDIODIR_OUT))
1305 {
1306 rc = pThis->pHostDrvAudio->pfnGetConf(pThis->pHostDrvAudio, &pThis->BackendCfg);
1307 AssertRC(rc);
1308
1309 if ( !pThis->BackendCfg.cSinks
1310 || !pThis->BackendCfg.cMaxStreamsOut)
1311 {
1312 int rc2 = RTCritSectLeave(&pThis->CritSect);
1313 AssertRC(rc2);
1314
1315 return VERR_NOT_AVAILABLE;
1316 }
1317 }
1318
1319 /*
1320 * Process all enabled host output streams.
1321 */
1322 uint32_t cSamplesPlayedMax = 0;
1323 PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
1324 while ((pHstStrmOut = drvAudioHstFindAnyEnabledOut(pThis, pHstStrmOut)))
1325 {
1326#if 0
1327 uint32_t cStreamsLive;
1328 uint32_t cSamplesLive = drvAudioHstOutSamplesLive(pHstStrmOut, &cStreamsLive);
1329 if (!cStreamsLive)
1330 cSamplesLive = 0;
1331
1332 /* Has this stream marked as disabled but there still were guest streams relying
1333 * on it? Check if this stream now can be closed and do so, if possible. */
1334 if ( pHstStrmOut->fPendingDisable
1335 && !cStreamsLive)
1336 {
1337 /* Stop playing the current (pending) stream. */
1338 int rc2 = pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut,
1339 PDMAUDIOSTREAMCMD_DISABLE);
1340 if (RT_SUCCESS(rc2))
1341 {
1342 pHstStrmOut->fEnabled = false;
1343 pHstStrmOut->fPendingDisable = false;
1344
1345 LogFunc(("\t%p: Disabling stream\n", pHstStrmOut));
1346 }
1347 else
1348 LogFunc(("\t%p: Backend vetoed against closing output stream, rc=%Rrc\n",
1349 pHstStrmOut, rc2));
1350
1351 continue;
1352 }
1353#endif
1354
1355 uint32_t cSamplesPlayed = 0;
1356 int rc2 = pThis->pHostDrvAudio->pfnPlayOut(pThis->pHostDrvAudio, pHstStrmOut, &cSamplesPlayed);
1357 if (RT_FAILURE(rc2))
1358 {
1359 int rc3 = pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut, PDMAUDIOSTREAMCMD_DISABLE);
1360 AssertRC(rc3);
1361 }
1362 else
1363 cSamplesPlayedMax = RT_MAX(cSamplesPlayed, cSamplesPlayedMax);
1364
1365 LogFlowFunc(("\t[%s] cSamplesPlayed=%RU32, cSamplesPlayedMax=%RU32, rc=%Rrc\n",
1366 pHstStrmOut->MixBuf.pszName, cSamplesPlayed, cSamplesPlayedMax, rc2));
1367
1368 bool fNeedsCleanup = false;
1369
1370 PPDMAUDIOGSTSTRMOUT pGstStrmOut;
1371 RTListForEach(&pHstStrmOut->lstGstStrmOut, pGstStrmOut, PDMAUDIOGSTSTRMOUT, Node)
1372 {
1373 if ( !pGstStrmOut->State.fActive
1374 && pGstStrmOut->State.fEmpty)
1375 continue;
1376
1377 if (AudioMixBufIsEmpty(&pGstStrmOut->MixBuf))
1378 {
1379 pGstStrmOut->State.fEmpty = true;
1380 fNeedsCleanup |= !pGstStrmOut->State.fActive;
1381 }
1382 }
1383
1384 if (fNeedsCleanup)
1385 {
1386 RTListForEach(&pHstStrmOut->lstGstStrmOut, pGstStrmOut, PDMAUDIOGSTSTRMOUT, Node)
1387 {
1388 if (!pGstStrmOut->State.fActive)
1389 drvAudioDestroyGstOut(pThis, pGstStrmOut);
1390 }
1391 }
1392 }
1393
1394 if (RT_SUCCESS(rc))
1395 {
1396 if (pcSamplesPlayed)
1397 *pcSamplesPlayed = cSamplesPlayedMax;
1398 }
1399
1400 int rc2 = RTCritSectLeave(&pThis->CritSect);
1401 if (RT_SUCCESS(rc))
1402 rc = rc2;
1403
1404 if (RT_FAILURE(rc))
1405 LogFlowFuncLeaveRC(rc);
1406
1407 return rc;
1408}
1409
1410#ifdef VBOX_WITH_AUDIO_CALLBACKS
1411static PPDMAUDIOCALLBACK drvAudioCallbackDuplicate(PPDMAUDIOCALLBACK pCB)
1412{
1413 PPDMAUDIOCALLBACK pCBCopy = (PPDMAUDIOCALLBACK)RTMemDup((void *)pCB, sizeof(PDMAUDIOCALLBACK));
1414 if (!pCBCopy)
1415 return NULL;
1416
1417 if (pCB->pvCtx)
1418 {
1419 pCBCopy->pvCtx = RTMemDup(pCB->pvCtx, pCB->cbCtx);
1420 if (!pCBCopy->pvCtx)
1421 {
1422 RTMemFree(pCBCopy);
1423 return NULL;
1424 }
1425
1426 pCBCopy->cbCtx = pCB->cbCtx;
1427 }
1428
1429 return pCBCopy;
1430}
1431
1432static void drvAudioCallbackDestroy(PPDMAUDIOCALLBACK pCB)
1433{
1434 if (!pCB)
1435 return;
1436
1437 RTListNodeRemove(&pCB->Node);
1438 if (pCB->pvCtx)
1439 {
1440 Assert(pCB->cbCtx);
1441 RTMemFree(pCB->pvCtx);
1442 }
1443 RTMemFree(pCB);
1444}
1445
1446static DECLCALLBACK(int) drvAudioRegisterCallbacks(PPDMIAUDIOCONNECTOR pInterface,
1447 PPDMAUDIOCALLBACK paCallbacks, size_t cCallbacks)
1448{
1449 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1450 AssertPtrReturn(paCallbacks, VERR_INVALID_POINTER);
1451 AssertReturn(cCallbacks, VERR_INVALID_PARAMETER);
1452
1453 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1454
1455 int rc = RTCritSectEnter(&pThis->CritSect);
1456 if (RT_FAILURE(rc))
1457 return rc;
1458
1459 for (size_t i = 0; i < cCallbacks; i++)
1460 {
1461 PPDMAUDIOCALLBACK pCB = drvAudioCallbackDuplicate(&paCallbacks[i]);
1462 if (!pCB)
1463 {
1464 rc = VERR_NO_MEMORY;
1465 break;
1466 }
1467
1468 switch (pCB->enmType)
1469 {
1470 case PDMAUDIOCALLBACKTYPE_INPUT:
1471 RTListAppend(&pThis->lstCBIn, &pCB->Node);
1472 break;
1473
1474 case PDMAUDIOCALLBACKTYPE_OUTPUT:
1475 RTListAppend(&pThis->lstCBOut, &pCB->Node);
1476 break;
1477
1478 default:
1479 AssertMsgFailed(("Not supported\n"));
1480 break;
1481 }
1482 }
1483
1484 /** @todo Undo allocations on error. */
1485
1486 int rc2 = RTCritSectLeave(&pThis->CritSect);
1487 if (RT_SUCCESS(rc))
1488 rc = rc2;
1489
1490 return rc;
1491}
1492
1493static DECLCALLBACK(int) drvAudioCallback(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIOCALLBACKTYPE enmType,
1494 void *pvUser, size_t cbUser)
1495{
1496 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1497 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
1498 AssertReturn(cbUser, VERR_INVALID_PARAMETER);
1499
1500 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1501 PRTLISTANCHOR pListAnchor = NULL;
1502
1503 switch (enmType)
1504 {
1505 case PDMAUDIOCALLBACKTYPE_INPUT:
1506 pListAnchor = &pThis->lstCBIn;
1507 break;
1508
1509 case PDMAUDIOCALLBACKTYPE_OUTPUT:
1510 pListAnchor = &pThis->lstCBOut;
1511 break;
1512
1513 default:
1514 AssertMsgFailed(("Not supported\n"));
1515 break;
1516 }
1517
1518 if (pListAnchor)
1519 {
1520 PPDMAUDIOCALLBACK pCB;
1521 RTListForEach(pListAnchor, pCB, PDMAUDIOCALLBACK, Node)
1522 {
1523 Assert(pCB->enmType == enmType);
1524 pCB->pfnCallback(enmType, pCB->pvCtx, pCB->cbCtx, pvUser, cbUser);
1525 }
1526 }
1527
1528 return VINF_SUCCESS;
1529}
1530#endif
1531
1532static int drvAudioHostInit(PCFGMNODE pCfgHandle, PDRVAUDIO pThis)
1533{
1534 /* pCfgHandle is optional. */
1535 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1536
1537 NOREF(pCfgHandle);
1538
1539 LogFlowFuncEnter();
1540
1541 AssertPtr(pThis->pHostDrvAudio);
1542 int rc = pThis->pHostDrvAudio->pfnInit(pThis->pHostDrvAudio);
1543 if (RT_FAILURE(rc))
1544 {
1545 LogFlowFunc(("Initialization of lower driver failed with rc=%Rrc\n", rc));
1546 return rc;
1547 }
1548
1549 /* Get the configuration data from backend. */
1550 rc = pThis->pHostDrvAudio->pfnGetConf(pThis->pHostDrvAudio, &pThis->BackendCfg);
1551 if (RT_FAILURE(rc))
1552 {
1553 LogFlowFunc(("Getting backend configuration failed with rc=%Rrc\n", rc));
1554 return rc;
1555 }
1556
1557 pThis->cStreamsFreeIn = 0;
1558 pThis->cStreamsFreeOut = 0;
1559
1560 if (pThis->BackendCfg.cSinks)
1561 {
1562 Assert(pThis->BackendCfg.cbStreamOut);
1563
1564 pThis->cStreamsFreeOut = pThis->BackendCfg.cMaxStreamsOut;
1565 }
1566
1567 if (pThis->BackendCfg.cSources)
1568 {
1569 Assert(pThis->BackendCfg.cbStreamIn);
1570
1571 pThis->cStreamsFreeIn = pThis->BackendCfg.cMaxStreamsIn;
1572 }
1573
1574 LogFlowFunc(("cStreamsFreeIn=%RU8, cStreamsFreeOut=%RU8\n", pThis->cStreamsFreeIn, pThis->cStreamsFreeOut));
1575
1576 LogRel2(("Audio: Host audio backend supports %RU32 input streams and %RU32 output streams at once\n",
1577 /* Clamp for logging. Unlimited streams are defined by UINT32_MAX. */
1578 RT_MIN(64, pThis->cStreamsFreeIn), RT_MIN(64, pThis->cStreamsFreeOut)));
1579
1580 LogFlowFuncLeave();
1581 return VINF_SUCCESS;
1582}
1583
1584static void drvAudioStateHandler(PPDMDRVINS pDrvIns, PDMAUDIOSTREAMCMD enmCmd)
1585{
1586 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1587 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1588
1589 LogFlowFunc(("enmCmd=%ld\n", enmCmd));
1590
1591 if (!pThis->pHostDrvAudio)
1592 return;
1593
1594 PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
1595 while ((pHstStrmOut = drvAudioFindAnyHstOut(pThis, pHstStrmOut)))
1596 drvAudioControlHstOut(pThis, pHstStrmOut, enmCmd);
1597
1598 PPDMAUDIOHSTSTRMIN pHstStrmIn = NULL;
1599 while ((pHstStrmIn = drvAudioFindAnyHstIn(pThis, pHstStrmIn)))
1600 drvAudioControlHstIn(pThis, pHstStrmIn, enmCmd);
1601}
1602
1603static DECLCALLBACK(int) drvAudioInit(PCFGMNODE pCfgHandle, PPDMDRVINS pDrvIns)
1604{
1605 AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
1606 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
1607
1608 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1609 LogFlowFunc(("pThis=%p, pDrvIns=%p\n", pThis, pDrvIns));
1610
1611 RTListInit(&pThis->lstHstStrmIn);
1612 RTListInit(&pThis->lstHstStrmOut);
1613#ifdef VBOX_WITH_AUDIO_CALLBACKS
1614 RTListInit(&pThis->lstCBIn);
1615 RTListInit(&pThis->lstCBOut);
1616#endif
1617
1618 int rc = RTCritSectInit(&pThis->CritSect);
1619
1620 /** @todo Add audio driver options. */
1621
1622 /*
1623 * If everything went well, initialize the lower driver.
1624 */
1625 if (RT_SUCCESS(rc))
1626 rc = drvAudioHostInit(pCfgHandle, pThis);
1627
1628 LogFlowFuncLeaveRC(rc);
1629 return rc;
1630}
1631
1632static DECLCALLBACK(int) drvAudioRead(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrmIn,
1633 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1634{
1635 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1636 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1637
1638 if (!pGstStrmIn)
1639 return VERR_NOT_AVAILABLE;
1640
1641 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1642 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1643 /* pcbWritten is optional. */
1644
1645 int rc = RTCritSectEnter(&pThis->CritSect);
1646 if (RT_FAILURE(rc))
1647 return rc;
1648
1649 if (!pThis->pHostDrvAudio->pfnIsEnabled(pThis->pHostDrvAudio, PDMAUDIODIR_IN))
1650 {
1651 if (pcbRead)
1652 *pcbRead = 0;
1653
1654 return RTCritSectLeave(&pThis->CritSect);
1655 }
1656
1657 PPDMAUDIOHSTSTRMIN pHstStrmIn = pGstStrmIn->pHstStrmIn;
1658 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
1659
1660 AssertMsg(pGstStrmIn->pHstStrmIn->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED,
1661 ("Reading from disabled host input stream \"%s\" not possible\n", pGstStrmIn->MixBuf.pszName));
1662
1663 /*
1664 * Read from the parent buffer (that is, the guest buffer) which
1665 * should have the audio data in the format the guest needs.
1666 */
1667 uint32_t cRead;
1668 rc = AudioMixBufReadCirc(&pGstStrmIn->MixBuf, pvBuf, cbBuf, &cRead);
1669 if (RT_SUCCESS(rc))
1670 {
1671 AudioMixBufFinish(&pGstStrmIn->MixBuf, cRead);
1672
1673 if (pcbRead)
1674 *pcbRead = AUDIOMIXBUF_S2B(&pGstStrmIn->MixBuf, cRead);
1675 }
1676
1677 LogFlowFunc(("cRead=%RU32 (%RU32 bytes), rc=%Rrc\n",
1678 cRead, AUDIOMIXBUF_S2B(&pGstStrmIn->MixBuf, cRead), rc));
1679
1680 int rc2 = RTCritSectLeave(&pThis->CritSect);
1681 if (RT_SUCCESS(rc))
1682 rc = rc2;
1683
1684 return rc;
1685}
1686
1687static DECLCALLBACK(int) drvAudioEnableOut(PPDMIAUDIOCONNECTOR pInterface,
1688 PPDMAUDIOGSTSTRMOUT pGstStrmOut, bool fEnable)
1689{
1690 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1691
1692 if (!pGstStrmOut)
1693 return VERR_NOT_AVAILABLE;
1694
1695 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1696
1697 int rc = VINF_SUCCESS;
1698
1699 PPDMAUDIOHSTSTRMOUT pHstStrmOut = pGstStrmOut->pHstStrmOut;
1700 AssertPtr(pHstStrmOut);
1701
1702 if (fEnable)
1703 {
1704 /* Is a pending disable outstanding? Then disable first. */
1705 if (pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE)
1706 {
1707 rc = drvAudioControlHstOut(pThis, pHstStrmOut, PDMAUDIOSTREAMCMD_DISABLE);
1708 if (RT_SUCCESS(rc))
1709 pHstStrmOut->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
1710 }
1711
1712 if (RT_SUCCESS(rc))
1713 rc = drvAudioControlHstOut(pThis, pHstStrmOut, PDMAUDIOSTREAMCMD_ENABLE);
1714 }
1715 else /* Disable */
1716 {
1717 if (pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED)
1718 {
1719 uint32_t cGstStrmsActive = 0;
1720
1721 /*
1722 * Check if there are any active guest streams assigned
1723 * to this host stream which still are being marked as active.
1724 *
1725 * In that case we have to defer closing the host stream and
1726 * wait until all guest streams have been finished.
1727 */
1728 PPDMAUDIOGSTSTRMOUT pIter;
1729 RTListForEach(&pHstStrmOut->lstGstStrmOut, pIter, PDMAUDIOGSTSTRMOUT, Node)
1730 {
1731 if (pIter->State.fActive)
1732 {
1733 cGstStrmsActive++;
1734 break; /* At least one assigned & active guest stream is enough. */
1735 }
1736 }
1737
1738 /* Do we need to defer closing the host stream? */
1739 if (cGstStrmsActive >= 1)
1740 pHstStrmOut->fStatus |= PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
1741
1742 /* Can we close the host stream now instead of deferring it? */
1743 if (!(pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE))
1744 rc = drvAudioControlHstOut(pThis, pHstStrmOut, PDMAUDIOSTREAMCMD_DISABLE);
1745 }
1746 }
1747
1748 if (RT_SUCCESS(rc))
1749 pGstStrmOut->State.fActive = fEnable;
1750
1751 LogFlowFunc(("%s: fEnable=%RTbool, fStatus=0x%x, rc=%Rrc\n",
1752 pGstStrmOut->MixBuf.pszName, fEnable, pHstStrmOut->fStatus, rc));
1753
1754 return rc;
1755}
1756
1757static DECLCALLBACK(int) drvAudioEnableIn(PPDMIAUDIOCONNECTOR pInterface,
1758 PPDMAUDIOGSTSTRMIN pGstStrmIn, bool fEnable)
1759{
1760 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1761
1762 if (!pGstStrmIn)
1763 return VERR_NOT_AVAILABLE;
1764
1765 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1766
1767 int rc = VINF_SUCCESS;
1768
1769 PPDMAUDIOHSTSTRMIN pHstStrmIn = pGstStrmIn->pHstStrmIn;
1770 AssertPtr(pHstStrmIn);
1771
1772 rc = drvAudioControlHstIn(pThis, pHstStrmIn,
1773 fEnable ? PDMAUDIOSTREAMCMD_ENABLE : PDMAUDIOSTREAMCMD_DISABLE);
1774 if (RT_SUCCESS(rc))
1775 pGstStrmIn->State.fActive = fEnable;
1776
1777 LogFlowFunc(("%s: fEnable=%RTbool, rc=%Rrc\n", pGstStrmIn->MixBuf.pszName, fEnable, rc));
1778
1779 return rc;
1780}
1781
1782static DECLCALLBACK(bool) drvAudioIsValidIn(PPDMIAUDIOCONNECTOR pInterface,
1783 PPDMAUDIOGSTSTRMIN pGstStrmIn)
1784{
1785 return (pGstStrmIn != NULL);
1786}
1787
1788static DECLCALLBACK(bool) drvAudioIsValidOut(PPDMIAUDIOCONNECTOR pInterface,
1789 PPDMAUDIOGSTSTRMOUT pGstStrmOut)
1790{
1791 return (pGstStrmOut != NULL);
1792}
1793
1794static DECLCALLBACK(int) drvAudioCreateIn(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAMCFG pCfg,
1795 PPDMAUDIOGSTSTRMIN *ppGstStrmIn)
1796{
1797 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1798 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1799 AssertPtrReturn(ppGstStrmIn, VERR_INVALID_POINTER);
1800
1801 Assert(pCfg->enmDir == PDMAUDIODIR_IN);
1802
1803 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1804
1805 int rc = RTCritSectEnter(&pThis->CritSect);
1806 if (RT_FAILURE(rc))
1807 return rc;
1808
1809 LogFlowFunc(("szName=%s\n", pCfg->szName));
1810
1811 if (!drvAudioStreamCfgIsValid(pCfg))
1812 {
1813 LogFunc(("Input stream configuration is not valid, bailing out\n"));
1814 rc = VERR_INVALID_PARAMETER;
1815 }
1816
1817 if (RT_SUCCESS(rc))
1818 {
1819 PPDMAUDIOGSTSTRMIN pGstStrmIn;
1820 rc = drvAudioCreateStreamPairIn(pThis, pCfg, &pGstStrmIn);
1821 if (RT_SUCCESS(rc))
1822 *ppGstStrmIn = pGstStrmIn;
1823 }
1824
1825 int rc2 = RTCritSectLeave(&pThis->CritSect);
1826 if (RT_SUCCESS(rc))
1827 rc = rc2;
1828
1829 LogFlowFuncLeaveRC(rc);
1830 return rc;
1831}
1832
1833static DECLCALLBACK(int) drvAudioCreateOut(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAMCFG pCfg,
1834 PPDMAUDIOGSTSTRMOUT *ppGstStrmOut)
1835{
1836 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1837 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1838 AssertPtrReturn(ppGstStrmOut, VERR_INVALID_POINTER);
1839
1840 Assert(pCfg->enmDir == PDMAUDIODIR_OUT);
1841
1842 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1843
1844 int rc = RTCritSectEnter(&pThis->CritSect);
1845 if (RT_FAILURE(rc))
1846 return rc;
1847
1848 LogFlowFunc(("pszName=%s\n", pCfg->szName));
1849
1850 if (!drvAudioStreamCfgIsValid(pCfg))
1851 {
1852 LogFunc(("Output stream configuration is not valid, bailing out\n"));
1853 rc = VERR_INVALID_PARAMETER;
1854 }
1855
1856 if (RT_SUCCESS(rc))
1857 {
1858 PPDMAUDIOGSTSTRMOUT pGstStrmOut;
1859 rc = drvAudioCreateStreamPairOut(pThis, pCfg, &pGstStrmOut);
1860 if (RT_SUCCESS(rc))
1861 *ppGstStrmOut = pGstStrmOut;
1862 }
1863
1864 int rc2 = RTCritSectLeave(&pThis->CritSect);
1865 if (RT_SUCCESS(rc))
1866 rc = rc2;
1867
1868 LogFlowFuncLeaveRC(rc);
1869 return rc;
1870}
1871
1872static DECLCALLBACK(int) drvAudioGetConfig(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOBACKENDCFG pCfg)
1873{
1874 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1875 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1876
1877 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1878
1879 int rc = RTCritSectEnter(&pThis->CritSect);
1880 if (RT_FAILURE(rc))
1881 return rc;
1882
1883 rc = pThis->pHostDrvAudio->pfnGetConf(pThis->pHostDrvAudio, pCfg);
1884
1885 int rc2 = RTCritSectLeave(&pThis->CritSect);
1886 if (RT_SUCCESS(rc))
1887 rc = rc2;
1888
1889 LogFlowFuncLeaveRC(rc);
1890 return rc;
1891}
1892
1893static DECLCALLBACK(bool) drvAudioIsActiveIn(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrmIn)
1894{
1895 AssertPtrReturn(pInterface, false);
1896 /* pGstStrmIn is optional. */
1897
1898 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1899
1900 int rc2 = RTCritSectEnter(&pThis->CritSect);
1901 AssertRC(rc2);
1902
1903 bool fRet = pGstStrmIn ? pGstStrmIn->State.fActive : false;
1904
1905 rc2 = RTCritSectLeave(&pThis->CritSect);
1906 AssertRC(rc2);
1907
1908 return fRet;
1909}
1910
1911static DECLCALLBACK(bool) drvAudioIsActiveOut(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut)
1912{
1913 AssertPtrReturn(pInterface, false);
1914 /* pGstStrmOut is optional. */
1915
1916 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1917
1918 int rc2 = RTCritSectEnter(&pThis->CritSect);
1919 AssertRC(rc2);
1920
1921 bool fRet = pGstStrmOut ? pGstStrmOut->State.fActive : false;
1922
1923 rc2 = RTCritSectLeave(&pThis->CritSect);
1924 AssertRC(rc2);
1925
1926 return fRet;
1927}
1928
1929static DECLCALLBACK(void) drvAudioDestroyIn(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrmIn)
1930{
1931 AssertPtrReturnVoid(pInterface);
1932 /* pGstStrmIn is optional. */
1933
1934 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1935
1936 if (!pGstStrmIn)
1937 return;
1938
1939 int rc2 = RTCritSectEnter(&pThis->CritSect);
1940 AssertRC(rc2);
1941
1942 rc2 = drvAudioDestroyGstIn(pThis, pGstStrmIn);
1943 AssertRC(rc2);
1944
1945 rc2 = RTCritSectLeave(&pThis->CritSect);
1946 AssertRC(rc2);
1947}
1948
1949static DECLCALLBACK(void) drvAudioDestroyOut(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut)
1950{
1951 AssertPtrReturnVoid(pInterface);
1952 /* pGstStrmOut is optional. */
1953
1954 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1955
1956 if (!pGstStrmOut)
1957 return;
1958
1959 int rc2 = RTCritSectEnter(&pThis->CritSect);
1960 AssertRC(rc2);
1961
1962 rc2 = drvAudioDestroyGstOut(pThis, pGstStrmOut);
1963 AssertRC(rc2);
1964
1965 rc2 = RTCritSectLeave(&pThis->CritSect);
1966 AssertRC(rc2);
1967}
1968
1969void drvAudioHstOutFreeRes(PPDMAUDIOHSTSTRMOUT pHstStrmOut)
1970{
1971 AssertPtrReturnVoid(pHstStrmOut);
1972 AudioMixBufDestroy(&pHstStrmOut->MixBuf);
1973}
1974
1975/********************************************************************/
1976
1977/**
1978 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1979 */
1980static DECLCALLBACK(void *) drvAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1981{
1982 LogFlowFunc(("pInterface=%p, pszIID=%s\n", pInterface, pszIID));
1983
1984 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1985 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1986
1987 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1988 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIAUDIOCONNECTOR, &pThis->IAudioConnector);
1989
1990 return NULL;
1991}
1992
1993/**
1994 * Power Off notification.
1995 *
1996 * @param pDrvIns The driver instance data.
1997 */
1998static DECLCALLBACK(void) drvAudioPowerOff(PPDMDRVINS pDrvIns)
1999{
2000 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2001
2002 LogFlowFuncEnter();
2003
2004 /* First, disable all streams. */
2005 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_DISABLE);
2006
2007 int rc2;
2008
2009 PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
2010 while ((pHstStrmOut = drvAudioFindAnyHstOut(pThis, pHstStrmOut)))
2011 {
2012 if (pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
2013 {
2014 rc2 = pThis->pHostDrvAudio->pfnFiniOut
2015 ? pThis->pHostDrvAudio->pfnFiniOut(pThis->pHostDrvAudio, pHstStrmOut) : VINF_SUCCESS;
2016 if (RT_SUCCESS(rc2))
2017 pHstStrmOut->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
2018 }
2019 else
2020 rc2 = VINF_SUCCESS;
2021
2022 AssertMsgRC(rc2, ("Host output stream %p failed to uninit: %Rrc\n", pHstStrmOut, rc2));
2023 }
2024
2025 PPDMAUDIOHSTSTRMIN pHstStrmIn = NULL;
2026 while ((pHstStrmIn = drvAudioFindAnyHstIn(pThis, pHstStrmIn)))
2027 {
2028 if (pHstStrmIn->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
2029 {
2030 rc2 = pThis->pHostDrvAudio->pfnFiniIn
2031 ? pThis->pHostDrvAudio->pfnFiniIn(pThis->pHostDrvAudio, pHstStrmIn) : VINF_SUCCESS;
2032 if (RT_SUCCESS(rc2))
2033 pHstStrmIn->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
2034 }
2035 else
2036 rc2 = VINF_SUCCESS;
2037
2038 AssertMsgRC(rc2, ("Host input stream %p failed to uninit: %Rrc\n", pHstStrmIn, rc2));
2039 }
2040
2041 /*
2042 * Last call for the driver below us.
2043 * Let it know that we reached end of life.
2044 */
2045 if (pThis->pHostDrvAudio->pfnShutdown)
2046 pThis->pHostDrvAudio->pfnShutdown(pThis->pHostDrvAudio);
2047
2048 pThis->pHostDrvAudio = NULL;
2049
2050 LogFlowFuncLeave();
2051}
2052
2053/**
2054 * Constructs an audio driver instance.
2055 *
2056 * @copydoc FNPDMDRVCONSTRUCT
2057 */
2058static DECLCALLBACK(int) drvAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
2059{
2060 LogFlowFunc(("pDrvIns=%#p, pCfgHandle=%#p, fFlags=%x\n", pDrvIns, pCfgHandle, fFlags));
2061
2062 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2063 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
2064
2065 /*
2066 * Init the static parts.
2067 */
2068 pThis->pDrvIns = pDrvIns;
2069 /* IBase. */
2070 pDrvIns->IBase.pfnQueryInterface = drvAudioQueryInterface;
2071 /* IAudioConnector. */
2072 pThis->IAudioConnector.pfnQueryStatus = drvAudioQueryStatus;
2073 pThis->IAudioConnector.pfnAddRefIn = drvAudioAddRefIn;
2074 pThis->IAudioConnector.pfnAddRefOut = drvAudioAddRefOut;
2075 pThis->IAudioConnector.pfnReleaseIn = drvAudioReleaseIn;
2076 pThis->IAudioConnector.pfnReleaseOut = drvAudioReleaseOut;
2077 pThis->IAudioConnector.pfnRead = drvAudioRead;
2078 pThis->IAudioConnector.pfnWrite = drvAudioWrite;
2079 pThis->IAudioConnector.pfnGetConfig = drvAudioGetConfig;
2080 pThis->IAudioConnector.pfnIsActiveIn = drvAudioIsActiveIn;
2081 pThis->IAudioConnector.pfnIsActiveOut = drvAudioIsActiveOut;
2082 pThis->IAudioConnector.pfnIsValidIn = drvAudioIsValidIn;
2083 pThis->IAudioConnector.pfnIsValidOut = drvAudioIsValidOut;
2084 pThis->IAudioConnector.pfnEnableOut = drvAudioEnableOut;
2085 pThis->IAudioConnector.pfnEnableIn = drvAudioEnableIn;
2086 pThis->IAudioConnector.pfnDestroyIn = drvAudioDestroyIn;
2087 pThis->IAudioConnector.pfnDestroyOut = drvAudioDestroyOut;
2088 pThis->IAudioConnector.pfnCreateIn = drvAudioCreateIn;
2089 pThis->IAudioConnector.pfnCreateOut = drvAudioCreateOut;
2090 pThis->IAudioConnector.pfnPlayOut = drvAudioPlayOut;
2091#ifdef VBOX_WITH_AUDIO_CALLBACKS
2092 pThis->IAudioConnector.pfnRegisterCallbacks = drvAudioRegisterCallbacks;
2093 pThis->IAudioConnector.pfnCallback = drvAudioCallback;
2094#endif
2095
2096 /*
2097 * Attach driver below and query its connector interface.
2098 */
2099 PPDMIBASE pDownBase;
2100 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pDownBase);
2101 if (RT_FAILURE(rc))
2102 {
2103 LogRel(("Audio: Failed to attach to driver %p below (flags=0x%x), rc=%Rrc\n",
2104 pDrvIns, fFlags, rc));
2105 return rc;
2106 }
2107
2108 pThis->pHostDrvAudio = PDMIBASE_QUERY_INTERFACE(pDownBase, PDMIHOSTAUDIO);
2109 if (!pThis->pHostDrvAudio)
2110 {
2111 LogRel(("Audio: Failed to query interface for underlying host driver\n"));
2112 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
2113 N_("Host audio backend missing or invalid"));
2114 }
2115
2116 rc = drvAudioInit(pCfgHandle, pDrvIns);
2117 if (RT_SUCCESS(rc))
2118 {
2119 pThis->fTerminate = false;
2120 pThis->pDrvIns = pDrvIns;
2121 }
2122
2123 LogFlowFuncLeaveRC(rc);
2124 return rc;
2125}
2126
2127/**
2128 * Destructs an audio driver instance.
2129 *
2130 * @copydoc FNPDMDRVDESTRUCT
2131 */
2132static DECLCALLBACK(void) drvAudioDestruct(PPDMDRVINS pDrvIns)
2133{
2134 LogFlowFuncEnter();
2135
2136 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
2137 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2138
2139 int rc2 = RTCritSectEnter(&pThis->CritSect);
2140 AssertRC(rc2);
2141
2142 /*
2143 * Note: No calls here to the driver below us anymore,
2144 * as PDM already has destroyed it.
2145 * If you need to call something from the host driver,
2146 * do this in drvAudioPowerOff() instead.
2147 */
2148
2149 /*
2150 * Destroy all host input streams.
2151 */
2152 PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
2153 while ((pHstStrmOut = drvAudioFindAnyHstOut(pThis, pHstStrmOut)))
2154 {
2155 rc2 = drvAudioDestroyHstOut(pThis, pHstStrmOut);
2156 AssertRC(rc2);
2157
2158 /* Sanity. */
2159 Assert(RTListIsEmpty(&pHstStrmOut->lstGstStrmOut));
2160
2161 pHstStrmOut = NULL;
2162 }
2163
2164 /*
2165 * Destroy all host input streams.
2166 */
2167 PPDMAUDIOHSTSTRMIN pHstStrmIn = NULL;
2168 while ((pHstStrmIn = drvAudioFindAnyHstIn(pThis, pHstStrmIn)))
2169 {
2170 rc2 = drvAudioDestroyHstIn(pThis, pHstStrmIn);
2171 AssertRC(rc2);
2172
2173 /* Sanity. */
2174 Assert(pHstStrmIn->pGstStrmIn == NULL);
2175
2176 pHstStrmIn = NULL;
2177 }
2178
2179 /* Sanity. */
2180 Assert(RTListIsEmpty(&pThis->lstHstStrmIn));
2181 Assert(RTListIsEmpty(&pThis->lstHstStrmOut));
2182
2183#ifdef VBOX_WITH_AUDIO_CALLBACKS
2184 /*
2185 * Destroy callbacks, if any.
2186 */
2187 PPDMAUDIOCALLBACK pCB, pCBNext;
2188 RTListForEachSafe(&pThis->lstCBIn, pCB, pCBNext, PDMAUDIOCALLBACK, Node)
2189 drvAudioCallbackDestroy(pCB);
2190
2191 RTListForEachSafe(&pThis->lstCBOut, pCB, pCBNext, PDMAUDIOCALLBACK, Node)
2192 drvAudioCallbackDestroy(pCB);
2193#endif
2194
2195 rc2 = RTCritSectLeave(&pThis->CritSect);
2196 AssertRC(rc2);
2197
2198 rc2 = RTCritSectDelete(&pThis->CritSect);
2199 AssertRC(rc2);
2200
2201 LogFlowFuncLeave();
2202}
2203
2204/**
2205 * Suspend notification.
2206 *
2207 * @param pDrvIns The driver instance data.
2208 */
2209static DECLCALLBACK(void) drvAudioSuspend(PPDMDRVINS pDrvIns)
2210{
2211 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_PAUSE);
2212}
2213
2214/**
2215 * Resume notification.
2216 *
2217 * @param pDrvIns The driver instance data.
2218 */
2219static DECLCALLBACK(void) drvAudioResume(PPDMDRVINS pDrvIns)
2220{
2221 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_RESUME);
2222}
2223
2224/**
2225 * Audio driver registration record.
2226 */
2227const PDMDRVREG g_DrvAUDIO =
2228{
2229 /* u32Version */
2230 PDM_DRVREG_VERSION,
2231 /* szName */
2232 "AUDIO",
2233 /* szRCMod */
2234 "",
2235 /* szR0Mod */
2236 "",
2237 /* pszDescription */
2238 "Audio connector driver",
2239 /* fFlags */
2240 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2241 /* fClass */
2242 PDM_DRVREG_CLASS_AUDIO,
2243 /* cMaxInstances */
2244 2,
2245 /* cbInstance */
2246 sizeof(DRVAUDIO),
2247 /* pfnConstruct */
2248 drvAudioConstruct,
2249 /* pfnDestruct */
2250 drvAudioDestruct,
2251 /* pfnRelocate */
2252 NULL,
2253 /* pfnIOCtl */
2254 NULL,
2255 /* pfnPowerOn */
2256 NULL,
2257 /* pfnReset */
2258 NULL,
2259 /* pfnSuspend */
2260 drvAudioSuspend,
2261 /* pfnResume */
2262 drvAudioResume,
2263 /* pfnAttach */
2264 NULL,
2265 /* pfnDetach */
2266 NULL,
2267 /* pfnPowerOff */
2268 drvAudioPowerOff,
2269 /* pfnSoftReset */
2270 NULL,
2271 /* u32EndVersion */
2272 PDM_DRVREG_VERSION
2273};
2274
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