VirtualBox

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

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

Audio: Bugfixes, logging.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette