VirtualBox

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

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

Audio: Update.

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