VirtualBox

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

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

Audio: Added more defines / documentation for the host interfaces to keep the maintenance effort to a minimum in the future, misc. cleanup.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 74.1 KB
Line 
1/* $Id: DrvAudio.cpp 63360 2016-08-12 13:58:38Z 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 drvAudioStreamInitInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgHost, PPDMAUDIOSTREAMCFG pCfgGuest);
49static int drvAudioStreamIterateInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream);
50static int drvAudioStreamReInitInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream);
51
52#ifndef VBOX_AUDIO_TESTCASE
53
54# if 0 /* unused */
55
56static PDMAUDIOFMT drvAudioGetConfFormat(PCFGMNODE pCfgHandle, const char *pszKey,
57 PDMAUDIOFMT enmDefault, bool *pfDefault)
58{
59 if ( pCfgHandle == NULL
60 || pszKey == NULL)
61 {
62 *pfDefault = true;
63 return enmDefault;
64 }
65
66 char *pszValue = NULL;
67 int rc = CFGMR3QueryStringAlloc(pCfgHandle, pszKey, &pszValue);
68 if (RT_FAILURE(rc))
69 {
70 *pfDefault = true;
71 return enmDefault;
72 }
73
74 PDMAUDIOFMT fmt = DrvAudioHlpStrToAudFmt(pszValue);
75 if (fmt == PDMAUDIOFMT_INVALID)
76 {
77 *pfDefault = true;
78 return enmDefault;
79 }
80
81 *pfDefault = false;
82 return fmt;
83}
84
85static int drvAudioGetConfInt(PCFGMNODE pCfgHandle, const char *pszKey,
86 int iDefault, bool *pfDefault)
87{
88
89 if ( pCfgHandle == NULL
90 || pszKey == NULL)
91 {
92 *pfDefault = true;
93 return iDefault;
94 }
95
96 uint64_t u64Data = 0;
97 int rc = CFGMR3QueryInteger(pCfgHandle, pszKey, &u64Data);
98 if (RT_FAILURE(rc))
99 {
100 *pfDefault = true;
101 return iDefault;
102
103 }
104
105 *pfDefault = false;
106 return u64Data;
107}
108
109static const char *drvAudioGetConfStr(PCFGMNODE pCfgHandle, const char *pszKey,
110 const char *pszDefault, bool *pfDefault)
111{
112 if ( pCfgHandle == NULL
113 || pszKey == NULL)
114 {
115 *pfDefault = true;
116 return pszDefault;
117 }
118
119 char *pszValue = NULL;
120 int rc = CFGMR3QueryStringAlloc(pCfgHandle, pszKey, &pszValue);
121 if (RT_FAILURE(rc))
122 {
123 *pfDefault = true;
124 return pszDefault;
125 }
126
127 *pfDefault = false;
128 return pszValue;
129}
130
131# endif /* unused */
132
133/**
134 * Returns the host stream part of an audio stream pair, or NULL
135 * if no host stream has been assigned / is not available.
136 *
137 * @returns IPRT status code.
138 * @param pStream Audio stream to retrieve host stream part for.
139 */
140DECLINLINE(PPDMAUDIOSTREAM) drvAudioGetHostStream(PPDMAUDIOSTREAM pStream)
141{
142 AssertPtrReturn(pStream, NULL);
143
144 if (!pStream)
145 return NULL;
146
147 PPDMAUDIOSTREAM pHstStream = pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST
148 ? pStream
149 : pStream->pPair;
150 if (pHstStream)
151 {
152 Assert(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
153 }
154 else
155 LogFlowFunc(("%s: Warning: Does not have a host stream (anymore)\n", pStream->szName));
156
157 return pHstStream;
158}
159
160# if 0 /* unused */
161static int drvAudioProcessOptions(PCFGMNODE pCfgHandle, const char *pszPrefix, audio_option *paOpts, size_t cOpts)
162{
163 AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
164 AssertPtrReturn(pszPrefix, VERR_INVALID_POINTER);
165 /* oaOpts and cOpts are optional. */
166
167 PCFGMNODE pCfgChildHandle = NULL;
168 PCFGMNODE pCfgChildChildHandle = NULL;
169
170 /* If pCfgHandle is NULL, let NULL be passed to get int and get string functions..
171 * The getter function will return default values.
172 */
173 if (pCfgHandle != NULL)
174 {
175 /* If its audio general setting, need to traverse to one child node.
176 * /Devices/ichac97/0/LUN#0/Config/Audio
177 */
178 if(!strncmp(pszPrefix, "AUDIO", 5)) /** @todo Use a \#define */
179 {
180 pCfgChildHandle = CFGMR3GetFirstChild(pCfgHandle);
181 if(pCfgChildHandle)
182 pCfgHandle = pCfgChildHandle;
183 }
184 else
185 {
186 /* If its driver specific configuration , then need to traverse two level deep child
187 * child nodes. for eg. in case of DirectSoundConfiguration item
188 * /Devices/ichac97/0/LUN#0/Config/Audio/DirectSoundConfig
189 */
190 pCfgChildHandle = CFGMR3GetFirstChild(pCfgHandle);
191 if (pCfgChildHandle)
192 {
193 pCfgChildChildHandle = CFGMR3GetFirstChild(pCfgChildHandle);
194 if (pCfgChildChildHandle)
195 pCfgHandle = pCfgChildChildHandle;
196 }
197 }
198 }
199
200 for (size_t i = 0; i < cOpts; i++)
201 {
202 audio_option *pOpt = &paOpts[i];
203 if (!pOpt->valp)
204 {
205 LogFlowFunc(("Option value pointer for `%s' is not set\n", pOpt->name));
206 continue;
207 }
208
209 bool fUseDefault;
210
211 switch (pOpt->tag)
212 {
213 case AUD_OPT_BOOL:
214 case AUD_OPT_INT:
215 {
216 int *intp = (int *)pOpt->valp;
217 *intp = drvAudioGetConfInt(pCfgHandle, pOpt->name, *intp, &fUseDefault);
218
219 break;
220 }
221
222 case AUD_OPT_FMT:
223 {
224 PDMAUDIOFMT *fmtp = (PDMAUDIOFMT *)pOpt->valp;
225 *fmtp = drvAudioGetConfFormat(pCfgHandle, pOpt->name, *fmtp, &fUseDefault);
226
227 break;
228 }
229
230 case AUD_OPT_STR:
231 {
232 const char **strp = (const char **)pOpt->valp;
233 *strp = drvAudioGetConfStr(pCfgHandle, pOpt->name, *strp, &fUseDefault);
234
235 break;
236 }
237
238 default:
239 LogFlowFunc(("Bad value tag for option `%s' - %d\n", pOpt->name, pOpt->tag));
240 fUseDefault = false;
241 break;
242 }
243
244 if (!pOpt->overridenp)
245 pOpt->overridenp = &pOpt->overriden;
246
247 *pOpt->overridenp = !fUseDefault;
248 }
249
250 return VINF_SUCCESS;
251}
252# endif /* unused */
253#endif /* !VBOX_AUDIO_TESTCASE */
254
255/**
256 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamControl}
257 */
258static DECLCALLBACK(int) drvAudioStreamControl(PPDMIAUDIOCONNECTOR pInterface,
259 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
260{
261 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
262
263 if (!pStream)
264 return VINF_SUCCESS;
265
266 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
267
268 int rc = RTCritSectEnter(&pThis->CritSect);
269 if (RT_FAILURE(rc))
270 return rc;
271
272 LogFlowFunc(("[%s] enmStreamCmd=%RU32\n", pStream->szName, enmStreamCmd));
273
274 rc = drvAudioStreamControlInternal(pThis, pStream, enmStreamCmd);
275
276 int rc2 = RTCritSectLeave(&pThis->CritSect);
277 if (RT_SUCCESS(rc))
278 rc = rc2;
279
280 return rc;
281}
282
283/**
284 * Controls an audio stream.
285 *
286 * @returns IPRT status code.
287 * @param pThis Pointer to driver instance.
288 * @param pStream Stream to control.
289 * @param enmStreamCmd Control command.
290 */
291static int drvAudioStreamControlInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
292{
293 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
294 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
295
296 LogFunc(("[%s] enmStreamCmd=%RU32\n", pStream->szName, enmStreamCmd));
297
298 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
299 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
300 AssertPtr(pGstStream);
301
302 LogFlowFunc(("Status host=0x%x, guest=0x%x\n", pHstStream->fStatus, pGstStream->fStatus));
303
304 int rc = VINF_SUCCESS;
305
306 switch (enmStreamCmd)
307 {
308 case PDMAUDIOSTREAMCMD_ENABLE:
309 {
310 if (!(pGstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
311 {
312 if (pHstStream)
313 {
314 /* Is a pending disable outstanding? Then disable first. */
315 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE)
316 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
317
318 if (RT_SUCCESS(rc))
319 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_ENABLE);
320 }
321
322 pGstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_ENABLED;
323 }
324 break;
325 }
326
327 case PDMAUDIOSTREAMCMD_DISABLE:
328 case PDMAUDIOSTREAMCMD_PAUSE:
329 {
330 if (pGstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED)
331 {
332 /* Is the guest side stream still active?
333 * Mark the host stream as pending disable and bail out. */
334 if (pHstStream)
335 {
336 LogFunc(("[%s] Pending disable/pause\n", pHstStream->szName));
337 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
338 }
339
340 if (enmStreamCmd == PDMAUDIOSTREAMCMD_DISABLE)
341 {
342 pGstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_ENABLED;
343 }
344 else if (enmStreamCmd == PDMAUDIOSTREAMCMD_PAUSE)
345 pGstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_PAUSED;
346 else
347 AssertFailedBreakStmt(rc = VERR_NOT_IMPLEMENTED);
348 }
349
350 if ( pHstStream
351 && !(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE))
352 {
353 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, enmStreamCmd);
354 if (RT_SUCCESS(rc))
355 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
356 }
357 break;
358 }
359
360 case PDMAUDIOSTREAMCMD_RESUME:
361 {
362 if (pGstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED)
363 {
364 if (pHstStream)
365 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_RESUME);
366
367 pGstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PAUSED;
368 }
369 break;
370 }
371
372 default:
373 AssertMsgFailed(("Command %RU32 not implemented\n", enmStreamCmd));
374 rc = VERR_NOT_IMPLEMENTED;
375 break;
376 }
377
378 if (RT_FAILURE(rc))
379 LogFunc(("[%s] Failed with %Rrc\n", pStream->szName, rc));
380
381 return rc;
382}
383
384/**
385 * Controls a stream's backend.
386 * If the stream has no backend available, VERR_NOT_FOUND is returned.
387 *
388 * @returns IPRT status code.
389 * @param pThis Pointer to driver instance.
390 * @param pStream Stream to control.
391 * @param enmStreamCmd Control command.
392 */
393static int drvAudioStreamControlInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
394{
395 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
396 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
397
398 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
399 if (!pHstStream) /* Stream does not have a host backend? Bail out. */
400 return VERR_NOT_FOUND;
401
402 LogFlowFunc(("[%s] enmStreamCmd=%RU32, fStatus=0x%x\n", pHstStream->szName, enmStreamCmd, pHstStream->fStatus));
403
404 AssertPtr(pThis->pHostDrvAudio);
405
406 int rc = VINF_SUCCESS;
407
408 switch (enmStreamCmd)
409 {
410 case PDMAUDIOSTREAMCMD_ENABLE:
411 {
412 if (!(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
413 {
414 LogRel2(("Audio: Enabling stream '%s'\n", pHstStream->szName));
415 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_ENABLE);
416 if (RT_SUCCESS(rc))
417 {
418 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_ENABLED;
419 }
420 else
421 LogRel2(("Audio: Disabling stream '%s' failed with %Rrc\n", pHstStream->szName, rc));
422 }
423 break;
424 }
425
426 case PDMAUDIOSTREAMCMD_DISABLE:
427 {
428 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED)
429 {
430 LogRel2(("Audio: Disabling stream '%s'\n", pHstStream->szName));
431 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
432 if (RT_SUCCESS(rc))
433 {
434 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_ENABLED;
435 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
436 AudioMixBufReset(&pHstStream->MixBuf);
437 }
438 else
439 LogRel2(("Audio: Disabling stream '%s' failed with %Rrc\n", pHstStream->szName, rc));
440 }
441 break;
442 }
443
444 case PDMAUDIOSTREAMCMD_PAUSE:
445 {
446 /* Only pause if the stream is enabled. */
447 if (!(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
448 break;
449
450 if (!(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED))
451 {
452 LogRel2(("Audio: Pausing stream '%s'\n", pHstStream->szName));
453 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_PAUSE);
454 if (RT_SUCCESS(rc))
455 {
456 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_PAUSED;
457 }
458 else
459 LogRel2(("Audio: Pausing stream '%s' failed with %Rrc\n", pHstStream->szName, rc));
460 }
461 break;
462 }
463
464 case PDMAUDIOSTREAMCMD_RESUME:
465 {
466 /* Only need to resume if the stream is enabled. */
467 if (!(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
468 break;
469
470 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED)
471 {
472 LogRel2(("Audio: Resuming stream '%s'\n", pHstStream->szName));
473 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_RESUME);
474 if (RT_SUCCESS(rc))
475 {
476 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PAUSED;
477 }
478 else
479 LogRel2(("Audio: Resuming stream '%s' failed with %Rrc\n", pHstStream->szName, rc));
480 }
481 break;
482 }
483
484 default:
485 {
486 AssertMsgFailed(("Command %RU32 not implemented\n", enmStreamCmd));
487 rc = VERR_NOT_IMPLEMENTED;
488 break;
489 }
490 }
491
492 if (RT_FAILURE(rc))
493 LogFunc(("[%s] Failed with %Rrc\n", pStream->szName, rc));
494
495 return rc;
496}
497
498/**
499 * Initializes an audio stream with a given host and guest stream configuration.
500 *
501 * @returns IPRT status code.
502 * @param pThis Pointer to driver instance.
503 * @param pStream Stream to initialize.
504 * @param pCfgHost Stream configuration to use for the host side (backend).
505 * @param pCfgGuest Stream configuration to use for the guest side.
506 */
507static int drvAudioStreamInitInternal(PDRVAUDIO pThis,
508 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgHost, PPDMAUDIOSTREAMCFG pCfgGuest)
509{
510 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
511 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
512
513 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
514 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
515 AssertPtr(pGstStream);
516
517 LogFlowFunc(("[%s]\n", pStream->szName));
518
519 /*
520 * Init host stream.
521 */
522
523 /* Make the acquired host configuration the requested host configuration by default. */
524 PDMAUDIOSTREAMCFG CfgAcq;
525 memcpy(&CfgAcq, pCfgHost, sizeof(PDMAUDIOSTREAMCFG));
526
527 uint32_t cSamples = 0;
528 int rc = pThis->pHostDrvAudio->pfnStreamCreate(pThis->pHostDrvAudio, pHstStream,
529 pCfgHost /* pCfgReq */, &CfgAcq /* pCfgAcq */);
530 if (RT_SUCCESS(rc))
531 {
532 /* Only set the host's stream to initialized if we were able create the stream
533 * in the host backend. This is necessary for trying to re-initialize the stream
534 * at some later point in time. */
535 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
536 }
537 else
538 LogFlowFunc(("[%s] Initializing stream in host backend failed with rc=%Rrc\n", pStream->szName, rc));
539
540 int rc2 = DrvAudioHlpStreamCfgToProps(pCfgHost, &pHstStream->Props);
541 AssertRC(rc2);
542
543 /* Destroy any former mixing buffer. */
544 AudioMixBufDestroy(&pHstStream->MixBuf);
545
546 if (cSamples)
547 {
548 cSamples = cSamples * 4;
549
550 LogFlowFunc(("[%s] cSamples=%RU32\n", pHstStream->szName, cSamples));
551
552 rc2 = AudioMixBufInit(&pHstStream->MixBuf, pHstStream->szName, &pHstStream->Props, cSamples);
553 AssertRC(rc2);
554 }
555
556 /* Make a copy of the host stream configuration. */
557 memcpy(&pHstStream->Cfg, pCfgHost, sizeof(PDMAUDIOSTREAMCFG));
558
559 /*
560 * Init guest stream.
561 */
562
563 rc2 = DrvAudioHlpStreamCfgToProps(pCfgGuest, &pGstStream->Props);
564 AssertRC(rc2);
565
566 /* Destroy any former mixing buffer. */
567 AudioMixBufDestroy(&pGstStream->MixBuf);
568
569 if (cSamples)
570 {
571 cSamples = cSamples * 2;
572
573 LogFlowFunc(("[%s] cSamples=%RU32\n", pGstStream->szName, cSamples));
574
575 rc2 = AudioMixBufInit(&pGstStream->MixBuf, pGstStream->szName, &pGstStream->Props, cSamples);
576 AssertRC(rc2);
577 }
578
579#ifdef VBOX_WITH_STATISTICS
580 char szStatName[255];
581#endif
582
583 if (cSamples)
584 {
585 if (pCfgGuest->enmDir == PDMAUDIODIR_IN)
586 {
587 /* Host (Parent) -> Guest (Child). */
588 rc2 = AudioMixBufLinkTo(&pHstStream->MixBuf, &pGstStream->MixBuf);
589 AssertRC(rc2);
590
591#ifdef VBOX_WITH_STATISTICS
592 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesElapsed", pGstStream->szName);
593 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStream->In.StatBytesElapsed,
594 szStatName, STAMUNIT_BYTES, "Elapsed bytes read.");
595
596 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesRead", pGstStream->szName);
597 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStream->In.StatBytesTotalRead,
598 szStatName, STAMUNIT_BYTES, "Total bytes read.");
599
600 RTStrPrintf(szStatName, sizeof(szStatName), "Host/%s/SamplesCaptured", pHstStream->szName);
601 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pHstStream->In.StatSamplesCaptured,
602 szStatName, STAMUNIT_COUNT, "Total samples captured.");
603#endif
604 }
605 else
606 {
607 /* Guest (Parent) -> Host (Child). */
608 rc2 = AudioMixBufLinkTo(&pGstStream->MixBuf, &pHstStream->MixBuf);
609 AssertRC(rc2);
610
611#ifdef VBOX_WITH_STATISTICS
612 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesElapsed", pGstStream->szName);
613 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStream->Out.StatBytesElapsed,
614 szStatName, STAMUNIT_BYTES, "Elapsed bytes written.");
615
616 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesRead", pGstStream->szName);
617 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStream->Out.StatBytesTotalWritten,
618 szStatName, STAMUNIT_BYTES, "Total bytes written.");
619
620 RTStrPrintf(szStatName, sizeof(szStatName), "Host/%s/SamplesPlayed", pHstStream->szName);
621 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pHstStream->Out.StatSamplesPlayed,
622 szStatName, STAMUNIT_COUNT, "Total samples played.");
623#endif
624 }
625 }
626
627 /* Make a copy of the host stream configuration. */
628 memcpy(&pGstStream->Cfg, pCfgGuest, sizeof(PDMAUDIOSTREAMCFG));
629
630 LogFlowFunc(("[%s] Returning %Rrc\n", pStream->szName, rc));
631 return rc;
632}
633
634/**
635 * Re-initializes an audio stream with its existing host and guest stream configuration.
636 *
637 * @returns IPRT status code.
638 * @param pThis Pointer to driver instance.
639 * @param pStream Stream to re-initialize.
640 */
641static int drvAudioStreamReInitInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream)
642{
643 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
644 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
645
646 LogFlowFunc(("[%s]\n", pStream->szName));
647
648 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
649 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
650 AssertPtr(pGstStream);
651
652 int rc;
653
654 if (/* Stream initialized? */
655 (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
656 /* Not in pending re-init before? */
657 && !(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_REINIT))
658 {
659 /* Disable first. */
660 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
661 if (RT_FAILURE(rc))
662 {
663 LogFunc(("[%s] Error disabling stream, rc=%Rrc\n", pStream->szName, rc));
664 return rc;
665 }
666
667 /* Give the backend the chance to clean up the old context. */
668 rc = pThis->pHostDrvAudio->pfnStreamDestroy(pThis->pHostDrvAudio, pHstStream);
669 if (RT_FAILURE(rc))
670 {
671 LogFunc(("[%s] Error destroying stream in backend, rc=%Rrc\n", pStream->szName, rc));
672 return rc;
673 }
674
675 /* Set the pending re-init bit. */
676 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_PENDING_REINIT;
677 }
678
679 LogFlowFunc(("[%s] Host status is 0x%x\n", pStream->szName, pHstStream->fStatus));
680
681 /* Try to re-initialize the stream. */
682 rc = drvAudioStreamInitInternal(pThis, pStream, &pHstStream->Cfg, &pGstStream->Cfg);
683 if (RT_SUCCESS(rc))
684 {
685 /* Try to restore the previous stream status, if possible. */
686 PDMAUDIOSTREAMCMD enmCmdRestore = PDMAUDIOSTREAMCMD_UNKNOWN;
687
688 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED) /* Stream was running before? */
689 {
690 LogFunc(("[%s] Re-enabling host stream ...\n", pStream->szName));
691 enmCmdRestore = PDMAUDIOSTREAMCMD_ENABLE;
692 }
693
694 if (enmCmdRestore != PDMAUDIOSTREAMCMD_UNKNOWN)
695 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_ENABLE);
696
697 /* Re-initialization successful, remove bit again. */
698 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_REINIT;
699 }
700
701 LogFunc(("[%s] Reinitialization returned %Rrc\n", pStream->szName, rc));
702 return rc;
703}
704
705/**
706 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamWrite}
707 */
708static DECLCALLBACK(int) drvAudioStreamWrite(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
709 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
710{
711 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
712 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
713 /* pcbWritten is optional. */
714
715 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
716
717 if ( !pStream
718 || !cbBuf)
719 {
720 if (pcbWritten)
721 *pcbWritten = 0;
722 return VINF_SUCCESS;
723 }
724
725 AssertMsg(pStream->enmDir == PDMAUDIODIR_OUT,
726 ("Stream '%s' is not an output stream and therefore cannot be written to (direction is 0x%x)\n",
727 pStream->szName, pStream->enmDir));
728
729 uint32_t cbWritten = 0;
730
731 int rc = RTCritSectEnter(&pThis->CritSect);
732 if (RT_FAILURE(rc))
733 return rc;
734
735 do
736 {
737 if ( pThis->pHostDrvAudio
738 && pThis->pHostDrvAudio->pfnGetStatus
739 && pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_OUT) != PDMAUDIOBACKENDSTS_RUNNING)
740 {
741 rc = VERR_NOT_AVAILABLE;
742 break;
743 }
744
745 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
746 if (!pHstStream)
747 {
748 rc = VERR_NOT_AVAILABLE;
749 break;
750 }
751
752 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
753
754 AssertMsg(pGstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED,
755 ("Writing to disabled guest output stream \"%s\" not possible\n", pGstStream->szName));
756
757 pGstStream->Out.tsLastWriteMS = RTTimeMilliTS();
758
759 if (!AudioMixBufFreeBytes(&pGstStream->MixBuf))
760 {
761 LogRel2(("Audio: Guest stream '%s' full, expect stuttering audio output\n", pGstStream->szName));
762 break;
763 }
764
765 uint32_t cWritten = 0;
766 rc = AudioMixBufWriteCirc(&pGstStream->MixBuf, pvBuf, cbBuf, &cWritten);
767 if (rc == VINF_BUFFER_OVERFLOW)
768 {
769 LogRel2(("Audio: Lost audio samples from guest stream '%s', expect stuttering audio output\n", pGstStream->szName));
770 rc = VINF_SUCCESS;
771 break;
772 }
773
774#ifdef VBOX_WITH_STATISTICS
775 STAM_COUNTER_ADD(&pThis->Stats.TotalBytesWritten, AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cWritten));
776 STAM_COUNTER_ADD(&pGstStream->Out.StatBytesTotalWritten, AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cWritten));
777#endif
778 cbWritten = AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cWritten);
779
780 Log3Func(("[%s] cUsed=%RU32, cLive=%RU32\n",
781 pGstStream->szName, AudioMixBufUsed(&pGstStream->MixBuf), AudioMixBufLive(&pGstStream->MixBuf)));
782
783 } while (0);
784
785 int rc2 = RTCritSectLeave(&pThis->CritSect);
786 if (RT_SUCCESS(rc))
787 rc = rc2;
788
789 if (RT_SUCCESS(rc))
790 {
791 if (pcbWritten)
792 *pcbWritten = cbWritten;
793 }
794
795 return rc;
796}
797
798/**
799 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRetain}
800 */
801static DECLCALLBACK(uint32_t) drvAudioStreamRetain(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
802{
803 AssertPtrReturn(pInterface, UINT32_MAX);
804 AssertPtrReturn(pStream, UINT32_MAX);
805
806 NOREF(pInterface);
807
808 return ++pStream->cRefs;
809}
810
811/**
812 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRelease}
813 */
814static DECLCALLBACK(uint32_t) drvAudioStreamRelease(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
815{
816 AssertPtrReturn(pInterface, UINT32_MAX);
817 AssertPtrReturn(pStream, UINT32_MAX);
818
819 NOREF(pInterface);
820
821 if (pStream->cRefs > 1) /* 1 reference always is kept by this audio driver. */
822 pStream->cRefs--;
823
824 return pStream->cRefs;
825}
826
827/**
828 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamIterate}
829 */
830static DECLCALLBACK(int) drvAudioStreamIterate(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
831{
832 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
833 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
834 /* pcData is optional. */
835
836 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
837
838 int rc = RTCritSectEnter(&pThis->CritSect);
839 if (RT_FAILURE(rc))
840 return rc;
841
842 rc = drvAudioStreamIterateInternal(pThis, pStream);
843
844 int rc2 = RTCritSectLeave(&pThis->CritSect);
845 if (RT_SUCCESS(rc))
846 rc = rc2;
847
848 if (RT_FAILURE(rc))
849 LogFlowFuncLeaveRC(rc);
850
851 return rc;
852}
853
854/**
855 * Does one iteration of an audio stream.
856 * This function gives the backend the chance of iterating / altering data and
857 * does the actual mixing between the guest <-> host mixing buffers.
858 *
859 * @returns IPRT status code.
860 * @param pThis Pointer to driver instance.
861 * @param pStream Stream to iterate.
862 *
863 * @remark
864 */
865static int drvAudioStreamIterateInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream)
866{
867 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
868
869 if (!pStream)
870 return VINF_SUCCESS;
871
872 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
873 AssertPtr(pHstStream);
874 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
875 AssertPtr(pGstStream);
876
877 int rc;
878
879 /* Whether to try closing a pending to close stream. */
880 bool fTryClosePending = false;
881
882 do
883 {
884 uint32_t cSamplesMixed = 0;
885
886 rc = pThis->pHostDrvAudio->pfnStreamIterate(pThis->pHostDrvAudio, pHstStream);
887 if (RT_FAILURE(rc))
888 break;
889
890 if (pHstStream->enmDir == PDMAUDIODIR_IN)
891 {
892 /* Has the host captured any samples which were not mixed to the guest side yet? */
893 uint32_t cSamplesCaptured = AudioMixBufUsed(&pHstStream->MixBuf);
894
895 Log3Func(("[%s] %RU32 samples captured\n", pHstStream->szName, cSamplesCaptured));
896
897 if (cSamplesCaptured)
898 {
899 /* When capturing samples, the guest is the parent while the host is the child.
900 * So try mixing not yet mixed host-side samples to the guest-side buffer. */
901 rc = AudioMixBufMixToParent(&pHstStream->MixBuf, cSamplesCaptured, &cSamplesMixed);
902 if ( RT_SUCCESS(rc)
903 && cSamplesMixed)
904 {
905 Log3Func(("[%s] %RU32 captured samples mixed\n", pHstStream->szName, cSamplesMixed));
906 }
907 }
908 else
909 {
910 fTryClosePending = true;
911 }
912 }
913 else if (pHstStream->enmDir == PDMAUDIODIR_OUT)
914 {
915 /* When playing samples, the host is the parent while the guest is the child.
916 * So try mixing not yet mixed guest-side samples to the host-side buffer. */
917 rc = AudioMixBufMixToParent(&pGstStream->MixBuf, AudioMixBufUsed(&pGstStream->MixBuf), &cSamplesMixed);
918 if ( RT_SUCCESS(rc)
919 && cSamplesMixed)
920 {
921 Log3Func(("[%s] %RU32 samples mixed, guest has %RU32 samples left (%RU32 live)\n",
922 pHstStream->szName, cSamplesMixed,
923 AudioMixBufUsed(&pGstStream->MixBuf), AudioMixBufLive(&pGstStream->MixBuf)));
924 }
925
926 uint32_t cSamplesLeft = AudioMixBufUsed(&pGstStream->MixBuf);
927 if (!cSamplesLeft) /* No samples (anymore)? */
928 {
929 fTryClosePending = true;
930 }
931 }
932 else
933 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
934
935 if (fTryClosePending)
936 {
937 /* Has the host stream marked as disabled but there still were guest streams relying
938 * on it? Check if the stream now can be closed and do so, if possible. */
939 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE)
940 {
941 LogFunc(("[%s] Closing pending stream\n", pHstStream->szName));
942 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
943 if (RT_SUCCESS(rc))
944 {
945 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
946 }
947 else
948 LogFunc(("%s: Backend vetoed against closing output stream, rc=%Rrc\n", pHstStream->szName, rc));
949 }
950 }
951
952 } while (0);
953
954 /* Update timestamps. */
955 pHstStream->tsLastIterateMS = RTTimeMilliTS();
956 pGstStream->tsLastIterateMS = RTTimeMilliTS();
957
958 if (RT_FAILURE(rc))
959 LogFunc(("Failed with %Rrc\n", rc));
960
961 return rc;
962}
963
964/**
965 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamPlay}
966 */
967static DECLCALLBACK(int) drvAudioStreamPlay(PPDMIAUDIOCONNECTOR pInterface,
968 PPDMAUDIOSTREAM pStream, uint32_t *pcSamplesPlayed)
969{
970 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
971 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
972 /* pcSamplesPlayed is optional. */
973
974 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
975
976 int rc = RTCritSectEnter(&pThis->CritSect);
977 if (RT_FAILURE(rc))
978 return rc;
979
980 AssertMsg(pStream->enmDir == PDMAUDIODIR_OUT,
981 ("Stream '%s' is not an output stream and therefore cannot be played back (direction is 0x%x)\n",
982 pStream->szName, pStream->enmDir));
983
984 uint32_t cSamplesPlayed = 0;
985
986 do
987 {
988 if (!pThis->pHostDrvAudio)
989 {
990 rc = VERR_NOT_AVAILABLE;
991 break;
992 }
993
994 /* Backend output (temporarily) disabled / unavailable? */
995 if ( pThis->pHostDrvAudio->pfnGetStatus
996 && pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_OUT) != PDMAUDIOBACKENDSTS_RUNNING)
997 {
998 /* Pull the new config from the backend and check again. */
999 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);
1000 AssertRC(rc);
1001
1002 if ( !pThis->BackendCfg.cSinks
1003 || !pThis->BackendCfg.cMaxStreamsOut)
1004 {
1005 rc = VERR_NOT_AVAILABLE;
1006 break;
1007 }
1008 }
1009
1010 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1011 AssertPtr(pHstStream);
1012 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
1013 AssertPtr(pGstStream);
1014
1015 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetStatus);
1016 PDMAUDIOSTRMSTS strmSts = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pHstStream);
1017 if (!(strmSts & PDMAUDIOSTRMSTS_FLAG_INITIALIZED))
1018 {
1019 LogFunc(("[%s] Backend not initialized (anymore), re-initializing ...\n", pHstStream->szName));
1020 rc = drvAudioStreamReInitInternal(pThis, pStream);
1021 if (RT_FAILURE(rc))
1022 {
1023 LogFunc(("[%s] Failed to re-initialize backend, rc=%Rrc\n", pHstStream->szName, rc));
1024 break;
1025 }
1026 }
1027
1028 uint32_t cSamplesLive = AudioMixBufLive(&pGstStream->MixBuf);
1029 if (cSamplesLive)
1030 {
1031 if ( (strmSts & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
1032 && (strmSts & PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE))
1033 {
1034 AssertPtr(pThis->pHostDrvAudio->pfnStreamPlay);
1035 rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pHstStream, NULL /* pvBuf */, 0 /* cbBuf */,
1036 &cSamplesPlayed);
1037 if (RT_FAILURE(rc))
1038 {
1039 int rc2 = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
1040 AssertRC(rc2);
1041 }
1042 else
1043 {
1044#ifdef VBOX_WITH_STATISTICS
1045 STAM_COUNTER_ADD(&pThis->Stats.TotalSamplesPlayed, cSamplesPlayed);
1046 STAM_COUNTER_ADD(&pHstStream->Out.StatSamplesPlayed, cSamplesPlayed);
1047#endif
1048 cSamplesLive = AudioMixBufLive(&pGstStream->MixBuf);
1049 }
1050 }
1051 }
1052
1053 if (!cSamplesLive)
1054 {
1055 /* Has the host stream marked as disabled but there still were guest streams relying
1056 * on it? Check if the stream now can be closed and do so, if possible. */
1057 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE)
1058 {
1059 LogFunc(("[%s] Closing pending stream\n", pHstStream->szName));
1060 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
1061 if (RT_SUCCESS(rc))
1062 {
1063 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
1064 }
1065 else
1066 LogFunc(("[%s] Backend vetoed against closing output stream, rc=%Rrc\n", pHstStream->szName, rc));
1067 }
1068 }
1069
1070 } while (0);
1071
1072 int rc2 = RTCritSectLeave(&pThis->CritSect);
1073 if (RT_SUCCESS(rc))
1074 rc = rc2;
1075
1076 if (RT_SUCCESS(rc))
1077 {
1078 if (pcSamplesPlayed)
1079 *pcSamplesPlayed = cSamplesPlayed;
1080 }
1081
1082 if (RT_FAILURE(rc))
1083 LogFlowFunc(("[%s] Failed with %Rrc\n", pStream->szName, rc));
1084
1085 return rc;
1086}
1087
1088/**
1089 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCapture}
1090 */
1091static DECLCALLBACK(int) drvAudioStreamCapture(PPDMIAUDIOCONNECTOR pInterface,
1092 PPDMAUDIOSTREAM pStream, uint32_t *pcSamplesCaptured)
1093{
1094 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1095
1096 int rc = RTCritSectEnter(&pThis->CritSect);
1097 if (RT_FAILURE(rc))
1098 return rc;
1099
1100 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN,
1101 ("Stream '%s' is not an input stream and therefore cannot be captured (direction is 0x%x)\n",
1102 pStream->szName, pStream->enmDir));
1103
1104 Log3Func(("[%s]\n", pStream->szName));
1105
1106 uint32_t cSamplesCaptured = 0;
1107
1108 do
1109 {
1110 /* Backend input (temporarily) disabled / unavailable? */
1111 if (pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_IN) != PDMAUDIOBACKENDSTS_RUNNING)
1112 {
1113 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);
1114 AssertRC(rc);
1115
1116 if ( !pThis->BackendCfg.cSources
1117 || !pThis->BackendCfg.cMaxStreamsIn)
1118 {
1119 rc = VERR_NOT_AVAILABLE;
1120 break;
1121 }
1122 }
1123
1124 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1125 AssertPtr(pHstStream);
1126 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
1127 AssertPtr(pGstStream);
1128
1129 PDMAUDIOSTRMSTS strmSts = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pHstStream);
1130 if (!(strmSts & PDMAUDIOSTRMSTS_FLAG_INITIALIZED))
1131 {
1132 LogFunc(("[%s] Backend not initialized (anymore), re-initializing ...\n", pHstStream->szName));
1133 rc = drvAudioStreamReInitInternal(pThis, pStream);
1134 break;
1135 }
1136
1137 uint32_t cSamplesLive = AudioMixBufLive(&pGstStream->MixBuf);
1138 if (!cSamplesLive)
1139 {
1140 if ( (strmSts & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
1141 && (strmSts & PDMAUDIOSTRMSTS_FLAG_DATA_READABLE))
1142 {
1143 rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pHstStream, NULL /* pvBuf */, 0 /* cbBuf */,
1144 &cSamplesCaptured);
1145 if (RT_FAILURE(rc))
1146 {
1147 int rc2 = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
1148 AssertRC(rc2);
1149 }
1150 else
1151 {
1152#ifdef VBOX_WITH_STATISTICS
1153 STAM_COUNTER_ADD(&pHstStream->In.StatSamplesCaptured, cSamplesCaptured);
1154#endif
1155 }
1156 }
1157
1158 Log3Func(("[%s] strmSts=0x%x, cSamplesCaptured=%RU32, rc=%Rrc\n", pHstStream->szName, strmSts, cSamplesCaptured, rc));
1159 }
1160
1161 } while (0);
1162
1163 if (RT_SUCCESS(rc))
1164 {
1165 if (pcSamplesCaptured)
1166 *pcSamplesCaptured = cSamplesCaptured;
1167 }
1168
1169 int rc2 = RTCritSectLeave(&pThis->CritSect);
1170 if (RT_SUCCESS(rc))
1171 rc = rc2;
1172
1173 if (RT_FAILURE(rc))
1174 LogFlowFuncLeaveRC(rc);
1175
1176 return rc;
1177}
1178
1179#ifdef VBOX_WITH_AUDIO_CALLBACKS
1180/**
1181 * Duplicates an audio callback.
1182 *
1183 * @returns Pointer to duplicated callback, or NULL on failure.
1184 * @param pCB Callback to duplicate.
1185 */
1186static PPDMAUDIOCALLBACK drvAudioCallbackDuplicate(PPDMAUDIOCALLBACK pCB)
1187{
1188 AssertPtrReturn(pCB, VERR_INVALID_POINTER);
1189
1190 PPDMAUDIOCALLBACK pCBCopy = (PPDMAUDIOCALLBACK)RTMemDup((void *)pCB, sizeof(PDMAUDIOCALLBACK));
1191 if (!pCBCopy)
1192 return NULL;
1193
1194 if (pCB->pvCtx)
1195 {
1196 pCBCopy->pvCtx = RTMemDup(pCB->pvCtx, pCB->cbCtx);
1197 if (!pCBCopy->pvCtx)
1198 {
1199 RTMemFree(pCBCopy);
1200 return NULL;
1201 }
1202
1203 pCBCopy->cbCtx = pCB->cbCtx;
1204 }
1205
1206 return pCBCopy;
1207}
1208
1209/**
1210 * Destroys a given callback.
1211 *
1212 * @param pCB Callback to destroy.
1213 */
1214static void drvAudioCallbackDestroy(PPDMAUDIOCALLBACK pCB)
1215{
1216 if (!pCB)
1217 return;
1218
1219 RTListNodeRemove(&pCB->Node);
1220 if (pCB->pvCtx)
1221 {
1222 Assert(pCB->cbCtx);
1223 RTMemFree(pCB->pvCtx);
1224 }
1225 RTMemFree(pCB);
1226}
1227
1228/**
1229 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnRegisterCallbacks}
1230 */
1231static DECLCALLBACK(int) drvAudioRegisterCallbacks(PPDMIAUDIOCONNECTOR pInterface,
1232 PPDMAUDIOCALLBACK paCallbacks, size_t cCallbacks)
1233{
1234 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1235 AssertPtrReturn(paCallbacks, VERR_INVALID_POINTER);
1236 AssertReturn(cCallbacks, VERR_INVALID_PARAMETER);
1237
1238 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1239
1240 int rc = RTCritSectEnter(&pThis->CritSect);
1241 if (RT_FAILURE(rc))
1242 return rc;
1243
1244 for (size_t i = 0; i < cCallbacks; i++)
1245 {
1246 PPDMAUDIOCALLBACK pCB = drvAudioCallbackDuplicate(&paCallbacks[i]);
1247 if (!pCB)
1248 {
1249 rc = VERR_NO_MEMORY;
1250 break;
1251 }
1252
1253 switch (pCB->enmType)
1254 {
1255 case PDMAUDIOCALLBACKTYPE_INPUT:
1256 RTListAppend(&pThis->lstCBIn, &pCB->Node);
1257 break;
1258
1259 case PDMAUDIOCALLBACKTYPE_OUTPUT:
1260 RTListAppend(&pThis->lstCBOut, &pCB->Node);
1261 break;
1262
1263 default:
1264 AssertMsgFailed(("Not supported\n"));
1265 break;
1266 }
1267 }
1268
1269 /** @todo Undo allocations on error. */
1270
1271 int rc2 = RTCritSectLeave(&pThis->CritSect);
1272 if (RT_SUCCESS(rc))
1273 rc = rc2;
1274
1275 return rc;
1276}
1277
1278/**
1279 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnCallback}
1280 */
1281static DECLCALLBACK(int) drvAudioCallback(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIOCALLBACKTYPE enmType,
1282 void *pvUser, size_t cbUser)
1283{
1284 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1285 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
1286 AssertReturn(cbUser, VERR_INVALID_PARAMETER);
1287
1288 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1289 PRTLISTANCHOR pListAnchor = NULL;
1290
1291 switch (enmType)
1292 {
1293 case PDMAUDIOCALLBACKTYPE_INPUT:
1294 pListAnchor = &pThis->lstCBIn;
1295 break;
1296
1297 case PDMAUDIOCALLBACKTYPE_OUTPUT:
1298 pListAnchor = &pThis->lstCBOut;
1299 break;
1300
1301 default:
1302 AssertMsgFailed(("Not supported\n"));
1303 break;
1304 }
1305
1306 if (pListAnchor)
1307 {
1308 PPDMAUDIOCALLBACK pCB;
1309 RTListForEach(pListAnchor, pCB, PDMAUDIOCALLBACK, Node)
1310 {
1311 Assert(pCB->enmType == enmType);
1312 pCB->pfnCallback(enmType, pCB->pvCtx, pCB->cbCtx, pvUser, cbUser);
1313 }
1314 }
1315
1316 return VINF_SUCCESS;
1317}
1318#endif
1319
1320/**
1321 * Initializes the host backend and queries its initial configuration.
1322 * If the host backend fails, VERR_AUDIO_BACKEND_INIT_FAILED will be returned.
1323 *
1324 * Note: As this routine is called when attaching to the device LUN in the
1325 * device emulation, we either check for success or VERR_AUDIO_BACKEND_INIT_FAILED.
1326 * Everything else is considered as fatal and must be handled separately in
1327 * the device emulation!
1328 *
1329 * @return IPRT status code.
1330 * @param pThis Driver instance to be called.
1331 * @param pCfgHandle CFGM configuration handle to use for this driver.
1332 */
1333static int drvAudioHostInit(PDRVAUDIO pThis, PCFGMNODE pCfgHandle)
1334{
1335 /* pCfgHandle is optional. */
1336 NOREF(pCfgHandle);
1337 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1338
1339 LogFlowFuncEnter();
1340
1341 AssertPtr(pThis->pHostDrvAudio);
1342 int rc = pThis->pHostDrvAudio->pfnInit(pThis->pHostDrvAudio);
1343 if (RT_FAILURE(rc))
1344 {
1345 LogRel(("Audio: Initialization of host backend failed with %Rrc\n", rc));
1346 return VERR_AUDIO_BACKEND_INIT_FAILED;
1347 }
1348
1349 /* Get the configuration data from backend. */
1350 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);
1351 if (RT_FAILURE(rc))
1352 {
1353 LogRel(("Audio: Getting host backend configuration failed with %Rrc\n", rc));
1354 return VERR_AUDIO_BACKEND_INIT_FAILED;
1355 }
1356
1357 pThis->cStreamsFreeIn = 0;
1358 pThis->cStreamsFreeOut = 0;
1359
1360 if (pThis->BackendCfg.cSinks)
1361 {
1362 Assert(pThis->BackendCfg.cbStreamOut);
1363
1364 pThis->cStreamsFreeOut = pThis->BackendCfg.cMaxStreamsOut;
1365 }
1366
1367 if (pThis->BackendCfg.cSources)
1368 {
1369 Assert(pThis->BackendCfg.cbStreamIn);
1370
1371 pThis->cStreamsFreeIn = pThis->BackendCfg.cMaxStreamsIn;
1372 }
1373
1374 LogFlowFunc(("cStreamsFreeIn=%RU8, cStreamsFreeOut=%RU8\n", pThis->cStreamsFreeIn, pThis->cStreamsFreeOut));
1375
1376 LogRel2(("Audio: Host audio backend supports %RU32 input streams and %RU32 output streams at once\n",
1377 /* Clamp for logging. Unlimited streams are defined by UINT32_MAX. */
1378 RT_MIN(64, pThis->cStreamsFreeIn), RT_MIN(64, pThis->cStreamsFreeOut)));
1379
1380 LogFlowFuncLeave();
1381 return VINF_SUCCESS;
1382}
1383
1384/**
1385 * Handles state changes for all audio streams.
1386 *
1387 * @param pDrvIns Pointer to driver instance.
1388 * @param enmCmd Stream command to set for all streams.
1389 */
1390static void drvAudioStateHandler(PPDMDRVINS pDrvIns, PDMAUDIOSTREAMCMD enmCmd)
1391{
1392 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1393 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1394
1395 LogFlowFunc(("enmCmd=%RU32\n", enmCmd));
1396
1397 if (!pThis->pHostDrvAudio)
1398 return;
1399
1400 PPDMAUDIOSTREAM pHstStream;
1401 RTListForEach(&pThis->lstHstStreams, pHstStream, PDMAUDIOSTREAM, Node)
1402 drvAudioStreamControlInternalBackend(pThis, pHstStream, enmCmd);
1403}
1404
1405/**
1406 * Intializes an audio driver instance.
1407 *
1408 * @returns IPRT status code.
1409 * @param pDrvIns Pointer to driver instance.
1410 * @param pCfgHandle CFGM handle to use for configuration.
1411 */
1412static int drvAudioInit(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
1413{
1414 AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
1415 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
1416
1417 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1418 LogFlowFunc(("pThis=%p, pDrvIns=%p\n", pThis, pDrvIns));
1419
1420 int rc = RTCritSectInit(&pThis->CritSect);
1421
1422 /** @todo Add audio driver options. */
1423
1424 /*
1425 * If everything went well, initialize the lower driver.
1426 */
1427 if (RT_SUCCESS(rc))
1428 rc = drvAudioHostInit(pThis, pCfgHandle);
1429
1430 LogFlowFuncLeaveRC(rc);
1431 return rc;
1432}
1433
1434/**
1435 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRead}
1436 */
1437static DECLCALLBACK(int) drvAudioStreamRead(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
1438 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1439{
1440 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1441 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1442
1443 if (!pStream)
1444 {
1445 if (pcbRead)
1446 *pcbRead = 0;
1447 return VINF_SUCCESS;
1448 }
1449
1450 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1451 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1452 /* pcbWritten is optional. */
1453
1454 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN,
1455 ("Stream '%s' is not an input stream and therefore cannot be read from (direction is 0x%x)\n",
1456 pStream->szName, pStream->enmDir));
1457
1458 uint32_t cbRead = 0;
1459
1460 int rc = RTCritSectEnter(&pThis->CritSect);
1461 if (RT_FAILURE(rc))
1462 return rc;
1463
1464 do
1465 {
1466 if ( pThis->pHostDrvAudio
1467 && pThis->pHostDrvAudio->pfnGetStatus
1468 && pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_IN) != PDMAUDIOBACKENDSTS_RUNNING)
1469 {
1470 rc = VERR_NOT_AVAILABLE;
1471 break;
1472 }
1473
1474 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1475 if (pHstStream)
1476 {
1477 rc = VERR_NOT_AVAILABLE;
1478 break;
1479 }
1480
1481 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
1482
1483 AssertMsg(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED,
1484 ("Reading from disabled host input stream '%s' not possible\n", pHstStream->szName));
1485
1486 pGstStream->In.tsLastReadMS = RTTimeMilliTS();
1487
1488 Log3Func(("%s\n", pStream->szName));
1489
1490 /*
1491 * Read from the parent buffer (that is, the guest buffer) which
1492 * should have the audio data in the format the guest needs.
1493 */
1494 uint32_t cRead;
1495 rc = AudioMixBufReadCirc(&pGstStream->MixBuf, pvBuf, cbBuf, &cRead);
1496 if (RT_SUCCESS(rc))
1497 {
1498 if (cRead)
1499 {
1500#ifdef VBOX_WITH_STATISTICS
1501 STAM_COUNTER_ADD(&pThis->Stats.TotalBytesRead, AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cRead));
1502 STAM_COUNTER_ADD(&pGstStream->In.StatBytesTotalRead, AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cRead));
1503#endif
1504 AudioMixBufFinish(&pGstStream->MixBuf, cRead);
1505
1506 cbRead = AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cRead);
1507 }
1508 }
1509
1510 Log3Func(("cRead=%RU32 (%RU32 bytes), rc=%Rrc\n", cRead, AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cRead), rc));
1511
1512 } while (0);
1513
1514 int rc2 = RTCritSectLeave(&pThis->CritSect);
1515 if (RT_SUCCESS(rc))
1516 rc = rc2;
1517
1518 if (RT_SUCCESS(rc))
1519 {
1520 if (pcbRead)
1521 *pcbRead = cbRead;
1522 }
1523
1524 return rc;
1525}
1526
1527/**
1528 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCreate}
1529 */
1530static DECLCALLBACK(int) drvAudioStreamCreate(PPDMIAUDIOCONNECTOR pInterface,
1531 PPDMAUDIOSTREAMCFG pCfgHost, PPDMAUDIOSTREAMCFG pCfgGuest,
1532 PPDMAUDIOSTREAM *ppStream)
1533{
1534 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1535 AssertPtrReturn(pCfgHost, VERR_INVALID_POINTER);
1536 AssertPtrReturn(pCfgGuest, VERR_INVALID_POINTER);
1537 AssertPtrReturn(ppStream, VERR_INVALID_POINTER);
1538
1539 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1540
1541 int rc = RTCritSectEnter(&pThis->CritSect);
1542 if (RT_FAILURE(rc))
1543 return rc;
1544
1545 LogFlowFunc(("Host=%s, Guest=%s\n", pCfgHost->szName, pCfgGuest->szName));
1546#ifdef DEBUG
1547 DrvAudioHlpStreamCfgPrint(pCfgHost);
1548 DrvAudioHlpStreamCfgPrint(pCfgGuest);
1549#endif
1550
1551 /*
1552 * The guest stream always will get the audio stream configuration told
1553 * by the device emulation (which in turn was/could be set by the guest OS).
1554 */
1555 PPDMAUDIOSTREAM pGstStrm = NULL;
1556
1557 /** @todo Docs! */
1558 PPDMAUDIOSTREAM pHstStrm = NULL;
1559
1560#define RC_BREAK(x) { rc = x; break; }
1561
1562 do
1563 {
1564 if ( !DrvAudioHlpStreamCfgIsValid(pCfgHost)
1565 || !DrvAudioHlpStreamCfgIsValid(pCfgGuest))
1566 {
1567 RC_BREAK(VERR_INVALID_PARAMETER);
1568 }
1569
1570 /* Make sure that both configurations actually intend the same thing. */
1571 if (pCfgHost->enmDir != pCfgGuest->enmDir)
1572 {
1573 AssertMsgFailed(("Stream configuration directions do not match\n"));
1574 RC_BREAK(VERR_INVALID_PARAMETER);
1575 }
1576
1577 /* Note: cbHstStrm will contain sizeof(PDMAUDIOSTREAM) + additional data
1578 * which the host backend will need. */
1579 size_t cbHstStrm;
1580 if (pCfgHost->enmDir == PDMAUDIODIR_IN)
1581 {
1582 if (!pThis->cStreamsFreeIn)
1583 LogFunc(("Warning: No more input streams free to use\n"));
1584
1585 /* Validate backend configuration. */
1586 if (!pThis->BackendCfg.cbStreamIn)
1587 {
1588 LogFunc(("Backend input configuration not valid, bailing out\n"));
1589 RC_BREAK(VERR_INVALID_PARAMETER);
1590 }
1591
1592 cbHstStrm = pThis->BackendCfg.cbStreamIn;
1593 }
1594 else /* Out */
1595 {
1596 if (!pThis->cStreamsFreeOut)
1597 {
1598 LogFlowFunc(("Maximum number of host output streams reached\n"));
1599 RC_BREAK(VERR_AUDIO_NO_FREE_OUTPUT_STREAMS);
1600 }
1601
1602 /* Validate backend configuration. */
1603 if (!pThis->BackendCfg.cbStreamOut)
1604 {
1605 LogFlowFunc(("Backend output configuration invalid, bailing out\n"));
1606 RC_BREAK(VERR_INVALID_PARAMETER);
1607 }
1608
1609 cbHstStrm = pThis->BackendCfg.cbStreamOut;
1610 }
1611
1612 pHstStrm = (PPDMAUDIOSTREAM)RTMemAllocZ(cbHstStrm);
1613 AssertPtrBreakStmt(pHstStrm, rc = VERR_NO_MEMORY);
1614
1615 pHstStrm->enmCtx = PDMAUDIOSTREAMCTX_HOST;
1616 pHstStrm->enmDir = pCfgHost->enmDir;
1617
1618 pGstStrm = (PPDMAUDIOSTREAM)RTMemAllocZ(sizeof(PDMAUDIOSTREAM));
1619 AssertPtrBreakStmt(pGstStrm, rc = VERR_NO_MEMORY);
1620
1621 pGstStrm->enmCtx = PDMAUDIOSTREAMCTX_GUEST;
1622 pGstStrm->enmDir = pCfgGuest->enmDir;
1623
1624 /*
1625 * Init host stream.
1626 */
1627 RTStrPrintf(pHstStrm->szName, RT_ELEMENTS(pHstStrm->szName), "%s (Host)",
1628 strlen(pCfgHost->szName) ? pCfgHost->szName : "<Untitled>");
1629
1630 pHstStrm->pPair = pGstStrm;
1631
1632 /*
1633 * Init guest stream.
1634 */
1635 RTStrPrintf(pGstStrm->szName, RT_ELEMENTS(pGstStrm->szName), "%s (Guest)",
1636 strlen(pCfgGuest->szName) ? pCfgGuest->szName : "<Untitled>");
1637
1638 pGstStrm->fStatus = pHstStrm->fStatus; /* Reflect the host stream's status. */
1639 pGstStrm->pPair = pHstStrm;
1640
1641 /*
1642 * Try to init the rest.
1643 */
1644 rc = drvAudioStreamInitInternal(pThis, pHstStrm, pCfgHost, pCfgGuest);
1645 if (RT_FAILURE(rc))
1646 {
1647 LogFlowFunc(("Stream not available (yet)\n"));
1648 rc = VINF_SUCCESS;
1649 }
1650
1651 } while (0);
1652
1653#undef RC_BREAK
1654
1655 if (RT_FAILURE(rc))
1656 {
1657 if (pGstStrm)
1658 {
1659 drvAudioStreamDestroyInternal(pThis, pGstStrm);
1660 pGstStrm = NULL;
1661 }
1662
1663 if (pHstStrm)
1664 {
1665 drvAudioStreamDestroyInternal(pThis, pHstStrm);
1666 pHstStrm = NULL;
1667 }
1668 }
1669 else
1670 {
1671 /* Set initial reference counts. */
1672 RTListAppend(&pThis->lstGstStreams, &pGstStrm->Node);
1673 pGstStrm->cRefs = 1;
1674
1675 RTListAppend(&pThis->lstHstStreams, &pHstStrm->Node);
1676 pHstStrm->cRefs = 1;
1677
1678 if (pCfgHost->enmDir == PDMAUDIODIR_IN)
1679 {
1680 if (pThis->cStreamsFreeIn)
1681 pThis->cStreamsFreeIn--;
1682 }
1683 else /* Out */
1684 {
1685 if (pThis->cStreamsFreeOut)
1686 pThis->cStreamsFreeOut--;
1687 }
1688
1689#ifdef VBOX_WITH_STATISTICS
1690 STAM_COUNTER_ADD(&pThis->Stats.TotalStreamsCreated, 1);
1691#endif
1692 /* Always return the guest-side part to the device emulation. */
1693 *ppStream = pGstStrm;
1694 }
1695
1696 int rc2 = RTCritSectLeave(&pThis->CritSect);
1697 if (RT_SUCCESS(rc))
1698 rc = rc2;
1699
1700 LogFlowFuncLeaveRC(rc);
1701 return rc;
1702}
1703
1704/**
1705 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetConfig}
1706 */
1707static DECLCALLBACK(int) drvAudioGetConfig(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOBACKENDCFG pCfg)
1708{
1709 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1710 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1711
1712 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1713
1714 int rc = RTCritSectEnter(&pThis->CritSect);
1715 if (RT_FAILURE(rc))
1716 return rc;
1717
1718 if (pThis->pHostDrvAudio)
1719 {
1720 if (pThis->pHostDrvAudio->pfnGetConfig)
1721 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, pCfg);
1722 else
1723 rc = VERR_NOT_SUPPORTED;
1724 }
1725 else
1726 AssertFailed();
1727
1728 int rc2 = RTCritSectLeave(&pThis->CritSect);
1729 if (RT_SUCCESS(rc))
1730 rc = rc2;
1731
1732 LogFlowFuncLeaveRC(rc);
1733 return rc;
1734}
1735
1736/**
1737 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetStatus}
1738 */
1739static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioGetStatus(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir)
1740{
1741 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
1742
1743 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1744
1745 PDMAUDIOBACKENDSTS backendSts = PDMAUDIOBACKENDSTS_UNKNOWN;
1746
1747 int rc = RTCritSectEnter(&pThis->CritSect);
1748 if (RT_SUCCESS(rc))
1749 {
1750 if ( pThis->pHostDrvAudio
1751 && pThis->pHostDrvAudio->pfnGetStatus)
1752 {
1753 backendSts = pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, enmDir);
1754 }
1755
1756 int rc2 = RTCritSectLeave(&pThis->CritSect);
1757 if (RT_SUCCESS(rc))
1758 rc = rc2;
1759 }
1760
1761 LogFlowFuncLeaveRC(rc);
1762 return backendSts;
1763}
1764
1765/**
1766 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetReadable}
1767 */
1768static DECLCALLBACK(uint32_t) drvAudioStreamGetReadable(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1769{
1770 AssertPtrReturn(pInterface, 0);
1771 AssertPtrReturn(pStream, 0);
1772
1773 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1774
1775 int rc2 = RTCritSectEnter(&pThis->CritSect);
1776 AssertRC(rc2);
1777
1778 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN, ("Can't read from a non-input stream\n"));
1779
1780 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1781 if (!pHstStream) /* No host stream available? Bail out early. */
1782 {
1783 rc2 = RTCritSectLeave(&pThis->CritSect);
1784 AssertRC(rc2);
1785
1786 return 0;
1787 }
1788
1789 uint32_t cReadable = 0;
1790
1791 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
1792 if (pGstStream)
1793 cReadable = AudioMixBufLive(&pGstStream->MixBuf);
1794
1795 Log3Func(("[%s] cbReadable=%RU32 (%zu bytes)\n", pHstStream->szName, cReadable,
1796 AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cReadable)));
1797
1798 rc2 = RTCritSectLeave(&pThis->CritSect);
1799 AssertRC(rc2);
1800
1801 /* Return bytes instead of audio samples. */
1802 return AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cReadable);
1803}
1804
1805/**
1806 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetWritable}
1807 */
1808static DECLCALLBACK(uint32_t) drvAudioStreamGetWritable(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1809{
1810 AssertPtrReturn(pInterface, 0);
1811 AssertPtrReturn(pStream, 0);
1812
1813 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1814
1815 int rc2 = RTCritSectEnter(&pThis->CritSect);
1816 AssertRC(rc2);
1817
1818 AssertMsg(pStream->enmDir == PDMAUDIODIR_OUT, ("Can't write to a non-output stream\n"));
1819
1820 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1821 if (!pHstStream) /* No host stream available? Bail out early. */
1822 {
1823 rc2 = RTCritSectLeave(&pThis->CritSect);
1824 AssertRC(rc2);
1825
1826 AssertMsgFailed(("Guest stream '%s' does not have a host stream attached\n", pStream->szName));
1827 return 0;
1828 }
1829
1830 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
1831 AssertPtr(pGstStream);
1832
1833 uint32_t cWritable = AudioMixBufFree(&pGstStream->MixBuf);
1834
1835 Log3Func(("[%s] cWritable=%RU32 (%zu bytes)\n", pHstStream->szName, cWritable,
1836 AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cWritable)));
1837
1838 rc2 = RTCritSectLeave(&pThis->CritSect);
1839 AssertRC(rc2);
1840
1841 /* Return bytes instead of audio samples. */
1842 return AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cWritable);
1843}
1844
1845/**
1846 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetStatus}
1847 */
1848static DECLCALLBACK(PDMAUDIOSTRMSTS) drvAudioStreamGetStatus(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1849{
1850 AssertPtrReturn(pInterface, false);
1851
1852 if (!pStream)
1853 return PDMAUDIOSTRMSTS_FLAG_NONE;
1854
1855 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1856
1857 int rc2 = RTCritSectEnter(&pThis->CritSect);
1858 AssertRC(rc2);
1859
1860 PDMAUDIOSTRMSTS strmSts = PDMAUDIOSTRMSTS_FLAG_NONE;
1861
1862 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1863 if (pHstStream)
1864 {
1865 strmSts = pHstStream->fStatus;
1866 Log3Func(("%s: strmSts=0x%x\n", pHstStream->szName, strmSts));
1867 }
1868
1869 rc2 = RTCritSectLeave(&pThis->CritSect);
1870 AssertRC(rc2);
1871
1872 return strmSts;
1873}
1874
1875/**
1876 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamSetVolume}
1877 */
1878static DECLCALLBACK(int) drvAudioStreamSetVolume(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, PPDMAUDIOVOLUME pVol)
1879{
1880 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1881 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1882 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
1883
1884 LogFlowFunc(("%s: volL=%RU32, volR=%RU32, fMute=%RTbool\n", pStream->szName, pVol->uLeft, pVol->uRight, pVol->fMuted));
1885
1886 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1887 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
1888
1889 AudioMixBufSetVolume(&pHstStream->MixBuf, pVol);
1890 AudioMixBufSetVolume(&pGstStream->MixBuf, pVol);
1891 return VINF_SUCCESS;
1892}
1893
1894/**
1895 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamDestroy}
1896 */
1897static DECLCALLBACK(int) drvAudioStreamDestroy(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1898{
1899 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1900 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1901
1902 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1903
1904 int rc = RTCritSectEnter(&pThis->CritSect);
1905 AssertRC(rc);
1906
1907 PDMAUDIODIR enmDir = pStream->enmDir;
1908
1909 LogFlowFunc(("%s: cRefs=%RU32\n", pStream->szName, pStream->cRefs));
1910 if (pStream->cRefs > 1)
1911 rc = VERR_WRONG_ORDER;
1912
1913 if (RT_SUCCESS(rc))
1914 {
1915 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1916 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
1917
1918 LogFlowFunc(("\tHost : %s\n", pHstStream ? pHstStream->szName : "<None>"));
1919 LogFlowFunc(("\tGuest: %s\n", pGstStream ? pGstStream->szName : "<None>"));
1920
1921 /* Should prevent double frees. */
1922 Assert(pHstStream != pGstStream);
1923
1924 if (pHstStream)
1925 {
1926 pHstStream->pPair = NULL;
1927 RTListNodeRemove(&pHstStream->Node);
1928 }
1929
1930 if (pGstStream)
1931 {
1932 pGstStream->pPair = NULL;
1933 RTListNodeRemove(&pGstStream->Node);
1934 }
1935
1936 if (pHstStream)
1937 {
1938 rc = drvAudioStreamDestroyInternal(pThis, pHstStream);
1939 AssertRC(rc);
1940
1941 pHstStream = NULL;
1942 }
1943
1944 if (pGstStream)
1945 {
1946 rc = drvAudioStreamDestroyInternal(pThis, pGstStream);
1947 AssertRC(rc);
1948
1949 pGstStream = NULL;
1950 }
1951 }
1952
1953 if (RT_SUCCESS(rc))
1954 {
1955 if (enmDir == PDMAUDIODIR_IN)
1956 {
1957 pThis->cStreamsFreeIn++;
1958 }
1959 else /* Out */
1960 {
1961 pThis->cStreamsFreeOut++;
1962 }
1963 }
1964
1965 int rc2 = RTCritSectLeave(&pThis->CritSect);
1966 if (RT_SUCCESS(rc))
1967 rc = rc2;
1968
1969 LogFlowFuncLeaveRC(rc);
1970 return rc;
1971}
1972
1973/**
1974 * Calls the backend to give it the chance to destroy its part of the audio stream.
1975 *
1976 * @returns IPRT status code.
1977 * @param pThis Pointer to driver instance.
1978 * @param pHstStream Host audio stream to call the backend destruction for.
1979 */
1980static int drvAudioStreamDestroyInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream)
1981{
1982 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1983 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
1984
1985 AssertMsg(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST,
1986 ("Stream '%s' is not a host stream and therefore has no backend\n", pHstStream->szName));
1987
1988 int rc = VINF_SUCCESS;
1989
1990 LogFlowFunc(("%s: fStatus=0x%x\n", pHstStream->szName, pHstStream->fStatus));
1991
1992 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
1993 {
1994 /* Check if the pointer to the host audio driver is still valid.
1995 * It can be NULL if we were called in drvAudioDestruct, for example. */
1996 if (pThis->pHostDrvAudio)
1997 rc = pThis->pHostDrvAudio->pfnStreamDestroy(pThis->pHostDrvAudio, pHstStream);
1998 if (RT_SUCCESS(rc))
1999 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
2000 }
2001
2002 LogFlowFunc(("%s: Returning %Rrc\n", pHstStream->szName, rc));
2003 return rc;
2004}
2005
2006/**
2007 * Destroys an audio stream.
2008 *
2009 * @returns IPRT status code.
2010 * @param pThis Pointer to driver instance.
2011 * @param pStream Pointer to audio stream to destroy.
2012 * That pointer will be invalid after successful destruction.
2013 */
2014static int drvAudioStreamDestroyInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream)
2015{
2016 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2017 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2018
2019 LogFlowFunc(("%s: cRefs=%RU32\n", pStream->szName, pStream->cRefs));
2020
2021 if (pStream->cRefs > 1)
2022 return VERR_WRONG_ORDER;
2023
2024 int rc = VINF_SUCCESS;
2025
2026 if (pStream->enmCtx == PDMAUDIOSTREAMCTX_GUEST)
2027 {
2028 if (pStream->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
2029 {
2030 rc = drvAudioStreamControlInternal(pThis, pStream, PDMAUDIOSTREAMCMD_DISABLE);
2031 if (RT_SUCCESS(rc))
2032 pStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
2033 }
2034
2035 if (pStream->enmDir == PDMAUDIODIR_IN)
2036 {
2037#ifdef VBOX_WITH_STATISTICS
2038 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->In.StatBytesElapsed);
2039 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->In.StatBytesTotalRead);
2040#endif
2041 }
2042 else if (pStream->enmDir == PDMAUDIODIR_OUT)
2043 {
2044#ifdef VBOX_WITH_STATISTICS
2045 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->Out.StatBytesElapsed);
2046 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->Out.StatBytesTotalWritten);
2047#endif
2048 }
2049 }
2050 else if (pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST)
2051 {
2052 rc = drvAudioStreamDestroyInternalBackend(pThis, pStream);
2053
2054 if (pStream->enmDir == PDMAUDIODIR_IN)
2055 {
2056#ifdef VBOX_WITH_STATISTICS
2057 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->In.StatSamplesCaptured);
2058#endif
2059 }
2060 else if (pStream->enmDir == PDMAUDIODIR_OUT)
2061 {
2062#ifdef VBOX_WITH_STATISTICS
2063 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->Out.StatSamplesPlayed);
2064#endif
2065 }
2066 }
2067 else
2068 AssertFailedReturn(VERR_NOT_IMPLEMENTED);
2069
2070 if (RT_SUCCESS(rc))
2071 {
2072 /* Destroy mixing buffer. */
2073 AudioMixBufDestroy(&pStream->MixBuf);
2074
2075 if (pStream)
2076 {
2077 RTMemFree(pStream);
2078 pStream = NULL;
2079 }
2080 }
2081
2082 LogFlowFunc(("Returning %Rrc\n", rc));
2083 return rc;
2084}
2085
2086/********************************************************************/
2087
2088/**
2089 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2090 */
2091static DECLCALLBACK(void *) drvAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
2092{
2093 LogFlowFunc(("pInterface=%p, pszIID=%s\n", pInterface, pszIID));
2094
2095 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
2096 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2097
2098 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
2099 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIAUDIOCONNECTOR, &pThis->IAudioConnector);
2100
2101 return NULL;
2102}
2103
2104/**
2105 * Power Off notification.
2106 *
2107 * @param pDrvIns The driver instance data.
2108 */
2109static DECLCALLBACK(void) drvAudioPowerOff(PPDMDRVINS pDrvIns)
2110{
2111 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2112
2113 LogFlowFuncEnter();
2114
2115 /* Just destroy the host stream on the backend side.
2116 * The rest will either be destructed by the device emulation or
2117 * in drvAudioDestruct(). */
2118 PPDMAUDIOSTREAM pStream;
2119 RTListForEach(&pThis->lstHstStreams, pStream, PDMAUDIOSTREAM, Node)
2120 drvAudioStreamDestroyInternalBackend(pThis, pStream);
2121
2122 /*
2123 * Last call for the driver below us.
2124 * Let it know that we reached end of life.
2125 */
2126 if (pThis->pHostDrvAudio->pfnShutdown)
2127 pThis->pHostDrvAudio->pfnShutdown(pThis->pHostDrvAudio);
2128
2129 pThis->pHostDrvAudio = NULL;
2130
2131 LogFlowFuncLeave();
2132}
2133
2134/**
2135 * Constructs an audio driver instance.
2136 *
2137 * @copydoc FNPDMDRVCONSTRUCT
2138 */
2139static DECLCALLBACK(int) drvAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
2140{
2141 LogFlowFunc(("pDrvIns=%#p, pCfgHandle=%#p, fFlags=%x\n", pDrvIns, pCfgHandle, fFlags));
2142
2143 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2144 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
2145
2146 RTListInit(&pThis->lstHstStreams);
2147 RTListInit(&pThis->lstGstStreams);
2148#ifdef VBOX_WITH_AUDIO_CALLBACKS
2149 RTListInit(&pThis->lstCBIn);
2150 RTListInit(&pThis->lstCBOut);
2151#endif
2152
2153 /*
2154 * Init the static parts.
2155 */
2156 pThis->pDrvIns = pDrvIns;
2157 /* IBase. */
2158 pDrvIns->IBase.pfnQueryInterface = drvAudioQueryInterface;
2159 /* IAudioConnector. */
2160 pThis->IAudioConnector.pfnGetConfig = drvAudioGetConfig;
2161 pThis->IAudioConnector.pfnGetStatus = drvAudioGetStatus;
2162 pThis->IAudioConnector.pfnStreamCreate = drvAudioStreamCreate;
2163 pThis->IAudioConnector.pfnStreamDestroy = drvAudioStreamDestroy;
2164 pThis->IAudioConnector.pfnStreamRetain = drvAudioStreamRetain;
2165 pThis->IAudioConnector.pfnStreamRelease = drvAudioStreamRelease;
2166 pThis->IAudioConnector.pfnStreamControl = drvAudioStreamControl;
2167 pThis->IAudioConnector.pfnStreamRead = drvAudioStreamRead;
2168 pThis->IAudioConnector.pfnStreamWrite = drvAudioStreamWrite;
2169 pThis->IAudioConnector.pfnStreamIterate = drvAudioStreamIterate;
2170 pThis->IAudioConnector.pfnStreamGetReadable = drvAudioStreamGetReadable;
2171 pThis->IAudioConnector.pfnStreamGetWritable = drvAudioStreamGetWritable;
2172 pThis->IAudioConnector.pfnStreamGetStatus = drvAudioStreamGetStatus;
2173 pThis->IAudioConnector.pfnStreamSetVolume = drvAudioStreamSetVolume;
2174 pThis->IAudioConnector.pfnStreamPlay = drvAudioStreamPlay;
2175 pThis->IAudioConnector.pfnStreamCapture = drvAudioStreamCapture;
2176#ifdef VBOX_WITH_AUDIO_CALLBACKS
2177 pThis->IAudioConnector.pfnRegisterCallbacks = drvAudioRegisterCallbacks;
2178 pThis->IAudioConnector.pfnCallback = drvAudioCallback;
2179#endif
2180
2181 /*
2182 * Attach driver below and query its connector interface.
2183 */
2184 PPDMIBASE pDownBase;
2185 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pDownBase);
2186 if (RT_FAILURE(rc))
2187 {
2188 LogRel(("Audio: Failed to attach to driver %p below (flags=0x%x), rc=%Rrc\n",
2189 pDrvIns, fFlags, rc));
2190 return rc;
2191 }
2192
2193 pThis->pHostDrvAudio = PDMIBASE_QUERY_INTERFACE(pDownBase, PDMIHOSTAUDIO);
2194 if (!pThis->pHostDrvAudio)
2195 {
2196 LogRel(("Audio: Failed to query interface for underlying host driver\n"));
2197 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
2198 N_("Host audio backend missing or invalid"));
2199 }
2200
2201 rc = drvAudioInit(pDrvIns, pCfgHandle);
2202 if (RT_SUCCESS(rc))
2203 {
2204 pThis->fTerminate = false;
2205 pThis->pDrvIns = pDrvIns;
2206
2207#ifdef VBOX_WITH_STATISTICS
2208 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalStreamsActive, "TotalStreamsActive",
2209 STAMUNIT_COUNT, "Active input streams.");
2210 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalStreamsCreated, "TotalStreamsCreated",
2211 STAMUNIT_COUNT, "Total created input streams.");
2212 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalSamplesPlayed, "TotalSamplesPlayed",
2213 STAMUNIT_COUNT, "Total samples played.");
2214 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalSamplesCaptured, "TotalSamplesCaptured",
2215 STAMUNIT_COUNT, "Total samples captured.");
2216 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalBytesRead, "TotalBytesRead",
2217 STAMUNIT_BYTES, "Total bytes read.");
2218 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalBytesWritten, "TotalBytesWritten",
2219 STAMUNIT_BYTES, "Total bytes written.");
2220#endif
2221 }
2222
2223 LogFlowFuncLeaveRC(rc);
2224 return rc;
2225}
2226
2227/**
2228 * Destructs an audio driver instance.
2229 *
2230 * @copydoc FNPDMDRVDESTRUCT
2231 */
2232static DECLCALLBACK(void) drvAudioDestruct(PPDMDRVINS pDrvIns)
2233{
2234 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
2235 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2236
2237 LogFlowFuncEnter();
2238
2239 int rc2 = RTCritSectEnter(&pThis->CritSect);
2240 AssertRC(rc2);
2241
2242 /*
2243 * Note: No calls here to the driver below us anymore,
2244 * as PDM already has destroyed it.
2245 * If you need to call something from the host driver,
2246 * do this in drvAudioPowerOff() instead.
2247 */
2248
2249 /* Thus, NULL the pointer to the host audio driver first,
2250 * so that routines like drvAudioStreamDestroyInternal() don't call the driver(s) below us anymore. */
2251 pThis->pHostDrvAudio = NULL;
2252
2253 PPDMAUDIOSTREAM pStream, pStreamNext;
2254 RTListForEachSafe(&pThis->lstHstStreams, pStream, pStreamNext, PDMAUDIOSTREAM, Node)
2255 drvAudioStreamDestroyInternal(pThis, pStream);
2256
2257 RTListForEachSafe(&pThis->lstGstStreams, pStream, pStreamNext, PDMAUDIOSTREAM, Node)
2258 drvAudioStreamDestroyInternal(pThis, pStream);
2259
2260#ifdef VBOX_WITH_AUDIO_CALLBACKS
2261 /*
2262 * Destroy callbacks, if any.
2263 */
2264 PPDMAUDIOCALLBACK pCB, pCBNext;
2265 RTListForEachSafe(&pThis->lstCBIn, pCB, pCBNext, PDMAUDIOCALLBACK, Node)
2266 drvAudioCallbackDestroy(pCB);
2267
2268 RTListForEachSafe(&pThis->lstCBOut, pCB, pCBNext, PDMAUDIOCALLBACK, Node)
2269 drvAudioCallbackDestroy(pCB);
2270#endif
2271
2272 rc2 = RTCritSectLeave(&pThis->CritSect);
2273 AssertRC(rc2);
2274
2275 rc2 = RTCritSectDelete(&pThis->CritSect);
2276 AssertRC(rc2);
2277
2278 LogFlowFuncLeave();
2279}
2280
2281/**
2282 * Suspend notification.
2283 *
2284 * @param pDrvIns The driver instance data.
2285 */
2286static DECLCALLBACK(void) drvAudioSuspend(PPDMDRVINS pDrvIns)
2287{
2288 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_PAUSE);
2289}
2290
2291/**
2292 * Resume notification.
2293 *
2294 * @param pDrvIns The driver instance data.
2295 */
2296static DECLCALLBACK(void) drvAudioResume(PPDMDRVINS pDrvIns)
2297{
2298 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_RESUME);
2299}
2300
2301/**
2302 * Audio driver registration record.
2303 */
2304const PDMDRVREG g_DrvAUDIO =
2305{
2306 /* u32Version */
2307 PDM_DRVREG_VERSION,
2308 /* szName */
2309 "AUDIO",
2310 /* szRCMod */
2311 "",
2312 /* szR0Mod */
2313 "",
2314 /* pszDescription */
2315 "Audio connector driver",
2316 /* fFlags */
2317 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2318 /* fClass */
2319 PDM_DRVREG_CLASS_AUDIO,
2320 /* cMaxInstances */
2321 UINT32_MAX,
2322 /* cbInstance */
2323 sizeof(DRVAUDIO),
2324 /* pfnConstruct */
2325 drvAudioConstruct,
2326 /* pfnDestruct */
2327 drvAudioDestruct,
2328 /* pfnRelocate */
2329 NULL,
2330 /* pfnIOCtl */
2331 NULL,
2332 /* pfnPowerOn */
2333 NULL,
2334 /* pfnReset */
2335 NULL,
2336 /* pfnSuspend */
2337 drvAudioSuspend,
2338 /* pfnResume */
2339 drvAudioResume,
2340 /* pfnAttach */
2341 NULL,
2342 /* pfnDetach */
2343 NULL,
2344 /* pfnPowerOff */
2345 drvAudioPowerOff,
2346 /* pfnSoftReset */
2347 NULL,
2348 /* u32EndVersion */
2349 PDM_DRVREG_VERSION
2350};
2351
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