VirtualBox

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

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

Logging.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 56.7 KB
Line 
1/* $Id: DrvAudio.cpp 61615 2016-06-09 10:36:48Z 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#define LOG_GROUP LOG_GROUP_DRV_AUDIO
22#include <VBox/log.h>
23#include <VBox/vmm/pdm.h>
24#include <VBox/err.h>
25#include <VBox/vmm/mm.h>
26#include <VBox/vmm/pdmaudioifs.h>
27
28#include <iprt/alloc.h>
29#include <iprt/asm-math.h>
30#include <iprt/assert.h>
31#include <iprt/circbuf.h>
32#include <iprt/string.h>
33#include <iprt/uuid.h>
34
35#include "VBoxDD.h"
36
37#include <ctype.h>
38#include <stdlib.h>
39
40#include "DrvAudio.h"
41#include "AudioMixBuffer.h"
42
43static DECLCALLBACK(int) drvAudioStreamDestroy(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream);
44static int drvAudioStreamControlInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd);
45static int drvAudioStreamControlInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd);
46static int drvAudioStreamDestroyInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream);
47static int drvAudioStreamDestroyInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream);
48static int drvAudioStreamIterateInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream);
49
50#ifndef VBOX_AUDIO_TESTCASE
51static PDMAUDIOFMT drvAudioGetConfFormat(PCFGMNODE pCfgHandle, const char *pszKey,
52 PDMAUDIOFMT enmDefault, bool *pfDefault)
53{
54 if ( pCfgHandle == NULL
55 || pszKey == NULL)
56 {
57 *pfDefault = true;
58 return enmDefault;
59 }
60
61 char *pszValue = NULL;
62 int rc = CFGMR3QueryStringAlloc(pCfgHandle, pszKey, &pszValue);
63 if (RT_FAILURE(rc))
64 {
65 *pfDefault = true;
66 return enmDefault;
67 }
68
69 PDMAUDIOFMT fmt = DrvAudioHlpStrToAudFmt(pszValue);
70 if (fmt == PDMAUDIOFMT_INVALID)
71 {
72 *pfDefault = true;
73 return enmDefault;
74 }
75
76 *pfDefault = false;
77 return fmt;
78}
79
80static int drvAudioGetConfInt(PCFGMNODE pCfgHandle, const char *pszKey,
81 int iDefault, bool *pfDefault)
82{
83
84 if ( pCfgHandle == NULL
85 || pszKey == NULL)
86 {
87 *pfDefault = true;
88 return iDefault;
89 }
90
91 uint64_t u64Data = 0;
92 int rc = CFGMR3QueryInteger(pCfgHandle, pszKey, &u64Data);
93 if (RT_FAILURE(rc))
94 {
95 *pfDefault = true;
96 return iDefault;
97
98 }
99
100 *pfDefault = false;
101 return u64Data;
102}
103
104static const char *drvAudioGetConfStr(PCFGMNODE pCfgHandle, const char *pszKey,
105 const char *pszDefault, bool *pfDefault)
106{
107 if ( pCfgHandle == NULL
108 || pszKey == NULL)
109 {
110 *pfDefault = true;
111 return pszDefault;
112 }
113
114 char *pszValue = NULL;
115 int rc = CFGMR3QueryStringAlloc(pCfgHandle, pszKey, &pszValue);
116 if (RT_FAILURE(rc))
117 {
118 *pfDefault = true;
119 return pszDefault;
120 }
121
122 *pfDefault = false;
123 return pszValue;
124}
125
126/**
127 * Returns the host stream part of an audio stream pair, or NULL
128 * if no host stream has been assigned / is not available.
129 *
130 * @returns IPRT status code.
131 * @param pStream Audio stream to retrieve host stream part for.
132 */
133inline PPDMAUDIOSTREAM drvAudioGetHostStream(PPDMAUDIOSTREAM pStream)
134{
135 AssertPtrReturn(pStream, NULL);
136
137 PPDMAUDIOSTREAM pStreamHst = pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST
138 ? pStream
139 : pStream->pPair;
140 if (pStreamHst)
141 {
142 Assert(pStreamHst->enmCtx == PDMAUDIOSTREAMCTX_HOST);
143 }
144 else
145 LogFlowFunc(("%s: Warning: Does not have a host stream (anymore)\n", pStream->szName));
146
147 return pStreamHst;
148}
149
150static int drvAudioProcessOptions(PCFGMNODE pCfgHandle, const char *pszPrefix, audio_option *paOpts, size_t cOpts)
151{
152 AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
153 AssertPtrReturn(pszPrefix, VERR_INVALID_POINTER);
154 /* oaOpts and cOpts are optional. */
155
156 PCFGMNODE pCfgChildHandle = NULL;
157 PCFGMNODE pCfgChildChildHandle = NULL;
158
159 /* If pCfgHandle is NULL, let NULL be passed to get int and get string functions..
160 * The getter function will return default values.
161 */
162 if (pCfgHandle != NULL)
163 {
164 /* If its audio general setting, need to traverse to one child node.
165 * /Devices/ichac97/0/LUN#0/Config/Audio
166 */
167 if(!strncmp(pszPrefix, "AUDIO", 5)) /** @todo Use a \#define */
168 {
169 pCfgChildHandle = CFGMR3GetFirstChild(pCfgHandle);
170 if(pCfgChildHandle)
171 pCfgHandle = pCfgChildHandle;
172 }
173 else
174 {
175 /* If its driver specific configuration , then need to traverse two level deep child
176 * child nodes. for eg. in case of DirectSoundConfiguration item
177 * /Devices/ichac97/0/LUN#0/Config/Audio/DirectSoundConfig
178 */
179 pCfgChildHandle = CFGMR3GetFirstChild(pCfgHandle);
180 if (pCfgChildHandle)
181 {
182 pCfgChildChildHandle = CFGMR3GetFirstChild(pCfgChildHandle);
183 if (pCfgChildChildHandle)
184 pCfgHandle = pCfgChildChildHandle;
185 }
186 }
187 }
188
189 for (size_t i = 0; i < cOpts; i++)
190 {
191 audio_option *pOpt = &paOpts[i];
192 if (!pOpt->valp)
193 {
194 LogFlowFunc(("Option value pointer for `%s' is not set\n", pOpt->name));
195 continue;
196 }
197
198 bool fUseDefault;
199
200 switch (pOpt->tag)
201 {
202 case AUD_OPT_BOOL:
203 case AUD_OPT_INT:
204 {
205 int *intp = (int *)pOpt->valp;
206 *intp = drvAudioGetConfInt(pCfgHandle, pOpt->name, *intp, &fUseDefault);
207
208 break;
209 }
210
211 case AUD_OPT_FMT:
212 {
213 PDMAUDIOFMT *fmtp = (PDMAUDIOFMT *)pOpt->valp;
214 *fmtp = drvAudioGetConfFormat(pCfgHandle, pOpt->name, *fmtp, &fUseDefault);
215
216 break;
217 }
218
219 case AUD_OPT_STR:
220 {
221 const char **strp = (const char **)pOpt->valp;
222 *strp = drvAudioGetConfStr(pCfgHandle, pOpt->name, *strp, &fUseDefault);
223
224 break;
225 }
226
227 default:
228 LogFlowFunc(("Bad value tag for option `%s' - %d\n", pOpt->name, pOpt->tag));
229 fUseDefault = false;
230 break;
231 }
232
233 if (!pOpt->overridenp)
234 pOpt->overridenp = &pOpt->overriden;
235
236 *pOpt->overridenp = !fUseDefault;
237 }
238
239 return VINF_SUCCESS;
240}
241#endif /* !VBOX_AUDIO_TESTCASE */
242
243static DECLCALLBACK(int) drvAudioStreamControl(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
244{
245 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
246
247 if (!pStream)
248 return VINF_SUCCESS;
249
250 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
251
252 int rc = RTCritSectEnter(&pThis->CritSect);
253 if (RT_FAILURE(rc))
254 return rc;
255
256 LogFlowFunc(("%s: enmStreamCmd=%ld\n", pStream->szName, enmStreamCmd));
257
258 rc = drvAudioStreamControlInternal(pThis, pStream, enmStreamCmd);
259
260 int rc2 = RTCritSectLeave(&pThis->CritSect);
261 if (RT_SUCCESS(rc))
262 rc = rc2;
263
264 return rc;
265}
266
267static int drvAudioStreamControlInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
268{
269 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
270 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
271
272 LogFlowFunc(("%s: enmStreamCmd=%ld\n", pStream->szName, enmStreamCmd));
273
274 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
275 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
276 AssertPtr(pGstStream);
277
278 int rc = VINF_SUCCESS;
279
280 switch (enmStreamCmd)
281 {
282 case PDMAUDIOSTREAMCMD_ENABLE:
283 {
284 if (!(pGstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
285 {
286 if (pHstStream)
287 {
288 /* Is a pending disable outstanding? Then disable first. */
289 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE)
290 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
291
292 if (RT_SUCCESS(rc))
293 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_ENABLE);
294 }
295
296 pGstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_ENABLED;
297 }
298 break;
299 }
300
301 case PDMAUDIOSTREAMCMD_DISABLE:
302 case PDMAUDIOSTREAMCMD_PAUSE:
303 {
304 if (pGstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED)
305 {
306 /* Is the guest side stream still active?
307 * Mark the host stream as pending disable and bail out. */
308 if (pHstStream)
309 {
310 LogFlowFunc(("%s: Pending disable/pause\n", pHstStream->szName));
311 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
312 }
313
314 if (enmStreamCmd == PDMAUDIOSTREAMCMD_DISABLE)
315 {
316 pGstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_ENABLED;
317 }
318 else if (enmStreamCmd == PDMAUDIOSTREAMCMD_PAUSE)
319 pGstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_PAUSED;
320 else
321 AssertFailedBreakStmt(rc = VERR_NOT_IMPLEMENTED);
322 }
323
324 if ( pHstStream
325 && !(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE))
326 {
327 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, enmStreamCmd);
328 if (RT_SUCCESS(rc))
329 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
330 }
331 break;
332 }
333
334 case PDMAUDIOSTREAMCMD_RESUME:
335 {
336 if (pGstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED)
337 {
338 if (pHstStream)
339 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_RESUME);
340
341 pGstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PAUSED;
342 }
343 break;
344 }
345
346 default:
347 AssertMsgFailed(("Command %ld not implemented\n", enmStreamCmd));
348 rc = VERR_NOT_IMPLEMENTED;
349 break;
350 }
351
352 if (RT_FAILURE(rc))
353 LogFunc(("%s: Failed with %Rrc\n", pStream->szName, rc));
354
355 return rc;
356}
357
358static int drvAudioStreamControlInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
359{
360 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
361 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
362
363 LogFlowFunc(("%s: enmStreamCmd=%ld\n", pStream->szName, enmStreamCmd));
364
365 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
366 AssertPtr(pHstStream);
367
368 AssertPtr(pThis->pHostDrvAudio);
369
370 int rc = VINF_SUCCESS;
371
372 switch (enmStreamCmd)
373 {
374 case PDMAUDIOSTREAMCMD_ENABLE:
375 {
376 if (!(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
377 {
378 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_ENABLE);
379 if (RT_SUCCESS(rc))
380 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_ENABLED;
381 }
382 break;
383 }
384
385 case PDMAUDIOSTREAMCMD_DISABLE:
386 {
387 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED)
388 {
389 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
390 if (RT_SUCCESS(rc))
391 {
392 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_ENABLED;
393 AudioMixBufReset(&pHstStream->MixBuf);
394 }
395 }
396 break;
397 }
398
399 case PDMAUDIOSTREAMCMD_PAUSE:
400 {
401 if (!(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED))
402 {
403 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_PAUSE);
404 if (RT_SUCCESS(rc))
405 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_PAUSED;
406 }
407 break;
408 }
409
410 case PDMAUDIOSTREAMCMD_RESUME:
411 {
412 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED)
413 {
414 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_RESUME);
415 if (RT_SUCCESS(rc))
416 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PAUSED;
417 }
418 break;
419 }
420
421 default:
422 AssertMsgFailed(("Command %ld not implemented\n", enmStreamCmd));
423 rc = VERR_NOT_IMPLEMENTED;
424 break;
425 }
426
427 if (RT_FAILURE(rc))
428 LogFunc(("%s: Failed with %Rrc\n", pStream->szName, rc));
429
430 return rc;
431}
432
433/**
434 * Writes VM audio output data from the guest stream into the host stream.
435 * The attached host driver backend then will play out the audio in a
436 * later step then.
437 *
438 * @return IPRT status code.
439 * @return int
440 * @param pThis
441 * @param pGstStrmOut
442 * @param pvBuf
443 * @param cbBuf
444 * @param pcbWritten
445 */
446static DECLCALLBACK(int) drvAudioStreamWrite(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
447 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
448{
449 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
450 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
451 /* pcbWritten is optional. */
452
453 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
454
455 if ( !pStream
456 || !cbBuf)
457 {
458 if (pcbWritten)
459 *pcbWritten = 0;
460 return VINF_SUCCESS;
461 }
462
463 AssertMsg(pStream->enmDir == PDMAUDIODIR_OUT,
464 ("Stream '%s' is not an output stream and therefore cannot be written to (direction is 0x%x)\n",
465 pStream->szName, pStream->enmDir));
466
467 LogFlowFunc(("[%s]: cbBuf=%RU32\n", pStream->szName, cbBuf));
468
469 int rc = RTCritSectEnter(&pThis->CritSect);
470 if (RT_FAILURE(rc))
471 return rc;
472
473 if (!pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_OUT))
474 {
475 rc = RTCritSectLeave(&pThis->CritSect);
476 AssertRC(rc);
477
478 return VERR_NOT_AVAILABLE;
479 }
480
481 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
482 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
483
484 AssertMsg(pGstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED,
485 ("Writing to disabled guest output stream \"%s\" not possible\n", pGstStream->szName));
486
487 if (!AudioMixBufFreeBytes(&pGstStream->MixBuf))
488 {
489 if (pcbWritten)
490 *pcbWritten = 0;
491
492 return RTCritSectLeave(&pThis->CritSect);
493 }
494
495 uint32_t cWritten = 0;
496 rc = AudioMixBufWriteCirc(&pGstStream->MixBuf, pvBuf, cbBuf, &cWritten);
497 if (rc == VINF_BUFFER_OVERFLOW)
498 {
499 LogRel2(("Audio: Lost audio samples from guest stream '%s' (only %zu / %zu bytes written), expect stuttering audio output\n",
500 pGstStream->szName, AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cWritten), cbBuf));
501 rc = VINF_SUCCESS;
502 }
503
504 if (RT_SUCCESS(rc))
505 {
506 if (pcbWritten)
507 *pcbWritten = AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cWritten);
508 }
509
510 LogFlowFunc(("cWritten=%RU32 (%RU32 bytes), cMixed=%RU32, rc=%Rrc\n",
511 cWritten, AUDIOMIXBUF_S2B(&pHstStream->MixBuf, cWritten), 0, rc));
512
513 int rc2 = RTCritSectLeave(&pThis->CritSect);
514 if (RT_SUCCESS(rc))
515 rc = rc2;
516
517 return rc;
518}
519
520static DECLCALLBACK(uint32_t) drvAudioStreamAddRef(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
521{
522 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
523 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
524
525 NOREF(pInterface);
526
527 return ++pStream->cRefs;
528}
529
530static DECLCALLBACK(uint32_t) drvAudioStreamRelease(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
531{
532 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
533 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
534
535 NOREF(pInterface);
536
537 if (pStream->cRefs > 1) /* 1 reference always is kept by this audio driver. */
538 pStream->cRefs--;
539
540 return pStream->cRefs;
541}
542
543static DECLCALLBACK(int) drvAudioStreamIterate(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
544{
545 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
546 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
547 /* pcData is optional. */
548
549 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
550
551 int rc = RTCritSectEnter(&pThis->CritSect);
552 if (RT_FAILURE(rc))
553 return rc;
554
555 LogFlowFunc(("%s\n", pStream->szName));
556
557 rc = drvAudioStreamIterateInternal(pThis, pStream);
558
559 int rc2 = RTCritSectLeave(&pThis->CritSect);
560 if (RT_SUCCESS(rc))
561 rc = rc2;
562
563 if (RT_FAILURE(rc))
564 LogFlowFuncLeaveRC(rc);
565
566 return rc;
567}
568
569static int drvAudioStreamIterateInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream)
570{
571 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
572
573 if (!pStream)
574 return VINF_SUCCESS;
575
576 LogFlowFunc(("%s\n", pStream->szName));
577
578 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
579 AssertPtr(pHstStream);
580 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
581 AssertPtr(pGstStream);
582
583 int rc;
584
585 do
586 {
587 uint32_t cSamplesMixed = 0;
588
589 rc = pThis->pHostDrvAudio->pfnStreamIterate(pThis->pHostDrvAudio, pHstStream);
590 if (RT_FAILURE(rc))
591 break;
592
593 if (pHstStream->enmDir == PDMAUDIODIR_IN)
594 {
595 /* Has the host captured any samples which were not mixed to the guest side yet? */
596 uint32_t cSamplesCaptured = AudioMixBufUsed(&pHstStream->MixBuf);
597
598 LogFlowFunc(("[%s] %RU32 samples captured\n", pHstStream->szName, cSamplesCaptured));
599
600 if (cSamplesCaptured)
601 {
602 /* When capturing samples, the guest is the parent while the host is the child.
603 * So try mixing not yet mixed host-side samples to the guest-side buffer. */
604 rc = AudioMixBufMixToParent(&pHstStream->MixBuf, cSamplesCaptured, &cSamplesMixed);
605 if ( RT_SUCCESS(rc)
606 && cSamplesMixed)
607 {
608 LogFlowFunc(("[%s] %RU32 captured samples mixed\n", pHstStream->szName, cSamplesMixed));
609 }
610 }
611 }
612 else if (pHstStream->enmDir == PDMAUDIODIR_OUT)
613 {
614 uint32_t cSamplesLive = AudioMixBufLive(&pGstStream->MixBuf);
615 if (!cSamplesLive) /* No live samples at the moment? */
616 {
617 /* When playing samples, the host is the parent while the guest is the child.
618 * So try mixing not yet mixed guest-side samples to the host-side buffer. */
619 rc = AudioMixBufMixToParent(&pGstStream->MixBuf, AudioMixBufUsed(&pGstStream->MixBuf), &cSamplesMixed);
620 if ( RT_SUCCESS(rc)
621 && cSamplesMixed)
622 {
623 LogFlowFunc(("[%s] %RU32 samples mixed\n", pHstStream->szName, cSamplesMixed));
624 }
625
626 if (RT_SUCCESS(rc))
627 cSamplesLive = AudioMixBufLive(&pGstStream->MixBuf);
628 }
629
630 LogFlowFunc(("[%s] %RU32 live samples\n", pHstStream->szName, cSamplesLive));
631
632 if (!cSamplesLive) /* No live samples at the moment? */
633 {
634 /* Has the host stream marked as disabled but there still were guest streams relying
635 * on it? Check if the stream now can be closed and do so, if possible. */
636 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE)
637 {
638 LogFunc(("[%s] Closing pending stream\n", pHstStream->szName));
639 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
640 if (RT_SUCCESS(rc))
641 {
642 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
643 }
644 else
645 LogFunc(("%s: Backend vetoed against closing output stream, rc=%Rrc\n", pHstStream->szName, rc));
646 }
647
648 break;
649 }
650 }
651 else
652 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
653
654 } while (0);
655
656 if (RT_FAILURE(rc))
657 LogFunc(("Failed with %Rrc\n", rc));
658
659 return rc;
660}
661
662static DECLCALLBACK(int) drvAudioStreamPlay(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
663 uint32_t *pcSamplesPlayed)
664{
665 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
666 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
667 /* pcSamplesPlayed is optional. */
668
669 AssertMsg(pStream->enmDir == PDMAUDIODIR_OUT,
670 ("Stream '%s' is not an output stream and therefore cannot be played back (direction is 0x%x)\n",
671 pStream->szName, pStream->enmDir));
672
673 AssertMsg(pStream->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED,
674 ("Unable to play stream '%s' (status is 0x%x)\n", pStream->szName, pStream->fStatus));
675
676 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
677
678 int rc = RTCritSectEnter(&pThis->CritSect);
679 if (RT_FAILURE(rc))
680 return rc;
681
682 uint32_t cSamplesPlayed = 0;
683
684 do
685 {
686 /* Backend output (temporarily) disabled / unavailable? */
687 if (pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_OUT) != PDMAUDIOBACKENDSTS_RUNNING)
688 {
689 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);
690 AssertRC(rc);
691
692 if ( !pThis->BackendCfg.cSinks
693 || !pThis->BackendCfg.cMaxStreamsOut)
694 {
695 rc = VERR_NOT_AVAILABLE;
696 break;
697 }
698 }
699
700 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
701 AssertPtr(pHstStream);
702 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
703 AssertPtr(pGstStream);
704
705 uint32_t cSamplesLive = AudioMixBufLive(&pGstStream->MixBuf);
706 if (cSamplesLive)
707 {
708 PDMAUDIOSTRMSTS strmSts = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pHstStream);
709 if (strmSts & PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE)
710 {
711 rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pHstStream, &cSamplesPlayed);
712 if (RT_FAILURE(rc))
713 {
714 int rc2 = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
715 AssertRC(rc2);
716 }
717 }
718
719 LogFlowFunc(("[%s] strmSts=0x%x, cSamplesPlayed=%RU32, rc=%Rrc\n", pHstStream->szName, strmSts, cSamplesPlayed, rc));
720 }
721
722 if (!cSamplesLive)
723 {
724 /* Has the host stream marked as disabled but there still were guest streams relying
725 * on it? Check if the stream now can be closed and do so, if possible. */
726 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE)
727 {
728 LogFunc(("[%s] Closing pending stream\n", pHstStream->szName));
729 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
730 if (RT_SUCCESS(rc))
731 {
732 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
733 }
734 else
735 LogFunc(("[%s] Backend vetoed against closing output stream, rc=%Rrc\n", pHstStream->szName, rc));
736 }
737 }
738
739 } while (0);
740
741 if (RT_SUCCESS(rc))
742 {
743 if (pcSamplesPlayed)
744 *pcSamplesPlayed = cSamplesPlayed;
745 }
746
747 int rc2 = RTCritSectLeave(&pThis->CritSect);
748 if (RT_SUCCESS(rc))
749 rc = rc2;
750
751 if (RT_FAILURE(rc))
752 LogFlowFunc(("[%s] Failed with %Rrc\n", pStream->szName, rc));
753
754 return rc;
755}
756
757static DECLCALLBACK(int) drvAudioStreamCapture(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
758 uint32_t *pcSamplesCaptured)
759{
760 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN,
761 ("Stream '%s' is not an input stream and therefore cannot be captured (direction is 0x%x)\n",
762 pStream->szName, pStream->enmDir));
763
764 AssertMsg(pStream->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED,
765 ("Unable to capture stream '%s' (status is 0x%x)\n", pStream->szName, pStream->fStatus));
766
767 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
768
769 int rc = RTCritSectEnter(&pThis->CritSect);
770 if (RT_FAILURE(rc))
771 return rc;
772
773 LogFlowFunc(("[%s]\n", pStream->szName));
774
775 uint32_t cSamplesCaptured = 0;
776
777 do
778 {
779 /* Backend input (temporarily) disabled / unavailable? */
780 if (pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_IN) != PDMAUDIOBACKENDSTS_RUNNING)
781 {
782 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);
783 AssertRC(rc);
784
785 if ( !pThis->BackendCfg.cSources
786 || !pThis->BackendCfg.cMaxStreamsIn)
787 {
788 rc = VERR_NOT_AVAILABLE;
789 break;
790 }
791 }
792
793 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
794 AssertPtr(pHstStream);
795 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
796 AssertPtr(pGstStream);
797
798 uint32_t cSamplesLive = AudioMixBufLive(&pGstStream->MixBuf);
799 if (!cSamplesLive)
800 {
801 PDMAUDIOSTRMSTS strmSts = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pHstStream);
802 if (strmSts & PDMAUDIOSTRMSTS_FLAG_DATA_READABLE)
803 {
804 rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pHstStream, &cSamplesCaptured);
805 if (RT_FAILURE(rc))
806 {
807 int rc2 = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
808 AssertRC(rc2);
809 }
810 }
811
812 LogFlowFunc(("[%s] strmSts=0x%x, cSamplesCaptured=%RU32, rc=%Rrc\n", pHstStream->szName, strmSts, cSamplesCaptured, rc));
813 }
814
815 } while (0);
816
817 if (RT_SUCCESS(rc))
818 {
819 if (pcSamplesCaptured)
820 *pcSamplesCaptured = cSamplesCaptured;
821 }
822
823 int rc2 = RTCritSectLeave(&pThis->CritSect);
824 if (RT_SUCCESS(rc))
825 rc = rc2;
826
827 if (RT_FAILURE(rc))
828 LogFlowFuncLeaveRC(rc);
829
830 return rc;
831}
832
833#ifdef VBOX_WITH_AUDIO_CALLBACKS
834static PPDMAUDIOCALLBACK drvAudioCallbackDuplicate(PPDMAUDIOCALLBACK pCB)
835{
836 PPDMAUDIOCALLBACK pCBCopy = (PPDMAUDIOCALLBACK)RTMemDup((void *)pCB, sizeof(PDMAUDIOCALLBACK));
837 if (!pCBCopy)
838 return NULL;
839
840 if (pCB->pvCtx)
841 {
842 pCBCopy->pvCtx = RTMemDup(pCB->pvCtx, pCB->cbCtx);
843 if (!pCBCopy->pvCtx)
844 {
845 RTMemFree(pCBCopy);
846 return NULL;
847 }
848
849 pCBCopy->cbCtx = pCB->cbCtx;
850 }
851
852 return pCBCopy;
853}
854
855static void drvAudioCallbackDestroy(PPDMAUDIOCALLBACK pCB)
856{
857 if (!pCB)
858 return;
859
860 RTListNodeRemove(&pCB->Node);
861 if (pCB->pvCtx)
862 {
863 Assert(pCB->cbCtx);
864 RTMemFree(pCB->pvCtx);
865 }
866 RTMemFree(pCB);
867}
868
869static DECLCALLBACK(int) drvAudioRegisterCallbacks(PPDMIAUDIOCONNECTOR pInterface,
870 PPDMAUDIOCALLBACK paCallbacks, size_t cCallbacks)
871{
872 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
873 AssertPtrReturn(paCallbacks, VERR_INVALID_POINTER);
874 AssertReturn(cCallbacks, VERR_INVALID_PARAMETER);
875
876 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
877
878 int rc = RTCritSectEnter(&pThis->CritSect);
879 if (RT_FAILURE(rc))
880 return rc;
881
882 for (size_t i = 0; i < cCallbacks; i++)
883 {
884 PPDMAUDIOCALLBACK pCB = drvAudioCallbackDuplicate(&paCallbacks[i]);
885 if (!pCB)
886 {
887 rc = VERR_NO_MEMORY;
888 break;
889 }
890
891 switch (pCB->enmType)
892 {
893 case PDMAUDIOCALLBACKTYPE_INPUT:
894 RTListAppend(&pThis->lstCBIn, &pCB->Node);
895 break;
896
897 case PDMAUDIOCALLBACKTYPE_OUTPUT:
898 RTListAppend(&pThis->lstCBOut, &pCB->Node);
899 break;
900
901 default:
902 AssertMsgFailed(("Not supported\n"));
903 break;
904 }
905 }
906
907 /** @todo Undo allocations on error. */
908
909 int rc2 = RTCritSectLeave(&pThis->CritSect);
910 if (RT_SUCCESS(rc))
911 rc = rc2;
912
913 return rc;
914}
915
916static DECLCALLBACK(int) drvAudioCallback(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIOCALLBACKTYPE enmType,
917 void *pvUser, size_t cbUser)
918{
919 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
920 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
921 AssertReturn(cbUser, VERR_INVALID_PARAMETER);
922
923 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
924 PRTLISTANCHOR pListAnchor = NULL;
925
926 switch (enmType)
927 {
928 case PDMAUDIOCALLBACKTYPE_INPUT:
929 pListAnchor = &pThis->lstCBIn;
930 break;
931
932 case PDMAUDIOCALLBACKTYPE_OUTPUT:
933 pListAnchor = &pThis->lstCBOut;
934 break;
935
936 default:
937 AssertMsgFailed(("Not supported\n"));
938 break;
939 }
940
941 if (pListAnchor)
942 {
943 PPDMAUDIOCALLBACK pCB;
944 RTListForEach(pListAnchor, pCB, PDMAUDIOCALLBACK, Node)
945 {
946 Assert(pCB->enmType == enmType);
947 pCB->pfnCallback(enmType, pCB->pvCtx, pCB->cbCtx, pvUser, cbUser);
948 }
949 }
950
951 return VINF_SUCCESS;
952}
953#endif
954
955/**
956 * Initializes the host backend and queries its initial configuration.
957 * If the host backend fails, VERR_AUDIO_BACKEND_INIT_FAILED will be returned.
958 *
959 * Note: As this routine is called when attaching to the device LUN in the
960 * device emulation, we either check for success or VERR_AUDIO_BACKEND_INIT_FAILED.
961 * Everything else is considered as fatal and must be handled separately in
962 * the device emulation!
963 *
964 * @return IPRT status code.
965 * @param pThis Driver instance to be called.
966 * @param pCfgHandle CFGM configuration handle to use for this driver.
967 */
968static int drvAudioHostInit(PDRVAUDIO pThis, PCFGMNODE pCfgHandle)
969{
970 /* pCfgHandle is optional. */
971 NOREF(pCfgHandle);
972 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
973
974 LogFlowFuncEnter();
975
976 AssertPtr(pThis->pHostDrvAudio);
977 int rc = pThis->pHostDrvAudio->pfnInit(pThis->pHostDrvAudio);
978 if (RT_FAILURE(rc))
979 {
980 LogRel(("Audio: Initialization of host backend failed with %Rrc\n", rc));
981 return VERR_AUDIO_BACKEND_INIT_FAILED;
982 }
983
984 /* Get the configuration data from backend. */
985 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);
986 if (RT_FAILURE(rc))
987 {
988 LogRel(("Audio: Getting host backend configuration failed with %Rrc\n", rc));
989 return VERR_AUDIO_BACKEND_INIT_FAILED;
990 }
991
992 pThis->cStreamsFreeIn = 0;
993 pThis->cStreamsFreeOut = 0;
994
995 if (pThis->BackendCfg.cSinks)
996 {
997 Assert(pThis->BackendCfg.cbStreamOut);
998
999 pThis->cStreamsFreeOut = pThis->BackendCfg.cMaxStreamsOut;
1000 }
1001
1002 if (pThis->BackendCfg.cSources)
1003 {
1004 Assert(pThis->BackendCfg.cbStreamIn);
1005
1006 pThis->cStreamsFreeIn = pThis->BackendCfg.cMaxStreamsIn;
1007 }
1008
1009 LogFlowFunc(("cStreamsFreeIn=%RU8, cStreamsFreeOut=%RU8\n", pThis->cStreamsFreeIn, pThis->cStreamsFreeOut));
1010
1011 LogRel2(("Audio: Host audio backend supports %RU32 input streams and %RU32 output streams at once\n",
1012 /* Clamp for logging. Unlimited streams are defined by UINT32_MAX. */
1013 RT_MIN(64, pThis->cStreamsFreeIn), RT_MIN(64, pThis->cStreamsFreeOut)));
1014
1015 LogFlowFuncLeave();
1016 return VINF_SUCCESS;
1017}
1018
1019static void drvAudioStateHandler(PPDMDRVINS pDrvIns, PDMAUDIOSTREAMCMD enmCmd)
1020{
1021 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1022 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1023
1024 LogFlowFunc(("enmCmd=%ld\n", enmCmd));
1025
1026 if (!pThis->pHostDrvAudio)
1027 return;
1028
1029 PPDMAUDIOSTREAM pHstStream;
1030 RTListForEach(&pThis->lstHstStreams, pHstStream, PDMAUDIOSTREAM, Node)
1031 drvAudioStreamControlInternalBackend(pThis, pHstStream, enmCmd);
1032}
1033
1034static DECLCALLBACK(int) drvAudioInit(PCFGMNODE pCfgHandle, PPDMDRVINS pDrvIns)
1035{
1036 AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
1037 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
1038
1039 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1040 LogFlowFunc(("pThis=%p, pDrvIns=%p\n", pThis, pDrvIns));
1041
1042 int rc = RTCritSectInit(&pThis->CritSect);
1043
1044 /** @todo Add audio driver options. */
1045
1046 /*
1047 * If everything went well, initialize the lower driver.
1048 */
1049 if (RT_SUCCESS(rc))
1050 rc = drvAudioHostInit(pThis, pCfgHandle);
1051
1052 LogFlowFuncLeaveRC(rc);
1053 return rc;
1054}
1055
1056static DECLCALLBACK(int) drvAudioStreamRead(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
1057 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1058{
1059 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1060 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1061
1062 if (!pStream)
1063 {
1064 if (pcbRead)
1065 *pcbRead = 0;
1066 return VINF_SUCCESS;
1067 }
1068
1069 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1070 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1071 /* pcbWritten is optional. */
1072
1073 int rc = RTCritSectEnter(&pThis->CritSect);
1074 if (RT_FAILURE(rc))
1075 return rc;
1076
1077 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN,
1078 ("Stream '%s' is not an input stream and therefore cannot be read from (direction is 0x%x)\n",
1079 pStream->szName, pStream->enmDir));
1080
1081 if (pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_IN) != PDMAUDIOBACKENDSTS_RUNNING)
1082 {
1083 if (pcbRead)
1084 *pcbRead = 0;
1085
1086 return RTCritSectLeave(&pThis->CritSect);
1087 }
1088
1089 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1090 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
1091
1092 AssertMsg(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED,
1093 ("Reading from disabled host input stream '%s' not possible\n", pHstStream->szName));
1094
1095 Log3Func(("%s\n", pStream->szName));
1096
1097 /*
1098 * Read from the parent buffer (that is, the guest buffer) which
1099 * should have the audio data in the format the guest needs.
1100 */
1101 uint32_t cRead;
1102 rc = AudioMixBufReadCirc(&pGstStream->MixBuf, pvBuf, cbBuf, &cRead);
1103 if (RT_SUCCESS(rc))
1104 {
1105 if (cRead)
1106 AudioMixBufFinish(&pGstStream->MixBuf, cRead);
1107
1108 if (pcbRead)
1109 *pcbRead = AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cRead);
1110 }
1111
1112 LogFlowFunc(("cRead=%RU32 (%RU32 bytes), rc=%Rrc\n",
1113 cRead, AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cRead), rc));
1114
1115 int rc2 = RTCritSectLeave(&pThis->CritSect);
1116 if (RT_SUCCESS(rc))
1117 rc = rc2;
1118
1119 return rc;
1120}
1121
1122static DECLCALLBACK(int) drvAudioStreamCreate(PPDMIAUDIOCONNECTOR pInterface,
1123 PPDMAUDIOSTREAMCFG pCfgHost, PPDMAUDIOSTREAMCFG pCfgGuest,
1124 PPDMAUDIOSTREAM *ppStream)
1125{
1126 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1127 AssertPtrReturn(pCfgHost, VERR_INVALID_POINTER);
1128 AssertPtrReturn(pCfgGuest, VERR_INVALID_POINTER);
1129 AssertPtrReturn(ppStream, VERR_INVALID_POINTER);
1130
1131 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1132
1133 int rc = RTCritSectEnter(&pThis->CritSect);
1134 if (RT_FAILURE(rc))
1135 return rc;
1136
1137 LogFlowFunc(("Host=%s, Guest=%s\n", pCfgHost->szName, pCfgGuest->szName));
1138#ifdef DEBUG
1139 DrvAudioHlpStreamCfgPrint(pCfgHost);
1140 DrvAudioHlpStreamCfgPrint(pCfgGuest);
1141#endif
1142
1143 /*
1144 * The guest stream always will get the audio stream configuration told
1145 * by the device emulation (which in turn was/could be set by the guest OS).
1146 */
1147 PPDMAUDIOSTREAM pGstStrm = NULL;
1148
1149 /** @todo Docs! */
1150 PPDMAUDIOSTREAM pHstStrm = NULL;
1151
1152#define RC_BREAK(x) { rc = x; break; }
1153
1154 do
1155 {
1156 if ( !DrvAudioHlpStreamCfgIsValid(pCfgHost)
1157 || !DrvAudioHlpStreamCfgIsValid(pCfgGuest))
1158 {
1159 RC_BREAK(VERR_INVALID_PARAMETER);
1160 }
1161
1162 /* Make sure that both configurations actually intend the same thing. */
1163 if (pCfgHost->enmDir != pCfgGuest->enmDir)
1164 {
1165 AssertMsgFailed(("Stream configuration directions do not match\n"));
1166 RC_BREAK(VERR_INVALID_PARAMETER);
1167 }
1168
1169 /* Note: cbHstStrm will contain sizeof(PDMAUDIOSTREAM) + additional data
1170 * which the host backend will need. */
1171 size_t cbHstStrm;
1172 if (pCfgHost->enmDir == PDMAUDIODIR_IN)
1173 {
1174 if (!pThis->cStreamsFreeIn)
1175 {
1176 LogFlowFunc(("No more input streams free to use, bailing out\n"));
1177 RC_BREAK(VERR_AUDIO_NO_FREE_INPUT_STREAMS);
1178 }
1179
1180 /* Validate backend configuration. */
1181 if (!pThis->BackendCfg.cbStreamIn)
1182 {
1183 LogFlowFunc(("Backend input configuration not valid, bailing out\n"));
1184 RC_BREAK(VERR_INVALID_PARAMETER);
1185 }
1186
1187 cbHstStrm = pThis->BackendCfg.cbStreamIn;
1188 }
1189 else /* Out */
1190 {
1191 if (!pThis->cStreamsFreeOut)
1192 {
1193 LogFlowFunc(("Maximum number of host output streams reached\n"));
1194 RC_BREAK(VERR_AUDIO_NO_FREE_OUTPUT_STREAMS);
1195 }
1196
1197 /* Validate backend configuration. */
1198 if (!pThis->BackendCfg.cbStreamOut)
1199 {
1200 LogFlowFunc(("Backend output configuration invalid, bailing out\n"));
1201 RC_BREAK(VERR_INVALID_PARAMETER);
1202 }
1203
1204 cbHstStrm = pThis->BackendCfg.cbStreamOut;
1205 }
1206
1207 pHstStrm = (PPDMAUDIOSTREAM)RTMemAllocZ(cbHstStrm);
1208 AssertPtrBreakStmt(pHstStrm, rc = VERR_NO_MEMORY);
1209
1210 pHstStrm->enmCtx = PDMAUDIOSTREAMCTX_HOST;
1211 pHstStrm->enmDir = pCfgHost->enmDir;
1212
1213 pGstStrm = (PPDMAUDIOSTREAM)RTMemAllocZ(sizeof(PDMAUDIOSTREAM));
1214 AssertPtrBreakStmt(pGstStrm, rc = VERR_NO_MEMORY);
1215
1216 pGstStrm->enmCtx = PDMAUDIOSTREAMCTX_GUEST;
1217 pGstStrm->enmDir = pCfgGuest->enmDir;
1218
1219 /*
1220 * Create host stream.
1221 */
1222
1223 RTStrPrintf(pHstStrm->szName, RT_ELEMENTS(pHstStrm->szName), "%s (Host)",
1224 strlen(pCfgHost->szName) ? pCfgHost->szName : "<Untitled>");
1225
1226 /* Note: Direction is always from child -> parent. */
1227 uint32_t cSamples = 0;
1228 rc = pThis->pHostDrvAudio->pfnStreamCreate(pThis->pHostDrvAudio, pHstStrm, pCfgHost, &cSamples);
1229 if (RT_FAILURE(rc))
1230 {
1231 LogFlowFunc(("Initializing host backend failed with rc=%Rrc\n", rc));
1232 break;
1233 }
1234
1235 pHstStrm->fStatus |= PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
1236 pHstStrm->pPair = pGstStrm;
1237
1238 rc = DrvAudioHlpStreamCfgToProps(pCfgHost, &pHstStrm->Props);
1239 AssertRCBreak(rc);
1240
1241 rc = AudioMixBufInit(&pHstStrm->MixBuf, pHstStrm->szName, &pHstStrm->Props, cSamples * 4);
1242 AssertRCBreak(rc);
1243
1244 /*
1245 * Create guest stream.
1246 */
1247
1248 RTStrPrintf(pGstStrm->szName, RT_ELEMENTS(pGstStrm->szName), "%s (Guest)",
1249 strlen(pCfgGuest->szName) ? pCfgGuest->szName : "<Untitled>");
1250
1251 rc = DrvAudioHlpStreamCfgToProps(pCfgGuest, &pGstStrm->Props);
1252 AssertRCBreak(rc);
1253
1254 rc = AudioMixBufInit(&pGstStrm->MixBuf, pGstStrm->szName, &pGstStrm->Props, cSamples * 2);
1255 if (RT_SUCCESS(rc))
1256 {
1257 if (pCfgGuest->enmDir == PDMAUDIODIR_IN)
1258 {
1259 /* Host (Parent) -> Guest (Child). */
1260 rc = AudioMixBufLinkTo(&pHstStrm->MixBuf, &pGstStrm->MixBuf);
1261 }
1262 else
1263 {
1264 /* Guest (Parent) -> Host (Child). */
1265 rc = AudioMixBufLinkTo(&pGstStrm->MixBuf, &pHstStrm->MixBuf);
1266 }
1267 }
1268
1269 pGstStrm->fStatus |= PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
1270 pGstStrm->pPair = pHstStrm;
1271
1272 AssertRCBreak(rc);
1273
1274 } while (0);
1275
1276#undef RC_BREAK
1277
1278 if (RT_FAILURE(rc))
1279 {
1280 if (pGstStrm)
1281 {
1282 drvAudioStreamDestroyInternal(pThis, pGstStrm);
1283 pGstStrm = NULL;
1284 }
1285
1286 if (pHstStrm)
1287 {
1288 drvAudioStreamDestroyInternal(pThis, pHstStrm);
1289 pHstStrm = NULL;
1290 }
1291 }
1292 else
1293 {
1294 /* Set initial reference counts. */
1295 RTListAppend(&pThis->lstGstStreams, &pGstStrm->Node);
1296 pGstStrm->cRefs = 1;
1297
1298 RTListAppend(&pThis->lstHstStreams, &pHstStrm->Node);
1299 pHstStrm->cRefs = 1;
1300
1301 if (pCfgHost->enmDir == PDMAUDIODIR_IN)
1302 {
1303 Assert(pThis->cStreamsFreeIn);
1304 pThis->cStreamsFreeIn--;
1305 }
1306 else /* Out */
1307 {
1308 Assert(pThis->cStreamsFreeOut);
1309 pThis->cStreamsFreeOut--;
1310 }
1311
1312 /* Always return the guest-side part to the device emulation. */
1313 *ppStream = pGstStrm;
1314 }
1315
1316 int rc2 = RTCritSectLeave(&pThis->CritSect);
1317 if (RT_SUCCESS(rc))
1318 rc = rc2;
1319
1320 LogFlowFuncLeaveRC(rc);
1321 return rc;
1322}
1323
1324#if 1
1325static DECLCALLBACK(int) drvAudioGetConfig(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOBACKENDCFG pCfg)
1326{
1327 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1328 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1329
1330 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1331
1332 int rc = RTCritSectEnter(&pThis->CritSect);
1333 if (RT_FAILURE(rc))
1334 return rc;
1335
1336 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, pCfg);
1337
1338 int rc2 = RTCritSectLeave(&pThis->CritSect);
1339 if (RT_SUCCESS(rc))
1340 rc = rc2;
1341
1342 LogFlowFuncLeaveRC(rc);
1343 return rc;
1344}
1345
1346static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioGetStatus(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir)
1347{
1348 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
1349
1350 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1351
1352 int rc = RTCritSectEnter(&pThis->CritSect);
1353 if (RT_FAILURE(rc))
1354 return PDMAUDIOBACKENDSTS_UNKNOWN;
1355
1356 PDMAUDIOBACKENDSTS backendSts = pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, enmDir);
1357
1358 int rc2 = RTCritSectLeave(&pThis->CritSect);
1359 if (RT_SUCCESS(rc))
1360 rc = rc2;
1361
1362 LogFlowFuncLeaveRC(rc);
1363 return backendSts;
1364}
1365
1366static DECLCALLBACK(uint32_t) drvAudioStreamGetReadable(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1367{
1368 AssertPtrReturn(pInterface, 0);
1369 AssertPtrReturn(pStream, 0);
1370
1371 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1372
1373 int rc2 = RTCritSectEnter(&pThis->CritSect);
1374 AssertRC(rc2);
1375
1376 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN, ("Can't read from a non-input stream\n"));
1377
1378 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1379 if (!pHstStream) /* No host stream available? Bail out early. */
1380 {
1381 rc2 = RTCritSectLeave(&pThis->CritSect);
1382 AssertRC(rc2);
1383
1384 return 0;
1385 }
1386
1387 uint32_t cbReadable = 0;
1388
1389 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
1390 if (pGstStream)
1391 cbReadable = AudioMixBufLive(&pGstStream->MixBuf);
1392
1393 LogFlowFunc(("[%s] cbReadable=%RU32\n", pHstStream->szName, cbReadable));
1394
1395 rc2 = RTCritSectLeave(&pThis->CritSect);
1396 AssertRC(rc2);
1397
1398 return cbReadable;
1399}
1400
1401static DECLCALLBACK(uint32_t) drvAudioStreamGetWritable(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1402{
1403 AssertPtrReturn(pInterface, 0);
1404 AssertPtrReturn(pStream, 0);
1405
1406 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1407
1408 int rc2 = RTCritSectEnter(&pThis->CritSect);
1409 AssertRC(rc2);
1410
1411 AssertMsg(pStream->enmDir == PDMAUDIODIR_OUT, ("Can't write to a non-output stream\n"));
1412
1413 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1414 if (!pHstStream) /* No host stream available? Bail out early. */
1415 {
1416 rc2 = RTCritSectLeave(&pThis->CritSect);
1417 AssertRC(rc2);
1418
1419 return 0;
1420 }
1421
1422 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
1423
1424 uint32_t cbWritable = 0;
1425
1426 if (AudioMixBufLive(&pHstStream->MixBuf) == 0)
1427 cbWritable = AudioMixBufFreeBytes(&pGstStream->MixBuf);
1428
1429 LogFlowFunc(("[%s] cWritable=%RU32\n", pHstStream->szName, cbWritable));
1430
1431 rc2 = RTCritSectLeave(&pThis->CritSect);
1432 AssertRC(rc2);
1433
1434 return cbWritable;
1435}
1436
1437static DECLCALLBACK(PDMAUDIOSTRMSTS) drvAudioStreamGetStatus(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1438{
1439 AssertPtrReturn(pInterface, false);
1440
1441 if (!pStream)
1442 return PDMAUDIOSTRMSTS_FLAG_NONE;
1443
1444 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1445
1446 int rc2 = RTCritSectEnter(&pThis->CritSect);
1447 AssertRC(rc2);
1448
1449 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1450 PDMAUDIOSTRMSTS strmSts = pHstStream->fStatus;
1451
1452 LogFlowFunc(("%s: strmSts=0x%x\n", pHstStream->szName, strmSts));
1453 rc2 = RTCritSectLeave(&pThis->CritSect);
1454 AssertRC(rc2);
1455
1456 return strmSts;
1457}
1458
1459static DECLCALLBACK(int) drvAudioStreamSetVolume(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, PPDMAUDIOVOLUME pVol)
1460{
1461 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1462 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1463 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
1464
1465 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1466
1467 LogFlowFunc(("%s: volL=%RU32, volR=%RU32, fMute=%RTbool\n", pStream->szName, pVol->uLeft, pVol->uRight, pVol->fMuted));
1468
1469 AudioMixBufSetVolume(&pStream->MixBuf, pVol);
1470
1471 return VINF_SUCCESS;
1472}
1473#endif
1474
1475static DECLCALLBACK(int) drvAudioStreamDestroy(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1476{
1477 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1478 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1479
1480 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1481
1482 int rc = RTCritSectEnter(&pThis->CritSect);
1483 AssertRC(rc);
1484
1485 PDMAUDIODIR enmDir = pStream->enmDir;
1486
1487 LogFlowFunc(("%s: cRefs=%RU32\n", pStream->szName, pStream->cRefs));
1488 if (pStream->cRefs > 1)
1489 rc = VERR_WRONG_ORDER;
1490
1491 if (RT_SUCCESS(rc))
1492 {
1493 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1494 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
1495
1496 LogFlowFunc(("\tHost : %s\n", pHstStream ? pHstStream->szName : "<None>"));
1497 LogFlowFunc(("\tGuest: %s\n", pGstStream ? pGstStream->szName : "<None>"));
1498
1499 /* Should prevent double frees. */
1500 Assert(pHstStream != pGstStream);
1501
1502 if (pHstStream)
1503 {
1504 pHstStream->pPair = NULL;
1505 RTListNodeRemove(&pHstStream->Node);
1506 }
1507
1508 if (pGstStream)
1509 {
1510 pGstStream->pPair = NULL;
1511 RTListNodeRemove(&pGstStream->Node);
1512 }
1513
1514 if (pHstStream)
1515 {
1516 rc = drvAudioStreamDestroyInternal(pThis, pHstStream);
1517 AssertRC(rc);
1518
1519 pHstStream = NULL;
1520 }
1521
1522 if (pGstStream)
1523 {
1524 rc = drvAudioStreamDestroyInternal(pThis, pGstStream);
1525 AssertRC(rc);
1526
1527 pGstStream = NULL;
1528 }
1529 }
1530
1531 if (RT_SUCCESS(rc))
1532 {
1533 if (enmDir == PDMAUDIODIR_IN)
1534 {
1535 pThis->cStreamsFreeIn++;
1536 }
1537 else /* Out */
1538 {
1539 pThis->cStreamsFreeOut++;
1540 }
1541 }
1542
1543 int rc2 = RTCritSectLeave(&pThis->CritSect);
1544 if (RT_SUCCESS(rc))
1545 rc = rc2;
1546
1547 LogFlowFuncLeaveRC(rc);
1548 return rc;
1549}
1550
1551static int drvAudioStreamDestroyInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream)
1552{
1553 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1554 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
1555
1556 AssertMsg(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST,
1557 ("Stream '%s' is not a host stream and therefore has no backend\n", pHstStream->szName));
1558
1559 int rc = VINF_SUCCESS;
1560
1561 LogFlowFunc(("%s: fStatus=0x%x\n", pHstStream->szName, pHstStream->fStatus));
1562
1563 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
1564 {
1565 if (pThis->pHostDrvAudio)
1566 rc = pThis->pHostDrvAudio->pfnStreamDestroy(pThis->pHostDrvAudio, pHstStream);
1567 if (RT_SUCCESS(rc))
1568 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
1569 }
1570
1571 LogFlowFunc(("%s: Returning %Rrc\n", pHstStream->szName, rc));
1572 return rc;
1573}
1574
1575static int drvAudioStreamDestroyInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream)
1576{
1577 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1578 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1579
1580 LogFlowFunc(("%s: cRefs=%RU32\n", pStream->szName, pStream->cRefs));
1581
1582 if (pStream->cRefs > 1)
1583 return VERR_WRONG_ORDER;
1584
1585 int rc = VINF_SUCCESS;
1586
1587 if (pStream->enmCtx == PDMAUDIOSTREAMCTX_GUEST)
1588 {
1589 if (pStream->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
1590 {
1591 rc = drvAudioStreamControlInternal(pThis, pStream, PDMAUDIOSTREAMCMD_DISABLE);
1592 if (RT_SUCCESS(rc))
1593 pStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
1594 }
1595 }
1596 else if (pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST)
1597 {
1598 rc = drvAudioStreamDestroyInternalBackend(pThis, pStream);
1599 }
1600 else
1601 AssertFailedReturn(VERR_NOT_IMPLEMENTED);
1602
1603 if (RT_SUCCESS(rc))
1604 {
1605 /* Destroy mixing buffer. */
1606 AudioMixBufDestroy(&pStream->MixBuf);
1607
1608 if (pStream)
1609 {
1610 RTMemFree(pStream);
1611 pStream = NULL;
1612 }
1613 }
1614
1615 LogFlowFunc(("Returning %Rrc\n", rc));
1616 return rc;
1617}
1618
1619/********************************************************************/
1620
1621/**
1622 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1623 */
1624static DECLCALLBACK(void *) drvAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1625{
1626 LogFlowFunc(("pInterface=%p, pszIID=%s\n", pInterface, pszIID));
1627
1628 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1629 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1630
1631 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1632 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIAUDIOCONNECTOR, &pThis->IAudioConnector);
1633
1634 return NULL;
1635}
1636
1637/**
1638 * Power Off notification.
1639 *
1640 * @param pDrvIns The driver instance data.
1641 */
1642static DECLCALLBACK(void) drvAudioPowerOff(PPDMDRVINS pDrvIns)
1643{
1644 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1645
1646 LogFlowFuncEnter();
1647
1648 /* Just destroy the host stream on the backend side.
1649 * The rest will either be destructed by the device emulation or
1650 * in drvAudioDestruct(). */
1651 PPDMAUDIOSTREAM pStream;
1652 RTListForEach(&pThis->lstHstStreams, pStream, PDMAUDIOSTREAM, Node)
1653 drvAudioStreamDestroyInternalBackend(pThis, pStream);
1654
1655 /*
1656 * Last call for the driver below us.
1657 * Let it know that we reached end of life.
1658 */
1659 if (pThis->pHostDrvAudio->pfnShutdown)
1660 pThis->pHostDrvAudio->pfnShutdown(pThis->pHostDrvAudio);
1661
1662 pThis->pHostDrvAudio = NULL;
1663
1664 LogFlowFuncLeave();
1665}
1666
1667/**
1668 * Constructs an audio driver instance.
1669 *
1670 * @copydoc FNPDMDRVCONSTRUCT
1671 */
1672static DECLCALLBACK(int) drvAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
1673{
1674 LogFlowFunc(("pDrvIns=%#p, pCfgHandle=%#p, fFlags=%x\n", pDrvIns, pCfgHandle, fFlags));
1675
1676 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1677 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1678
1679 RTListInit(&pThis->lstHstStreams);
1680 RTListInit(&pThis->lstGstStreams);
1681#ifdef VBOX_WITH_AUDIO_CALLBACKS
1682 RTListInit(&pThis->lstCBIn);
1683 RTListInit(&pThis->lstCBOut);
1684#endif
1685
1686 /*
1687 * Init the static parts.
1688 */
1689 pThis->pDrvIns = pDrvIns;
1690 /* IBase. */
1691 pDrvIns->IBase.pfnQueryInterface = drvAudioQueryInterface;
1692 /* IAudioConnector. */
1693 pThis->IAudioConnector.pfnGetConfig = drvAudioGetConfig;
1694 pThis->IAudioConnector.pfnGetStatus = drvAudioGetStatus;
1695 pThis->IAudioConnector.pfnStreamCreate = drvAudioStreamCreate;
1696 pThis->IAudioConnector.pfnStreamDestroy = drvAudioStreamDestroy;
1697 pThis->IAudioConnector.pfnStreamAddRef = drvAudioStreamAddRef;
1698 pThis->IAudioConnector.pfnStreamRelease = drvAudioStreamRelease;
1699 pThis->IAudioConnector.pfnStreamControl = drvAudioStreamControl;
1700 pThis->IAudioConnector.pfnStreamRead = drvAudioStreamRead;
1701 pThis->IAudioConnector.pfnStreamWrite = drvAudioStreamWrite;
1702 pThis->IAudioConnector.pfnStreamIterate = drvAudioStreamIterate;
1703 pThis->IAudioConnector.pfnStreamGetReadable = drvAudioStreamGetReadable;
1704 pThis->IAudioConnector.pfnStreamGetWritable = drvAudioStreamGetWritable;
1705 pThis->IAudioConnector.pfnStreamGetStatus = drvAudioStreamGetStatus;
1706 pThis->IAudioConnector.pfnStreamSetVolume = drvAudioStreamSetVolume;
1707 pThis->IAudioConnector.pfnStreamPlay = drvAudioStreamPlay;
1708 pThis->IAudioConnector.pfnStreamCapture = drvAudioStreamCapture;
1709#ifdef VBOX_WITH_AUDIO_CALLBACKS
1710 pThis->IAudioConnector.pfnRegisterCallbacks = drvAudioRegisterCallbacks;
1711 pThis->IAudioConnector.pfnCallback = drvAudioCallback;
1712#endif
1713
1714 /*
1715 * Attach driver below and query its connector interface.
1716 */
1717 PPDMIBASE pDownBase;
1718 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pDownBase);
1719 if (RT_FAILURE(rc))
1720 {
1721 LogRel(("Audio: Failed to attach to driver %p below (flags=0x%x), rc=%Rrc\n",
1722 pDrvIns, fFlags, rc));
1723 return rc;
1724 }
1725
1726 pThis->pHostDrvAudio = PDMIBASE_QUERY_INTERFACE(pDownBase, PDMIHOSTAUDIO);
1727 if (!pThis->pHostDrvAudio)
1728 {
1729 LogRel(("Audio: Failed to query interface for underlying host driver\n"));
1730 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
1731 N_("Host audio backend missing or invalid"));
1732 }
1733
1734 rc = drvAudioInit(pCfgHandle, pDrvIns);
1735 if (RT_SUCCESS(rc))
1736 {
1737 pThis->fTerminate = false;
1738 pThis->pDrvIns = pDrvIns;
1739 }
1740
1741 LogFlowFuncLeaveRC(rc);
1742 return rc;
1743}
1744
1745/**
1746 * Destructs an audio driver instance.
1747 *
1748 * @copydoc FNPDMDRVDESTRUCT
1749 */
1750static DECLCALLBACK(void) drvAudioDestruct(PPDMDRVINS pDrvIns)
1751{
1752 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1753 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1754
1755 LogFlowFuncEnter();
1756
1757 int rc2 = RTCritSectEnter(&pThis->CritSect);
1758 AssertRC(rc2);
1759
1760 PPDMAUDIOSTREAM pStream, pStreamNext;
1761 RTListForEachSafe(&pThis->lstHstStreams, pStream, pStreamNext, PDMAUDIOSTREAM, Node)
1762 drvAudioStreamDestroyInternal(pThis, pStream);
1763
1764 RTListForEachSafe(&pThis->lstGstStreams, pStream, pStreamNext, PDMAUDIOSTREAM, Node)
1765 drvAudioStreamDestroyInternal(pThis, pStream);
1766
1767 /*
1768 * Note: No calls here to the driver below us anymore,
1769 * as PDM already has destroyed it.
1770 * If you need to call something from the host driver,
1771 * do this in drvAudioPowerOff() instead.
1772 */
1773
1774 /* Sanity. */
1775 Assert(RTListIsEmpty(&pThis->lstHstStreams));
1776 Assert(RTListIsEmpty(&pThis->lstGstStreams));
1777
1778#ifdef VBOX_WITH_AUDIO_CALLBACKS
1779 /*
1780 * Destroy callbacks, if any.
1781 */
1782 PPDMAUDIOCALLBACK pCB, pCBNext;
1783 RTListForEachSafe(&pThis->lstCBIn, pCB, pCBNext, PDMAUDIOCALLBACK, Node)
1784 drvAudioCallbackDestroy(pCB);
1785
1786 RTListForEachSafe(&pThis->lstCBOut, pCB, pCBNext, PDMAUDIOCALLBACK, Node)
1787 drvAudioCallbackDestroy(pCB);
1788#endif
1789
1790 rc2 = RTCritSectLeave(&pThis->CritSect);
1791 AssertRC(rc2);
1792
1793 rc2 = RTCritSectDelete(&pThis->CritSect);
1794 AssertRC(rc2);
1795
1796 LogFlowFuncLeave();
1797}
1798
1799/**
1800 * Suspend notification.
1801 *
1802 * @param pDrvIns The driver instance data.
1803 */
1804static DECLCALLBACK(void) drvAudioSuspend(PPDMDRVINS pDrvIns)
1805{
1806 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_PAUSE);
1807}
1808
1809/**
1810 * Resume notification.
1811 *
1812 * @param pDrvIns The driver instance data.
1813 */
1814static DECLCALLBACK(void) drvAudioResume(PPDMDRVINS pDrvIns)
1815{
1816 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_RESUME);
1817}
1818
1819/**
1820 * Audio driver registration record.
1821 */
1822const PDMDRVREG g_DrvAUDIO =
1823{
1824 /* u32Version */
1825 PDM_DRVREG_VERSION,
1826 /* szName */
1827 "AUDIO",
1828 /* szRCMod */
1829 "",
1830 /* szR0Mod */
1831 "",
1832 /* pszDescription */
1833 "Audio connector driver",
1834 /* fFlags */
1835 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1836 /* fClass */
1837 PDM_DRVREG_CLASS_AUDIO,
1838 /* cMaxInstances */
1839 2,
1840 /* cbInstance */
1841 sizeof(DRVAUDIO),
1842 /* pfnConstruct */
1843 drvAudioConstruct,
1844 /* pfnDestruct */
1845 drvAudioDestruct,
1846 /* pfnRelocate */
1847 NULL,
1848 /* pfnIOCtl */
1849 NULL,
1850 /* pfnPowerOn */
1851 NULL,
1852 /* pfnReset */
1853 NULL,
1854 /* pfnSuspend */
1855 drvAudioSuspend,
1856 /* pfnResume */
1857 drvAudioResume,
1858 /* pfnAttach */
1859 NULL,
1860 /* pfnDetach */
1861 NULL,
1862 /* pfnPowerOff */
1863 drvAudioPowerOff,
1864 /* pfnSoftReset */
1865 NULL,
1866 /* u32EndVersion */
1867 PDM_DRVREG_VERSION
1868};
1869
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