VirtualBox

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

Last change on this file since 89558 was 89556, checked in by vboxsync, 4 years ago

DrvAudio: Missing stream locking in drvAudioEnable. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 203.6 KB
Line 
1/* $Id: DrvAudio.cpp 89556 2021-06-08 08:03:03Z vboxsync $ */
2/** @file
3 * Intermediate audio driver - Connects the audio device emulation with the host backend.
4 */
5
6/*
7 * Copyright (C) 2006-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_AUDIO
23#include <VBox/log.h>
24#include <VBox/vmm/pdm.h>
25#include <VBox/err.h>
26#include <VBox/vmm/mm.h>
27#include <VBox/vmm/pdmaudioifs.h>
28#include <VBox/vmm/pdmaudioinline.h>
29#include <VBox/vmm/pdmaudiohostenuminline.h>
30
31#include <iprt/alloc.h>
32#include <iprt/asm-math.h>
33#include <iprt/assert.h>
34#include <iprt/circbuf.h>
35#include <iprt/req.h>
36#include <iprt/string.h>
37#include <iprt/thread.h>
38#include <iprt/uuid.h>
39
40#include "VBoxDD.h"
41
42#include <ctype.h>
43#include <stdlib.h>
44
45#include "AudioHlp.h"
46#include "AudioMixBuffer.h"
47
48
49/*********************************************************************************************************************************
50* Defined Constants And Macros *
51*********************************************************************************************************************************/
52/** @name PDMAUDIOSTREAM_STS_XXX - Used internally by DRVAUDIOSTREAM::fStatus.
53 * @{ */
54/** No flags being set. */
55#define PDMAUDIOSTREAM_STS_NONE UINT32_C(0)
56/** Set if the stream is enabled, clear if disabled. */
57#define PDMAUDIOSTREAM_STS_ENABLED RT_BIT_32(0)
58/** Set if the stream is paused.
59 * Requires the ENABLED status to be set when used. */
60#define PDMAUDIOSTREAM_STS_PAUSED RT_BIT_32(1)
61/** Output only: Set when the stream is draining.
62 * Requires the ENABLED status to be set when used. */
63#define PDMAUDIOSTREAM_STS_PENDING_DISABLE RT_BIT_32(2)
64
65/** Set if the backend for the stream has been created.
66 *
67 * This is generally always set after stream creation, but
68 * can be cleared if the re-initialization of the stream fails later on.
69 * Asynchronous init may still be incomplete, see
70 * PDMAUDIOSTREAM_STS_BACKEND_READY. */
71#define PDMAUDIOSTREAM_STS_BACKEND_CREATED RT_BIT_32(3)
72/** The backend is ready (PDMIHOSTAUDIO::pfnStreamInitAsync is done).
73 * Requires the BACKEND_CREATED status to be set. */
74#define PDMAUDIOSTREAM_STS_BACKEND_READY RT_BIT_32(4)
75/** Set if the stream needs to be re-initialized by the device (i.e. call
76 * PDMIAUDIOCONNECTOR::pfnStreamReInit). (The other status bits are preserved
77 * and are worked as normal while in this state, so that the stream can
78 * resume operation where it left off.) */
79#define PDMAUDIOSTREAM_STS_NEED_REINIT RT_BIT_32(5)
80/** Validation mask for PDMIAUDIOCONNECTOR. */
81#define PDMAUDIOSTREAM_STS_VALID_MASK UINT32_C(0x0000003f)
82/** Asserts the validity of the given stream status mask for PDMIAUDIOCONNECTOR. */
83#define PDMAUDIOSTREAM_STS_ASSERT_VALID(a_fStreamStatus) do { \
84 AssertMsg(!((a_fStreamStatus) & ~PDMAUDIOSTREAM_STS_VALID_MASK), ("%#x\n", (a_fStreamStatus))); \
85 Assert(!((a_fStreamStatus) & PDMAUDIOSTREAM_STS_PAUSED) || ((a_fStreamStatus) & PDMAUDIOSTREAM_STS_ENABLED)); \
86 Assert(!((a_fStreamStatus) & PDMAUDIOSTREAM_STS_PENDING_DISABLE) || ((a_fStreamStatus) & PDMAUDIOSTREAM_STS_ENABLED)); \
87 Assert(!((a_fStreamStatus) & PDMAUDIOSTREAM_STS_BACKEND_READY) || ((a_fStreamStatus) & PDMAUDIOSTREAM_STS_BACKEND_CREATED)); \
88 } while (0)
89
90/** @} */
91
92
93/*********************************************************************************************************************************
94* Structures and Typedefs *
95*********************************************************************************************************************************/
96/**
97 * Audio stream context.
98 *
99 * Needed for separating data from the guest and host side (per stream).
100 */
101typedef struct DRVAUDIOSTREAMCTX
102{
103 /** The stream's audio configuration. */
104 PDMAUDIOSTREAMCFG Cfg;
105} DRVAUDIOSTREAMCTX;
106
107/**
108 * Capture state of a stream wrt backend.
109 */
110typedef enum DRVAUDIOCAPTURESTATE
111{
112 /** Invalid zero value. */
113 DRVAUDIOCAPTURESTATE_INVALID = 0,
114 /** No capturing or pre-buffering. */
115 DRVAUDIOCAPTURESTATE_NO_CAPTURE,
116 /** Regular capturing. */
117 DRVAUDIOCAPTURESTATE_CAPTURING,
118 /** Returning silence till the backend buffer has reched the configured
119 * pre-buffering level. */
120 DRVAUDIOCAPTURESTATE_PREBUF,
121 /** End of valid values. */
122 DRVAUDIOCAPTURESTATE_END
123} DRVAUDIOCAPTURESTATE;
124
125/**
126 * Play state of a stream wrt backend.
127 */
128typedef enum DRVAUDIOPLAYSTATE
129{
130 /** Invalid zero value. */
131 DRVAUDIOPLAYSTATE_INVALID = 0,
132 /** No playback or pre-buffering. */
133 DRVAUDIOPLAYSTATE_NOPLAY,
134 /** Playing w/o any prebuffering. */
135 DRVAUDIOPLAYSTATE_PLAY,
136 /** Parallel pre-buffering prior to a device switch (i.e. we're outputting to
137 * the old device and pre-buffering the same data in parallel). */
138 DRVAUDIOPLAYSTATE_PLAY_PREBUF,
139 /** Initial pre-buffering or the pre-buffering for a device switch (if it
140 * the device setup took less time than filling up the pre-buffer). */
141 DRVAUDIOPLAYSTATE_PREBUF,
142 /** The device initialization is taking too long, pre-buffering wraps around
143 * and drops samples. */
144 DRVAUDIOPLAYSTATE_PREBUF_OVERDUE,
145 /** Same as play-prebuf, but we don't have a working output device any more. */
146 DRVAUDIOPLAYSTATE_PREBUF_SWITCHING,
147 /** Working on committing the pre-buffered data.
148 * We'll typically leave this state immediately and go to PLAY, however if
149 * the backend cannot handle all the pre-buffered data at once, we'll stay
150 * here till it does. */
151 DRVAUDIOPLAYSTATE_PREBUF_COMMITTING,
152 /** End of valid values. */
153 DRVAUDIOPLAYSTATE_END
154} DRVAUDIOPLAYSTATE;
155
156
157/**
158 * Extended stream structure.
159 */
160typedef struct DRVAUDIOSTREAM
161{
162 /** The publicly visible bit. */
163 PDMAUDIOSTREAM Core;
164
165 /** Just an extra magic to verify that we allocated the stream rather than some
166 * faked up stuff from the device (DRVAUDIOSTREAM_MAGIC). */
167 uintptr_t uMagic;
168
169 /** List entry in DRVAUDIO::LstStreams. */
170 RTLISTNODE ListEntry;
171
172 /** Number of references to this stream.
173 * Only can be destroyed when the reference count reaches 0. */
174 uint32_t volatile cRefs;
175 /** Stream status - PDMAUDIOSTREAM_STS_XXX. */
176 uint32_t fStatus;
177
178 /** Data to backend-specific stream data.
179 * This data block will be casted by the backend to access its backend-dependent data.
180 *
181 * That way the backends do not have access to the audio connector's data. */
182 PPDMAUDIOBACKENDSTREAM pBackend;
183
184 /** Set if pfnStreamCreate returned VINF_AUDIO_STREAM_ASYNC_INIT_NEEDED. */
185 bool fNeedAsyncInit;
186 /** The fImmediate parameter value for pfnStreamDestroy. */
187 bool fDestroyImmediate;
188 bool afPadding[2];
189
190 /** Number of (re-)tries while re-initializing the stream. */
191 uint32_t cTriesReInit;
192
193 /** The last backend state we saw.
194 * This is used to detect state changes (for what that is worth). */
195 PDMHOSTAUDIOSTREAMSTATE enmLastBackendState;
196
197 /** The pre-buffering threshold expressed in bytes. */
198 uint32_t cbPreBufThreshold;
199
200 /** The pfnStreamInitAsync request handle. */
201 PRTREQ hReqInitAsync;
202
203 /** The nanosecond timestamp when the stream was started. */
204 uint64_t nsStarted;
205 /** Internal stream position (as per pfnStreamPlay/pfnStreamCapture). */
206 uint64_t offInternal;
207
208 /** Timestamp (in ns) since last trying to re-initialize.
209 * Might be 0 if has not been tried yet. */
210 uint64_t nsLastReInit;
211 /** Timestamp (in ns) since last iteration. */
212 uint64_t nsLastIterated;
213 /** Timestamp (in ns) since last playback / capture. */
214 uint64_t nsLastPlayedCaptured;
215 /** Timestamp (in ns) since last read (input streams) or
216 * write (output streams). */
217 uint64_t nsLastReadWritten;
218
219
220 /** Union for input/output specifics depending on enmDir. */
221 union
222 {
223 /**
224 * The specifics for an audio input stream.
225 */
226 struct
227 {
228 /** The capture state. */
229 DRVAUDIOCAPTURESTATE enmCaptureState;
230
231 struct
232 {
233 /** File for writing non-interleaved captures. */
234 PAUDIOHLPFILE pFileCapture;
235 } Dbg;
236 struct
237 {
238 uint32_t cbBackendReadableBefore;
239 uint32_t cbBackendReadableAfter;
240
241 STAMCOUNTER TotalFramesCaptured;
242 STAMCOUNTER AvgFramesCaptured;
243 STAMCOUNTER TotalTimesCaptured;
244 STAMCOUNTER TotalFramesRead;
245 STAMCOUNTER AvgFramesRead;
246 STAMCOUNTER TotalTimesRead;
247 } Stats;
248 } In;
249
250 /**
251 * The specifics for an audio output stream.
252 */
253 struct
254 {
255 struct
256 {
257 /** File for writing stream playback. */
258 PAUDIOHLPFILE pFilePlay;
259 } Dbg;
260 struct
261 {
262 uint32_t cbBackendWritableBefore;
263 uint32_t cbBackendWritableAfter;
264 } Stats;
265
266 /** Space for pre-buffering. */
267 uint8_t *pbPreBuf;
268 /** The size of the pre-buffer allocation (in bytes). */
269 uint32_t cbPreBufAlloc;
270 /** The current pre-buffering read offset. */
271 uint32_t offPreBuf;
272 /** Number of bytes we've pre-buffered. */
273 uint32_t cbPreBuffered;
274 /** The play state. */
275 DRVAUDIOPLAYSTATE enmPlayState;
276 } Out;
277 } RT_UNION_NM(u);
278} DRVAUDIOSTREAM;
279/** Pointer to an extended stream structure. */
280typedef DRVAUDIOSTREAM *PDRVAUDIOSTREAM;
281
282/** Value for DRVAUDIOSTREAM::uMagic (Johann Sebastian Bach). */
283#define DRVAUDIOSTREAM_MAGIC UINT32_C(0x16850331)
284/** Value for DRVAUDIOSTREAM::uMagic after destruction */
285#define DRVAUDIOSTREAM_MAGIC_DEAD UINT32_C(0x17500728)
286
287
288/**
289 * Audio driver configuration data, tweakable via CFGM.
290 */
291typedef struct DRVAUDIOCFG
292{
293 /** PCM properties to use. */
294 PDMAUDIOPCMPROPS Props;
295 /** Whether using signed sample data or not.
296 * Needed in order to know whether there is a custom value set in CFGM or not.
297 * By default set to UINT8_MAX if not set to a custom value. */
298 uint8_t uSigned;
299 /** Whether swapping endianess of sample data or not.
300 * Needed in order to know whether there is a custom value set in CFGM or not.
301 * By default set to UINT8_MAX if not set to a custom value. */
302 uint8_t uSwapEndian;
303 /** Configures the period size (in ms).
304 * This value reflects the time in between each hardware interrupt on the
305 * backend (host) side. */
306 uint32_t uPeriodSizeMs;
307 /** Configures the (ring) buffer size (in ms). Often is a multiple of uPeriodMs. */
308 uint32_t uBufferSizeMs;
309 /** Configures the pre-buffering size (in ms).
310 * Time needed in buffer before the stream becomes active (pre buffering).
311 * The bigger this value is, the more latency for the stream will occur.
312 * Set to 0 to disable pre-buffering completely.
313 * By default set to UINT32_MAX if not set to a custom value. */
314 uint32_t uPreBufSizeMs;
315 /** The driver's debugging configuration. */
316 struct
317 {
318 /** Whether audio debugging is enabled or not. */
319 bool fEnabled;
320 /** Where to store the debugging files. */
321 char szPathOut[RTPATH_MAX];
322 } Dbg;
323} DRVAUDIOCFG;
324/** Pointer to tweakable audio configuration. */
325typedef DRVAUDIOCFG *PDRVAUDIOCFG;
326/** Pointer to const tweakable audio configuration. */
327typedef DRVAUDIOCFG const *PCDRVAUDIOCFG;
328
329
330/**
331 * Audio driver instance data.
332 *
333 * @implements PDMIAUDIOCONNECTOR
334 */
335typedef struct DRVAUDIO
336{
337 /** Read/Write critical section for guarding changes to pHostDrvAudio and
338 * BackendCfg during deteach/attach. Mostly taken in shared mode.
339 * @note Locking order: Must be entered after CritSectGlobals.
340 * @note Locking order: Must be entered after PDMAUDIOSTREAM::CritSect. */
341 RTCRITSECTRW CritSectHotPlug;
342 /** Critical section for protecting:
343 * - LstStreams
344 * - cStreams
345 * - In.fEnabled
346 * - In.cStreamsFree
347 * - Out.fEnabled
348 * - Out.cStreamsFree
349 * @note Locking order: Must be entered before PDMAUDIOSTREAM::CritSect.
350 * @note Locking order: Must be entered before CritSectHotPlug. */
351 RTCRITSECTRW CritSectGlobals;
352 /** List of audio streams (DRVAUDIOSTREAM). */
353 RTLISTANCHOR LstStreams;
354 /** Number of streams in the list. */
355 size_t cStreams;
356 struct
357 {
358 /** Whether this driver's input streams are enabled or not.
359 * This flag overrides all the attached stream statuses. */
360 bool fEnabled;
361 /** Max. number of free input streams.
362 * UINT32_MAX for unlimited streams. */
363 uint32_t cStreamsFree;
364 } In;
365 struct
366 {
367 /** Whether this driver's output streams are enabled or not.
368 * This flag overrides all the attached stream statuses. */
369 bool fEnabled;
370 /** Max. number of free output streams.
371 * UINT32_MAX for unlimited streams. */
372 uint32_t cStreamsFree;
373 } Out;
374
375 /** Audio configuration settings retrieved from the backend.
376 * The szName field is used for the DriverName config value till we get the
377 * authoritative name from the backend (only for logging). */
378 PDMAUDIOBACKENDCFG BackendCfg;
379 /** Our audio connector interface. */
380 PDMIAUDIOCONNECTOR IAudioConnector;
381 /** Interface used by the host backend. */
382 PDMIHOSTAUDIOPORT IHostAudioPort;
383 /** Pointer to the driver instance. */
384 PPDMDRVINS pDrvIns;
385 /** Pointer to audio driver below us. */
386 PPDMIHOSTAUDIO pHostDrvAudio;
387
388 /** Request pool if the backend needs it for async stream creation. */
389 RTREQPOOL hReqPool;
390
391#ifdef VBOX_WITH_STATISTICS
392 /** Statistics. */
393 struct
394 {
395 STAMCOUNTER TotalStreamsActive;
396 STAMCOUNTER TotalStreamsCreated;
397 STAMCOUNTER TotalFramesRead;
398 STAMCOUNTER TotalFramesIn;
399 STAMCOUNTER TotalBytesRead;
400 } Stats;
401#endif
402
403#ifdef VBOX_WITH_AUDIO_ENUM
404 /** Handle to the timer for delayed re-enumeration of backend devices. */
405 TMTIMERHANDLE hEnumTimer;
406 /** Unique name for the the disable-iteration timer. */
407 char szEnumTimerName[24];
408#endif
409
410 /** Input audio configuration values (static). */
411 DRVAUDIOCFG CfgIn;
412 /** Output audio configuration values (static). */
413 DRVAUDIOCFG CfgOut;
414} DRVAUDIO;
415/** Pointer to the instance data of an audio driver. */
416typedef DRVAUDIO *PDRVAUDIO;
417/** Pointer to const instance data of an audio driver. */
418typedef DRVAUDIO const *PCDRVAUDIO;
419
420
421/*********************************************************************************************************************************
422* Internal Functions *
423*********************************************************************************************************************************/
424static int drvAudioStreamControlInternalBackend(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, PDMAUDIOSTREAMCMD enmStreamCmd);
425static int drvAudioStreamControlInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, PDMAUDIOSTREAMCMD enmStreamCmd);
426static int drvAudioStreamUninitInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx);
427static uint32_t drvAudioStreamRetainInternal(PDRVAUDIOSTREAM pStreamEx);
428static uint32_t drvAudioStreamReleaseInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, bool fMayDestroy);
429static void drvAudioStreamResetInternal(PDRVAUDIOSTREAM pStreamEx);
430
431
432/** Buffer size for drvAudioStreamStatusToStr. */
433# define DRVAUDIO_STATUS_STR_MAX sizeof("BACKEND_CREATED BACKEND_READY ENABLED PAUSED PENDING_DISABLED NEED_REINIT 0x12345678")
434
435/**
436 * Converts an audio stream status to a string.
437 *
438 * @returns pszDst
439 * @param pszDst Buffer to convert into, at least minimum size is
440 * DRVAUDIO_STATUS_STR_MAX.
441 * @param fStatus Stream status flags to convert.
442 */
443static const char *drvAudioStreamStatusToStr(char pszDst[DRVAUDIO_STATUS_STR_MAX], uint32_t fStatus)
444{
445 static const struct
446 {
447 const char *pszMnemonic;
448 uint32_t cchMnemnonic;
449 uint32_t fFlag;
450 } s_aFlags[] =
451 {
452 { RT_STR_TUPLE("BACKEND_CREATED "), PDMAUDIOSTREAM_STS_BACKEND_CREATED },
453 { RT_STR_TUPLE("BACKEND_READY "), PDMAUDIOSTREAM_STS_BACKEND_READY },
454 { RT_STR_TUPLE("ENABLED "), PDMAUDIOSTREAM_STS_ENABLED },
455 { RT_STR_TUPLE("PAUSED "), PDMAUDIOSTREAM_STS_PAUSED },
456 { RT_STR_TUPLE("PENDING_DISABLE "), PDMAUDIOSTREAM_STS_PENDING_DISABLE },
457 { RT_STR_TUPLE("NEED_REINIT "), PDMAUDIOSTREAM_STS_NEED_REINIT },
458 };
459 if (!fStatus)
460 strcpy(pszDst, "NONE");
461 else
462 {
463 char *psz = pszDst;
464 for (size_t i = 0; i < RT_ELEMENTS(s_aFlags); i++)
465 if (fStatus & s_aFlags[i].fFlag)
466 {
467 memcpy(psz, s_aFlags[i].pszMnemonic, s_aFlags[i].cchMnemnonic);
468 psz += s_aFlags[i].cchMnemnonic;
469 fStatus &= ~s_aFlags[i].fFlag;
470 if (!fStatus)
471 break;
472 }
473 if (fStatus == 0)
474 psz[-1] = '\0';
475 else
476 psz += RTStrPrintf(psz, DRVAUDIO_STATUS_STR_MAX - (psz - pszDst), "%#x", fStatus);
477 Assert((uintptr_t)(psz - pszDst) <= DRVAUDIO_STATUS_STR_MAX);
478 }
479 return pszDst;
480}
481
482
483/**
484 * Get play state name string.
485 */
486static const char *drvAudioPlayStateName(DRVAUDIOPLAYSTATE enmState)
487{
488 switch (enmState)
489 {
490 case DRVAUDIOPLAYSTATE_INVALID: return "INVALID";
491 case DRVAUDIOPLAYSTATE_NOPLAY: return "NOPLAY";
492 case DRVAUDIOPLAYSTATE_PLAY: return "PLAY";
493 case DRVAUDIOPLAYSTATE_PLAY_PREBUF: return "PLAY_PREBUF";
494 case DRVAUDIOPLAYSTATE_PREBUF: return "PREBUF";
495 case DRVAUDIOPLAYSTATE_PREBUF_OVERDUE: return "PREBUF_OVERDUE";
496 case DRVAUDIOPLAYSTATE_PREBUF_SWITCHING: return "PREBUF_SWITCHING";
497 case DRVAUDIOPLAYSTATE_PREBUF_COMMITTING: return "PREBUF_COMMITTING";
498 case DRVAUDIOPLAYSTATE_END:
499 break;
500 }
501 return "BAD";
502}
503
504#ifdef LOG_ENABLED
505/**
506 * Get capture state name string.
507 */
508static const char *drvAudioCaptureStateName(DRVAUDIOCAPTURESTATE enmState)
509{
510 switch (enmState)
511 {
512 case DRVAUDIOCAPTURESTATE_INVALID: return "INVALID";
513 case DRVAUDIOCAPTURESTATE_NO_CAPTURE: return "NO_CAPTURE";
514 case DRVAUDIOCAPTURESTATE_CAPTURING: return "CAPTURING";
515 case DRVAUDIOCAPTURESTATE_PREBUF: return "PREBUF";
516 case DRVAUDIOCAPTURESTATE_END:
517 break;
518 }
519 return "BAD";
520}
521#endif
522
523/**
524 * Checks if the stream status is one that can be read from.
525 *
526 * @returns @c true if ready to be read from, @c false if not.
527 * @param fStatus Stream status to evaluate, PDMAUDIOSTREAM_STS_XXX.
528 * @note Not for backend statuses (use PDMAudioStrmStatusBackendCanRead)!
529 */
530DECLINLINE(bool) PDMAudioStrmStatusCanRead(uint32_t fStatus)
531{
532 PDMAUDIOSTREAM_STS_ASSERT_VALID(fStatus);
533 AssertReturn(!(fStatus & ~PDMAUDIOSTREAM_STS_VALID_MASK), false);
534 return (fStatus & ( PDMAUDIOSTREAM_STS_BACKEND_CREATED
535 | PDMAUDIOSTREAM_STS_ENABLED
536 | PDMAUDIOSTREAM_STS_PAUSED
537 | PDMAUDIOSTREAM_STS_NEED_REINIT))
538 == ( PDMAUDIOSTREAM_STS_BACKEND_CREATED
539 | PDMAUDIOSTREAM_STS_ENABLED);
540}
541
542/**
543 * Checks if the stream status is one that can be written to.
544 *
545 * @returns @c true if ready to be written to, @c false if not.
546 * @param fStatus Stream status to evaluate, PDMAUDIOSTREAM_STS_XXX.
547 * @note Not for backend statuses (use PDMAudioStrmStatusBackendCanWrite)!
548 */
549DECLINLINE(bool) PDMAudioStrmStatusCanWrite(uint32_t fStatus)
550{
551 PDMAUDIOSTREAM_STS_ASSERT_VALID(fStatus);
552 AssertReturn(!(fStatus & ~PDMAUDIOSTREAM_STS_VALID_MASK), false);
553 return (fStatus & ( PDMAUDIOSTREAM_STS_BACKEND_CREATED
554 | PDMAUDIOSTREAM_STS_ENABLED
555 | PDMAUDIOSTREAM_STS_PAUSED
556 | PDMAUDIOSTREAM_STS_PENDING_DISABLE
557 | PDMAUDIOSTREAM_STS_NEED_REINIT))
558 == ( PDMAUDIOSTREAM_STS_BACKEND_CREATED
559 | PDMAUDIOSTREAM_STS_ENABLED);
560}
561
562/**
563 * Checks if the stream status is a ready-to-operate one.
564 *
565 * @returns @c true if ready to operate, @c false if not.
566 * @param fStatus Stream status to evaluate, PDMAUDIOSTREAM_STS_XXX.
567 * @note Not for backend statuses!
568 */
569DECLINLINE(bool) PDMAudioStrmStatusIsReady(uint32_t fStatus)
570{
571 PDMAUDIOSTREAM_STS_ASSERT_VALID(fStatus);
572 AssertReturn(!(fStatus & ~PDMAUDIOSTREAM_STS_VALID_MASK), false);
573 return (fStatus & ( PDMAUDIOSTREAM_STS_BACKEND_CREATED
574 | PDMAUDIOSTREAM_STS_ENABLED
575 | PDMAUDIOSTREAM_STS_NEED_REINIT))
576 == ( PDMAUDIOSTREAM_STS_BACKEND_CREATED
577 | PDMAUDIOSTREAM_STS_ENABLED);
578}
579
580
581/**
582 * Wrapper around PDMIHOSTAUDIO::pfnStreamGetStatus and checks the result.
583 *
584 * @returns A PDMHOSTAUDIOSTREAMSTATE value.
585 * @param pThis Pointer to the DrvAudio instance data.
586 * @param pStreamEx The stream to get the backend status for.
587 */
588DECLINLINE(PDMHOSTAUDIOSTREAMSTATE) drvAudioStreamGetBackendState(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx)
589{
590 if (pThis->pHostDrvAudio)
591 {
592 PDMHOSTAUDIOSTREAMSTATE enmState = pThis->pHostDrvAudio->pfnStreamGetState(pThis->pHostDrvAudio, pStreamEx->pBackend);
593 Log9Func(("%s: %s\n", pStreamEx->Core.Cfg.szName, PDMHostAudioStreamStateGetName(enmState) ));
594 Assert( enmState > PDMHOSTAUDIOSTREAMSTATE_INVALID
595 && enmState < PDMHOSTAUDIOSTREAMSTATE_END
596 && (enmState != PDMHOSTAUDIOSTREAMSTATE_DRAINING || pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_OUT));
597 return enmState;
598 }
599 Log9Func(("%s: not-working\n", pStreamEx->Core.Cfg.szName));
600 return PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING;
601}
602
603
604/**
605 * Worker for drvAudioStreamProcessBackendStateChange that completes draining.
606 */
607DECLINLINE(void) drvAudioStreamProcessBackendStateChangeWasDraining(PDRVAUDIOSTREAM pStreamEx)
608{
609 Log(("drvAudioStreamProcessBackendStateChange: Stream '%s': Done draining - disabling stream.\n", pStreamEx->Core.Cfg.szName));
610 pStreamEx->fStatus &= ~(PDMAUDIOSTREAM_STS_ENABLED | PDMAUDIOSTREAM_STS_PENDING_DISABLE);
611 drvAudioStreamResetInternal(pStreamEx);
612}
613
614
615/**
616 * Processes backend state change.
617 *
618 * @returns the new state value.
619 */
620static PDMHOSTAUDIOSTREAMSTATE drvAudioStreamProcessBackendStateChange(PDRVAUDIOSTREAM pStreamEx,
621 PDMHOSTAUDIOSTREAMSTATE enmNewState,
622 PDMHOSTAUDIOSTREAMSTATE enmOldState)
623{
624 PDMAUDIODIR const enmDir = pStreamEx->Core.Cfg.enmDir;
625#ifdef LOG_ENABLED
626 DRVAUDIOPLAYSTATE const enmPlayState = enmDir == PDMAUDIODIR_OUT
627 ? pStreamEx->Out.enmPlayState : DRVAUDIOPLAYSTATE_INVALID;
628 DRVAUDIOCAPTURESTATE const enmCaptureState = enmDir == PDMAUDIODIR_OUT
629 ? pStreamEx->In.enmCaptureState : DRVAUDIOCAPTURESTATE_INVALID;
630#endif
631 Assert(enmNewState != enmOldState);
632 Assert(enmOldState > PDMHOSTAUDIOSTREAMSTATE_INVALID && enmOldState < PDMHOSTAUDIOSTREAMSTATE_END);
633 AssertReturn(enmNewState > PDMHOSTAUDIOSTREAMSTATE_INVALID && enmNewState < PDMHOSTAUDIOSTREAMSTATE_END, enmOldState);
634
635 /*
636 * Figure out what happend and how that reflects on the playback state and stuff.
637 */
638 switch (enmNewState)
639 {
640 case PDMHOSTAUDIOSTREAMSTATE_INITIALIZING:
641 /* Guess we're switching device. Nothing to do because the backend will tell us, right? */
642 break;
643
644 case PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING:
645 case PDMHOSTAUDIOSTREAMSTATE_INACTIVE:
646 /* The stream has stopped working or is inactive. Switch stop any draining & to noplay mode. */
647 if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE)
648 drvAudioStreamProcessBackendStateChangeWasDraining(pStreamEx);
649 if (enmDir == PDMAUDIODIR_OUT)
650 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_NOPLAY;
651 else
652 pStreamEx->In.enmCaptureState = DRVAUDIOCAPTURESTATE_NO_CAPTURE;
653 break;
654
655 case PDMHOSTAUDIOSTREAMSTATE_OKAY:
656 switch (enmOldState)
657 {
658 case PDMHOSTAUDIOSTREAMSTATE_INITIALIZING:
659 /* Should be taken care of elsewhere, so do nothing. */
660 break;
661
662 case PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING:
663 case PDMHOSTAUDIOSTREAMSTATE_INACTIVE:
664 /* Go back to pre-buffering/playing depending on whether it is enabled
665 or not, resetting the stream state. */
666 drvAudioStreamResetInternal(pStreamEx);
667 break;
668
669 case PDMHOSTAUDIOSTREAMSTATE_DRAINING:
670 /* Complete the draining. May race the iterate code. */
671 if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE)
672 drvAudioStreamProcessBackendStateChangeWasDraining(pStreamEx);
673 break;
674
675 /* no default: */
676 case PDMHOSTAUDIOSTREAMSTATE_OKAY: /* impossible */
677 case PDMHOSTAUDIOSTREAMSTATE_INVALID:
678 case PDMHOSTAUDIOSTREAMSTATE_END:
679 case PDMHOSTAUDIOSTREAMSTATE_32BIT_HACK:
680 break;
681 }
682 break;
683
684 case PDMHOSTAUDIOSTREAMSTATE_DRAINING:
685 /* We do all we need to do when issuing the DRAIN command. */
686 Assert(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE);
687 break;
688
689 /* no default: */
690 case PDMHOSTAUDIOSTREAMSTATE_INVALID:
691 case PDMHOSTAUDIOSTREAMSTATE_END:
692 case PDMHOSTAUDIOSTREAMSTATE_32BIT_HACK:
693 break;
694 }
695
696 if (enmDir == PDMAUDIODIR_OUT)
697 LogFunc(("Output stream '%s': %s/%s -> %s/%s\n", pStreamEx->Core.Cfg.szName,
698 PDMHostAudioStreamStateGetName(enmOldState), drvAudioPlayStateName(enmPlayState),
699 PDMHostAudioStreamStateGetName(enmNewState), drvAudioPlayStateName(pStreamEx->Out.enmPlayState) ));
700 else
701 LogFunc(("Input stream '%s': %s/%s -> %s/%s\n", pStreamEx->Core.Cfg.szName,
702 PDMHostAudioStreamStateGetName(enmOldState), drvAudioCaptureStateName(enmCaptureState),
703 PDMHostAudioStreamStateGetName(enmNewState), drvAudioCaptureStateName(pStreamEx->In.enmCaptureState) ));
704
705 pStreamEx->enmLastBackendState = enmNewState;
706 return enmNewState;
707}
708
709
710/**
711 * This gets the backend state and handles changes compared to
712 * DRVAUDIOSTREAM::enmLastBackendState (updated).
713 *
714 * @returns A PDMHOSTAUDIOSTREAMSTATE value.
715 * @param pThis Pointer to the DrvAudio instance data.
716 * @param pStreamEx The stream to get the backend status for.
717 */
718DECLINLINE(PDMHOSTAUDIOSTREAMSTATE) drvAudioStreamGetBackendStateAndProcessChanges(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx)
719{
720 PDMHOSTAUDIOSTREAMSTATE const enmBackendState = drvAudioStreamGetBackendState(pThis, pStreamEx);
721 if (pStreamEx->enmLastBackendState == enmBackendState)
722 return enmBackendState;
723 return drvAudioStreamProcessBackendStateChange(pStreamEx, enmBackendState, pStreamEx->enmLastBackendState);
724}
725
726
727#ifdef VBOX_WITH_AUDIO_ENUM
728/**
729 * Enumerates all host audio devices.
730 *
731 * This functionality might not be implemented by all backends and will return
732 * VERR_NOT_SUPPORTED if not being supported.
733 *
734 * @note Must not hold the driver's critical section!
735 *
736 * @returns VBox status code.
737 * @param pThis Driver instance to be called.
738 * @param fLog Whether to print the enumerated device to the release log or not.
739 * @param pDevEnum Where to store the device enumeration.
740 *
741 * @remarks This is currently ONLY used for release logging.
742 */
743static DECLCALLBACK(int) drvAudioDevicesEnumerateInternal(PDRVAUDIO pThis, bool fLog, PPDMAUDIOHOSTENUM pDevEnum)
744{
745 RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
746
747 int rc;
748
749 /*
750 * If the backend supports it, do a device enumeration.
751 */
752 if (pThis->pHostDrvAudio->pfnGetDevices)
753 {
754 PDMAUDIOHOSTENUM DevEnum;
755 rc = pThis->pHostDrvAudio->pfnGetDevices(pThis->pHostDrvAudio, &DevEnum);
756 if (RT_SUCCESS(rc))
757 {
758 if (fLog)
759 {
760 LogRel(("Audio: Found %RU16 devices for driver '%s'\n", DevEnum.cDevices, pThis->BackendCfg.szName));
761
762 PPDMAUDIOHOSTDEV pDev;
763 RTListForEach(&DevEnum.LstDevices, pDev, PDMAUDIOHOSTDEV, ListEntry)
764 {
765 char szFlags[PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN];
766 LogRel(("Audio: Device '%s':\n"
767 "Audio: ID = %s\n"
768 "Audio: Usage = %s\n"
769 "Audio: Flags = %s\n"
770 "Audio: Input channels = %RU8\n"
771 "Audio: Output channels = %RU8\n",
772 pDev->pszName, pDev->pszId ? pDev->pszId : "",
773 PDMAudioDirGetName(pDev->enmUsage), PDMAudioHostDevFlagsToString(szFlags, pDev->fFlags),
774 pDev->cMaxInputChannels, pDev->cMaxOutputChannels));
775 }
776 }
777
778 if (pDevEnum)
779 rc = PDMAudioHostEnumCopy(pDevEnum, &DevEnum, PDMAUDIODIR_INVALID /*all*/, true /*fOnlyCoreData*/);
780
781 PDMAudioHostEnumDelete(&DevEnum);
782 }
783 else
784 {
785 if (fLog)
786 LogRel(("Audio: Device enumeration for driver '%s' failed with %Rrc\n", pThis->BackendCfg.szName, rc));
787 /* Not fatal. */
788 }
789 }
790 else
791 {
792 rc = VERR_NOT_SUPPORTED;
793
794 if (fLog)
795 LogRel2(("Audio: Host driver '%s' does not support audio device enumeration, skipping\n", pThis->BackendCfg.szName));
796 }
797
798 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
799 LogFunc(("Returning %Rrc\n", rc));
800 return rc;
801}
802#endif /* VBOX_WITH_AUDIO_ENUM */
803
804
805/*********************************************************************************************************************************
806* PDMIAUDIOCONNECTOR *
807*********************************************************************************************************************************/
808
809/**
810 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnEnable}
811 */
812static DECLCALLBACK(int) drvAudioEnable(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir, bool fEnable)
813{
814 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
815 AssertPtr(pThis);
816 LogFlowFunc(("enmDir=%s fEnable=%d\n", PDMAudioDirGetName(enmDir), fEnable));
817
818 /*
819 * Figure which status flag variable is being updated.
820 */
821 bool *pfEnabled;
822 if (enmDir == PDMAUDIODIR_IN)
823 pfEnabled = &pThis->In.fEnabled;
824 else if (enmDir == PDMAUDIODIR_OUT)
825 pfEnabled = &pThis->Out.fEnabled;
826 else
827 AssertFailedReturn(VERR_INVALID_PARAMETER);
828
829 /*
830 * Grab the driver wide lock and check it. Ignore call if no change.
831 */
832 int rc = RTCritSectRwEnterExcl(&pThis->CritSectGlobals);
833 AssertRCReturn(rc, rc);
834
835 if (fEnable != *pfEnabled)
836 {
837 LogRel(("Audio: %s %s for driver '%s'\n",
838 fEnable ? "Enabling" : "Disabling", enmDir == PDMAUDIODIR_IN ? "input" : "output", pThis->BackendCfg.szName));
839
840 /*
841 * When enabling, we must update flag before calling drvAudioStreamControlInternalBackend.
842 */
843 if (fEnable)
844 *pfEnabled = true;
845
846 /*
847 * Update the backend status for the streams in the given direction.
848 *
849 * The pThis->Out.fEnable / pThis->In.fEnable status flags only reflect in the
850 * direction of the backend, drivers and devices above us in the chain does not
851 * know about this. When disabled playback goes to /dev/null and we capture
852 * only silence. This means pStreamEx->fStatus holds the nominal status
853 * and we'll use it to restore the operation. (See also @bugref{9882}.)
854 */
855 PDRVAUDIOSTREAM pStreamEx;
856 RTListForEach(&pThis->LstStreams, pStreamEx, DRVAUDIOSTREAM, ListEntry)
857 {
858 if (pStreamEx->Core.Cfg.enmDir == enmDir)
859 {
860 RTCritSectEnter(&pStreamEx->Core.CritSect);
861
862 /*
863 * When (re-)enabling a stream, clear the disabled warning bit again.
864 */
865 if (fEnable)
866 pStreamEx->Core.fWarningsShown &= ~PDMAUDIOSTREAM_WARN_FLAGS_DISABLED;
867
868 /*
869 * We don't need to do anything unless the stream is enabled.
870 * Paused includes enabled, as does draining, but we only want the former.
871 */
872 uint32_t const fStatus = pStreamEx->fStatus;
873 if (fStatus & PDMAUDIOSTREAM_STS_ENABLED)
874 {
875 const char *pszOperation;
876 int rc2;
877 if (fEnable)
878 {
879 if (!(fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE))
880 {
881 /** @todo r=bird: We need to redo pre-buffering OR switch to
882 * DRVAUDIOPLAYSTATE_PREBUF_SWITCHING playback mode when disabling
883 * output streams. The former is preferred if associated with
884 * reporting the stream as INACTIVE. */
885 rc2 = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_ENABLE);
886 pszOperation = "enable";
887 if (RT_SUCCESS(rc2) && (fStatus & PDMAUDIOSTREAM_STS_PAUSED))
888 {
889 rc2 = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_PAUSE);
890 pszOperation = "pause";
891 }
892 }
893 else
894 {
895 rc2 = VINF_SUCCESS;
896 pszOperation = NULL;
897 }
898 }
899 else
900 {
901 rc2 = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
902 pszOperation = "disable";
903 }
904 if (RT_FAILURE(rc2))
905 {
906 LogRel(("Audio: Failed to %s %s stream '%s': %Rrc\n",
907 pszOperation, enmDir == PDMAUDIODIR_IN ? "input" : "output", pStreamEx->Core.Cfg.szName, rc2));
908 if (RT_SUCCESS(rc))
909 rc = rc2; /** @todo r=bird: This isn't entirely helpful to the caller since we'll update the status
910 * regardless of the status code we return. And anyway, there is nothing that can be done
911 * about individual stream by the caller... */
912 }
913 }
914
915 RTCritSectLeave(&pStreamEx->Core.CritSect);
916 }
917 }
918
919 /*
920 * When disabling, we must update the status flag after the
921 * drvAudioStreamControlInternalBackend(DISABLE) calls.
922 */
923 *pfEnabled = fEnable;
924 }
925
926 RTCritSectRwLeaveExcl(&pThis->CritSectGlobals);
927 LogFlowFuncLeaveRC(rc);
928 return rc;
929}
930
931
932/**
933 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnIsEnabled}
934 */
935static DECLCALLBACK(bool) drvAudioIsEnabled(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir)
936{
937 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
938 AssertPtr(pThis);
939 int rc = RTCritSectRwEnterShared(&pThis->CritSectGlobals);
940 AssertRCReturn(rc, false);
941
942 bool fEnabled;
943 if (enmDir == PDMAUDIODIR_IN)
944 fEnabled = pThis->In.fEnabled;
945 else if (enmDir == PDMAUDIODIR_OUT)
946 fEnabled = pThis->Out.fEnabled;
947 else
948 AssertFailedStmt(fEnabled = false);
949
950 RTCritSectRwLeaveShared(&pThis->CritSectGlobals);
951 return fEnabled;
952}
953
954
955/**
956 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetConfig}
957 */
958static DECLCALLBACK(int) drvAudioGetConfig(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOBACKENDCFG pCfg)
959{
960 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
961 AssertPtr(pThis);
962 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
963 int rc = RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
964 AssertRCReturn(rc, rc);
965
966 if (pThis->pHostDrvAudio)
967 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, pCfg);
968 else
969 rc = VERR_PDM_NO_ATTACHED_DRIVER;
970
971 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
972 LogFlowFuncLeaveRC(rc);
973 return rc;
974}
975
976
977/**
978 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetStatus}
979 */
980static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioGetStatus(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir)
981{
982 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
983 AssertPtr(pThis);
984 int rc = RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
985 AssertRCReturn(rc, PDMAUDIOBACKENDSTS_UNKNOWN);
986
987 PDMAUDIOBACKENDSTS fBackendStatus;
988 if (pThis->pHostDrvAudio)
989 {
990 if (pThis->pHostDrvAudio->pfnGetStatus)
991 fBackendStatus = pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, enmDir);
992 else
993 fBackendStatus = PDMAUDIOBACKENDSTS_UNKNOWN;
994 }
995 else
996 fBackendStatus = PDMAUDIOBACKENDSTS_NOT_ATTACHED;
997
998 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
999 LogFlowFunc(("LEAVE - %#x\n", fBackendStatus));
1000 return fBackendStatus;
1001}
1002
1003
1004/**
1005 * Frees an audio stream and its allocated resources.
1006 *
1007 * @param pStreamEx Audio stream to free. After this call the pointer will
1008 * not be valid anymore.
1009 */
1010static void drvAudioStreamFree(PDRVAUDIOSTREAM pStreamEx)
1011{
1012 if (pStreamEx)
1013 {
1014 LogFunc(("[%s]\n", pStreamEx->Core.Cfg.szName));
1015 Assert(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC);
1016 Assert(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC);
1017
1018 pStreamEx->Core.uMagic = ~PDMAUDIOSTREAM_MAGIC;
1019 pStreamEx->pBackend = NULL;
1020 pStreamEx->uMagic = DRVAUDIOSTREAM_MAGIC_DEAD;
1021
1022 RTCritSectDelete(&pStreamEx->Core.CritSect);
1023
1024 RTMemFree(pStreamEx);
1025 }
1026}
1027
1028
1029/**
1030 * Adjusts the request stream configuration, applying our settings.
1031 *
1032 * This also does some basic validations.
1033 *
1034 * Used by both the stream creation and stream configuration hinting code.
1035 *
1036 * @returns VBox status code.
1037 * @param pThis Pointer to the DrvAudio instance data.
1038 * @param pCfg The configuration that should be adjusted.
1039 * @param pszName Stream name to use when logging warnings and errors.
1040 */
1041static int drvAudioStreamAdjustConfig(PCDRVAUDIO pThis, PPDMAUDIOSTREAMCFG pCfg, const char *pszName)
1042{
1043 /* Get the right configuration for the stream to be created. */
1044 PCDRVAUDIOCFG pDrvCfg = pCfg->enmDir == PDMAUDIODIR_IN ? &pThis->CfgIn: &pThis->CfgOut;
1045
1046 /* Fill in the tweakable parameters into the requested host configuration.
1047 * All parameters in principle can be changed and returned by the backend via the acquired configuration. */
1048
1049 /*
1050 * PCM
1051 */
1052 if (PDMAudioPropsSampleSize(&pDrvCfg->Props) != 0) /* Anything set via custom extra-data? */
1053 {
1054 PDMAudioPropsSetSampleSize(&pCfg->Props, PDMAudioPropsSampleSize(&pDrvCfg->Props));
1055 LogRel2(("Audio: Using custom sample size of %RU8 bytes for stream '%s'\n",
1056 PDMAudioPropsSampleSize(&pCfg->Props), pszName));
1057 }
1058
1059 if (pDrvCfg->Props.uHz) /* Anything set via custom extra-data? */
1060 {
1061 pCfg->Props.uHz = pDrvCfg->Props.uHz;
1062 LogRel2(("Audio: Using custom Hz rate %RU32 for stream '%s'\n", pCfg->Props.uHz, pszName));
1063 }
1064
1065 if (pDrvCfg->uSigned != UINT8_MAX) /* Anything set via custom extra-data? */
1066 {
1067 pCfg->Props.fSigned = RT_BOOL(pDrvCfg->uSigned);
1068 LogRel2(("Audio: Using custom %s sample format for stream '%s'\n",
1069 pCfg->Props.fSigned ? "signed" : "unsigned", pszName));
1070 }
1071
1072 if (pDrvCfg->uSwapEndian != UINT8_MAX) /* Anything set via custom extra-data? */
1073 {
1074 pCfg->Props.fSwapEndian = RT_BOOL(pDrvCfg->uSwapEndian);
1075 LogRel2(("Audio: Using custom %s endianess for samples of stream '%s'\n",
1076 pCfg->Props.fSwapEndian ? "swapped" : "original", pszName));
1077 }
1078
1079 if (PDMAudioPropsChannels(&pDrvCfg->Props) != 0) /* Anything set via custom extra-data? */
1080 {
1081 PDMAudioPropsSetChannels(&pCfg->Props, PDMAudioPropsChannels(&pDrvCfg->Props));
1082 LogRel2(("Audio: Using custom %RU8 channel(s) for stream '%s'\n", PDMAudioPropsChannels(&pDrvCfg->Props), pszName));
1083 }
1084
1085 /* Validate PCM properties. */
1086 if (!AudioHlpPcmPropsAreValid(&pCfg->Props))
1087 {
1088 LogRel(("Audio: Invalid custom PCM properties set for stream '%s', cannot create stream\n", pszName));
1089 return VERR_INVALID_PARAMETER;
1090 }
1091
1092 /*
1093 * Period size
1094 */
1095 const char *pszWhat = "device-specific";
1096 if (pDrvCfg->uPeriodSizeMs)
1097 {
1098 pCfg->Backend.cFramesPeriod = PDMAudioPropsMilliToFrames(&pCfg->Props, pDrvCfg->uPeriodSizeMs);
1099 pszWhat = "custom";
1100 }
1101
1102 if (!pCfg->Backend.cFramesPeriod) /* Set default period size if nothing explicitly is set. */
1103 {
1104 pCfg->Backend.cFramesPeriod = PDMAudioPropsMilliToFrames(&pCfg->Props, 150 /*ms*/);
1105 pszWhat = "default";
1106 }
1107
1108 LogRel2(("Audio: Using %s period size %RU64 ms / %RU32 frames for stream '%s'\n",
1109 pszWhat, PDMAudioPropsFramesToMilli(&pCfg->Props, pCfg->Backend.cFramesPeriod),
1110 pCfg->Backend.cFramesPeriod, pszName));
1111
1112 /*
1113 * Buffer size
1114 */
1115 pszWhat = "device-specific";
1116 if (pDrvCfg->uBufferSizeMs)
1117 {
1118 pCfg->Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(&pCfg->Props, pDrvCfg->uBufferSizeMs);
1119 pszWhat = "custom";
1120 }
1121
1122 if (!pCfg->Backend.cFramesBufferSize) /* Set default buffer size if nothing explicitly is set. */
1123 {
1124 pCfg->Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(&pCfg->Props, 300 /*ms*/);
1125 pszWhat = "default";
1126 }
1127
1128 LogRel2(("Audio: Using %s buffer size %RU64 ms / %RU32 frames for stream '%s'\n",
1129 pszWhat, PDMAudioPropsFramesToMilli(&pCfg->Props, pCfg->Backend.cFramesBufferSize),
1130 pCfg->Backend.cFramesBufferSize, pszName));
1131
1132 /*
1133 * Pre-buffering size
1134 */
1135 pszWhat = "device-specific";
1136 if (pDrvCfg->uPreBufSizeMs != UINT32_MAX) /* Anything set via global / per-VM extra-data? */
1137 {
1138 pCfg->Backend.cFramesPreBuffering = PDMAudioPropsMilliToFrames(&pCfg->Props, pDrvCfg->uPreBufSizeMs);
1139 pszWhat = "custom";
1140 }
1141 else /* No, then either use the default or device-specific settings (if any). */
1142 {
1143 if (pCfg->Backend.cFramesPreBuffering == UINT32_MAX) /* Set default pre-buffering size if nothing explicitly is set. */
1144 {
1145 /* Pre-buffer 50% for both output & input. Capping both at 200ms.
1146 The 50% reasoning being that we need to have sufficient slack space
1147 in both directions as the guest DMA timer might be delayed by host
1148 scheduling as well as sped up afterwards because of TM catch-up. */
1149 uint32_t const cFramesMax = PDMAudioPropsMilliToFrames(&pCfg->Props, 200);
1150 pCfg->Backend.cFramesPreBuffering = pCfg->Backend.cFramesBufferSize / 2;
1151 pCfg->Backend.cFramesPreBuffering = RT_MIN(pCfg->Backend.cFramesPreBuffering, cFramesMax);
1152 pszWhat = "default";
1153 }
1154 }
1155
1156 LogRel2(("Audio: Using %s pre-buffering size %RU64 ms / %RU32 frames for stream '%s'\n",
1157 pszWhat, PDMAudioPropsFramesToMilli(&pCfg->Props, pCfg->Backend.cFramesPreBuffering),
1158 pCfg->Backend.cFramesPreBuffering, pszName));
1159
1160 /*
1161 * Validate input.
1162 */
1163 if (pCfg->Backend.cFramesBufferSize < pCfg->Backend.cFramesPeriod)
1164 {
1165 LogRel(("Audio: Error for stream '%s': Buffering size (%RU64ms) must not be smaller than the period size (%RU64ms)\n",
1166 pszName, PDMAudioPropsFramesToMilli(&pCfg->Props, pCfg->Backend.cFramesBufferSize),
1167 PDMAudioPropsFramesToMilli(&pCfg->Props, pCfg->Backend.cFramesPeriod)));
1168 return VERR_INVALID_PARAMETER;
1169 }
1170
1171 if ( pCfg->Backend.cFramesPreBuffering != UINT32_MAX /* Custom pre-buffering set? */
1172 && pCfg->Backend.cFramesPreBuffering)
1173 {
1174 if (pCfg->Backend.cFramesBufferSize < pCfg->Backend.cFramesPreBuffering)
1175 {
1176 LogRel(("Audio: Error for stream '%s': Buffering size (%RU64ms) must not be smaller than the pre-buffering size (%RU64ms)\n",
1177 pszName, PDMAudioPropsFramesToMilli(&pCfg->Props, pCfg->Backend.cFramesPreBuffering),
1178 PDMAudioPropsFramesToMilli(&pCfg->Props, pCfg->Backend.cFramesBufferSize)));
1179 return VERR_INVALID_PARAMETER;
1180 }
1181 }
1182
1183 return VINF_SUCCESS;
1184}
1185
1186
1187/**
1188 * Worker thread function for drvAudioStreamConfigHint that's used when
1189 * PDMAUDIOBACKEND_F_ASYNC_HINT is in effect.
1190 */
1191static DECLCALLBACK(void) drvAudioStreamConfigHintWorker(PDRVAUDIO pThis, PPDMAUDIOSTREAMCFG pCfg)
1192{
1193 LogFlowFunc(("pThis=%p pCfg=%p\n", pThis, pCfg));
1194 AssertPtrReturnVoid(pCfg);
1195 int rc = RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
1196 AssertRCReturnVoid(rc);
1197
1198 PPDMIHOSTAUDIO const pHostDrvAudio = pThis->pHostDrvAudio;
1199 if (pHostDrvAudio)
1200 {
1201 AssertPtr(pHostDrvAudio->pfnStreamConfigHint);
1202 if (pHostDrvAudio->pfnStreamConfigHint)
1203 pHostDrvAudio->pfnStreamConfigHint(pHostDrvAudio, pCfg);
1204 }
1205 PDMAudioStrmCfgFree(pCfg);
1206
1207 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
1208 LogFlowFunc(("returns\n"));
1209}
1210
1211
1212/**
1213 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamConfigHint}
1214 */
1215static DECLCALLBACK(void) drvAudioStreamConfigHint(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAMCFG pCfg)
1216{
1217 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
1218 AssertReturnVoid(pCfg->enmDir == PDMAUDIODIR_IN || pCfg->enmDir == PDMAUDIODIR_OUT);
1219
1220 int rc = RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
1221 AssertRCReturnVoid(rc);
1222
1223 /*
1224 * Don't do anything unless the backend has a pfnStreamConfigHint method
1225 * and the direction is currently enabled.
1226 */
1227 if ( pThis->pHostDrvAudio
1228 && pThis->pHostDrvAudio->pfnStreamConfigHint)
1229 {
1230 if (pCfg->enmDir == PDMAUDIODIR_OUT ? pThis->Out.fEnabled : pThis->In.fEnabled)
1231 {
1232 /*
1233 * Adjust the configuration (applying out settings) then call the backend driver.
1234 */
1235 rc = drvAudioStreamAdjustConfig(pThis, pCfg, pCfg->szName);
1236 AssertLogRelRC(rc);
1237 if (RT_SUCCESS(rc))
1238 {
1239 rc = VERR_CALLBACK_RETURN;
1240 if (pThis->BackendCfg.fFlags & PDMAUDIOBACKEND_F_ASYNC_HINT)
1241 {
1242 PPDMAUDIOSTREAMCFG pDupCfg = PDMAudioStrmCfgDup(pCfg);
1243 if (pDupCfg)
1244 {
1245 rc = RTReqPoolCallVoidNoWait(pThis->hReqPool, (PFNRT)drvAudioStreamConfigHintWorker, 2, pThis, pDupCfg);
1246 if (RT_SUCCESS(rc))
1247 LogFlowFunc(("Asynchronous call running on worker thread.\n"));
1248 else
1249 PDMAudioStrmCfgFree(pDupCfg);
1250 }
1251 }
1252 if (RT_FAILURE_NP(rc))
1253 {
1254 LogFlowFunc(("Doing synchronous call...\n"));
1255 pThis->pHostDrvAudio->pfnStreamConfigHint(pThis->pHostDrvAudio, pCfg);
1256 }
1257 }
1258 }
1259 else
1260 LogFunc(("Ignoring hint because direction is not currently enabled\n"));
1261 }
1262 else
1263 LogFlowFunc(("Ignoring hint because backend has no pfnStreamConfigHint method.\n"));
1264
1265 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
1266}
1267
1268
1269/**
1270 * Common worker for synchronizing the ENABLED and PAUSED status bits with the
1271 * backend after it becomes ready.
1272 *
1273 * Used by async init and re-init.
1274 *
1275 * @note Is sometimes called w/o having entered DRVAUDIO::CritSectHotPlug.
1276 * Caller must however own the stream critsect.
1277 */
1278static int drvAudioStreamUpdateBackendOnStatus(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, const char *pszWhen)
1279{
1280 int rc = VINF_SUCCESS;
1281 if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_ENABLED)
1282 {
1283 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_ENABLE);
1284 if (RT_SUCCESS(rc))
1285 {
1286 if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PAUSED)
1287 {
1288 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_PAUSE);
1289 if (RT_FAILURE(rc))
1290 LogRelMax(64, ("Audio: Failed to pause stream '%s' after %s: %Rrc\n", pStreamEx->Core.Cfg.szName, pszWhen, rc));
1291 }
1292 }
1293 else
1294 LogRelMax(64, ("Audio: Failed to enable stream '%s' after %s: %Rrc\n", pStreamEx->Core.Cfg.szName, pszWhen, rc));
1295 }
1296 return rc;
1297}
1298
1299
1300/**
1301 * For performing PDMIHOSTAUDIO::pfnStreamInitAsync on a worker thread.
1302 *
1303 * @param pThis Pointer to the DrvAudio instance data.
1304 * @param pStreamEx The stream. One reference for us to release.
1305 */
1306static DECLCALLBACK(void) drvAudioStreamInitAsync(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx)
1307{
1308 LogFlow(("pThis=%p pStreamEx=%p (%s)\n", pThis, pStreamEx, pStreamEx->Core.Cfg.szName));
1309
1310 int rc = RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
1311 AssertRCReturnVoid(rc);
1312
1313 /*
1314 * Do the init job.
1315 */
1316 bool fDestroyed;
1317 PPDMIHOSTAUDIO pIHostDrvAudio = pThis->pHostDrvAudio;
1318 AssertPtr(pIHostDrvAudio);
1319 if (pIHostDrvAudio && pIHostDrvAudio->pfnStreamInitAsync)
1320 {
1321 fDestroyed = pStreamEx->cRefs <= 1;
1322 rc = pIHostDrvAudio->pfnStreamInitAsync(pIHostDrvAudio, pStreamEx->pBackend, fDestroyed);
1323 LogFlow(("pfnStreamInitAsync returns %Rrc (on %p, fDestroyed=%d)\n", rc, pStreamEx, fDestroyed));
1324 }
1325 else
1326 {
1327 fDestroyed = true;
1328 rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
1329 }
1330
1331 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
1332 RTCritSectEnter(&pStreamEx->Core.CritSect);
1333
1334 /*
1335 * On success, update the backend on the stream status and mark it ready for business.
1336 */
1337 if (RT_SUCCESS(rc) && !fDestroyed)
1338 {
1339 RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
1340
1341 /*
1342 * Update the backend state.
1343 */
1344 pStreamEx->fStatus |= PDMAUDIOSTREAM_STS_BACKEND_READY; /* before the backend control call! */
1345
1346 rc = drvAudioStreamUpdateBackendOnStatus(pThis, pStreamEx, "asynchronous initialization completed");
1347
1348 /*
1349 * Modify the play state if output stream.
1350 */
1351 if (pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_OUT)
1352 {
1353 DRVAUDIOPLAYSTATE const enmPlayState = pStreamEx->Out.enmPlayState;
1354 switch (enmPlayState)
1355 {
1356 case DRVAUDIOPLAYSTATE_PREBUF:
1357 case DRVAUDIOPLAYSTATE_PREBUF_SWITCHING:
1358 break;
1359 case DRVAUDIOPLAYSTATE_PREBUF_OVERDUE:
1360 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PREBUF_COMMITTING;
1361 break;
1362 case DRVAUDIOPLAYSTATE_NOPLAY:
1363 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PREBUF;
1364 break;
1365 case DRVAUDIOPLAYSTATE_PLAY:
1366 case DRVAUDIOPLAYSTATE_PLAY_PREBUF:
1367 case DRVAUDIOPLAYSTATE_PREBUF_COMMITTING:
1368 AssertFailedBreak();
1369 /* no default */
1370 case DRVAUDIOPLAYSTATE_END:
1371 case DRVAUDIOPLAYSTATE_INVALID:
1372 break;
1373 }
1374 LogFunc(("enmPlayState: %s -> %s\n", drvAudioPlayStateName(enmPlayState),
1375 drvAudioPlayStateName(pStreamEx->Out.enmPlayState) ));
1376 }
1377
1378 /*
1379 * Update the last backend state.
1380 */
1381 pStreamEx->enmLastBackendState = drvAudioStreamGetBackendState(pThis, pStreamEx);
1382
1383 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
1384 }
1385 /*
1386 * Don't quite know what to do on failure...
1387 */
1388 else if (!fDestroyed)
1389 {
1390 LogRelMax(64, ("Audio: Failed to initialize stream '%s': %Rrc\n", pStreamEx->Core.Cfg.szName, rc));
1391 }
1392
1393 /*
1394 * Release the request handle, must be done while inside the critical section.
1395 */
1396 if (pStreamEx->hReqInitAsync != NIL_RTREQ)
1397 {
1398 LogFlowFunc(("Releasing hReqInitAsync=%p\n", pStreamEx->hReqInitAsync));
1399 RTReqRelease(pStreamEx->hReqInitAsync);
1400 pStreamEx->hReqInitAsync = NIL_RTREQ;
1401 }
1402
1403 RTCritSectLeave(&pStreamEx->Core.CritSect);
1404
1405 /*
1406 * Release our stream reference.
1407 */
1408 uint32_t cRefs = drvAudioStreamReleaseInternal(pThis, pStreamEx, true /*fMayDestroy*/);
1409 LogFlowFunc(("returns (fDestroyed=%d, cRefs=%u)\n", fDestroyed, cRefs)); RT_NOREF(cRefs);
1410}
1411
1412
1413/**
1414 * Worker for drvAudioStreamInitInternal and drvAudioStreamReInitInternal that
1415 * creates the backend (host driver) side of an audio stream.
1416 *
1417 * @returns VBox status code.
1418 * @param pThis Pointer to driver instance.
1419 * @param pStreamEx Stream to create backend for. The Core.Cfg field
1420 * contains the requested configuration when we're called
1421 * and the actual configuration when successfully
1422 * returning.
1423 *
1424 * @note Configuration precedence for requested audio stream configuration (first has highest priority, if set):
1425 * - per global extra-data
1426 * - per-VM extra-data
1427 * - requested configuration (by pCfgReq)
1428 * - default value
1429 */
1430static int drvAudioStreamCreateInternalBackend(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx)
1431{
1432 AssertMsg((pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_CREATED) == 0,
1433 ("Stream '%s' already initialized in backend\n", pStreamEx->Core.Cfg.szName));
1434
1435 /*
1436 * Adjust the stream config, applying defaults and any overriding settings.
1437 */
1438 int rc = drvAudioStreamAdjustConfig(pThis, &pStreamEx->Core.Cfg, pStreamEx->Core.Cfg.szName);
1439 if (RT_FAILURE(rc))
1440 return rc;
1441 PDMAUDIOSTREAMCFG const CfgReq = pStreamEx->Core.Cfg;
1442
1443 /*
1444 * Call the host driver to create the stream.
1445 */
1446 RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
1447
1448 AssertLogRelMsgStmt(RT_VALID_PTR(pThis->pHostDrvAudio),
1449 ("Audio: %p\n", pThis->pHostDrvAudio), rc = VERR_PDM_NO_ATTACHED_DRIVER);
1450 if (RT_SUCCESS(rc))
1451 AssertLogRelMsgStmt(pStreamEx->Core.cbBackend == pThis->BackendCfg.cbStream,
1452 ("Audio: Backend changed? cbBackend changed from %#x to %#x\n",
1453 pStreamEx->Core.cbBackend, pThis->BackendCfg.cbStream),
1454 rc = VERR_STATE_CHANGED);
1455 if (RT_SUCCESS(rc))
1456 rc = pThis->pHostDrvAudio->pfnStreamCreate(pThis->pHostDrvAudio, pStreamEx->pBackend, &CfgReq, &pStreamEx->Core.Cfg);
1457 if (RT_SUCCESS(rc))
1458 {
1459 pStreamEx->enmLastBackendState = drvAudioStreamGetBackendState(pThis, pStreamEx);
1460
1461 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
1462
1463 AssertLogRelReturn(pStreamEx->pBackend->uMagic == PDMAUDIOBACKENDSTREAM_MAGIC, VERR_INTERNAL_ERROR_3);
1464 AssertLogRelReturn(pStreamEx->pBackend->pStream == &pStreamEx->Core, VERR_INTERNAL_ERROR_3);
1465
1466 /* Must set the backend-initialized flag now or the backend won't be
1467 destroyed (this used to be done at the end of this function, with
1468 several possible early return paths before it). */
1469 pStreamEx->fStatus |= PDMAUDIOSTREAM_STS_BACKEND_CREATED;
1470 }
1471 else
1472 {
1473 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
1474 if (rc == VERR_NOT_SUPPORTED)
1475 LogRel2(("Audio: Creating stream '%s' in backend not supported\n", pStreamEx->Core.Cfg.szName));
1476 else if (rc == VERR_AUDIO_STREAM_COULD_NOT_CREATE)
1477 LogRel2(("Audio: Stream '%s' could not be created in backend because of missing hardware / drivers\n",
1478 pStreamEx->Core.Cfg.szName));
1479 else
1480 LogRel(("Audio: Creating stream '%s' in backend failed with %Rrc\n", pStreamEx->Core.Cfg.szName, rc));
1481 return rc;
1482 }
1483
1484 /* Remember if we need to call pfnStreamInitAsync. */
1485 pStreamEx->fNeedAsyncInit = rc == VINF_AUDIO_STREAM_ASYNC_INIT_NEEDED;
1486 AssertStmt(rc != VINF_AUDIO_STREAM_ASYNC_INIT_NEEDED || pThis->pHostDrvAudio->pfnStreamInitAsync != NULL,
1487 pStreamEx->fNeedAsyncInit = false);
1488 AssertMsg( rc != VINF_AUDIO_STREAM_ASYNC_INIT_NEEDED
1489 || pStreamEx->enmLastBackendState == PDMHOSTAUDIOSTREAMSTATE_INITIALIZING,
1490 ("rc=%Rrc %s\n", rc, PDMHostAudioStreamStateGetName(pStreamEx->enmLastBackendState)));
1491
1492 PPDMAUDIOSTREAMCFG const pCfgAcq = &pStreamEx->Core.Cfg;
1493
1494 /*
1495 * Validate acquired configuration.
1496 */
1497 char szTmp[PDMAUDIOPROPSTOSTRING_MAX];
1498 LogFunc(("Backend returned: %s\n", PDMAudioStrmCfgToString(pCfgAcq, szTmp, sizeof(szTmp)) ));
1499 AssertLogRelMsgReturn(AudioHlpStreamCfgIsValid(pCfgAcq),
1500 ("Audio: Creating stream '%s' returned an invalid backend configuration (%s), skipping\n",
1501 pCfgAcq->szName, PDMAudioPropsToString(&pCfgAcq->Props, szTmp, sizeof(szTmp))),
1502 VERR_INVALID_PARAMETER);
1503
1504 /* Let the user know that the backend changed one of the values requested above. */
1505 if (pCfgAcq->Backend.cFramesBufferSize != CfgReq.Backend.cFramesBufferSize)
1506 LogRel2(("Audio: Backend changed buffer size from %RU64ms (%RU32 frames) to %RU64ms (%RU32 frames)\n",
1507 PDMAudioPropsFramesToMilli(&CfgReq.Props, CfgReq.Backend.cFramesBufferSize), CfgReq.Backend.cFramesBufferSize,
1508 PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesBufferSize), pCfgAcq->Backend.cFramesBufferSize));
1509
1510 if (pCfgAcq->Backend.cFramesPeriod != CfgReq.Backend.cFramesPeriod)
1511 LogRel2(("Audio: Backend changed period size from %RU64ms (%RU32 frames) to %RU64ms (%RU32 frames)\n",
1512 PDMAudioPropsFramesToMilli(&CfgReq.Props, CfgReq.Backend.cFramesPeriod), CfgReq.Backend.cFramesPeriod,
1513 PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesPeriod), pCfgAcq->Backend.cFramesPeriod));
1514
1515 /* Was pre-buffering requested, but the acquired configuration from the backend told us something else? */
1516 if (CfgReq.Backend.cFramesPreBuffering)
1517 {
1518 if (pCfgAcq->Backend.cFramesPreBuffering != CfgReq.Backend.cFramesPreBuffering)
1519 LogRel2(("Audio: Backend changed pre-buffering size from %RU64ms (%RU32 frames) to %RU64ms (%RU32 frames)\n",
1520 PDMAudioPropsFramesToMilli(&CfgReq.Props, CfgReq.Backend.cFramesPreBuffering), CfgReq.Backend.cFramesPreBuffering,
1521 PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesPreBuffering), pCfgAcq->Backend.cFramesPreBuffering));
1522
1523 if (pCfgAcq->Backend.cFramesPreBuffering > pCfgAcq->Backend.cFramesBufferSize)
1524 {
1525 pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesBufferSize;
1526 LogRel2(("Audio: Pre-buffering size bigger than buffer size for stream '%s', adjusting to %RU64ms (%RU32 frames)\n", pCfgAcq->szName,
1527 PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesPreBuffering), pCfgAcq->Backend.cFramesPreBuffering));
1528 }
1529 }
1530 else if (CfgReq.Backend.cFramesPreBuffering == 0) /* Was the pre-buffering requested as being disabeld? Tell the users. */
1531 {
1532 LogRel2(("Audio: Pre-buffering is disabled for stream '%s'\n", pCfgAcq->szName));
1533 pCfgAcq->Backend.cFramesPreBuffering = 0;
1534 }
1535
1536 /* Sanity for detecting buggy backends. */
1537 AssertMsgReturn(pCfgAcq->Backend.cFramesPeriod < pCfgAcq->Backend.cFramesBufferSize,
1538 ("Acquired period size must be smaller than buffer size\n"),
1539 VERR_INVALID_PARAMETER);
1540 AssertMsgReturn(pCfgAcq->Backend.cFramesPreBuffering <= pCfgAcq->Backend.cFramesBufferSize,
1541 ("Acquired pre-buffering size must be smaller or as big as the buffer size\n"),
1542 VERR_INVALID_PARAMETER);
1543
1544 /*
1545 * Check if the backend did return sane values and correct if necessary.
1546 * Should never happen with our own backends, but you never know ...
1547 */
1548 uint32_t const cFramesPreBufferingMax = pCfgAcq->Backend.cFramesBufferSize - RT_MIN(16, pCfgAcq->Backend.cFramesBufferSize);
1549 if (pCfgAcq->Backend.cFramesPreBuffering > cFramesPreBufferingMax)
1550 {
1551 LogRel2(("Audio: Warning: Pre-buffering size of %RU32 frames for stream '%s' is too close to or larger than the %RU32 frames buffer size, reducing it to %RU32 frames!\n",
1552 pCfgAcq->Backend.cFramesPreBuffering, pCfgAcq->szName, pCfgAcq->Backend.cFramesBufferSize, cFramesPreBufferingMax));
1553 AssertFailed();
1554 pCfgAcq->Backend.cFramesPreBuffering = cFramesPreBufferingMax;
1555 }
1556
1557 if (pCfgAcq->Backend.cFramesPeriod > pCfgAcq->Backend.cFramesBufferSize)
1558 {
1559 LogRel2(("Audio: Warning: Period size of %RU32 frames for stream '%s' is larger than the %RU32 frames buffer size, reducing it to %RU32 frames!\n",
1560 pCfgAcq->Backend.cFramesPeriod, pCfgAcq->szName, pCfgAcq->Backend.cFramesBufferSize, pCfgAcq->Backend.cFramesBufferSize / 2));
1561 AssertFailed();
1562 pCfgAcq->Backend.cFramesPeriod = pCfgAcq->Backend.cFramesBufferSize / 2;
1563 }
1564
1565 LogRel2(("Audio: Buffer size for stream '%s' is %RU64 ms / %RU32 frames\n", pCfgAcq->szName,
1566 PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesBufferSize), pCfgAcq->Backend.cFramesBufferSize));
1567 LogRel2(("Audio: Pre-buffering size for stream '%s' is %RU64 ms / %RU32 frames\n", pCfgAcq->szName,
1568 PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesPreBuffering), pCfgAcq->Backend.cFramesPreBuffering));
1569 LogRel2(("Audio: Scheduling hint for stream '%s' is %RU32ms / %RU32 frames\n", pCfgAcq->szName,
1570 pCfgAcq->Device.cMsSchedulingHint, PDMAudioPropsMilliToFrames(&pCfgAcq->Props, pCfgAcq->Device.cMsSchedulingHint)));
1571
1572 /* Make sure the configured buffer size by the backend at least can hold the configured latency. */
1573 uint32_t const cMsPeriod = PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesPeriod);
1574 LogRel2(("Audio: Period size of stream '%s' is %RU64 ms / %RU32 frames\n",
1575 pCfgAcq->szName, cMsPeriod, pCfgAcq->Backend.cFramesPeriod));
1576 /** @todo r=bird: This is probably a misleading/harmless warning as we'd just
1577 * have to transfer more each time we move data. The period is generally
1578 * pure irrelevant fiction anyway. A more relevant comparison would
1579 * be to half the buffer size, i.e. making sure we get scheduled often
1580 * enough to keep the buffer at least half full (probably more
1581 * sensible if the buffer size was more than 2x scheduling periods). */
1582 if ( CfgReq.Device.cMsSchedulingHint /* Any scheduling hint set? */
1583 && CfgReq.Device.cMsSchedulingHint > cMsPeriod) /* This might lead to buffer underflows. */
1584 LogRel(("Audio: Warning: Scheduling hint of stream '%s' is bigger (%RU64ms) than used period size (%RU64ms)\n",
1585 pCfgAcq->szName, CfgReq.Device.cMsSchedulingHint, cMsPeriod));
1586
1587 /*
1588 * Done, just log the result:
1589 */
1590 LogFunc(("[%s] Acquired format: %s\n", pCfgAcq->szName,
1591 PDMAudioStrmCfgToString(&pStreamEx->Core.Cfg, szTmp, sizeof(szTmp)) ));
1592 LogRel2(("Audio: Acquired format: %s\n", PDMAudioStrmCfgToString(&pStreamEx->Core.Cfg, szTmp, sizeof(szTmp)) ));
1593
1594 return VINF_SUCCESS;
1595}
1596
1597
1598/**
1599 * Worker for drvAudioStreamCreate that initializes the audio stream.
1600 *
1601 * @returns VBox status code.
1602 * @param pThis Pointer to driver instance.
1603 * @param pStreamEx Stream to initialize. Caller already set a few fields.
1604 * The Core.Cfg field contains the requested configuration
1605 * when we're called and the actual configuration when
1606 * successfully returning.
1607 */
1608static int drvAudioStreamInitInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx)
1609{
1610 /*
1611 * Init host stream.
1612 */
1613 pStreamEx->Core.uMagic = PDMAUDIOSTREAM_MAGIC;
1614
1615 char szTmp[PDMAUDIOSTRMCFGTOSTRING_MAX];
1616 LogFunc(("[%s] Requested host format: %s\n", pStreamEx->Core.Cfg.szName,
1617 PDMAudioStrmCfgToString(&pStreamEx->Core.Cfg, szTmp, sizeof(szTmp)) ));
1618 LogRel2(("Audio: Creating stream: %s\n", PDMAudioStrmCfgToString(&pStreamEx->Core.Cfg, szTmp, sizeof(szTmp))));
1619
1620 int rc = drvAudioStreamCreateInternalBackend(pThis, pStreamEx);
1621 if (RT_FAILURE(rc))
1622 return rc;
1623
1624 /*
1625 * Configure host buffers.
1626 */
1627 Assert(pStreamEx->cbPreBufThreshold == 0);
1628 if (pStreamEx->Core.Cfg.Backend.cFramesPreBuffering != 0)
1629 pStreamEx->cbPreBufThreshold = PDMAudioPropsFramesToBytes(&pStreamEx->Core.Cfg.Props,
1630 pStreamEx->Core.Cfg.Backend.cFramesPreBuffering);
1631
1632 /* Allocate space for pre-buffering of output stream w/o mixing buffers. */
1633 if (pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_OUT)
1634 {
1635 Assert(pStreamEx->Out.cbPreBufAlloc == 0);
1636 Assert(pStreamEx->Out.cbPreBuffered == 0);
1637 Assert(pStreamEx->Out.offPreBuf == 0);
1638 if (pStreamEx->Core.Cfg.Backend.cFramesPreBuffering != 0)
1639 {
1640 pStreamEx->Out.cbPreBufAlloc = PDMAudioPropsFramesToBytes(&pStreamEx->Core.Cfg.Props,
1641 pStreamEx->Core.Cfg.Backend.cFramesBufferSize - 2);
1642 pStreamEx->Out.cbPreBufAlloc = RT_MIN(RT_ALIGN_32(pStreamEx->cbPreBufThreshold + _8K, _4K),
1643 pStreamEx->Out.cbPreBufAlloc);
1644 pStreamEx->Out.pbPreBuf = (uint8_t *)RTMemAllocZ(pStreamEx->Out.cbPreBufAlloc);
1645 AssertReturn(pStreamEx->Out.pbPreBuf, VERR_NO_MEMORY);
1646 }
1647 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_NOPLAY; /* Changed upon enable. */
1648 }
1649
1650 /*
1651 * Register statistics.
1652 */
1653 PPDMDRVINS const pDrvIns = pThis->pDrvIns;
1654 /** @todo expose config and more. */
1655 PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Core.Cfg.Backend.cFramesBufferSize, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
1656 "Host side: The size of the backend buffer (in frames)", "%s/0-HostBackendBufSize", pStreamEx->Core.Cfg.szName);
1657 if (pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_IN)
1658 {
1659 /** @todo later? */
1660 }
1661 else
1662 {
1663 PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.cbBackendWritableBefore, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
1664 "Host side: Free space in backend buffer before play", "%s/0-HostBackendBufFreeBefore", pStreamEx->Core.Cfg.szName);
1665 PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.cbBackendWritableAfter, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
1666 "Host side: Free space in backend buffer after play", "%s/0-HostBackendBufFreeAfter", pStreamEx->Core.Cfg.szName);
1667 }
1668
1669#ifdef VBOX_WITH_STATISTICS
1670 if (pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_IN)
1671 {
1672 PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->In.Stats.TotalFramesCaptured, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
1673 "Total frames played.", "%s/TotalFramesCaptured", pStreamEx->Core.Cfg.szName);
1674 PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->In.Stats.TotalTimesCaptured, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
1675 "Total number of playbacks.", "%s/TotalTimesCaptured", pStreamEx->Core.Cfg.szName);
1676 PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->In.Stats.TotalTimesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
1677 "Total number of reads.", "%s/TotalTimesRead", pStreamEx->Core.Cfg.szName);
1678 }
1679 else
1680 {
1681 Assert(pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_OUT);
1682 }
1683#endif /* VBOX_WITH_STATISTICS */
1684
1685 LogFlowFunc(("[%s] Returning %Rrc\n", pStreamEx->Core.Cfg.szName, rc));
1686 return rc;
1687}
1688
1689
1690/**
1691 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCreate}
1692 */
1693static DECLCALLBACK(int) drvAudioStreamCreate(PPDMIAUDIOCONNECTOR pInterface, uint32_t fFlags, PCPDMAUDIOSTREAMCFG pCfgReq,
1694 PPDMAUDIOSTREAM *ppStream)
1695{
1696 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
1697 AssertPtr(pThis);
1698
1699 /*
1700 * Assert sanity.
1701 */
1702 AssertReturn(!(fFlags & ~PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF), VERR_INVALID_FLAGS);
1703 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
1704 AssertPtrReturn(ppStream, VERR_INVALID_POINTER);
1705 *ppStream = NULL;
1706 LogFlowFunc(("pCfgReq=%s\n", pCfgReq->szName));
1707#ifdef LOG_ENABLED
1708 PDMAudioStrmCfgLog(pCfgReq);
1709#endif
1710 AssertReturn(AudioHlpStreamCfgIsValid(pCfgReq), VERR_INVALID_PARAMETER);
1711 AssertReturn(pCfgReq->enmDir == PDMAUDIODIR_IN || pCfgReq->enmDir == PDMAUDIODIR_OUT, VERR_NOT_SUPPORTED);
1712
1713 /*
1714 * Grab a free stream count now.
1715 */
1716 int rc = RTCritSectRwEnterExcl(&pThis->CritSectGlobals);
1717 AssertRCReturn(rc, rc);
1718
1719 uint32_t * const pcFreeStreams = pCfgReq->enmDir == PDMAUDIODIR_IN ? &pThis->In.cStreamsFree : &pThis->Out.cStreamsFree;
1720 if (*pcFreeStreams > 0)
1721 *pcFreeStreams -= 1;
1722 else
1723 {
1724 RTCritSectRwLeaveExcl(&pThis->CritSectGlobals);
1725 LogFlowFunc(("Maximum number of host %s streams reached\n", PDMAudioDirGetName(pCfgReq->enmDir) ));
1726 return pCfgReq->enmDir == PDMAUDIODIR_IN ? VERR_AUDIO_NO_FREE_INPUT_STREAMS : VERR_AUDIO_NO_FREE_OUTPUT_STREAMS;
1727 }
1728
1729 RTCritSectRwLeaveExcl(&pThis->CritSectGlobals);
1730
1731 /*
1732 * Get and check the backend size.
1733 *
1734 * Since we'll have to leave the hot-plug lock before we call the backend,
1735 * we'll have revalidate the size at that time.
1736 */
1737 RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
1738
1739 size_t const cbHstStrm = pThis->BackendCfg.cbStream;
1740 AssertStmt(cbHstStrm >= sizeof(PDMAUDIOBACKENDSTREAM), rc = VERR_OUT_OF_RANGE);
1741 AssertStmt(cbHstStrm < _16M, rc = VERR_OUT_OF_RANGE);
1742
1743 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
1744 if (RT_SUCCESS(rc))
1745 {
1746 /*
1747 * Allocate and initialize common state.
1748 */
1749 PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)RTMemAllocZ(sizeof(DRVAUDIOSTREAM) + RT_ALIGN_Z(cbHstStrm, 64));
1750 if (pStreamEx)
1751 {
1752 rc = RTCritSectInit(&pStreamEx->Core.CritSect); /* (drvAudioStreamFree assumes it's initailized) */
1753 if (RT_SUCCESS(rc))
1754 {
1755 PPDMAUDIOBACKENDSTREAM pBackend = (PPDMAUDIOBACKENDSTREAM)(pStreamEx + 1);
1756 pBackend->uMagic = PDMAUDIOBACKENDSTREAM_MAGIC;
1757 pBackend->pStream = &pStreamEx->Core;
1758
1759 pStreamEx->pBackend = pBackend;
1760 pStreamEx->Core.Cfg = *pCfgReq;
1761 pStreamEx->Core.cbBackend = (uint32_t)cbHstStrm;
1762 pStreamEx->fDestroyImmediate = true;
1763 pStreamEx->hReqInitAsync = NIL_RTREQ;
1764 pStreamEx->uMagic = DRVAUDIOSTREAM_MAGIC;
1765
1766 /* Make a unqiue stream name including the host (backend) driver name. */
1767 AssertPtr(pThis->pHostDrvAudio);
1768 size_t cchName = RTStrPrintf(pStreamEx->Core.Cfg.szName, RT_ELEMENTS(pStreamEx->Core.Cfg.szName), "[%s] %s:0",
1769 pThis->BackendCfg.szName, pCfgReq->szName[0] != '\0' ? pCfgReq->szName : "<NoName>");
1770 if (cchName < sizeof(pStreamEx->Core.Cfg.szName))
1771 {
1772 RTCritSectRwEnterShared(&pThis->CritSectGlobals);
1773 for (uint32_t i = 0; i < 256; i++)
1774 {
1775 bool fDone = true;
1776 PDRVAUDIOSTREAM pIt;
1777 RTListForEach(&pThis->LstStreams, pIt, DRVAUDIOSTREAM, ListEntry)
1778 {
1779 if (strcmp(pIt->Core.Cfg.szName, pStreamEx->Core.Cfg.szName) == 0)
1780 {
1781 RTStrPrintf(pStreamEx->Core.Cfg.szName, RT_ELEMENTS(pStreamEx->Core.Cfg.szName), "[%s] %s:%u",
1782 pThis->BackendCfg.szName, pCfgReq->szName[0] != '\0' ? pCfgReq->szName : "<NoName>",
1783 i);
1784 fDone = false;
1785 break;
1786 }
1787 }
1788 if (fDone)
1789 break;
1790 }
1791 RTCritSectRwLeaveShared(&pThis->CritSectGlobals);
1792 }
1793
1794 /*
1795 * Try to init the rest.
1796 */
1797 rc = drvAudioStreamInitInternal(pThis, pStreamEx);
1798 if (RT_SUCCESS(rc))
1799 {
1800 /* Set initial reference counts. */
1801 pStreamEx->cRefs = pStreamEx->fNeedAsyncInit ? 2 : 1;
1802
1803 /* Add it to the list. */
1804 RTCritSectRwEnterExcl(&pThis->CritSectGlobals);
1805
1806 RTListAppend(&pThis->LstStreams, &pStreamEx->ListEntry);
1807 pThis->cStreams++;
1808 STAM_COUNTER_INC(&pThis->Stats.TotalStreamsCreated);
1809
1810 RTCritSectRwLeaveExcl(&pThis->CritSectGlobals);
1811
1812 /*
1813 * Init debug stuff if enabled (ignore failures).
1814 */
1815 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
1816 {
1817 if (pThis->CfgIn.Dbg.fEnabled)
1818 AudioHlpFileCreateAndOpen(&pStreamEx->In.Dbg.pFileCapture, pThis->CfgIn.Dbg.szPathOut,
1819 "DrvAudioCapture", pThis->pDrvIns->iInstance, &pStreamEx->Core.Cfg.Props);
1820 }
1821 else /* Out */
1822 {
1823 if (pThis->CfgOut.Dbg.fEnabled)
1824 AudioHlpFileCreateAndOpen(&pStreamEx->Out.Dbg.pFilePlay, pThis->CfgOut.Dbg.szPathOut,
1825 "DrvAudioPlay", pThis->pDrvIns->iInstance, &pStreamEx->Core.Cfg.Props);
1826 }
1827
1828 /*
1829 * Kick off the asynchronous init.
1830 */
1831 if (!pStreamEx->fNeedAsyncInit)
1832 {
1833 pStreamEx->fStatus |= PDMAUDIOSTREAM_STS_BACKEND_READY;
1834 PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus);
1835 }
1836 else
1837 {
1838 int rc2 = RTReqPoolCallEx(pThis->hReqPool, 0 /*cMillies*/, &pStreamEx->hReqInitAsync,
1839 RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
1840 (PFNRT)drvAudioStreamInitAsync, 2, pThis, pStreamEx);
1841 LogFlowFunc(("hReqInitAsync=%p rc2=%Rrc\n", pStreamEx->hReqInitAsync, rc2));
1842 AssertRCStmt(rc2, drvAudioStreamInitAsync(pThis, pStreamEx));
1843 }
1844
1845#ifdef VBOX_STRICT
1846 /*
1847 * Assert lock order to make sure the lock validator picks up on it.
1848 */
1849 RTCritSectRwEnterShared(&pThis->CritSectGlobals);
1850 RTCritSectEnter(&pStreamEx->Core.CritSect);
1851 RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
1852 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
1853 RTCritSectLeave(&pStreamEx->Core.CritSect);
1854 RTCritSectRwLeaveShared(&pThis->CritSectGlobals);
1855#endif
1856
1857 *ppStream = &pStreamEx->Core;
1858 LogFlowFunc(("returns VINF_SUCCESS (pStreamEx=%p)\n", pStreamEx));
1859 return VINF_SUCCESS;
1860 }
1861
1862 LogFunc(("drvAudioStreamInitInternal failed: %Rrc\n", rc));
1863 int rc2 = drvAudioStreamUninitInternal(pThis, pStreamEx);
1864 AssertRC(rc2);
1865 drvAudioStreamFree(pStreamEx);
1866 }
1867 else
1868 RTMemFree(pStreamEx);
1869 }
1870 else
1871 rc = VERR_NO_MEMORY;
1872 }
1873
1874 /*
1875 * Give back the stream count, we couldn't use it after all.
1876 */
1877 RTCritSectRwEnterExcl(&pThis->CritSectGlobals);
1878 *pcFreeStreams += 1;
1879 RTCritSectRwLeaveExcl(&pThis->CritSectGlobals);
1880
1881 LogFlowFuncLeaveRC(rc);
1882 return rc;
1883}
1884
1885
1886/**
1887 * Calls the backend to give it the chance to destroy its part of the audio stream.
1888 *
1889 * Called from drvAudioPowerOff, drvAudioStreamUninitInternal and
1890 * drvAudioStreamReInitInternal.
1891 *
1892 * @returns VBox status code.
1893 * @param pThis Pointer to driver instance.
1894 * @param pStreamEx Audio stream destruct backend for.
1895 */
1896static int drvAudioStreamDestroyInternalBackend(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx)
1897{
1898 AssertPtr(pThis);
1899 AssertPtr(pStreamEx);
1900
1901 int rc = VINF_SUCCESS;
1902
1903#ifdef LOG_ENABLED
1904 char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
1905#endif
1906 LogFunc(("[%s] fStatus=%s\n", pStreamEx->Core.Cfg.szName, drvAudioStreamStatusToStr(szStreamSts, pStreamEx->fStatus)));
1907
1908 if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_CREATED)
1909 {
1910 AssertPtr(pStreamEx->pBackend);
1911
1912 /* Check if the pointer to the host audio driver is still valid.
1913 * It can be NULL if we were called in drvAudioDestruct, for example. */
1914 RTCritSectRwEnterShared(&pThis->CritSectHotPlug); /** @todo needed? */
1915 if (pThis->pHostDrvAudio)
1916 rc = pThis->pHostDrvAudio->pfnStreamDestroy(pThis->pHostDrvAudio, pStreamEx->pBackend, pStreamEx->fDestroyImmediate);
1917 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
1918
1919 pStreamEx->fStatus &= ~(PDMAUDIOSTREAM_STS_BACKEND_CREATED | PDMAUDIOSTREAM_STS_BACKEND_READY);
1920 PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus);
1921 }
1922
1923 LogFlowFunc(("[%s] Returning %Rrc\n", pStreamEx->Core.Cfg.szName, rc));
1924 return rc;
1925}
1926
1927
1928/**
1929 * Uninitializes an audio stream - worker for drvAudioStreamDestroy,
1930 * drvAudioDestruct and drvAudioStreamCreate.
1931 *
1932 * @returns VBox status code.
1933 * @param pThis Pointer to driver instance.
1934 * @param pStreamEx Pointer to audio stream to uninitialize.
1935 */
1936static int drvAudioStreamUninitInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx)
1937{
1938 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1939 AssertMsgReturn(pStreamEx->cRefs <= 1,
1940 ("Stream '%s' still has %RU32 references held when uninitializing\n", pStreamEx->Core.Cfg.szName, pStreamEx->cRefs),
1941 VERR_WRONG_ORDER);
1942 LogFlowFunc(("[%s] cRefs=%RU32\n", pStreamEx->Core.Cfg.szName, pStreamEx->cRefs));
1943
1944 RTCritSectEnter(&pStreamEx->Core.CritSect);
1945
1946 /*
1947 * ...
1948 */
1949 if (pStreamEx->fDestroyImmediate)
1950 drvAudioStreamControlInternal(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
1951 int rc = drvAudioStreamDestroyInternalBackend(pThis, pStreamEx);
1952
1953 /* Free pre-buffer space. */
1954 if ( pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_OUT
1955 && pStreamEx->Out.pbPreBuf)
1956 {
1957 RTMemFree(pStreamEx->Out.pbPreBuf);
1958 pStreamEx->Out.pbPreBuf = NULL;
1959 pStreamEx->Out.cbPreBufAlloc = 0;
1960 pStreamEx->Out.cbPreBuffered = 0;
1961 pStreamEx->Out.offPreBuf = 0;
1962 }
1963
1964 if (RT_SUCCESS(rc))
1965 {
1966#ifdef LOG_ENABLED
1967 if (pStreamEx->fStatus != PDMAUDIOSTREAM_STS_NONE)
1968 {
1969 char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
1970 LogFunc(("[%s] Warning: Still has %s set when uninitializing\n",
1971 pStreamEx->Core.Cfg.szName, drvAudioStreamStatusToStr(szStreamSts, pStreamEx->fStatus)));
1972 }
1973#endif
1974 pStreamEx->fStatus = PDMAUDIOSTREAM_STS_NONE;
1975 }
1976
1977 PPDMDRVINS const pDrvIns = pThis->pDrvIns;
1978 PDMDrvHlpSTAMDeregisterByPrefix(pDrvIns, pStreamEx->Core.Cfg.szName);
1979
1980 if (pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_IN)
1981 {
1982 if (pThis->CfgIn.Dbg.fEnabled)
1983 {
1984 AudioHlpFileDestroy(pStreamEx->In.Dbg.pFileCapture);
1985 pStreamEx->In.Dbg.pFileCapture = NULL;
1986 }
1987 }
1988 else
1989 {
1990 Assert(pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_OUT);
1991 if (pThis->CfgOut.Dbg.fEnabled)
1992 {
1993 AudioHlpFileDestroy(pStreamEx->Out.Dbg.pFilePlay);
1994 pStreamEx->Out.Dbg.pFilePlay = NULL;
1995 }
1996 }
1997
1998 RTCritSectLeave(&pStreamEx->Core.CritSect);
1999 LogFlowFunc(("Returning %Rrc\n", rc));
2000 return rc;
2001}
2002
2003
2004/**
2005 * Internal release function.
2006 *
2007 * @returns New reference count, UINT32_MAX if bad stream.
2008 * @param pThis Pointer to the DrvAudio instance data.
2009 * @param pStreamEx The stream to reference.
2010 * @param fMayDestroy Whether the caller is allowed to implicitly destroy
2011 * the stream or not.
2012 */
2013static uint32_t drvAudioStreamReleaseInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, bool fMayDestroy)
2014{
2015 AssertPtrReturn(pStreamEx, UINT32_MAX);
2016 AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, UINT32_MAX);
2017 AssertReturn(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC, UINT32_MAX);
2018 Assert(!RTCritSectIsOwner(&pStreamEx->Core.CritSect));
2019
2020 uint32_t cRefs = ASMAtomicDecU32(&pStreamEx->cRefs);
2021 if (cRefs != 0)
2022 Assert(cRefs < _1K);
2023 else if (fMayDestroy)
2024 {
2025/** @todo r=bird: Caching one stream in each direction for some time,
2026 * depending on the time it took to create it. drvAudioStreamCreate can use it
2027 * if the configuration matches, otherwise it'll throw it away. This will
2028 * provide a general speedup independ of device (HDA used to do this, but
2029 * doesn't) and backend implementation. Ofc, the backend probably needs an
2030 * opt-out here. */
2031 int rc = drvAudioStreamUninitInternal(pThis, pStreamEx);
2032 if (RT_SUCCESS(rc))
2033 {
2034 RTCritSectRwEnterExcl(&pThis->CritSectGlobals);
2035
2036 if (pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_IN)
2037 pThis->In.cStreamsFree++;
2038 else /* Out */
2039 pThis->Out.cStreamsFree++;
2040 pThis->cStreams--;
2041
2042 RTListNodeRemove(&pStreamEx->ListEntry);
2043
2044 RTCritSectRwLeaveExcl(&pThis->CritSectGlobals);
2045
2046 drvAudioStreamFree(pStreamEx);
2047 }
2048 else
2049 {
2050 LogRel(("Audio: Uninitializing stream '%s' failed with %Rrc\n", pStreamEx->Core.Cfg.szName, rc));
2051 /** @todo r=bird: What's the plan now? */
2052 }
2053 }
2054 else
2055 {
2056 cRefs = ASMAtomicIncU32(&pStreamEx->cRefs);
2057 AssertFailed();
2058 }
2059
2060 Log12Func(("returns %u (%s)\n", cRefs, cRefs > 0 ? pStreamEx->Core.Cfg.szName : "destroyed"));
2061 return cRefs;
2062}
2063
2064
2065/**
2066 * Asynchronous worker for drvAudioStreamDestroy.
2067 *
2068 * Does DISABLE and releases reference, possibly destroying the stream.
2069 *
2070 * @param pThis Pointer to the DrvAudio instance data.
2071 * @param pStreamEx The stream. One reference for us to release.
2072 * @param fImmediate How to treat draining streams.
2073 */
2074static DECLCALLBACK(void) drvAudioStreamDestroyAsync(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, bool fImmediate)
2075{
2076 LogFlowFunc(("pThis=%p pStreamEx=%p (%s) fImmediate=%RTbool\n", pThis, pStreamEx, pStreamEx->Core.Cfg.szName, fImmediate));
2077#ifdef LOG_ENABLED
2078 uint64_t const nsStart = RTTimeNanoTS();
2079#endif
2080 RTCritSectEnter(&pStreamEx->Core.CritSect);
2081
2082 pStreamEx->fDestroyImmediate = fImmediate; /* Do NOT adjust for draining status, just pass it as-is. CoreAudio needs this. */
2083
2084 if (!fImmediate && (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE))
2085 LogFlowFunc(("No DISABLE\n"));
2086 else
2087 {
2088 int rc2 = drvAudioStreamControlInternal(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
2089 LogFlowFunc(("DISABLE done: %Rrc\n", rc2));
2090 AssertRC(rc2);
2091 }
2092
2093 RTCritSectLeave(&pStreamEx->Core.CritSect);
2094
2095 drvAudioStreamReleaseInternal(pThis, pStreamEx, true /*fMayDestroy*/);
2096
2097 LogFlowFunc(("returning (after %'RU64 ns)\n", RTTimeNanoTS() - nsStart));
2098}
2099
2100
2101/**
2102 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamDestroy}
2103 */
2104static DECLCALLBACK(int) drvAudioStreamDestroy(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, bool fImmediate)
2105{
2106 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
2107 AssertPtr(pThis);
2108
2109 /* Ignore NULL streams. */
2110 if (!pStream)
2111 return VINF_SUCCESS;
2112
2113 PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream; /* Note! Do not touch pStream after this! */
2114 AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
2115 LogFlowFunc(("ENTER - %p (%s) fImmediate=%RTbool\n", pStreamEx, pStreamEx->Core.Cfg.szName, fImmediate));
2116 AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
2117 AssertReturn(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
2118 AssertReturn(pStreamEx->pBackend && pStreamEx->pBackend->uMagic == PDMAUDIOBACKENDSTREAM_MAGIC, VERR_INVALID_MAGIC);
2119
2120 /*
2121 * The main difference from a regular release is that this will disable
2122 * (or drain if we could) the stream and we can cancel any pending
2123 * pfnStreamInitAsync call.
2124 */
2125 int rc = RTCritSectEnter(&pStreamEx->Core.CritSect);
2126 AssertRCReturn(rc, rc);
2127
2128 if (pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC)
2129 {
2130 if (pStreamEx->cRefs > 0 && pStreamEx->cRefs < UINT32_MAX / 4)
2131 {
2132 char szStatus[DRVAUDIO_STATUS_STR_MAX];
2133 LogRel2(("Audio: Destroying stream '%s': cRefs=%u; status: %s; backend: %s; hReqInitAsync=%p\n",
2134 pStreamEx->Core.Cfg.szName, pStreamEx->cRefs, drvAudioStreamStatusToStr(szStatus, pStreamEx->fStatus),
2135 PDMHostAudioStreamStateGetName(drvAudioStreamGetBackendState(pThis, pStreamEx)),
2136 pStreamEx->hReqInitAsync));
2137
2138 /* Try cancel pending async init request and release the it. */
2139 if (pStreamEx->hReqInitAsync != NIL_RTREQ)
2140 {
2141 Assert(pStreamEx->cRefs >= 2);
2142 int rc2 = RTReqCancel(pStreamEx->hReqInitAsync);
2143
2144 RTReqRelease(pStreamEx->hReqInitAsync);
2145 pStreamEx->hReqInitAsync = NIL_RTREQ;
2146
2147 RTCritSectLeave(&pStreamEx->Core.CritSect); /* (exit before releasing the stream to avoid assertion) */
2148
2149 if (RT_SUCCESS(rc2))
2150 {
2151 LogFlowFunc(("Successfully cancelled pending pfnStreamInitAsync call (hReqInitAsync=%p).\n",
2152 pStreamEx->hReqInitAsync));
2153 drvAudioStreamReleaseInternal(pThis, pStreamEx, true /*fMayDestroy*/);
2154 }
2155 else
2156 {
2157 LogFlowFunc(("Failed to cancel pending pfnStreamInitAsync call (hReqInitAsync=%p): %Rrc\n",
2158 pStreamEx->hReqInitAsync, rc2));
2159 Assert(rc2 == VERR_RT_REQUEST_STATE);
2160 }
2161 }
2162 else
2163 RTCritSectLeave(&pStreamEx->Core.CritSect);
2164
2165 /*
2166 * Now, if the backend requests asynchronous disabling and destruction
2167 * push the disabling and destroying over to a worker thread.
2168 *
2169 * This is a general offloading feature that all backends should make use of,
2170 * however it's rather precarious on macs where stopping an already draining
2171 * stream may take 8-10ms which naturally isn't something we should be doing
2172 * on an EMT.
2173 */
2174 if (!(pThis->BackendCfg.fFlags & PDMAUDIOBACKEND_F_ASYNC_STREAM_DESTROY))
2175 drvAudioStreamDestroyAsync(pThis, pStreamEx, fImmediate);
2176 else
2177 {
2178 int rc2 = RTReqPoolCallEx(pThis->hReqPool, 0 /*cMillies*/, NULL /*phReq*/,
2179 RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
2180 (PFNRT)drvAudioStreamDestroyAsync, 3, pThis, pStreamEx, fImmediate);
2181 LogFlowFunc(("hReqInitAsync=%p rc2=%Rrc\n", pStreamEx->hReqInitAsync, rc2));
2182 AssertRCStmt(rc2, drvAudioStreamDestroyAsync(pThis, pStreamEx, fImmediate));
2183 }
2184 }
2185 else
2186 {
2187 AssertLogRelMsgFailedStmt(("%p cRefs=%#x\n", pStreamEx, pStreamEx->cRefs), rc = VERR_CALLER_NO_REFERENCE);
2188 RTCritSectLeave(&pStreamEx->Core.CritSect); /*??*/
2189 }
2190 }
2191 else
2192 {
2193 AssertLogRelMsgFailedStmt(("%p uMagic=%#x\n", pStreamEx, pStreamEx->uMagic), rc = VERR_INVALID_MAGIC);
2194 RTCritSectLeave(&pStreamEx->Core.CritSect); /*??*/
2195 }
2196
2197 LogFlowFuncLeaveRC(rc);
2198 return rc;
2199}
2200
2201
2202/**
2203 * Drops all audio data (and associated state) of a stream.
2204 *
2205 * Used by drvAudioStreamIterateInternal(), drvAudioStreamResetOnDisable(), and
2206 * drvAudioStreamReInitInternal().
2207 *
2208 * @param pStreamEx Stream to drop data for.
2209 */
2210static void drvAudioStreamResetInternal(PDRVAUDIOSTREAM pStreamEx)
2211{
2212 LogFunc(("[%s]\n", pStreamEx->Core.Cfg.szName));
2213 Assert(RTCritSectIsOwner(&pStreamEx->Core.CritSect));
2214
2215 pStreamEx->nsLastIterated = 0;
2216 pStreamEx->nsLastPlayedCaptured = 0;
2217 pStreamEx->nsLastReadWritten = 0;
2218 if (pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_OUT)
2219 {
2220 pStreamEx->Out.cbPreBuffered = 0;
2221 pStreamEx->Out.offPreBuf = 0;
2222 pStreamEx->Out.enmPlayState = pStreamEx->cbPreBufThreshold > 0
2223 ? DRVAUDIOPLAYSTATE_PREBUF : DRVAUDIOPLAYSTATE_PLAY;
2224 }
2225 else
2226 pStreamEx->In.enmCaptureState = pStreamEx->cbPreBufThreshold > 0
2227 ? DRVAUDIOCAPTURESTATE_PREBUF : DRVAUDIOCAPTURESTATE_CAPTURING;
2228}
2229
2230
2231/**
2232 * Re-initializes an audio stream with its existing host and guest stream
2233 * configuration.
2234 *
2235 * This might be the case if the backend told us we need to re-initialize
2236 * because something on the host side has changed.
2237 *
2238 * @note Does not touch the stream's status flags.
2239 *
2240 * @returns VBox status code.
2241 * @param pThis Pointer to driver instance.
2242 * @param pStreamEx Stream to re-initialize.
2243 */
2244static int drvAudioStreamReInitInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx)
2245{
2246 char szTmp[RT_MAX(PDMAUDIOSTRMCFGTOSTRING_MAX, DRVAUDIO_STATUS_STR_MAX)];
2247 LogFlowFunc(("[%s] status: %s\n", pStreamEx->Core.Cfg.szName, drvAudioStreamStatusToStr(szTmp, pStreamEx->fStatus) ));
2248 Assert(RTCritSectIsOwner(&pStreamEx->Core.CritSect));
2249 RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
2250
2251 /*
2252 * Destroy and re-create stream on backend side.
2253 */
2254 if ( (pStreamEx->fStatus & (PDMAUDIOSTREAM_STS_ENABLED | PDMAUDIOSTREAM_STS_BACKEND_CREATED | PDMAUDIOSTREAM_STS_BACKEND_READY))
2255 == (PDMAUDIOSTREAM_STS_ENABLED | PDMAUDIOSTREAM_STS_BACKEND_CREATED | PDMAUDIOSTREAM_STS_BACKEND_READY))
2256 drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
2257
2258 if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_CREATED)
2259 drvAudioStreamDestroyInternalBackend(pThis, pStreamEx);
2260
2261 int rc = VERR_AUDIO_STREAM_NOT_READY;
2262 if (!(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_CREATED))
2263 {
2264 drvAudioStreamResetInternal(pStreamEx);
2265
2266 RT_BZERO(pStreamEx->pBackend + 1, pStreamEx->Core.cbBackend - sizeof(*pStreamEx->pBackend));
2267
2268 rc = drvAudioStreamCreateInternalBackend(pThis, pStreamEx);
2269 if (RT_SUCCESS(rc))
2270 {
2271 LogFunc(("[%s] Acquired host format: %s\n",
2272 pStreamEx->Core.Cfg.szName, PDMAudioStrmCfgToString(&pStreamEx->Core.Cfg, szTmp, sizeof(szTmp)) ));
2273 /** @todo Validate (re-)acquired configuration with pStreamEx->Core.Core.Cfg?
2274 * drvAudioStreamInitInternal() does some setup and a bunch of
2275 * validations + adjustments of the stream config, so this surely is quite
2276 * optimistic. */
2277 if (true)
2278 {
2279 /*
2280 * Kick off the asynchronous init.
2281 */
2282 if (!pStreamEx->fNeedAsyncInit)
2283 {
2284 pStreamEx->fStatus |= PDMAUDIOSTREAM_STS_BACKEND_READY;
2285 PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus);
2286 }
2287 else
2288 {
2289 drvAudioStreamRetainInternal(pStreamEx);
2290 int rc2 = RTReqPoolCallEx(pThis->hReqPool, 0 /*cMillies*/, &pStreamEx->hReqInitAsync,
2291 RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
2292 (PFNRT)drvAudioStreamInitAsync, 2, pThis, pStreamEx);
2293 LogFlowFunc(("hReqInitAsync=%p rc2=%Rrc\n", pStreamEx->hReqInitAsync, rc2));
2294 AssertRCStmt(rc2, drvAudioStreamInitAsync(pThis, pStreamEx));
2295 }
2296
2297 /*
2298 * Update the backend on the stream state if it's ready, otherwise
2299 * let the worker thread do it after the async init has completed.
2300 */
2301 if ( (pStreamEx->fStatus & (PDMAUDIOSTREAM_STS_BACKEND_READY | PDMAUDIOSTREAM_STS_BACKEND_CREATED))
2302 == (PDMAUDIOSTREAM_STS_BACKEND_READY | PDMAUDIOSTREAM_STS_BACKEND_CREATED))
2303 {
2304 rc = drvAudioStreamUpdateBackendOnStatus(pThis, pStreamEx, "re-initializing");
2305 /** @todo not sure if we really need to care about this status code... */
2306 }
2307 else if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_CREATED)
2308 {
2309 Assert(pStreamEx->hReqInitAsync != NIL_RTREQ);
2310 LogFunc(("Asynchronous stream init (%p) ...\n", pStreamEx->hReqInitAsync));
2311 }
2312 else
2313 {
2314 LogRel(("Audio: Re-initializing stream '%s' somehow failed, status: %s\n", pStreamEx->Core.Cfg.szName,
2315 drvAudioStreamStatusToStr(szTmp, pStreamEx->fStatus) ));
2316 AssertFailed();
2317 rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
2318 }
2319 }
2320 }
2321 else
2322 LogRel(("Audio: Re-initializing stream '%s' failed with %Rrc\n", pStreamEx->Core.Cfg.szName, rc));
2323 }
2324 else
2325 {
2326 LogRel(("Audio: Re-initializing stream '%s' failed to destroy previous backend.\n", pStreamEx->Core.Cfg.szName));
2327 AssertFailed();
2328 }
2329
2330 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
2331 LogFunc(("[%s] Returning %Rrc\n", pStreamEx->Core.Cfg.szName, rc));
2332 return rc;
2333}
2334
2335
2336/**
2337 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamReInit}
2338 */
2339static DECLCALLBACK(int) drvAudioStreamReInit(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2340{
2341 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
2342 PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
2343 AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
2344 AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
2345 AssertReturn(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
2346 AssertReturn(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_NEED_REINIT, VERR_INVALID_STATE);
2347 LogFlowFunc(("\n"));
2348
2349 int rc = RTCritSectEnter(&pStreamEx->Core.CritSect);
2350 AssertRCReturn(rc, rc);
2351
2352 if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_NEED_REINIT)
2353 {
2354 const unsigned cMaxTries = 5;
2355 const uint64_t nsNow = RTTimeNanoTS();
2356
2357 /* Throttle re-initializing streams on failure. */
2358 if ( pStreamEx->cTriesReInit < cMaxTries
2359 && pStreamEx->hReqInitAsync == NIL_RTREQ
2360 && ( pStreamEx->nsLastReInit == 0
2361 || nsNow - pStreamEx->nsLastReInit >= RT_NS_1SEC * pStreamEx->cTriesReInit))
2362 {
2363 rc = drvAudioStreamReInitInternal(pThis, pStreamEx);
2364 if (RT_SUCCESS(rc))
2365 {
2366 /* Remove the pending re-init flag on success. */
2367 pStreamEx->fStatus &= ~PDMAUDIOSTREAM_STS_NEED_REINIT;
2368 PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus);
2369 }
2370 else
2371 {
2372 pStreamEx->nsLastReInit = nsNow;
2373 pStreamEx->cTriesReInit++;
2374
2375 /* Did we exceed our tries re-initializing the stream?
2376 * Then this one is dead-in-the-water, so disable it for further use. */
2377 if (pStreamEx->cTriesReInit >= cMaxTries)
2378 {
2379 LogRel(("Audio: Re-initializing stream '%s' exceeded maximum retries (%u), leaving as disabled\n",
2380 pStreamEx->Core.Cfg.szName, cMaxTries));
2381
2382 /* Don't try to re-initialize anymore and mark as disabled. */
2383 /** @todo should mark it as not-initialized too, shouldn't we? */
2384 pStreamEx->fStatus &= ~(PDMAUDIOSTREAM_STS_NEED_REINIT | PDMAUDIOSTREAM_STS_ENABLED);
2385 PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus);
2386
2387 /* Note: Further writes to this stream go to / will be read from the bit bucket (/dev/null) from now on. */
2388 }
2389 }
2390 }
2391 else
2392 Log8Func(("cTriesReInit=%d hReqInitAsync=%p nsLast=%RU64 nsNow=%RU64 nsDelta=%RU64\n", pStreamEx->cTriesReInit,
2393 pStreamEx->hReqInitAsync, pStreamEx->nsLastReInit, nsNow, nsNow - pStreamEx->nsLastReInit));
2394
2395#ifdef LOG_ENABLED
2396 char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
2397#endif
2398 Log3Func(("[%s] fStatus=%s\n", pStreamEx->Core.Cfg.szName, drvAudioStreamStatusToStr(szStreamSts, pStreamEx->fStatus)));
2399 }
2400 else
2401 {
2402 AssertFailed();
2403 rc = VERR_INVALID_STATE;
2404 }
2405
2406 RTCritSectLeave(&pStreamEx->Core.CritSect);
2407
2408 LogFlowFuncLeaveRC(rc);
2409 return rc;
2410}
2411
2412
2413/**
2414 * Internal retain function.
2415 *
2416 * @returns New reference count, UINT32_MAX if bad stream.
2417 * @param pStreamEx The stream to reference.
2418 */
2419static uint32_t drvAudioStreamRetainInternal(PDRVAUDIOSTREAM pStreamEx)
2420{
2421 AssertPtrReturn(pStreamEx, UINT32_MAX);
2422 AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, UINT32_MAX);
2423 AssertReturn(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC, UINT32_MAX);
2424
2425 uint32_t const cRefs = ASMAtomicIncU32(&pStreamEx->cRefs);
2426 Assert(cRefs > 1);
2427 Assert(cRefs < _1K);
2428
2429 Log12Func(("returns %u (%s)\n", cRefs, pStreamEx->Core.Cfg.szName));
2430 return cRefs;
2431}
2432
2433
2434/**
2435 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRetain}
2436 */
2437static DECLCALLBACK(uint32_t) drvAudioStreamRetain(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2438{
2439 RT_NOREF(pInterface);
2440 return drvAudioStreamRetainInternal((PDRVAUDIOSTREAM)pStream);
2441}
2442
2443
2444/**
2445 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRelease}
2446 */
2447static DECLCALLBACK(uint32_t) drvAudioStreamRelease(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2448{
2449 return drvAudioStreamReleaseInternal(RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector),
2450 (PDRVAUDIOSTREAM)pStream,
2451 false /*fMayDestroy*/);
2452}
2453
2454
2455/**
2456 * Controls a stream's backend.
2457 *
2458 * @returns VBox status code.
2459 * @param pThis Pointer to driver instance.
2460 * @param pStreamEx Stream to control.
2461 * @param enmStreamCmd Control command.
2462 *
2463 * @note Caller has entered the critical section of the stream.
2464 * @note Can be called w/o having entered DRVAUDIO::CritSectHotPlug.
2465 */
2466static int drvAudioStreamControlInternalBackend(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, PDMAUDIOSTREAMCMD enmStreamCmd)
2467{
2468 AssertPtr(pThis);
2469 AssertPtr(pStreamEx);
2470 Assert(RTCritSectIsOwner(&pStreamEx->Core.CritSect));
2471
2472 int rc = RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
2473 AssertRCReturn(rc, rc);
2474
2475 /*
2476 * Whether to propagate commands down to the backend.
2477 *
2478 * 1. If the stream direction is disabled on the driver level, we should
2479 * obviously not call the backend. Our stream status will reflect the
2480 * actual state so drvAudioEnable() can tell the backend if the user
2481 * re-enables the stream direction.
2482 *
2483 * 2. If the backend hasn't finished initializing yet, don't try call
2484 * it to start/stop/pause/whatever the stream. (Better to do it here
2485 * than to replicate this in the relevant backends.) When the backend
2486 * finish initializing the stream, we'll update it about the stream state.
2487 */
2488 bool const fDirEnabled = pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_IN
2489 ? pThis->In.fEnabled : pThis->Out.fEnabled;
2490 PDMHOSTAUDIOSTREAMSTATE const enmBackendState = drvAudioStreamGetBackendState(pThis, pStreamEx);
2491 /* ^^^ (checks pThis->pHostDrvAudio != NULL too) */
2492
2493 char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
2494 LogRel2(("Audio: %s stream '%s' backend (%s is %s; status: %s; backend-status: %s)\n",
2495 PDMAudioStrmCmdGetName(enmStreamCmd), pStreamEx->Core.Cfg.szName, PDMAudioDirGetName(pStreamEx->Core.Cfg.enmDir),
2496 fDirEnabled ? "enabled" : "disabled", drvAudioStreamStatusToStr(szStreamSts, pStreamEx->fStatus),
2497 PDMHostAudioStreamStateGetName(enmBackendState) ));
2498
2499 if (fDirEnabled)
2500 {
2501 if ( (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY /* don't really need this check, do we? */)
2502 && ( enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY
2503 || enmBackendState == PDMHOSTAUDIOSTREAMSTATE_DRAINING) )
2504 {
2505 switch (enmStreamCmd)
2506 {
2507 case PDMAUDIOSTREAMCMD_ENABLE:
2508 rc = pThis->pHostDrvAudio->pfnStreamEnable(pThis->pHostDrvAudio, pStreamEx->pBackend);
2509 break;
2510
2511 case PDMAUDIOSTREAMCMD_DISABLE:
2512 rc = pThis->pHostDrvAudio->pfnStreamDisable(pThis->pHostDrvAudio, pStreamEx->pBackend);
2513 break;
2514
2515 case PDMAUDIOSTREAMCMD_PAUSE:
2516 rc = pThis->pHostDrvAudio->pfnStreamPause(pThis->pHostDrvAudio, pStreamEx->pBackend);
2517 break;
2518
2519 case PDMAUDIOSTREAMCMD_RESUME:
2520 rc = pThis->pHostDrvAudio->pfnStreamResume(pThis->pHostDrvAudio, pStreamEx->pBackend);
2521 break;
2522
2523 case PDMAUDIOSTREAMCMD_DRAIN:
2524 if (pThis->pHostDrvAudio->pfnStreamDrain)
2525 rc = pThis->pHostDrvAudio->pfnStreamDrain(pThis->pHostDrvAudio, pStreamEx->pBackend);
2526 else
2527 rc = VERR_NOT_SUPPORTED;
2528 break;
2529
2530 default:
2531 AssertMsgFailedBreakStmt(("Command %RU32 not implemented\n", enmStreamCmd), rc = VERR_INTERNAL_ERROR_2);
2532 }
2533 if (RT_SUCCESS(rc))
2534 Log2Func(("[%s] %s succeeded (%Rrc)\n", pStreamEx->Core.Cfg.szName, PDMAudioStrmCmdGetName(enmStreamCmd), rc));
2535 else
2536 {
2537 LogFunc(("[%s] %s failed with %Rrc\n", pStreamEx->Core.Cfg.szName, PDMAudioStrmCmdGetName(enmStreamCmd), rc));
2538 if ( rc != VERR_NOT_IMPLEMENTED
2539 && rc != VERR_NOT_SUPPORTED
2540 && rc != VERR_AUDIO_STREAM_NOT_READY)
2541 LogRel(("Audio: %s stream '%s' failed with %Rrc\n", PDMAudioStrmCmdGetName(enmStreamCmd), pStreamEx->Core.Cfg.szName, rc));
2542 }
2543 }
2544 else
2545 LogFlowFunc(("enmBackendStat(=%s) != OKAY || !(fStatus(=%#x) & BACKEND_READY)\n",
2546 PDMHostAudioStreamStateGetName(enmBackendState), pStreamEx->fStatus));
2547 }
2548 else
2549 LogFlowFunc(("fDirEnabled=false\n"));
2550
2551 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
2552 return rc;
2553}
2554
2555
2556/**
2557 * Resets the given audio stream.
2558 *
2559 * @param pStreamEx Stream to reset.
2560 */
2561static void drvAudioStreamResetOnDisable(PDRVAUDIOSTREAM pStreamEx)
2562{
2563 drvAudioStreamResetInternal(pStreamEx);
2564
2565 LogFunc(("[%s]\n", pStreamEx->Core.Cfg.szName));
2566
2567 pStreamEx->fStatus &= PDMAUDIOSTREAM_STS_BACKEND_CREATED | PDMAUDIOSTREAM_STS_BACKEND_READY;
2568 pStreamEx->Core.fWarningsShown = PDMAUDIOSTREAM_WARN_FLAGS_NONE;
2569
2570#ifdef VBOX_WITH_STATISTICS
2571 /*
2572 * Reset statistics.
2573 */
2574 if (pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_IN)
2575 {
2576 STAM_COUNTER_RESET(&pStreamEx->In.Stats.TotalFramesCaptured);
2577 STAM_COUNTER_RESET(&pStreamEx->In.Stats.TotalTimesCaptured);
2578 STAM_COUNTER_RESET(&pStreamEx->In.Stats.TotalTimesRead);
2579 }
2580 else if (pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_OUT)
2581 {
2582 }
2583 else
2584 AssertFailed();
2585#endif
2586}
2587
2588
2589/**
2590 * Controls an audio stream.
2591 *
2592 * @returns VBox status code.
2593 * @param pThis Pointer to driver instance.
2594 * @param pStreamEx Stream to control.
2595 * @param enmStreamCmd Control command.
2596 */
2597static int drvAudioStreamControlInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, PDMAUDIOSTREAMCMD enmStreamCmd)
2598{
2599 AssertPtr(pThis);
2600 AssertPtr(pStreamEx);
2601 Assert(RTCritSectIsOwner(&pStreamEx->Core.CritSect));
2602
2603#ifdef LOG_ENABLED
2604 char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
2605#endif
2606 LogFunc(("[%s] enmStreamCmd=%s fStatus=%s\n", pStreamEx->Core.Cfg.szName, PDMAudioStrmCmdGetName(enmStreamCmd),
2607 drvAudioStreamStatusToStr(szStreamSts, pStreamEx->fStatus)));
2608
2609 int rc = VINF_SUCCESS;
2610
2611 switch (enmStreamCmd)
2612 {
2613 case PDMAUDIOSTREAMCMD_ENABLE:
2614 if (!(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_ENABLED))
2615 {
2616 /* Are we still draining this stream? Then we must disable it first. */
2617 if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE)
2618 {
2619 LogFunc(("Stream '%s' is still draining - disabling...\n", pStreamEx->Core.Cfg.szName));
2620 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
2621 AssertRC(rc);
2622 if (drvAudioStreamGetBackendState(pThis, pStreamEx) != PDMHOSTAUDIOSTREAMSTATE_DRAINING)
2623 {
2624 pStreamEx->fStatus &= ~(PDMAUDIOSTREAM_STS_ENABLED | PDMAUDIOSTREAM_STS_PENDING_DISABLE);
2625 drvAudioStreamResetInternal(pStreamEx);
2626 rc = VINF_SUCCESS;
2627 }
2628 }
2629
2630 if (RT_SUCCESS(rc))
2631 {
2632 /* Reset the state before we try to start. */
2633 PDMHOSTAUDIOSTREAMSTATE const enmBackendState = drvAudioStreamGetBackendState(pThis, pStreamEx);
2634 pStreamEx->enmLastBackendState = enmBackendState;
2635 pStreamEx->offInternal = 0;
2636
2637 if (pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_OUT)
2638 {
2639 pStreamEx->Out.cbPreBuffered = 0;
2640 pStreamEx->Out.offPreBuf = 0;
2641 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_NOPLAY;
2642 switch (enmBackendState)
2643 {
2644 case PDMHOSTAUDIOSTREAMSTATE_INITIALIZING:
2645 if (pStreamEx->cbPreBufThreshold > 0)
2646 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PREBUF;
2647 break;
2648 case PDMHOSTAUDIOSTREAMSTATE_DRAINING:
2649 AssertFailed();
2650 RT_FALL_THROUGH();
2651 case PDMHOSTAUDIOSTREAMSTATE_OKAY:
2652 pStreamEx->Out.enmPlayState = pStreamEx->cbPreBufThreshold > 0
2653 ? DRVAUDIOPLAYSTATE_PREBUF : DRVAUDIOPLAYSTATE_PLAY;
2654 break;
2655 case PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING:
2656 case PDMHOSTAUDIOSTREAMSTATE_INACTIVE:
2657 break;
2658 /* no default */
2659 case PDMHOSTAUDIOSTREAMSTATE_INVALID:
2660 case PDMHOSTAUDIOSTREAMSTATE_END:
2661 case PDMHOSTAUDIOSTREAMSTATE_32BIT_HACK:
2662 break;
2663 }
2664 LogFunc(("ENABLE: enmBackendState=%s enmPlayState=%s\n", PDMHostAudioStreamStateGetName(enmBackendState),
2665 drvAudioPlayStateName(pStreamEx->Out.enmPlayState)));
2666 }
2667 else
2668 {
2669 pStreamEx->In.enmCaptureState = DRVAUDIOCAPTURESTATE_NO_CAPTURE;
2670 switch (enmBackendState)
2671 {
2672 case PDMHOSTAUDIOSTREAMSTATE_INITIALIZING:
2673 pStreamEx->In.enmCaptureState = DRVAUDIOCAPTURESTATE_PREBUF;
2674 break;
2675 case PDMHOSTAUDIOSTREAMSTATE_DRAINING:
2676 AssertFailed();
2677 RT_FALL_THROUGH();
2678 case PDMHOSTAUDIOSTREAMSTATE_OKAY:
2679 pStreamEx->In.enmCaptureState = pStreamEx->cbPreBufThreshold > 0
2680 ? DRVAUDIOCAPTURESTATE_PREBUF : DRVAUDIOCAPTURESTATE_CAPTURING;
2681 break;
2682 case PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING:
2683 case PDMHOSTAUDIOSTREAMSTATE_INACTIVE:
2684 break;
2685 /* no default */
2686 case PDMHOSTAUDIOSTREAMSTATE_INVALID:
2687 case PDMHOSTAUDIOSTREAMSTATE_END:
2688 case PDMHOSTAUDIOSTREAMSTATE_32BIT_HACK:
2689 break;
2690 }
2691 LogFunc(("ENABLE: enmBackendState=%s enmCaptureState=%s\n", PDMHostAudioStreamStateGetName(enmBackendState),
2692 drvAudioCaptureStateName(pStreamEx->In.enmCaptureState)));
2693 }
2694
2695 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_ENABLE);
2696 if (RT_SUCCESS(rc))
2697 {
2698 pStreamEx->nsStarted = RTTimeNanoTS();
2699 pStreamEx->fStatus |= PDMAUDIOSTREAM_STS_ENABLED;
2700 PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus);
2701 }
2702 }
2703 }
2704 break;
2705
2706 case PDMAUDIOSTREAMCMD_DISABLE:
2707 if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_ENABLED)
2708 {
2709 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
2710 LogFunc(("DISABLE '%s': Backend DISABLE -> %Rrc\n", pStreamEx->Core.Cfg.szName, rc));
2711 if (RT_SUCCESS(rc)) /** @todo ignore this and reset it anyway? */
2712 drvAudioStreamResetOnDisable(pStreamEx);
2713 }
2714 break;
2715
2716 case PDMAUDIOSTREAMCMD_PAUSE:
2717 if ((pStreamEx->fStatus & (PDMAUDIOSTREAM_STS_ENABLED | PDMAUDIOSTREAM_STS_PAUSED)) == PDMAUDIOSTREAM_STS_ENABLED)
2718 {
2719 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_PAUSE);
2720 if (RT_SUCCESS(rc))
2721 {
2722 pStreamEx->fStatus |= PDMAUDIOSTREAM_STS_PAUSED;
2723 PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus);
2724 }
2725 }
2726 break;
2727
2728 case PDMAUDIOSTREAMCMD_RESUME:
2729 if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PAUSED)
2730 {
2731 Assert(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_ENABLED);
2732 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_RESUME);
2733 if (RT_SUCCESS(rc))
2734 {
2735 pStreamEx->fStatus &= ~PDMAUDIOSTREAM_STS_PAUSED;
2736 PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus);
2737 }
2738 }
2739 break;
2740
2741 case PDMAUDIOSTREAMCMD_DRAIN:
2742 /*
2743 * Only for output streams and we don't want this command more than once.
2744 */
2745 AssertReturn(pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_OUT, VERR_INVALID_FUNCTION);
2746 AssertBreak(!(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE));
2747 if (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_ENABLED)
2748 {
2749 rc = VERR_INTERNAL_ERROR_2;
2750 switch (pStreamEx->Out.enmPlayState)
2751 {
2752 case DRVAUDIOPLAYSTATE_PREBUF:
2753 if (pStreamEx->Out.cbPreBuffered > 0)
2754 {
2755 LogFunc(("DRAIN '%s': Initiating draining of pre-buffered data...\n", pStreamEx->Core.Cfg.szName));
2756 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PREBUF_COMMITTING;
2757 pStreamEx->fStatus |= PDMAUDIOSTREAM_STS_PENDING_DISABLE;
2758 PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus);
2759 rc = VINF_SUCCESS;
2760 break;
2761 }
2762 RT_FALL_THROUGH();
2763 case DRVAUDIOPLAYSTATE_NOPLAY:
2764 case DRVAUDIOPLAYSTATE_PREBUF_SWITCHING:
2765 case DRVAUDIOPLAYSTATE_PREBUF_OVERDUE:
2766 LogFunc(("DRAIN '%s': Nothing to drain (enmPlayState=%s)\n",
2767 pStreamEx->Core.Cfg.szName, drvAudioPlayStateName(pStreamEx->Out.enmPlayState)));
2768 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
2769 AssertRC(rc);
2770 drvAudioStreamResetOnDisable(pStreamEx);
2771 break;
2772
2773 case DRVAUDIOPLAYSTATE_PLAY:
2774 case DRVAUDIOPLAYSTATE_PLAY_PREBUF:
2775 LogFunc(("DRAIN '%s': Initiating backend draining (enmPlayState=%s -> NOPLAY) ...\n",
2776 pStreamEx->Core.Cfg.szName, drvAudioPlayStateName(pStreamEx->Out.enmPlayState)));
2777 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_NOPLAY;
2778 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DRAIN);
2779 if (RT_SUCCESS(rc))
2780 {
2781 pStreamEx->fStatus |= PDMAUDIOSTREAM_STS_PENDING_DISABLE;
2782 PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus);
2783 }
2784 else
2785 {
2786 LogFunc(("DRAIN '%s': Backend DRAIN failed with %Rrc, disabling the stream instead...\n",
2787 pStreamEx->Core.Cfg.szName, rc));
2788 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
2789 AssertRC(rc);
2790 drvAudioStreamResetOnDisable(pStreamEx);
2791 }
2792 break;
2793
2794 case DRVAUDIOPLAYSTATE_PREBUF_COMMITTING:
2795 LogFunc(("DRAIN '%s': Initiating draining of pre-buffered data (already committing)...\n",
2796 pStreamEx->Core.Cfg.szName));
2797 pStreamEx->fStatus |= PDMAUDIOSTREAM_STS_PENDING_DISABLE;
2798 PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus);
2799 rc = VINF_SUCCESS;
2800 break;
2801
2802 /* no default */
2803 case DRVAUDIOPLAYSTATE_INVALID:
2804 case DRVAUDIOPLAYSTATE_END:
2805 AssertFailedBreak();
2806 }
2807 }
2808 break;
2809
2810 default:
2811 rc = VERR_NOT_IMPLEMENTED;
2812 break;
2813 }
2814
2815 if (RT_FAILURE(rc))
2816 LogFunc(("[%s] Failed with %Rrc\n", pStreamEx->Core.Cfg.szName, rc));
2817
2818 return rc;
2819}
2820
2821
2822/**
2823 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamControl}
2824 */
2825static DECLCALLBACK(int) drvAudioStreamControl(PPDMIAUDIOCONNECTOR pInterface,
2826 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
2827{
2828 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
2829 AssertPtr(pThis);
2830
2831 /** @todo r=bird: why? It's not documented to ignore NULL streams. */
2832 if (!pStream)
2833 return VINF_SUCCESS;
2834 PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
2835 AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
2836 AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
2837 AssertReturn(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
2838
2839 int rc = RTCritSectEnter(&pStreamEx->Core.CritSect);
2840 AssertRCReturn(rc, rc);
2841
2842 LogFlowFunc(("[%s] enmStreamCmd=%s\n", pStreamEx->Core.Cfg.szName, PDMAudioStrmCmdGetName(enmStreamCmd)));
2843
2844 rc = drvAudioStreamControlInternal(pThis, pStreamEx, enmStreamCmd);
2845
2846 RTCritSectLeave(&pStreamEx->Core.CritSect);
2847 return rc;
2848}
2849
2850
2851/**
2852 * Copy data to the pre-buffer, ring-buffer style.
2853 *
2854 * The @a cbMax parameter is almost always set to the threshold size, the
2855 * exception is when commiting the buffer and we want to top it off to reduce
2856 * the number of transfers to the backend (the first transfer may start
2857 * playback, so more data is better).
2858 */
2859static int drvAudioStreamPreBuffer(PDRVAUDIOSTREAM pStreamEx, const uint8_t *pbBuf, uint32_t cbBuf, uint32_t cbMax)
2860{
2861 uint32_t const cbAlloc = pStreamEx->Out.cbPreBufAlloc;
2862 AssertReturn(cbAlloc >= cbMax, VERR_INTERNAL_ERROR_3);
2863 AssertReturn(cbAlloc >= 8, VERR_INTERNAL_ERROR_4);
2864 AssertReturn(cbMax >= 8, VERR_INTERNAL_ERROR_5);
2865
2866 uint32_t offRead = pStreamEx->Out.offPreBuf;
2867 uint32_t cbCur = pStreamEx->Out.cbPreBuffered;
2868 AssertStmt(offRead < cbAlloc, offRead %= cbAlloc);
2869 AssertStmt(cbCur <= cbMax, offRead = (offRead + cbCur - cbMax) % cbAlloc; cbCur = cbMax);
2870
2871 /*
2872 * First chunk.
2873 */
2874 uint32_t offWrite = (offRead + cbCur) % cbAlloc;
2875 uint32_t cbToCopy = RT_MIN(cbAlloc - offWrite, cbBuf);
2876 memcpy(&pStreamEx->Out.pbPreBuf[offWrite], pbBuf, cbToCopy);
2877
2878 /* Advance. */
2879 offWrite = (offWrite + cbToCopy) % cbAlloc;
2880 for (;;)
2881 {
2882 pbBuf += cbToCopy;
2883 cbCur += cbToCopy;
2884 if (cbCur > cbMax)
2885 offRead = (offRead + cbCur - cbMax) % cbAlloc;
2886 cbBuf -= cbToCopy;
2887 if (!cbBuf)
2888 break;
2889
2890 /*
2891 * Second+ chunk, from the start of the buffer.
2892 *
2893 * Note! It is assumed very unlikely that we will ever see a cbBuf larger than
2894 * cbMax, so we don't waste space on clipping cbBuf here (can happen with
2895 * custom pre-buffer sizes).
2896 */
2897 Assert(offWrite == 0);
2898 cbToCopy = RT_MIN(cbAlloc, cbBuf);
2899 memcpy(pStreamEx->Out.pbPreBuf, pbBuf, cbToCopy);
2900 }
2901
2902 /*
2903 * Update the pre-buffering size and position.
2904 */
2905 pStreamEx->Out.cbPreBuffered = RT_MIN(cbCur, cbMax);
2906 pStreamEx->Out.offPreBuf = offRead;
2907 return VINF_SUCCESS;
2908}
2909
2910
2911/**
2912 * Worker for drvAudioStreamPlay() and drvAudioStreamPreBufComitting().
2913 *
2914 * Caller owns the lock.
2915 */
2916static int drvAudioStreamPlayLocked(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx,
2917 const uint8_t *pbBuf, uint32_t cbBuf, uint32_t *pcbWritten)
2918{
2919 Log3Func(("%s: @%#RX64: cbBuf=%#x\n", pStreamEx->Core.Cfg.szName, pStreamEx->offInternal, cbBuf));
2920
2921 uint32_t cbWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pStreamEx->pBackend);
2922 pStreamEx->Out.Stats.cbBackendWritableBefore = cbWritable;
2923
2924 uint32_t cbWritten = 0;
2925 int rc = VINF_SUCCESS;
2926 uint8_t const cbFrame = PDMAudioPropsFrameSize(&pStreamEx->Core.Cfg.Props);
2927 while (cbBuf >= cbFrame && cbWritable >= cbFrame)
2928 {
2929 uint32_t const cbToWrite = PDMAudioPropsFloorBytesToFrame(&pStreamEx->Core.Cfg.Props, RT_MIN(cbBuf, cbWritable));
2930 uint32_t cbWrittenNow = 0;
2931 rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pStreamEx->pBackend, pbBuf, cbToWrite, &cbWrittenNow);
2932 if (RT_SUCCESS(rc))
2933 {
2934 if (cbWrittenNow != cbToWrite)
2935 Log3Func(("%s: @%#RX64: Wrote fewer bytes than requested: %#x, requested %#x\n",
2936 pStreamEx->Core.Cfg.szName, pStreamEx->offInternal, cbWrittenNow, cbToWrite));
2937#ifdef DEBUG_bird
2938 Assert(cbWrittenNow == cbToWrite);
2939#endif
2940 AssertStmt(cbWrittenNow <= cbToWrite, cbWrittenNow = cbToWrite);
2941 cbWritten += cbWrittenNow;
2942 cbBuf -= cbWrittenNow;
2943 pbBuf += cbWrittenNow;
2944 pStreamEx->offInternal += cbWrittenNow;
2945 }
2946 else
2947 {
2948 *pcbWritten = cbWritten;
2949 LogFunc(("%s: @%#RX64: pfnStreamPlay failed writing %#x bytes (%#x previous written, %#x writable): %Rrc\n",
2950 pStreamEx->Core.Cfg.szName, pStreamEx->offInternal, cbToWrite, cbWritten, cbWritable, rc));
2951 return cbWritten ? VINF_SUCCESS : rc;
2952 }
2953
2954 cbWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pStreamEx->pBackend);
2955 }
2956
2957 *pcbWritten = cbWritten;
2958 pStreamEx->Out.Stats.cbBackendWritableAfter = cbWritable;
2959 if (cbWritten)
2960 pStreamEx->nsLastPlayedCaptured = RTTimeNanoTS();
2961
2962 Log3Func(("%s: @%#RX64: Wrote %#x bytes (%#x bytes left)\n", pStreamEx->Core.Cfg.szName, pStreamEx->offInternal, cbWritten, cbBuf));
2963 return rc;
2964}
2965
2966
2967/**
2968 * Worker for drvAudioStreamPlay() and drvAudioStreamPreBufComitting().
2969 */
2970static int drvAudioStreamPlayToPreBuffer(PDRVAUDIOSTREAM pStreamEx, const void *pvBuf, uint32_t cbBuf, uint32_t cbMax,
2971 uint32_t *pcbWritten)
2972{
2973 int rc = drvAudioStreamPreBuffer(pStreamEx, (uint8_t const *)pvBuf, cbBuf, cbMax);
2974 if (RT_SUCCESS(rc))
2975 {
2976 *pcbWritten = cbBuf;
2977 pStreamEx->offInternal += cbBuf;
2978 Log3Func(("[%s] Pre-buffering (%s): wrote %#x bytes => %#x bytes / %u%%\n",
2979 pStreamEx->Core.Cfg.szName, drvAudioPlayStateName(pStreamEx->Out.enmPlayState), cbBuf, pStreamEx->Out.cbPreBuffered,
2980 pStreamEx->Out.cbPreBuffered * 100 / RT_MAX(pStreamEx->cbPreBufThreshold, 1)));
2981
2982 }
2983 else
2984 *pcbWritten = 0;
2985 return rc;
2986}
2987
2988
2989/**
2990 * Used when we're committing (transfering) the pre-buffered bytes to the
2991 * device.
2992 *
2993 * This is called both from drvAudioStreamPlay() and
2994 * drvAudioStreamIterateInternal().
2995 *
2996 * @returns VBox status code.
2997 * @param pThis Pointer to the DrvAudio instance data.
2998 * @param pStreamEx The stream to commit the pre-buffering for.
2999 * @param pbBuf Buffer with new bytes to write. Can be NULL when called
3000 * in the PENDING_DISABLE state from
3001 * drvAudioStreamIterateInternal().
3002 * @param cbBuf Number of new bytes. Can be zero.
3003 * @param pcbWritten Where to return the number of bytes written.
3004 *
3005 * @note Locking: Stream critsect and hot-plug in shared mode.
3006 */
3007static int drvAudioStreamPreBufComitting(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx,
3008 const uint8_t *pbBuf, uint32_t cbBuf, uint32_t *pcbWritten)
3009{
3010 /*
3011 * First, top up the buffer with new data from pbBuf.
3012 */
3013 *pcbWritten = 0;
3014 if (cbBuf > 0)
3015 {
3016 uint32_t const cbToCopy = RT_MIN(pStreamEx->Out.cbPreBufAlloc - pStreamEx->Out.cbPreBuffered, cbBuf);
3017 if (cbToCopy > 0)
3018 {
3019 int rc = drvAudioStreamPlayToPreBuffer(pStreamEx, pbBuf, cbBuf, pStreamEx->Out.cbPreBufAlloc, pcbWritten);
3020 AssertRCReturn(rc, rc);
3021 pbBuf += cbToCopy;
3022 cbBuf -= cbToCopy;
3023 }
3024 }
3025
3026 AssertReturn(pThis->pHostDrvAudio, VERR_AUDIO_BACKEND_NOT_ATTACHED);
3027
3028 /*
3029 * Write the pre-buffered chunk.
3030 */
3031 int rc = VINF_SUCCESS;
3032 uint32_t const cbAlloc = pStreamEx->Out.cbPreBufAlloc;
3033 AssertReturn(cbAlloc > 0, VERR_INTERNAL_ERROR_2);
3034 uint32_t off = pStreamEx->Out.offPreBuf;
3035 AssertStmt(off < pStreamEx->Out.cbPreBufAlloc, off %= cbAlloc);
3036 uint32_t cbLeft = pStreamEx->Out.cbPreBuffered;
3037 while (cbLeft > 0)
3038 {
3039 uint32_t const cbToWrite = RT_MIN(cbAlloc - off, cbLeft);
3040 Assert(cbToWrite > 0);
3041
3042 uint32_t cbPreBufWritten = 0;
3043 rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pStreamEx->pBackend, &pStreamEx->Out.pbPreBuf[off],
3044 cbToWrite, &cbPreBufWritten);
3045 AssertRCBreak(rc);
3046 if (!cbPreBufWritten)
3047 break;
3048 AssertStmt(cbPreBufWritten <= cbToWrite, cbPreBufWritten = cbToWrite);
3049 off = (off + cbPreBufWritten) % cbAlloc;
3050 cbLeft -= cbPreBufWritten;
3051 }
3052
3053 if (cbLeft == 0)
3054 {
3055 LogFunc(("@%#RX64: Wrote all %#x bytes of pre-buffered audio data. %s -> PLAY\n", pStreamEx->offInternal,
3056 pStreamEx->Out.cbPreBuffered, drvAudioPlayStateName(pStreamEx->Out.enmPlayState) ));
3057 pStreamEx->Out.cbPreBuffered = 0;
3058 pStreamEx->Out.offPreBuf = 0;
3059 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PLAY;
3060
3061 if (cbBuf > 0)
3062 {
3063 uint32_t cbWritten2 = 0;
3064 rc = drvAudioStreamPlayLocked(pThis, pStreamEx, pbBuf, cbBuf, &cbWritten2);
3065 if (RT_SUCCESS(rc))
3066 *pcbWritten += cbWritten2;
3067 }
3068 else
3069 pStreamEx->nsLastPlayedCaptured = RTTimeNanoTS();
3070 }
3071 else
3072 {
3073 if (cbLeft != pStreamEx->Out.cbPreBuffered)
3074 pStreamEx->nsLastPlayedCaptured = RTTimeNanoTS();
3075
3076 LogRel2(("Audio: @%#RX64: Stream '%s' pre-buffering commit problem: wrote %#x out of %#x + %#x - rc=%Rrc *pcbWritten=%#x %s -> PREBUF_COMMITTING\n",
3077 pStreamEx->offInternal, pStreamEx->Core.Cfg.szName, pStreamEx->Out.cbPreBuffered - cbLeft,
3078 pStreamEx->Out.cbPreBuffered, cbBuf, rc, *pcbWritten, drvAudioPlayStateName(pStreamEx->Out.enmPlayState) ));
3079 AssertMsg( pStreamEx->Out.enmPlayState == DRVAUDIOPLAYSTATE_PREBUF_COMMITTING
3080 || pStreamEx->Out.enmPlayState == DRVAUDIOPLAYSTATE_PREBUF
3081 || RT_FAILURE(rc),
3082 ("Buggy host driver buffer reporting? cbLeft=%#x cbPreBuffered=%#x enmPlayState=%s\n",
3083 cbLeft, pStreamEx->Out.cbPreBuffered, drvAudioPlayStateName(pStreamEx->Out.enmPlayState) ));
3084
3085 pStreamEx->Out.cbPreBuffered = cbLeft;
3086 pStreamEx->Out.offPreBuf = off;
3087 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PREBUF_COMMITTING;
3088 }
3089
3090 return *pcbWritten ? VINF_SUCCESS : rc;
3091}
3092
3093
3094/**
3095 * Does one iteration of an audio stream.
3096 *
3097 * This function gives the backend the chance of iterating / altering data and
3098 * does the actual mixing between the guest <-> host mixing buffers.
3099 *
3100 * @returns VBox status code.
3101 * @param pThis Pointer to driver instance.
3102 * @param pStreamEx Stream to iterate.
3103 */
3104static int drvAudioStreamIterateInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx)
3105{
3106 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
3107
3108#ifdef LOG_ENABLED
3109 char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
3110#endif
3111 Log3Func(("[%s] fStatus=%s\n", pStreamEx->Core.Cfg.szName, drvAudioStreamStatusToStr(szStreamSts, pStreamEx->fStatus)));
3112
3113 /* Not enabled or paused? Skip iteration. */
3114 if ((pStreamEx->fStatus & (PDMAUDIOSTREAM_STS_ENABLED | PDMAUDIOSTREAM_STS_PAUSED)) != PDMAUDIOSTREAM_STS_ENABLED)
3115 return VINF_SUCCESS;
3116
3117 /*
3118 * Pending disable is really what we're here for.
3119 *
3120 * This only happens to output streams. We ASSUME the caller (MixerBuffer)
3121 * implements a timeout on the draining, so we skip that here.
3122 */
3123 if (!(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_PENDING_DISABLE))
3124 { /* likely until we get to the end of the stream at least. */ }
3125 else
3126 {
3127 AssertReturn(pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_OUT, VINF_SUCCESS);
3128 RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
3129
3130 /*
3131 * Move pre-buffered samples to the backend.
3132 */
3133 if (pStreamEx->Out.enmPlayState == DRVAUDIOPLAYSTATE_PREBUF_COMMITTING)
3134 {
3135 if (pStreamEx->Out.cbPreBuffered > 0)
3136 {
3137 uint32_t cbIgnored = 0;
3138 drvAudioStreamPreBufComitting(pThis, pStreamEx, NULL, 0, &cbIgnored);
3139 Log3Func(("Stream '%s': Transferred %#x bytes\n", pStreamEx->Core.Cfg.szName, cbIgnored));
3140 }
3141 if (pStreamEx->Out.cbPreBuffered == 0)
3142 {
3143 Log3Func(("Stream '%s': No more pre-buffered data -> NOPLAY + backend DRAIN\n", pStreamEx->Core.Cfg.szName));
3144 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_NOPLAY;
3145
3146 int rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DRAIN);
3147 if (RT_FAILURE(rc))
3148 {
3149 LogFunc(("Stream '%s': Backend DRAIN failed with %Rrc, disabling the stream instead...\n",
3150 pStreamEx->Core.Cfg.szName, rc));
3151 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
3152 AssertRC(rc);
3153 drvAudioStreamResetOnDisable(pStreamEx);
3154 }
3155 }
3156 }
3157 else
3158 Assert(pStreamEx->Out.enmPlayState == DRVAUDIOPLAYSTATE_NOPLAY);
3159
3160 /*
3161 * Check the backend status to see if it's still draining and to
3162 * update our status when it stops doing so.
3163 */
3164 PDMHOSTAUDIOSTREAMSTATE const enmBackendState = drvAudioStreamGetBackendState(pThis, pStreamEx);
3165 if (enmBackendState == PDMHOSTAUDIOSTREAMSTATE_DRAINING)
3166 {
3167 uint32_t cbIgnored = 0;
3168 pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pStreamEx->pBackend, NULL, 0, &cbIgnored);
3169 }
3170 else
3171 {
3172 LogFunc(("Stream '%s': Backend finished draining.\n", pStreamEx->Core.Cfg.szName));
3173 drvAudioStreamResetOnDisable(pStreamEx);
3174 }
3175
3176 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
3177 }
3178
3179 /* Update timestamps. */
3180 pStreamEx->nsLastIterated = RTTimeNanoTS();
3181
3182 return VINF_SUCCESS; /** @todo r=bird: What can the caller do with an error status here? */
3183}
3184
3185
3186/**
3187 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamIterate}
3188 */
3189static DECLCALLBACK(int) drvAudioStreamIterate(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
3190{
3191 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
3192 AssertPtr(pThis);
3193 PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
3194 AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
3195 AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
3196 AssertReturn(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
3197
3198 int rc = RTCritSectEnter(&pStreamEx->Core.CritSect);
3199 AssertRCReturn(rc, rc);
3200
3201 rc = drvAudioStreamIterateInternal(pThis, pStreamEx);
3202
3203 RTCritSectLeave(&pStreamEx->Core.CritSect);
3204
3205 if (RT_FAILURE(rc))
3206 LogFlowFuncLeaveRC(rc);
3207 return rc;
3208}
3209
3210
3211/**
3212 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetState}
3213 */
3214static DECLCALLBACK(PDMAUDIOSTREAMSTATE) drvAudioStreamGetState(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
3215{
3216 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
3217 PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
3218 AssertPtrReturn(pStreamEx, PDMAUDIOSTREAMSTATE_INVALID);
3219 AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, PDMAUDIOSTREAMSTATE_INVALID);
3220 AssertReturn(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC, PDMAUDIOSTREAMSTATE_INVALID);
3221
3222 /*
3223 * Get the status mask.
3224 */
3225 int rc = RTCritSectEnter(&pStreamEx->Core.CritSect);
3226 AssertRCReturn(rc, PDMAUDIOSTREAMSTATE_INVALID);
3227 RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
3228
3229 PDMHOSTAUDIOSTREAMSTATE const enmBackendState = drvAudioStreamGetBackendStateAndProcessChanges(pThis, pStreamEx);
3230 uint32_t const fStrmStatus = pStreamEx->fStatus;
3231 PDMAUDIODIR const enmDir = pStreamEx->Core.Cfg.enmDir;
3232 Assert(enmDir == PDMAUDIODIR_IN || enmDir == PDMAUDIODIR_OUT);
3233
3234 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
3235 RTCritSectLeave(&pStreamEx->Core.CritSect);
3236
3237 /*
3238 * Translate it to state enum value.
3239 */
3240 PDMAUDIOSTREAMSTATE enmState;
3241 if (!(fStrmStatus & PDMAUDIOSTREAM_STS_NEED_REINIT))
3242 {
3243 if (fStrmStatus & PDMAUDIOSTREAM_STS_BACKEND_CREATED)
3244 {
3245 if ( (fStrmStatus & PDMAUDIOSTREAM_STS_ENABLED)
3246 && (enmDir == PDMAUDIODIR_IN ? pThis->In.fEnabled : pThis->Out.fEnabled)
3247 && ( enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY
3248 || enmBackendState == PDMHOSTAUDIOSTREAMSTATE_DRAINING
3249 || enmBackendState == PDMHOSTAUDIOSTREAMSTATE_INITIALIZING ))
3250 enmState = enmDir == PDMAUDIODIR_IN ? PDMAUDIOSTREAMSTATE_ENABLED_READABLE : PDMAUDIOSTREAMSTATE_ENABLED_WRITABLE;
3251 else
3252 enmState = PDMAUDIOSTREAMSTATE_INACTIVE;
3253 }
3254 else
3255 enmState = PDMAUDIOSTREAMSTATE_NOT_WORKING;
3256 }
3257 else
3258 enmState = PDMAUDIOSTREAMSTATE_NEED_REINIT;
3259
3260#ifdef LOG_ENABLED
3261 char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
3262#endif
3263 Log3Func(("[%s] returns %s (status: %s)\n", pStreamEx->Core.Cfg.szName, PDMAudioStreamStateGetName(enmState),
3264 drvAudioStreamStatusToStr(szStreamSts, fStrmStatus)));
3265 return enmState;
3266}
3267
3268
3269/**
3270 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetWritable}
3271 */
3272static DECLCALLBACK(uint32_t) drvAudioStreamGetWritable(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
3273{
3274 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
3275 AssertPtr(pThis);
3276 PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
3277 AssertPtrReturn(pStreamEx, 0);
3278 AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, 0);
3279 AssertReturn(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC, 0);
3280 AssertMsgReturn(pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_OUT, ("Can't write to a non-output stream\n"), 0);
3281
3282 int rc = RTCritSectEnter(&pStreamEx->Core.CritSect);
3283 AssertRCReturn(rc, 0);
3284 RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
3285
3286 /*
3287 * Use the playback and backend states to determin how much can be written, if anything.
3288 */
3289 uint32_t cbWritable = 0;
3290 DRVAUDIOPLAYSTATE const enmPlayMode = pStreamEx->Out.enmPlayState;
3291 PDMHOSTAUDIOSTREAMSTATE const enmBackendState = drvAudioStreamGetBackendState(pThis, pStreamEx);
3292 if ( PDMAudioStrmStatusCanWrite(pStreamEx->fStatus)
3293 && pThis->pHostDrvAudio != NULL
3294 && enmBackendState != PDMHOSTAUDIOSTREAMSTATE_DRAINING)
3295 {
3296 switch (enmPlayMode)
3297 {
3298 /*
3299 * Whatever the backend can hold.
3300 */
3301 case DRVAUDIOPLAYSTATE_PLAY:
3302 case DRVAUDIOPLAYSTATE_PLAY_PREBUF:
3303 Assert(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY);
3304 Assert(enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY /* potential unplug race */);
3305 cbWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pStreamEx->pBackend);
3306 break;
3307
3308 /*
3309 * Whatever we've got of available space in the pre-buffer.
3310 * Note! For the last round when we pass the pre-buffering threshold, we may
3311 * report fewer bytes than what a DMA timer period for the guest device
3312 * typically produces, however that should be transfered in the following
3313 * round that goes directly to the backend buffer.
3314 */
3315 case DRVAUDIOPLAYSTATE_PREBUF:
3316 cbWritable = pStreamEx->Out.cbPreBufAlloc - pStreamEx->Out.cbPreBuffered;
3317 if (!cbWritable)
3318 cbWritable = PDMAudioPropsFramesToBytes(&pStreamEx->Core.Cfg.Props, 2);
3319 break;
3320
3321 /*
3322 * These are slightly more problematic and can go wrong if the pre-buffer is
3323 * manually configured to be smaller than the output of a typeical DMA timer
3324 * period for the guest device. So, to overcompensate, we just report back
3325 * the backend buffer size (the pre-buffer is circular, so no overflow issue).
3326 */
3327 case DRVAUDIOPLAYSTATE_PREBUF_OVERDUE:
3328 case DRVAUDIOPLAYSTATE_PREBUF_SWITCHING:
3329 cbWritable = PDMAudioPropsFramesToBytes(&pStreamEx->Core.Cfg.Props,
3330 RT_MAX(pStreamEx->Core.Cfg.Backend.cFramesBufferSize,
3331 pStreamEx->Core.Cfg.Backend.cFramesPreBuffering));
3332 break;
3333
3334 case DRVAUDIOPLAYSTATE_PREBUF_COMMITTING:
3335 {
3336 /* Buggy backend: We weren't able to copy all the pre-buffered data to it
3337 when reaching the threshold. Try escape this situation, or at least
3338 keep the extra buffering to a minimum. We must try write something
3339 as long as there is space for it, as we need the pfnStreamWrite call
3340 to move the data. */
3341 Assert(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY);
3342 Assert(enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY /* potential unplug race */);
3343 uint32_t const cbMin = PDMAudioPropsFramesToBytes(&pStreamEx->Core.Cfg.Props, 8);
3344 cbWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pStreamEx->pBackend);
3345 if (cbWritable >= pStreamEx->Out.cbPreBuffered + cbMin)
3346 cbWritable -= pStreamEx->Out.cbPreBuffered + cbMin / 2;
3347 else
3348 cbWritable = RT_MIN(cbMin, pStreamEx->Out.cbPreBufAlloc - pStreamEx->Out.cbPreBuffered);
3349 AssertLogRel(cbWritable);
3350 break;
3351 }
3352
3353 case DRVAUDIOPLAYSTATE_NOPLAY:
3354 break;
3355 case DRVAUDIOPLAYSTATE_INVALID:
3356 case DRVAUDIOPLAYSTATE_END:
3357 AssertFailed();
3358 break;
3359 }
3360
3361 /* Make sure to align the writable size to the host's frame size. */
3362 cbWritable = PDMAudioPropsFloorBytesToFrame(&pStreamEx->Core.Cfg.Props, cbWritable);
3363 }
3364
3365 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
3366 RTCritSectLeave(&pStreamEx->Core.CritSect);
3367 Log3Func(("[%s] cbWritable=%#RX32 (%RU64ms) enmPlayMode=%s enmBackendState=%s\n",
3368 pStreamEx->Core.Cfg.szName, cbWritable, PDMAudioPropsBytesToMilli(&pStreamEx->Core.Cfg.Props, cbWritable),
3369 drvAudioPlayStateName(enmPlayMode), PDMHostAudioStreamStateGetName(enmBackendState) ));
3370 return cbWritable;
3371}
3372
3373
3374/**
3375 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamPlay}
3376 */
3377static DECLCALLBACK(int) drvAudioStreamPlay(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
3378 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
3379{
3380 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
3381 AssertPtr(pThis);
3382
3383 /*
3384 * Check input and sanity.
3385 */
3386 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
3387 PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
3388 AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
3389 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
3390 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
3391 uint32_t uTmp;
3392 if (pcbWritten)
3393 AssertPtrReturn(pcbWritten, VERR_INVALID_PARAMETER);
3394 else
3395 pcbWritten = &uTmp;
3396
3397 AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
3398 AssertReturn(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
3399 AssertMsgReturn(pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_OUT,
3400 ("Stream '%s' is not an output stream and therefore cannot be written to (direction is '%s')\n",
3401 pStreamEx->Core.Cfg.szName, PDMAudioDirGetName(pStreamEx->Core.Cfg.enmDir)), VERR_ACCESS_DENIED);
3402
3403 AssertMsg(PDMAudioPropsIsSizeAligned(&pStreamEx->Core.Cfg.Props, cbBuf),
3404 ("Stream '%s' got a non-frame-aligned write (%#RX32 bytes)\n", pStreamEx->Core.Cfg.szName, cbBuf));
3405
3406 int rc = RTCritSectEnter(&pStreamEx->Core.CritSect);
3407 AssertRCReturn(rc, rc);
3408
3409 /*
3410 * First check that we can write to the stream, and if not,
3411 * whether to just drop the input into the bit bucket.
3412 */
3413 if (PDMAudioStrmStatusIsReady(pStreamEx->fStatus))
3414 {
3415 RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
3416 if ( pThis->Out.fEnabled /* (see @bugref{9882}) */
3417 && pThis->pHostDrvAudio != NULL)
3418 {
3419 /*
3420 * Get the backend state and process changes to it since last time we checked.
3421 */
3422 PDMHOSTAUDIOSTREAMSTATE const enmBackendState = drvAudioStreamGetBackendStateAndProcessChanges(pThis, pStreamEx);
3423
3424 /*
3425 * Do the transfering.
3426 */
3427 switch (pStreamEx->Out.enmPlayState)
3428 {
3429 case DRVAUDIOPLAYSTATE_PLAY:
3430 Assert(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY);
3431 Assert(enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY);
3432 rc = drvAudioStreamPlayLocked(pThis, pStreamEx, (uint8_t const *)pvBuf, cbBuf, pcbWritten);
3433 break;
3434
3435 case DRVAUDIOPLAYSTATE_PLAY_PREBUF:
3436 Assert(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY);
3437 Assert(enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY);
3438 rc = drvAudioStreamPlayLocked(pThis, pStreamEx, (uint8_t const *)pvBuf, cbBuf, pcbWritten);
3439 drvAudioStreamPreBuffer(pStreamEx, (uint8_t const *)pvBuf, *pcbWritten, pStreamEx->cbPreBufThreshold);
3440 break;
3441
3442 case DRVAUDIOPLAYSTATE_PREBUF:
3443 if (cbBuf + pStreamEx->Out.cbPreBuffered < pStreamEx->cbPreBufThreshold)
3444 rc = drvAudioStreamPlayToPreBuffer(pStreamEx, pvBuf, cbBuf, pStreamEx->cbPreBufThreshold, pcbWritten);
3445 else if ( enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY
3446 && (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY))
3447 {
3448 Log3Func(("[%s] Pre-buffering completing: cbBuf=%#x cbPreBuffered=%#x => %#x vs cbPreBufThreshold=%#x\n",
3449 pStreamEx->Core.Cfg.szName, cbBuf, pStreamEx->Out.cbPreBuffered,
3450 cbBuf + pStreamEx->Out.cbPreBuffered, pStreamEx->cbPreBufThreshold));
3451 rc = drvAudioStreamPreBufComitting(pThis, pStreamEx, (uint8_t const *)pvBuf, cbBuf, pcbWritten);
3452 }
3453 else
3454 {
3455 Log3Func(("[%s] Pre-buffering completing but device not ready: cbBuf=%#x cbPreBuffered=%#x => %#x vs cbPreBufThreshold=%#x; PREBUF -> PREBUF_OVERDUE\n",
3456 pStreamEx->Core.Cfg.szName, cbBuf, pStreamEx->Out.cbPreBuffered,
3457 cbBuf + pStreamEx->Out.cbPreBuffered, pStreamEx->cbPreBufThreshold));
3458 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PREBUF_OVERDUE;
3459 rc = drvAudioStreamPlayToPreBuffer(pStreamEx, pvBuf, cbBuf, pStreamEx->cbPreBufThreshold, pcbWritten);
3460 }
3461 break;
3462
3463 case DRVAUDIOPLAYSTATE_PREBUF_OVERDUE:
3464 Assert( !(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY)
3465 || enmBackendState != PDMHOSTAUDIOSTREAMSTATE_OKAY);
3466 RT_FALL_THRU();
3467 case DRVAUDIOPLAYSTATE_PREBUF_SWITCHING:
3468 rc = drvAudioStreamPlayToPreBuffer(pStreamEx, pvBuf, cbBuf, pStreamEx->cbPreBufThreshold, pcbWritten);
3469 break;
3470
3471 case DRVAUDIOPLAYSTATE_PREBUF_COMMITTING:
3472 Assert(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY);
3473 Assert(enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY);
3474 rc = drvAudioStreamPreBufComitting(pThis, pStreamEx, (uint8_t const *)pvBuf, cbBuf, pcbWritten);
3475 break;
3476
3477 case DRVAUDIOPLAYSTATE_NOPLAY:
3478 *pcbWritten = cbBuf;
3479 pStreamEx->offInternal += cbBuf;
3480 Log3Func(("[%s] Discarding the data, backend state: %s\n", pStreamEx->Core.Cfg.szName,
3481 PDMHostAudioStreamStateGetName(enmBackendState) ));
3482 break;
3483
3484 default:
3485 *pcbWritten = cbBuf;
3486 AssertMsgFailedBreak(("%d; cbBuf=%#x\n", pStreamEx->Out.enmPlayState, cbBuf));
3487 }
3488
3489 if (!pThis->CfgOut.Dbg.fEnabled || RT_FAILURE(rc))
3490 { /* likely */ }
3491 else
3492 AudioHlpFileWrite(pStreamEx->Out.Dbg.pFilePlay, pvBuf, *pcbWritten, 0 /* fFlags */);
3493 }
3494 else
3495 {
3496 *pcbWritten = cbBuf;
3497 pStreamEx->offInternal += cbBuf;
3498 Log3Func(("[%s] Backend stream %s, discarding the data\n", pStreamEx->Core.Cfg.szName,
3499 !pThis->Out.fEnabled ? "disabled" : !pThis->pHostDrvAudio ? "not attached" : "not ready yet"));
3500 }
3501 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
3502 }
3503 else
3504 rc = VERR_AUDIO_STREAM_NOT_READY;
3505
3506 RTCritSectLeave(&pStreamEx->Core.CritSect);
3507 return rc;
3508}
3509
3510
3511/**
3512 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetReadable}
3513 */
3514static DECLCALLBACK(uint32_t) drvAudioStreamGetReadable(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
3515{
3516 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
3517 AssertPtr(pThis);
3518 PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
3519 AssertPtrReturn(pStreamEx, 0);
3520 AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, 0);
3521 AssertReturn(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC, 0);
3522 AssertMsg(pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_IN, ("Can't read from a non-input stream\n"));
3523
3524
3525 int rc = RTCritSectEnter(&pStreamEx->Core.CritSect);
3526 AssertRCReturn(rc, 0);
3527 RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
3528
3529 /*
3530 * Use the capture state to determin how much can be written, if anything.
3531 */
3532 uint32_t cbReadable = 0;
3533 DRVAUDIOCAPTURESTATE const enmCaptureState = pStreamEx->In.enmCaptureState;
3534 PDMHOSTAUDIOSTREAMSTATE const enmBackendState = drvAudioStreamGetBackendState(pThis, pStreamEx); RT_NOREF(enmBackendState);
3535 if ( PDMAudioStrmStatusCanRead(pStreamEx->fStatus)
3536 && pThis->pHostDrvAudio != NULL)
3537 {
3538 switch (enmCaptureState)
3539 {
3540 /*
3541 * Whatever the backend has to offer when in capture mode.
3542 */
3543 case DRVAUDIOCAPTURESTATE_CAPTURING:
3544 Assert(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY);
3545 Assert(enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY /* potential unplug race */);
3546 cbReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pStreamEx->pBackend);
3547 break;
3548
3549 /*
3550 * Same calculation as in drvAudioStreamCaptureSilence, only we cap it
3551 * at the pre-buffering threshold so we don't get into trouble when we
3552 * switch to capture mode between now and pfnStreamCapture.
3553 */
3554 case DRVAUDIOCAPTURESTATE_PREBUF:
3555 {
3556 uint64_t const cNsStream = RTTimeNanoTS() - pStreamEx->nsStarted;
3557 uint64_t const offCur = PDMAudioPropsNanoToBytes64(&pStreamEx->Core.Cfg.Props, cNsStream);
3558 if (offCur > pStreamEx->offInternal)
3559 {
3560 uint64_t const cbUnread = offCur - pStreamEx->offInternal;
3561 cbReadable = (uint32_t)RT_MIN(pStreamEx->cbPreBufThreshold, cbUnread);
3562 }
3563 break;
3564 }
3565
3566 case DRVAUDIOCAPTURESTATE_NO_CAPTURE:
3567 break;
3568
3569 case DRVAUDIOCAPTURESTATE_INVALID:
3570 case DRVAUDIOCAPTURESTATE_END:
3571 AssertFailed();
3572 break;
3573 }
3574
3575 /* Make sure to align the readable size to the host's frame size. */
3576 cbReadable = PDMAudioPropsFloorBytesToFrame(&pStreamEx->Core.Cfg.Props, cbReadable);
3577 }
3578
3579 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
3580 RTCritSectLeave(&pStreamEx->Core.CritSect);
3581 Log3Func(("[%s] cbReadable=%#RX32 (%RU64ms) enmCaptureMode=%s enmBackendState=%s\n",
3582 pStreamEx->Core.Cfg.szName, cbReadable, PDMAudioPropsBytesToMilli(&pStreamEx->Core.Cfg.Props, cbReadable),
3583 drvAudioCaptureStateName(enmCaptureState), PDMHostAudioStreamStateGetName(enmBackendState) ));
3584 return cbReadable;
3585}
3586
3587
3588/**
3589 * Worker for drvAudioStreamCapture that returns silence.
3590 *
3591 * The amount of silence returned is a function of how long the stream has been
3592 * enabled.
3593 *
3594 * @returns VINF_SUCCESS
3595 * @param pStreamEx The stream to commit the pre-buffering for.
3596 * @param pbBuf The output buffer.
3597 * @param cbBuf The size of the output buffer.
3598 * @param pcbRead Where to return the number of bytes actually read.
3599 */
3600static int drvAudioStreamCaptureSilence(PDRVAUDIOSTREAM pStreamEx, uint8_t *pbBuf, uint32_t cbBuf, uint32_t *pcbRead)
3601{
3602 /** @todo Does not take paused time into account... */
3603 uint64_t const cNsStream = RTTimeNanoTS() - pStreamEx->nsStarted;
3604 uint64_t const offCur = PDMAudioPropsNanoToBytes64(&pStreamEx->Core.Cfg.Props, cNsStream);
3605 if (offCur > pStreamEx->offInternal)
3606 {
3607 uint64_t const cbUnread = offCur - pStreamEx->offInternal;
3608 uint32_t const cbToClear = (uint32_t)RT_MIN(cbBuf, cbUnread);
3609 *pcbRead = cbToClear;
3610 pStreamEx->offInternal += cbToClear;
3611 cbBuf -= cbToClear;
3612 PDMAudioPropsClearBuffer(&pStreamEx->Core.Cfg.Props, pbBuf, cbToClear,
3613 PDMAudioPropsBytesToFrames(&pStreamEx->Core.Cfg.Props, cbToClear));
3614 }
3615 else
3616 *pcbRead = 0;
3617 Log4Func(("%s: @%#RX64: Read %#x bytes of silence (%#x bytes left)\n",
3618 pStreamEx->Core.Cfg.szName, pStreamEx->offInternal, *pcbRead, cbBuf));
3619 return VINF_SUCCESS;
3620}
3621
3622
3623/**
3624 * Worker for drvAudioStreamCapture.
3625 */
3626static int drvAudioStreamCaptureLocked(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx,
3627 uint8_t *pbBuf, uint32_t cbBuf, uint32_t *pcbRead)
3628{
3629 Log4Func(("%s: @%#RX64: cbBuf=%#x\n", pStreamEx->Core.Cfg.szName, pStreamEx->offInternal, cbBuf));
3630
3631 uint32_t cbReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pStreamEx->pBackend);
3632 pStreamEx->In.Stats.cbBackendReadableBefore = cbReadable;
3633
3634 uint32_t cbRead = 0;
3635 int rc = VINF_SUCCESS;
3636 uint8_t const cbFrame = PDMAudioPropsFrameSize(&pStreamEx->Core.Cfg.Props);
3637 while (cbBuf >= cbFrame && cbReadable >= cbFrame)
3638 {
3639 uint32_t const cbToRead = PDMAudioPropsFloorBytesToFrame(&pStreamEx->Core.Cfg.Props, RT_MIN(cbBuf, cbReadable));
3640 uint32_t cbReadNow = 0;
3641 rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pStreamEx->pBackend, pbBuf, cbToRead, &cbReadNow);
3642 if (RT_SUCCESS(rc))
3643 {
3644 if (cbReadNow != cbToRead)
3645 Log4Func(("%s: @%#RX64: Read fewer bytes than requested: %#x, requested %#x\n",
3646 pStreamEx->Core.Cfg.szName, pStreamEx->offInternal, cbReadNow, cbToRead));
3647#ifdef DEBUG_bird
3648 Assert(cbReadNow == cbToRead);
3649#endif
3650 AssertStmt(cbReadNow <= cbToRead, cbReadNow = cbToRead);
3651 cbRead += cbReadNow;
3652 cbBuf -= cbReadNow;
3653 pbBuf += cbReadNow;
3654 pStreamEx->offInternal += cbReadNow;
3655 }
3656 else
3657 {
3658 *pcbRead = cbRead;
3659 LogFunc(("%s: @%#RX64: pfnStreamCapture failed read %#x bytes (%#x previous read, %#x readable): %Rrc\n",
3660 pStreamEx->Core.Cfg.szName, pStreamEx->offInternal, cbToRead, cbRead, cbReadable, rc));
3661 return cbRead ? VINF_SUCCESS : rc;
3662 }
3663
3664 cbReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pStreamEx->pBackend);
3665 }
3666
3667 *pcbRead = cbRead;
3668 pStreamEx->In.Stats.cbBackendReadableAfter = cbReadable;
3669 if (cbRead)
3670 pStreamEx->nsLastPlayedCaptured = RTTimeNanoTS();
3671
3672 Log4Func(("%s: @%#RX64: Read %#x bytes (%#x bytes left)\n", pStreamEx->Core.Cfg.szName, pStreamEx->offInternal, cbRead, cbBuf));
3673 return rc;
3674}
3675
3676
3677/**
3678 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCapture}
3679 */
3680static DECLCALLBACK(int) drvAudioStreamCapture(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
3681 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
3682{
3683 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
3684 AssertPtr(pThis);
3685
3686 /*
3687 * Check input and sanity.
3688 */
3689 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
3690 PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
3691 AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
3692 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
3693 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
3694 uint32_t uTmp;
3695 if (pcbRead)
3696 AssertPtrReturn(pcbRead, VERR_INVALID_PARAMETER);
3697 else
3698 pcbRead = &uTmp;
3699
3700 AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
3701 AssertReturn(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
3702 AssertMsgReturn(pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_IN,
3703 ("Stream '%s' is not an input stream and therefore cannot be read from (direction is '%s')\n",
3704 pStreamEx->Core.Cfg.szName, PDMAudioDirGetName(pStreamEx->Core.Cfg.enmDir)), VERR_ACCESS_DENIED);
3705
3706 AssertMsg(PDMAudioPropsIsSizeAligned(&pStreamEx->Core.Cfg.Props, cbBuf),
3707 ("Stream '%s' got a non-frame-aligned write (%#RX32 bytes)\n", pStreamEx->Core.Cfg.szName, cbBuf));
3708
3709 int rc = RTCritSectEnter(&pStreamEx->Core.CritSect);
3710 AssertRCReturn(rc, rc);
3711
3712 /*
3713 * First check that we can read from the stream, and if not,
3714 * whether to just drop the input into the bit bucket.
3715 */
3716 if (PDMAudioStrmStatusIsReady(pStreamEx->fStatus))
3717 {
3718 RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
3719 if ( pThis->In.fEnabled /* (see @bugref{9882}) */
3720 && pThis->pHostDrvAudio != NULL)
3721 {
3722 /*
3723 * Get the backend state and process changes to it since last time we checked.
3724 */
3725 PDMHOSTAUDIOSTREAMSTATE const enmBackendState = drvAudioStreamGetBackendStateAndProcessChanges(pThis, pStreamEx);
3726
3727 /*
3728 * Do the transfering.
3729 */
3730 switch (pStreamEx->In.enmCaptureState)
3731 {
3732 case DRVAUDIOCAPTURESTATE_CAPTURING:
3733 Assert(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY);
3734 Assert(enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY);
3735 rc = drvAudioStreamCaptureLocked(pThis, pStreamEx, (uint8_t *)pvBuf, cbBuf, pcbRead);
3736 break;
3737
3738 case DRVAUDIOCAPTURESTATE_PREBUF:
3739 if (enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY)
3740 {
3741 uint32_t const cbReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio,
3742 pStreamEx->pBackend);
3743 if (cbReadable >= pStreamEx->cbPreBufThreshold)
3744 {
3745 Log4Func(("[%s] Pre-buffering completed: cbReadable=%#x vs cbPreBufThreshold=%#x (cbBuf=%#x)\n",
3746 pStreamEx->Core.Cfg.szName, cbReadable, pStreamEx->cbPreBufThreshold, cbBuf));
3747 pStreamEx->In.enmCaptureState = DRVAUDIOCAPTURESTATE_CAPTURING;
3748 rc = drvAudioStreamCaptureLocked(pThis, pStreamEx, (uint8_t *)pvBuf, cbBuf, pcbRead);
3749 break;
3750 }
3751 pStreamEx->In.Stats.cbBackendReadableBefore = cbReadable;
3752 pStreamEx->In.Stats.cbBackendReadableAfter = cbReadable;
3753 Log4Func(("[%s] Pre-buffering: Got %#x out of %#x\n",
3754 pStreamEx->Core.Cfg.szName, cbReadable, pStreamEx->cbPreBufThreshold));
3755 }
3756 else
3757 Log4Func(("[%s] Pre-buffering: Backend status %s\n",
3758 pStreamEx->Core.Cfg.szName, PDMHostAudioStreamStateGetName(enmBackendState) ));
3759 drvAudioStreamCaptureSilence(pStreamEx, (uint8_t *)pvBuf, cbBuf, pcbRead);
3760 break;
3761
3762 case DRVAUDIOCAPTURESTATE_NO_CAPTURE:
3763 *pcbRead = 0;
3764 Log4Func(("[%s] Not capturing - backend state: %s\n",
3765 pStreamEx->Core.Cfg.szName, PDMHostAudioStreamStateGetName(enmBackendState) ));
3766 break;
3767
3768 default:
3769 *pcbRead = 0;
3770 AssertMsgFailedBreak(("%d; cbBuf=%#x\n", pStreamEx->In.enmCaptureState, cbBuf));
3771 }
3772
3773 if (!pThis->CfgIn.Dbg.fEnabled || RT_FAILURE(rc))
3774 { /* likely */ }
3775 else
3776 AudioHlpFileWrite(pStreamEx->In.Dbg.pFileCapture, pvBuf, *pcbRead, 0 /* fFlags */);
3777 }
3778 else
3779 {
3780 *pcbRead = 0;
3781 Log4Func(("[%s] Backend stream %s, returning no data\n", pStreamEx->Core.Cfg.szName,
3782 !pThis->Out.fEnabled ? "disabled" : !pThis->pHostDrvAudio ? "not attached" : "not ready yet"));
3783 }
3784 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
3785 }
3786 else
3787 rc = VERR_AUDIO_STREAM_NOT_READY;
3788
3789 RTCritSectLeave(&pStreamEx->Core.CritSect);
3790 return rc;
3791}
3792
3793
3794/*********************************************************************************************************************************
3795* PDMIHOSTAUDIOPORT interface implementation. *
3796*********************************************************************************************************************************/
3797
3798/**
3799 * Worker for drvAudioHostPort_DoOnWorkerThread with stream argument, called on
3800 * worker thread.
3801 */
3802static DECLCALLBACK(void) drvAudioHostPort_DoOnWorkerThreadStreamWorker(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx,
3803 uintptr_t uUser, void *pvUser)
3804{
3805 LogFlowFunc(("pThis=%p uUser=%#zx pvUser=%p\n", pThis, uUser, pvUser));
3806 AssertPtrReturnVoid(pThis);
3807 AssertPtrReturnVoid(pStreamEx);
3808 AssertReturnVoid(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC);
3809
3810 /*
3811 * The CritSectHotPlug lock should not be needed here as detach will destroy
3812 * the thread pool. So, we'll leave taking the stream lock to the worker we're
3813 * calling as there are no lock order concerns.
3814 */
3815 PPDMIHOSTAUDIO const pIHostDrvAudio = pThis->pHostDrvAudio;
3816 AssertPtrReturnVoid(pIHostDrvAudio);
3817 AssertPtrReturnVoid(pIHostDrvAudio->pfnDoOnWorkerThread);
3818 pIHostDrvAudio->pfnDoOnWorkerThread(pIHostDrvAudio, pStreamEx->pBackend, uUser, pvUser);
3819
3820 drvAudioStreamReleaseInternal(pThis, pStreamEx, true /*fMayDestroy*/);
3821 LogFlowFunc(("returns\n"));
3822}
3823
3824
3825/**
3826 * Worker for drvAudioHostPort_DoOnWorkerThread without stream argument, called
3827 * on worker thread.
3828 *
3829 * This wrapper isn't technically required, but it helps with logging and a few
3830 * extra sanity checks.
3831 */
3832static DECLCALLBACK(void) drvAudioHostPort_DoOnWorkerThreadWorker(PDRVAUDIO pThis, uintptr_t uUser, void *pvUser)
3833{
3834 LogFlowFunc(("pThis=%p uUser=%#zx pvUser=%p\n", pThis, uUser, pvUser));
3835 AssertPtrReturnVoid(pThis);
3836
3837 /*
3838 * The CritSectHotPlug lock should not be needed here as detach will destroy
3839 * the thread pool.
3840 */
3841 PPDMIHOSTAUDIO const pIHostDrvAudio = pThis->pHostDrvAudio;
3842 AssertPtrReturnVoid(pIHostDrvAudio);
3843 AssertPtrReturnVoid(pIHostDrvAudio->pfnDoOnWorkerThread);
3844
3845 pIHostDrvAudio->pfnDoOnWorkerThread(pIHostDrvAudio, NULL, uUser, pvUser);
3846
3847 LogFlowFunc(("returns\n"));
3848}
3849
3850
3851/**
3852 * @interface_method_impl{PDMIHOSTAUDIOPORT,pfnDoOnWorkerThread}
3853 */
3854static DECLCALLBACK(int) drvAudioHostPort_DoOnWorkerThread(PPDMIHOSTAUDIOPORT pInterface, PPDMAUDIOBACKENDSTREAM pStream,
3855 uintptr_t uUser, void *pvUser)
3856{
3857 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IHostAudioPort);
3858 LogFlowFunc(("pStream=%p uUser=%#zx pvUser=%p\n", pStream, uUser, pvUser));
3859
3860 /*
3861 * Assert some sanity.
3862 */
3863 PDRVAUDIOSTREAM pStreamEx;
3864 if (!pStream)
3865 pStreamEx = NULL;
3866 else
3867 {
3868 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
3869 AssertReturn(pStream->uMagic == PDMAUDIOBACKENDSTREAM_MAGIC, VERR_INVALID_MAGIC);
3870 pStreamEx = (PDRVAUDIOSTREAM)pStream->pStream;
3871 AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
3872 AssertReturn(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
3873 AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
3874 }
3875
3876 int rc = RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
3877 AssertRCReturn(rc, rc);
3878
3879 Assert(pThis->hReqPool != NIL_RTREQPOOL);
3880 AssertPtr(pThis->pHostDrvAudio);
3881 if ( pThis->hReqPool != NIL_RTREQPOOL
3882 && pThis->pHostDrvAudio != NULL)
3883 {
3884 AssertPtr(pThis->pHostDrvAudio->pfnDoOnWorkerThread);
3885 if (pThis->pHostDrvAudio->pfnDoOnWorkerThread)
3886 {
3887 /*
3888 * Try do the work.
3889 */
3890 if (!pStreamEx)
3891 {
3892 rc = RTReqPoolCallEx(pThis->hReqPool, 0 /*cMillies*/, NULL /*phReq*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
3893 (PFNRT)drvAudioHostPort_DoOnWorkerThreadWorker, 3, pThis, uUser, pvUser);
3894 AssertRC(rc);
3895 }
3896 else
3897 {
3898 uint32_t cRefs = drvAudioStreamRetainInternal(pStreamEx);
3899 if (cRefs != UINT32_MAX)
3900 {
3901 rc = RTReqPoolCallEx(pThis->hReqPool, 0 /*cMillies*/, NULL, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
3902 (PFNRT)drvAudioHostPort_DoOnWorkerThreadStreamWorker,
3903 4, pThis, pStreamEx, uUser, pvUser);
3904 AssertRC(rc);
3905 if (RT_FAILURE(rc))
3906 {
3907 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
3908 drvAudioStreamReleaseInternal(pThis, pStreamEx, true /*fMayDestroy*/);
3909 RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
3910 }
3911 }
3912 else
3913 rc = VERR_INVALID_PARAMETER;
3914 }
3915 }
3916 else
3917 rc = VERR_INVALID_FUNCTION;
3918 }
3919 else
3920 rc = VERR_INVALID_STATE;
3921
3922 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
3923 LogFlowFunc(("returns %Rrc\n", rc));
3924 return rc;
3925}
3926
3927
3928/**
3929 * Marks a stream for re-init.
3930 */
3931static void drvAudioStreamMarkNeedReInit(PDRVAUDIOSTREAM pStreamEx, const char *pszCaller)
3932{
3933 LogFlow((LOG_FN_FMT ": Flagging %s for re-init.\n", pszCaller, pStreamEx->Core.Cfg.szName)); RT_NOREF(pszCaller);
3934 Assert(RTCritSectIsOwner(&pStreamEx->Core.CritSect));
3935
3936 pStreamEx->fStatus |= PDMAUDIOSTREAM_STS_NEED_REINIT;
3937 PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus);
3938 pStreamEx->cTriesReInit = 0;
3939 pStreamEx->nsLastReInit = 0;
3940}
3941
3942
3943/**
3944 * @interface_method_impl{PDMIHOSTAUDIOPORT,pfnNotifyDeviceChanged}
3945 */
3946static DECLCALLBACK(void) drvAudioHostPort_NotifyDeviceChanged(PPDMIHOSTAUDIOPORT pInterface, PDMAUDIODIR enmDir, void *pvUser)
3947{
3948 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IHostAudioPort);
3949 AssertReturnVoid(enmDir == PDMAUDIODIR_IN || enmDir == PDMAUDIODIR_OUT);
3950 LogRel(("Audio: The %s device for %s is changing.\n", enmDir == PDMAUDIODIR_IN ? "input" : "output", pThis->BackendCfg.szName));
3951
3952 /*
3953 * Grab the list lock in shared mode and do the work.
3954 */
3955 int rc = RTCritSectRwEnterShared(&pThis->CritSectGlobals);
3956 AssertRCReturnVoid(rc);
3957
3958 PDRVAUDIOSTREAM pStreamEx;
3959 RTListForEach(&pThis->LstStreams, pStreamEx, DRVAUDIOSTREAM, ListEntry)
3960 {
3961 if (pStreamEx->Core.Cfg.enmDir == enmDir)
3962 {
3963 RTCritSectEnter(&pStreamEx->Core.CritSect);
3964 RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
3965
3966 if (pThis->pHostDrvAudio->pfnStreamNotifyDeviceChanged)
3967 {
3968 LogFlowFunc(("Calling pfnStreamNotifyDeviceChanged on %s, old backend state: %s...\n", pStreamEx->Core.Cfg.szName,
3969 PDMHostAudioStreamStateGetName(drvAudioStreamGetBackendState(pThis, pStreamEx)) ));
3970 pThis->pHostDrvAudio->pfnStreamNotifyDeviceChanged(pThis->pHostDrvAudio, pStreamEx->pBackend, pvUser);
3971 LogFlowFunc(("New stream backend state: %s\n",
3972 PDMHostAudioStreamStateGetName(drvAudioStreamGetBackendState(pThis, pStreamEx)) ));
3973 }
3974 else
3975 drvAudioStreamMarkNeedReInit(pStreamEx, __PRETTY_FUNCTION__);
3976
3977 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
3978 RTCritSectLeave(&pStreamEx->Core.CritSect);
3979 }
3980 }
3981
3982 RTCritSectRwLeaveShared(&pThis->CritSectGlobals);
3983}
3984
3985
3986/**
3987 * @interface_method_impl{PDMIHOSTAUDIOPORT,pfnStreamNotifyPreparingDeviceSwitch}
3988 */
3989static DECLCALLBACK(void) drvAudioHostPort_StreamNotifyPreparingDeviceSwitch(PPDMIHOSTAUDIOPORT pInterface,
3990 PPDMAUDIOBACKENDSTREAM pStream)
3991{
3992 RT_NOREF(pInterface);
3993
3994 /*
3995 * Backend stream to validated DrvAudio stream:
3996 */
3997 AssertPtrReturnVoid(pStream);
3998 AssertReturnVoid(pStream->uMagic == PDMAUDIOBACKENDSTREAM_MAGIC);
3999 PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream->pStream;
4000 AssertPtrReturnVoid(pStreamEx);
4001 AssertReturnVoid(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC);
4002 AssertReturnVoid(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC);
4003 LogFlowFunc(("pStreamEx=%p '%s'\n", pStreamEx, pStreamEx->Core.Cfg.szName));
4004
4005 /*
4006 * Grab the lock and do switch the state (only needed for output streams for now).
4007 */
4008 RTCritSectEnter(&pStreamEx->Core.CritSect);
4009 AssertReturnVoidStmt(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC, RTCritSectLeave(&pStreamEx->Core.CritSect)); /* paranoia */
4010
4011 if (pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_OUT)
4012 {
4013 if (pStreamEx->cbPreBufThreshold > 0)
4014 {
4015 DRVAUDIOPLAYSTATE const enmPlayState = pStreamEx->Out.enmPlayState;
4016 switch (enmPlayState)
4017 {
4018 case DRVAUDIOPLAYSTATE_PREBUF:
4019 case DRVAUDIOPLAYSTATE_PREBUF_OVERDUE:
4020 case DRVAUDIOPLAYSTATE_NOPLAY:
4021 case DRVAUDIOPLAYSTATE_PREBUF_COMMITTING: /* simpler */
4022 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PREBUF_SWITCHING;
4023 break;
4024 case DRVAUDIOPLAYSTATE_PLAY:
4025 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PLAY_PREBUF;
4026 break;
4027 case DRVAUDIOPLAYSTATE_PREBUF_SWITCHING:
4028 case DRVAUDIOPLAYSTATE_PLAY_PREBUF:
4029 break;
4030 /* no default */
4031 case DRVAUDIOPLAYSTATE_END:
4032 case DRVAUDIOPLAYSTATE_INVALID:
4033 break;
4034 }
4035 LogFunc(("%s -> %s\n", drvAudioPlayStateName(enmPlayState), drvAudioPlayStateName(pStreamEx->Out.enmPlayState) ));
4036 }
4037 else
4038 LogFunc(("No pre-buffering configured.\n"));
4039 }
4040 else
4041 LogFunc(("input stream, nothing to do.\n"));
4042
4043 RTCritSectLeave(&pStreamEx->Core.CritSect);
4044}
4045
4046
4047/**
4048 * @interface_method_impl{PDMIHOSTAUDIOPORT,pfnStreamNotifyDeviceChanged}
4049 */
4050static DECLCALLBACK(void) drvAudioHostPort_StreamNotifyDeviceChanged(PPDMIHOSTAUDIOPORT pInterface,
4051 PPDMAUDIOBACKENDSTREAM pStream, bool fReInit)
4052{
4053 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IHostAudioPort);
4054
4055 /*
4056 * Backend stream to validated DrvAudio stream:
4057 */
4058 AssertPtrReturnVoid(pStream);
4059 AssertReturnVoid(pStream->uMagic == PDMAUDIOBACKENDSTREAM_MAGIC);
4060 PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream->pStream;
4061 AssertPtrReturnVoid(pStreamEx);
4062 AssertReturnVoid(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC);
4063 AssertReturnVoid(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC);
4064
4065 /*
4066 * Grab the lock and do the requested work.
4067 */
4068 RTCritSectEnter(&pStreamEx->Core.CritSect);
4069 AssertReturnVoidStmt(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC, RTCritSectLeave(&pStreamEx->Core.CritSect)); /* paranoia */
4070
4071 if (fReInit)
4072 drvAudioStreamMarkNeedReInit(pStreamEx, __PRETTY_FUNCTION__);
4073 else
4074 {
4075 /*
4076 * Adjust the stream state now that the device has (perhaps finally) been switched.
4077 *
4078 * For enabled output streams, we must update the play state. We could try commit
4079 * pre-buffered data here, but it's really not worth the hazzle and risk (don't
4080 * know which thread we're on, do we now).
4081 */
4082 AssertStmt(!(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_NEED_REINIT),
4083 pStreamEx->fStatus &= ~PDMAUDIOSTREAM_STS_NEED_REINIT);
4084
4085
4086 if (pStreamEx->Core.Cfg.enmDir == PDMAUDIODIR_OUT)
4087 {
4088 DRVAUDIOPLAYSTATE const enmPlayState = pStreamEx->Out.enmPlayState;
4089 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PREBUF;
4090 LogFunc(("%s: %s -> %s\n", pStreamEx->Core.Cfg.szName, drvAudioPlayStateName(enmPlayState),
4091 drvAudioPlayStateName(pStreamEx->Out.enmPlayState) ));
4092 RT_NOREF(enmPlayState);
4093 }
4094
4095 /* Disable and then fully resync. */
4096 /** @todo This doesn't work quite reliably if we're in draining mode
4097 * (PENDING_DISABLE, so the backend needs to take care of that prior to calling
4098 * us. Sigh. The idea was to avoid extra state mess in the backend... */
4099 drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
4100 drvAudioStreamUpdateBackendOnStatus(pThis, pStreamEx, "device changed");
4101 }
4102
4103 RTCritSectLeave(&pStreamEx->Core.CritSect);
4104}
4105
4106
4107#ifdef VBOX_WITH_AUDIO_ENUM
4108/**
4109 * @callback_method_impl{FNTMTIMERDRV, Re-enumerate backend devices.}
4110 *
4111 * Used to do/trigger re-enumeration of backend devices with a delay after we
4112 * got notification as there can be further notifications following shortly
4113 * after the first one. Also good to get it of random COM/whatever threads.
4114 */
4115static DECLCALLBACK(void) drvAudioEnumerateTimer(PPDMDRVINS pDrvIns, TMTIMERHANDLE hTimer, void *pvUser)
4116{
4117 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
4118 RT_NOREF(hTimer, pvUser);
4119
4120 /* Try push the work over to the thread-pool if we've got one. */
4121 RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
4122 if (pThis->hReqPool != NIL_RTREQPOOL)
4123 {
4124 int rc = RTReqPoolCallEx(pThis->hReqPool, 0 /*cMillies*/, NULL, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
4125 (PFNRT)drvAudioDevicesEnumerateInternal,
4126 3, pThis, true /*fLog*/, (PPDMAUDIOHOSTENUM)NULL /*pDevEnum*/);
4127 LogFunc(("RTReqPoolCallEx: %Rrc\n", rc));
4128 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
4129 if (RT_SUCCESS(rc))
4130 return;
4131 }
4132 else
4133 RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
4134
4135 LogFunc(("Calling drvAudioDevicesEnumerateInternal...\n"));
4136 drvAudioDevicesEnumerateInternal(pThis, true /* fLog */, NULL /* pDevEnum */);
4137}
4138#endif /* VBOX_WITH_AUDIO_ENUM */
4139
4140
4141/**
4142 * @interface_method_impl{PDMIHOSTAUDIOPORT,pfnNotifyDevicesChanged}
4143 */
4144static DECLCALLBACK(void) drvAudioHostPort_NotifyDevicesChanged(PPDMIHOSTAUDIOPORT pInterface)
4145{
4146 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IHostAudioPort);
4147 LogRel(("Audio: Device configuration of driver '%s' has changed\n", pThis->BackendCfg.szName));
4148
4149#ifdef RT_OS_DARWIN /** @todo Remove legacy behaviour: */
4150 /* Mark all host streams to re-initialize. */
4151 int rc2 = RTCritSectRwEnterShared(&pThis->CritSectGlobals);
4152 AssertRCReturnVoid(rc2);
4153 PDRVAUDIOSTREAM pStreamEx;
4154 RTListForEach(&pThis->LstStreams, pStreamEx, DRVAUDIOSTREAM, ListEntry)
4155 {
4156 RTCritSectEnter(&pStreamEx->Core.CritSect);
4157 drvAudioStreamMarkNeedReInit(pStreamEx, __PRETTY_FUNCTION__);
4158 RTCritSectLeave(&pStreamEx->Core.CritSect);
4159 }
4160 RTCritSectRwLeaveShared(&pThis->CritSectGlobals);
4161#endif
4162
4163#ifdef VBOX_WITH_AUDIO_ENUM
4164 /*
4165 * Re-enumerate all host devices with a tiny delay to avoid re-doing this
4166 * when a bunch of changes happens at once (they typically do on windows).
4167 * We'll keep postponing it till it quiesces for a fraction of a second.
4168 */
4169 int rc = PDMDrvHlpTimerSetMillies(pThis->pDrvIns, pThis->hEnumTimer, RT_MS_1SEC / 3);
4170 AssertRC(rc);
4171#endif
4172}
4173
4174
4175/*********************************************************************************************************************************
4176* PDMIBASE interface implementation. *
4177*********************************************************************************************************************************/
4178
4179/**
4180 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4181 */
4182static DECLCALLBACK(void *) drvAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4183{
4184 LogFlowFunc(("pInterface=%p, pszIID=%s\n", pInterface, pszIID));
4185
4186 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
4187 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
4188
4189 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
4190 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIAUDIOCONNECTOR, &pThis->IAudioConnector);
4191 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIOPORT, &pThis->IHostAudioPort);
4192
4193 return NULL;
4194}
4195
4196
4197/*********************************************************************************************************************************
4198* PDMDRVREG interface implementation. *
4199*********************************************************************************************************************************/
4200
4201/**
4202 * Power Off notification.
4203 *
4204 * @param pDrvIns The driver instance data.
4205 */
4206static DECLCALLBACK(void) drvAudioPowerOff(PPDMDRVINS pDrvIns)
4207{
4208 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
4209
4210 LogFlowFuncEnter();
4211
4212 /** @todo locking? */
4213 if (pThis->pHostDrvAudio) /* If not lower driver is configured, bail out. */
4214 {
4215 /*
4216 * Just destroy the host stream on the backend side.
4217 * The rest will either be destructed by the device emulation or
4218 * in drvAudioDestruct().
4219 */
4220 int rc = RTCritSectRwEnterShared(&pThis->CritSectGlobals);
4221 AssertRCReturnVoid(rc);
4222
4223 PDRVAUDIOSTREAM pStreamEx;
4224 RTListForEach(&pThis->LstStreams, pStreamEx, DRVAUDIOSTREAM, ListEntry)
4225 {
4226 RTCritSectEnter(&pStreamEx->Core.CritSect);
4227 drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
4228 RTCritSectLeave(&pStreamEx->Core.CritSect);
4229 }
4230
4231 RTCritSectRwLeaveShared(&pThis->CritSectGlobals);
4232 }
4233
4234 LogFlowFuncLeave();
4235}
4236
4237
4238/**
4239 * Detach notification.
4240 *
4241 * @param pDrvIns The driver instance data.
4242 * @param fFlags Detach flags.
4243 */
4244static DECLCALLBACK(void) drvAudioDetach(PPDMDRVINS pDrvIns, uint32_t fFlags)
4245{
4246 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
4247 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
4248 RT_NOREF(fFlags);
4249
4250 int rc = RTCritSectRwEnterExcl(&pThis->CritSectHotPlug);
4251 AssertLogRelRCReturnVoid(rc);
4252
4253 LogFunc(("%s (detached %p, hReqPool=%p)\n", pThis->BackendCfg.szName, pThis->pHostDrvAudio, pThis->hReqPool));
4254
4255 /*
4256 * Must first destroy the thread pool first so we are certain no threads
4257 * are still using the instance being detached. Release lock while doing
4258 * this as the thread functions may need to take it to complete.
4259 */
4260 if (pThis->pHostDrvAudio && pThis->hReqPool != NIL_RTREQPOOL)
4261 {
4262 RTREQPOOL hReqPool = pThis->hReqPool;
4263 pThis->hReqPool = NIL_RTREQPOOL;
4264
4265 RTCritSectRwLeaveExcl(&pThis->CritSectHotPlug);
4266
4267 RTReqPoolRelease(hReqPool);
4268
4269 RTCritSectRwEnterExcl(&pThis->CritSectHotPlug);
4270 }
4271
4272 /*
4273 * Now we can safely set pHostDrvAudio to NULL.
4274 */
4275 pThis->pHostDrvAudio = NULL;
4276
4277 RTCritSectRwLeaveExcl(&pThis->CritSectHotPlug);
4278}
4279
4280
4281/**
4282 * Initializes the host backend and queries its initial configuration.
4283 *
4284 * @returns VBox status code.
4285 * @param pThis Driver instance to be called.
4286 */
4287static int drvAudioHostInit(PDRVAUDIO pThis)
4288{
4289 LogFlowFuncEnter();
4290
4291 /*
4292 * Check the function pointers, make sure the ones we define as
4293 * mandatory are present.
4294 */
4295 PPDMIHOSTAUDIO pIHostDrvAudio = pThis->pHostDrvAudio;
4296 AssertPtrReturn(pIHostDrvAudio, VERR_INVALID_POINTER);
4297 AssertPtrReturn(pIHostDrvAudio->pfnGetConfig, VERR_INVALID_POINTER);
4298 AssertPtrNullReturn(pIHostDrvAudio->pfnGetDevices, VERR_INVALID_POINTER);
4299 AssertPtrNullReturn(pIHostDrvAudio->pfnSetDevice, VERR_INVALID_POINTER);
4300 AssertPtrNullReturn(pIHostDrvAudio->pfnGetStatus, VERR_INVALID_POINTER);
4301 AssertPtrNullReturn(pIHostDrvAudio->pfnDoOnWorkerThread, VERR_INVALID_POINTER);
4302 AssertPtrNullReturn(pIHostDrvAudio->pfnStreamConfigHint, VERR_INVALID_POINTER);
4303 AssertPtrReturn(pIHostDrvAudio->pfnStreamCreate, VERR_INVALID_POINTER);
4304 AssertPtrNullReturn(pIHostDrvAudio->pfnStreamInitAsync, VERR_INVALID_POINTER);
4305 AssertPtrReturn(pIHostDrvAudio->pfnStreamDestroy, VERR_INVALID_POINTER);
4306 AssertPtrNullReturn(pIHostDrvAudio->pfnStreamNotifyDeviceChanged, VERR_INVALID_POINTER);
4307 AssertPtrReturn(pIHostDrvAudio->pfnStreamEnable, VERR_INVALID_POINTER);
4308 AssertPtrReturn(pIHostDrvAudio->pfnStreamDisable, VERR_INVALID_POINTER);
4309 AssertPtrReturn(pIHostDrvAudio->pfnStreamPause, VERR_INVALID_POINTER);
4310 AssertPtrReturn(pIHostDrvAudio->pfnStreamResume, VERR_INVALID_POINTER);
4311 AssertPtrNullReturn(pIHostDrvAudio->pfnStreamDrain, VERR_INVALID_POINTER);
4312 AssertPtrReturn(pIHostDrvAudio->pfnStreamGetReadable, VERR_INVALID_POINTER);
4313 AssertPtrReturn(pIHostDrvAudio->pfnStreamGetWritable, VERR_INVALID_POINTER);
4314 AssertPtrNullReturn(pIHostDrvAudio->pfnStreamGetPending, VERR_INVALID_POINTER);
4315 AssertPtrReturn(pIHostDrvAudio->pfnStreamGetState, VERR_INVALID_POINTER);
4316 AssertPtrReturn(pIHostDrvAudio->pfnStreamPlay, VERR_INVALID_POINTER);
4317 AssertPtrReturn(pIHostDrvAudio->pfnStreamCapture, VERR_INVALID_POINTER);
4318
4319 /*
4320 * Get the backend configuration.
4321 *
4322 * Note! Limit the number of streams to max 128 in each direction to
4323 * prevent wasting resources.
4324 * Note! Take care not to wipe the DriverName config value on failure.
4325 */
4326 PDMAUDIOBACKENDCFG BackendCfg;
4327 RT_ZERO(BackendCfg);
4328 int rc = pIHostDrvAudio->pfnGetConfig(pIHostDrvAudio, &BackendCfg);
4329 if (RT_SUCCESS(rc))
4330 {
4331 if (LogIsEnabled() && strcmp(BackendCfg.szName, pThis->BackendCfg.szName) != 0)
4332 LogFunc(("BackendCfg.szName: '%s' -> '%s'\n", pThis->BackendCfg.szName, BackendCfg.szName));
4333 pThis->BackendCfg = BackendCfg;
4334 pThis->In.cStreamsFree = RT_MIN(BackendCfg.cMaxStreamsIn, 128);
4335 pThis->Out.cStreamsFree = RT_MIN(BackendCfg.cMaxStreamsOut, 128);
4336
4337 LogFlowFunc(("cStreamsFreeIn=%RU8, cStreamsFreeOut=%RU8\n", pThis->In.cStreamsFree, pThis->Out.cStreamsFree));
4338 }
4339 else
4340 {
4341 LogRel(("Audio: Getting configuration for driver '%s' failed with %Rrc\n", pThis->BackendCfg.szName, rc));
4342 return VERR_AUDIO_BACKEND_INIT_FAILED;
4343 }
4344
4345 LogRel2(("Audio: Host driver '%s' supports %RU32 input streams and %RU32 output streams at once.\n",
4346 pThis->BackendCfg.szName, pThis->In.cStreamsFree, pThis->Out.cStreamsFree));
4347
4348#ifdef VBOX_WITH_AUDIO_ENUM
4349 int rc2 = drvAudioDevicesEnumerateInternal(pThis, true /* fLog */, NULL /* pDevEnum */);
4350 if (rc2 != VERR_NOT_SUPPORTED) /* Some backends don't implement device enumeration. */
4351 AssertRC(rc2);
4352 /* Ignore rc2. */
4353#endif
4354
4355 /*
4356 * Create a thread pool if stream creation can be asynchronous.
4357 *
4358 * The pool employs no pushback as the caller is typically EMT and
4359 * shouldn't be delayed.
4360 *
4361 * The number of threads limits and the device implementations use
4362 * of pfnStreamDestroy limits the number of streams pending async
4363 * init. We use RTReqCancel in drvAudioStreamDestroy to allow us
4364 * to release extra reference held by the pfnStreamInitAsync call
4365 * if successful. Cancellation will only be possible if the call
4366 * hasn't been picked up by a worker thread yet, so the max number
4367 * of threads in the pool defines how many destroyed streams that
4368 * can be lingering. (We must keep this under control, otherwise
4369 * an evil guest could just rapidly trigger stream creation and
4370 * destruction to consume host heap and hog CPU resources for
4371 * configuring audio backends.)
4372 */
4373 if ( pThis->hReqPool == NIL_RTREQPOOL
4374 && ( pIHostDrvAudio->pfnStreamInitAsync
4375 || pIHostDrvAudio->pfnDoOnWorkerThread
4376 || (pThis->BackendCfg.fFlags & (PDMAUDIOBACKEND_F_ASYNC_HINT | PDMAUDIOBACKEND_F_ASYNC_STREAM_DESTROY)) ))
4377 {
4378 char szName[16];
4379 RTStrPrintf(szName, sizeof(szName), "Aud%uWr", pThis->pDrvIns->iInstance);
4380 RTREQPOOL hReqPool = NIL_RTREQPOOL;
4381 rc = RTReqPoolCreate(3 /*cMaxThreads*/, RT_MS_30SEC /*cMsMinIdle*/, UINT32_MAX /*cThreadsPushBackThreshold*/,
4382 1 /*cMsMaxPushBack*/, szName, &hReqPool);
4383 LogFlowFunc(("Creating thread pool '%s': %Rrc, hReqPool=%p\n", szName, rc, hReqPool));
4384 AssertRCReturn(rc, rc);
4385
4386 rc = RTReqPoolSetCfgVar(hReqPool, RTREQPOOLCFGVAR_THREAD_FLAGS, RTTHREADFLAGS_COM_MTA);
4387 AssertRCReturnStmt(rc, RTReqPoolRelease(hReqPool), rc);
4388
4389 rc = RTReqPoolSetCfgVar(hReqPool, RTREQPOOLCFGVAR_MIN_THREADS, 1);
4390 AssertRC(rc); /* harmless */
4391
4392 pThis->hReqPool = hReqPool;
4393 }
4394 else
4395 LogFlowFunc(("No thread pool.\n"));
4396
4397 LogFlowFuncLeave();
4398 return VINF_SUCCESS;
4399}
4400
4401
4402/**
4403 * Does the actual backend driver attaching and queries the backend's interface.
4404 *
4405 * This is a worker for both drvAudioAttach and drvAudioConstruct.
4406 *
4407 * @returns VBox status code.
4408 * @param pDrvIns The driver instance.
4409 * @param pThis Pointer to driver instance.
4410 * @param fFlags Attach flags; see PDMDrvHlpAttach().
4411 */
4412static int drvAudioDoAttachInternal(PPDMDRVINS pDrvIns, PDRVAUDIO pThis, uint32_t fFlags)
4413{
4414 Assert(pThis->pHostDrvAudio == NULL); /* No nested attaching. */
4415
4416 /*
4417 * Attach driver below and query its connector interface.
4418 */
4419 PPDMIBASE pDownBase;
4420 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pDownBase);
4421 if (RT_SUCCESS(rc))
4422 {
4423 pThis->pHostDrvAudio = PDMIBASE_QUERY_INTERFACE(pDownBase, PDMIHOSTAUDIO);
4424 if (pThis->pHostDrvAudio)
4425 {
4426 /*
4427 * If everything went well, initialize the lower driver.
4428 */
4429 rc = drvAudioHostInit(pThis);
4430 if (RT_FAILURE(rc))
4431 pThis->pHostDrvAudio = NULL;
4432 }
4433 else
4434 {
4435 LogRel(("Audio: Failed to query interface for underlying host driver '%s'\n", pThis->BackendCfg.szName));
4436 rc = PDMDRV_SET_ERROR(pThis->pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
4437 N_("The host audio driver does not implement PDMIHOSTAUDIO!"));
4438 }
4439 }
4440 /*
4441 * If the host driver below us failed to construct for some beningn reason,
4442 * we'll report it as a runtime error and replace it with the Null driver.
4443 *
4444 * Note! We do NOT change anything in PDM (or CFGM), so pDrvIns->pDownBase
4445 * will remain NULL in this case.
4446 */
4447 else if ( rc == VERR_AUDIO_BACKEND_INIT_FAILED
4448 || rc == VERR_MODULE_NOT_FOUND
4449 || rc == VERR_SYMBOL_NOT_FOUND
4450 || rc == VERR_FILE_NOT_FOUND
4451 || rc == VERR_PATH_NOT_FOUND)
4452 {
4453 /* Complain: */
4454 LogRel(("DrvAudio: Host audio driver '%s' init failed with %Rrc. Switching to the NULL driver for now.\n",
4455 pThis->BackendCfg.szName, rc));
4456 PDMDrvHlpVMSetRuntimeError(pDrvIns, 0 /*fFlags*/, "HostAudioNotResponding",
4457 N_("Host audio backend (%s) initialization has failed. Selecting the NULL audio backend with the consequence that no sound is audible"),
4458 pThis->BackendCfg.szName);
4459
4460 /* Replace with null audio: */
4461 pThis->pHostDrvAudio = (PPDMIHOSTAUDIO)&g_DrvHostAudioNull;
4462 RTStrCopy(pThis->BackendCfg.szName, sizeof(pThis->BackendCfg.szName), "NULL");
4463 rc = drvAudioHostInit(pThis);
4464 AssertRC(rc);
4465 }
4466
4467 LogFunc(("[%s] rc=%Rrc\n", pThis->BackendCfg.szName, rc));
4468 return rc;
4469}
4470
4471
4472/**
4473 * Attach notification.
4474 *
4475 * @param pDrvIns The driver instance data.
4476 * @param fFlags Attach flags.
4477 */
4478static DECLCALLBACK(int) drvAudioAttach(PPDMDRVINS pDrvIns, uint32_t fFlags)
4479{
4480 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
4481 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
4482 LogFunc(("%s\n", pThis->BackendCfg.szName));
4483
4484 int rc = RTCritSectRwEnterExcl(&pThis->CritSectHotPlug);
4485 AssertRCReturn(rc, rc);
4486
4487 rc = drvAudioDoAttachInternal(pDrvIns, pThis, fFlags);
4488
4489 RTCritSectRwLeaveExcl(&pThis->CritSectHotPlug);
4490 return rc;
4491}
4492
4493
4494/**
4495 * Handles state changes for all audio streams.
4496 *
4497 * @param pDrvIns Pointer to driver instance.
4498 * @param enmCmd Stream command to set for all streams.
4499 */
4500static void drvAudioStateHandler(PPDMDRVINS pDrvIns, PDMAUDIOSTREAMCMD enmCmd)
4501{
4502 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
4503 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
4504 LogFlowFunc(("enmCmd=%s\n", PDMAudioStrmCmdGetName(enmCmd)));
4505
4506 int rc2 = RTCritSectRwEnterShared(&pThis->CritSectGlobals);
4507 AssertRCReturnVoid(rc2);
4508
4509 PDRVAUDIOSTREAM pStreamEx;
4510 RTListForEach(&pThis->LstStreams, pStreamEx, DRVAUDIOSTREAM, ListEntry)
4511 {
4512 RTCritSectEnter(&pStreamEx->Core.CritSect);
4513 drvAudioStreamControlInternal(pThis, pStreamEx, enmCmd);
4514 RTCritSectLeave(&pStreamEx->Core.CritSect);
4515 }
4516
4517 RTCritSectRwLeaveShared(&pThis->CritSectGlobals);
4518}
4519
4520
4521/**
4522 * Resume notification.
4523 *
4524 * @param pDrvIns The driver instance data.
4525 */
4526static DECLCALLBACK(void) drvAudioResume(PPDMDRVINS pDrvIns)
4527{
4528 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_RESUME);
4529}
4530
4531
4532/**
4533 * Suspend notification.
4534 *
4535 * @param pDrvIns The driver instance data.
4536 */
4537static DECLCALLBACK(void) drvAudioSuspend(PPDMDRVINS pDrvIns)
4538{
4539 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_PAUSE);
4540}
4541
4542
4543/**
4544 * Destructs an audio driver instance.
4545 *
4546 * @copydoc FNPDMDRVDESTRUCT
4547 */
4548static DECLCALLBACK(void) drvAudioDestruct(PPDMDRVINS pDrvIns)
4549{
4550 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
4551 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
4552
4553 LogFlowFuncEnter();
4554
4555 /*
4556 * We must start by setting pHostDrvAudio to NULL here as the anything below
4557 * us has already been destroyed at this point.
4558 */
4559 if (RTCritSectRwIsInitialized(&pThis->CritSectHotPlug))
4560 {
4561 RTCritSectRwEnterExcl(&pThis->CritSectHotPlug);
4562 pThis->pHostDrvAudio = NULL;
4563 RTCritSectRwLeaveExcl(&pThis->CritSectHotPlug);
4564 }
4565 else
4566 {
4567 Assert(pThis->pHostDrvAudio == NULL);
4568 pThis->pHostDrvAudio = NULL;
4569 }
4570
4571 /*
4572 * Make sure the thread pool is out of the picture before we terminate all the streams.
4573 */
4574 if (pThis->hReqPool != NIL_RTREQPOOL)
4575 {
4576 uint32_t cRefs = RTReqPoolRelease(pThis->hReqPool);
4577 Assert(cRefs == 0); RT_NOREF(cRefs);
4578 pThis->hReqPool = NIL_RTREQPOOL;
4579 }
4580
4581 /*
4582 * Destroy all streams.
4583 */
4584 if (RTCritSectRwIsInitialized(&pThis->CritSectGlobals))
4585 {
4586 RTCritSectRwEnterExcl(&pThis->CritSectGlobals);
4587
4588 PDRVAUDIOSTREAM pStreamEx, pStreamExNext;
4589 RTListForEachSafe(&pThis->LstStreams, pStreamEx, pStreamExNext, DRVAUDIOSTREAM, ListEntry)
4590 {
4591 int rc = drvAudioStreamUninitInternal(pThis, pStreamEx);
4592 if (RT_SUCCESS(rc))
4593 {
4594 RTListNodeRemove(&pStreamEx->ListEntry);
4595 drvAudioStreamFree(pStreamEx);
4596 }
4597 }
4598
4599 RTCritSectRwLeaveExcl(&pThis->CritSectGlobals);
4600 RTCritSectRwDelete(&pThis->CritSectGlobals);
4601 }
4602
4603
4604 /* Sanity. */
4605 Assert(RTListIsEmpty(&pThis->LstStreams));
4606
4607 if (RTCritSectRwIsInitialized(&pThis->CritSectHotPlug))
4608 RTCritSectRwDelete(&pThis->CritSectHotPlug);
4609
4610#ifdef VBOX_WITH_STATISTICS
4611 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->Stats.TotalStreamsActive);
4612 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->Stats.TotalStreamsCreated);
4613 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->Stats.TotalFramesRead);
4614 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->Stats.TotalFramesIn);
4615 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->Stats.TotalBytesRead);
4616#endif
4617
4618 LogFlowFuncLeave();
4619}
4620
4621
4622/**
4623 * Constructs an audio driver instance.
4624 *
4625 * @copydoc FNPDMDRVCONSTRUCT
4626 */
4627static DECLCALLBACK(int) drvAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
4628{
4629 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
4630 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
4631 LogFlowFunc(("pDrvIns=%#p, pCfgHandle=%#p, fFlags=%x\n", pDrvIns, pCfg, fFlags));
4632
4633 /*
4634 * Basic instance init.
4635 */
4636 RTListInit(&pThis->LstStreams);
4637 pThis->hReqPool = NIL_RTREQPOOL;
4638
4639 /*
4640 * Read configuration.
4641 */
4642 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns,
4643 "DriverName|"
4644 "InputEnabled|"
4645 "OutputEnabled|"
4646 "DebugEnabled|"
4647 "DebugPathOut|"
4648 /* Deprecated: */
4649 "PCMSampleBitIn|"
4650 "PCMSampleBitOut|"
4651 "PCMSampleHzIn|"
4652 "PCMSampleHzOut|"
4653 "PCMSampleSignedIn|"
4654 "PCMSampleSignedOut|"
4655 "PCMSampleSwapEndianIn|"
4656 "PCMSampleSwapEndianOut|"
4657 "PCMSampleChannelsIn|"
4658 "PCMSampleChannelsOut|"
4659 "PeriodSizeMsIn|"
4660 "PeriodSizeMsOut|"
4661 "BufferSizeMsIn|"
4662 "BufferSizeMsOut|"
4663 "PreBufferSizeMsIn|"
4664 "PreBufferSizeMsOut",
4665 "In|Out");
4666
4667 int rc = CFGMR3QueryStringDef(pCfg, "DriverName", pThis->BackendCfg.szName, sizeof(pThis->BackendCfg.szName), "Untitled");
4668 AssertLogRelRCReturn(rc, rc);
4669
4670 /* Neither input nor output by default for security reasons. */
4671 rc = CFGMR3QueryBoolDef(pCfg, "InputEnabled", &pThis->In.fEnabled, false);
4672 AssertLogRelRCReturn(rc, rc);
4673
4674 rc = CFGMR3QueryBoolDef(pCfg, "OutputEnabled", &pThis->Out.fEnabled, false);
4675 AssertLogRelRCReturn(rc, rc);
4676
4677 /* Debug stuff (same for both directions). */
4678 rc = CFGMR3QueryBoolDef(pCfg, "DebugEnabled", &pThis->CfgIn.Dbg.fEnabled, false);
4679 AssertLogRelRCReturn(rc, rc);
4680
4681 rc = CFGMR3QueryStringDef(pCfg, "DebugPathOut", pThis->CfgIn.Dbg.szPathOut, sizeof(pThis->CfgIn.Dbg.szPathOut), "");
4682 AssertLogRelRCReturn(rc, rc);
4683 if (pThis->CfgIn.Dbg.szPathOut[0] == '\0')
4684 {
4685 rc = RTPathTemp(pThis->CfgIn.Dbg.szPathOut, sizeof(pThis->CfgIn.Dbg.szPathOut));
4686 if (RT_FAILURE(rc))
4687 {
4688 LogRel(("Audio: Warning! Failed to retrieve temporary directory: %Rrc - disabling debugging.\n", rc));
4689 pThis->CfgIn.Dbg.szPathOut[0] = '\0';
4690 pThis->CfgIn.Dbg.fEnabled = false;
4691 }
4692 }
4693 if (pThis->CfgIn.Dbg.fEnabled)
4694 LogRel(("Audio: Debugging for driver '%s' enabled (audio data written to '%s')\n",
4695 pThis->BackendCfg.szName, pThis->CfgIn.Dbg.szPathOut));
4696
4697 /* Copy debug setup to the output direction. */
4698 pThis->CfgOut.Dbg = pThis->CfgIn.Dbg;
4699
4700 LogRel2(("Audio: Verbose logging for driver '%s' is probably enabled too.\n", pThis->BackendCfg.szName));
4701 /* This ^^^^^^^ is the *WRONG* place for that kind of statement. Verbose logging might only be enabled for DrvAudio. */
4702 LogRel2(("Audio: Initial status for driver '%s' is: input is %s, output is %s\n",
4703 pThis->BackendCfg.szName, pThis->In.fEnabled ? "enabled" : "disabled", pThis->Out.fEnabled ? "enabled" : "disabled"));
4704
4705 /*
4706 * Per direction configuration. A bit complicated as
4707 * these wasn't originally in sub-nodes.
4708 */
4709 for (unsigned iDir = 0; iDir < 2; iDir++)
4710 {
4711 char szNm[48];
4712 PDRVAUDIOCFG pAudioCfg = iDir == 0 ? &pThis->CfgIn : &pThis->CfgOut;
4713 const char *pszDir = iDir == 0 ? "In" : "Out";
4714
4715#define QUERY_VAL_RET(a_Width, a_szName, a_pValue, a_uDefault, a_ExprValid, a_szValidRange) \
4716 do { \
4717 rc = RT_CONCAT(CFGMR3QueryU,a_Width)(pDirNode, strcpy(szNm, a_szName), a_pValue); \
4718 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT) \
4719 { \
4720 rc = RT_CONCAT(CFGMR3QueryU,a_Width)(pCfg, strcat(szNm, pszDir), a_pValue); \
4721 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT) \
4722 { \
4723 *(a_pValue) = a_uDefault; \
4724 rc = VINF_SUCCESS; \
4725 } \
4726 else \
4727 LogRel(("DrvAudio: Warning! Please use '%s/" a_szName "' instead of '%s' for your VBoxInternal hacks\n", pszDir, szNm)); \
4728 } \
4729 AssertRCReturn(rc, PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, \
4730 N_("Configuration error: Failed to read %s config value '%s'"), pszDir, szNm)); \
4731 if (!(a_ExprValid)) \
4732 return PDMDrvHlpVMSetError(pDrvIns, VERR_OUT_OF_RANGE, RT_SRC_POS, \
4733 N_("Configuration error: Unsupported %s value %u. " a_szValidRange), szNm, *(a_pValue)); \
4734 } while (0)
4735
4736 PCFGMNODE const pDirNode = CFGMR3GetChild(pCfg, pszDir);
4737 rc = CFGMR3ValidateConfig(pDirNode, iDir == 0 ? "In/" : "Out/",
4738 "PCMSampleBit|"
4739 "PCMSampleHz|"
4740 "PCMSampleSigned|"
4741 "PCMSampleSwapEndian|"
4742 "PCMSampleChannels|"
4743 "PeriodSizeMs|"
4744 "BufferSizeMs|"
4745 "PreBufferSizeMs",
4746 "", pDrvIns->pReg->szName, pDrvIns->iInstance);
4747 AssertRCReturn(rc, rc);
4748
4749 uint8_t cSampleBits = 0;
4750 QUERY_VAL_RET(8, "PCMSampleBit", &cSampleBits, 0,
4751 cSampleBits == 0
4752 || cSampleBits == 8
4753 || cSampleBits == 16
4754 || cSampleBits == 32
4755 || cSampleBits == 64,
4756 "Must be either 0, 8, 16, 32 or 64");
4757 if (cSampleBits)
4758 PDMAudioPropsSetSampleSize(&pAudioCfg->Props, cSampleBits / 8);
4759
4760 uint8_t cChannels;
4761 QUERY_VAL_RET(8, "PCMSampleChannels", &cChannels, 0, cChannels <= 16, "Max 16");
4762 if (cChannels)
4763 PDMAudioPropsSetChannels(&pAudioCfg->Props, cChannels);
4764
4765 QUERY_VAL_RET(32, "PCMSampleHz", &pAudioCfg->Props.uHz, 0,
4766 pAudioCfg->Props.uHz == 0 || (pAudioCfg->Props.uHz >= 6000 && pAudioCfg->Props.uHz <= 768000),
4767 "In the range 6000 thru 768000, or 0");
4768
4769 QUERY_VAL_RET(8, "PCMSampleSigned", &pAudioCfg->uSigned, UINT8_MAX,
4770 pAudioCfg->uSigned == 0 || pAudioCfg->uSigned == 1 || pAudioCfg->uSigned == UINT8_MAX,
4771 "Must be either 0, 1, or 255");
4772
4773 QUERY_VAL_RET(8, "PCMSampleSwapEndian", &pAudioCfg->uSwapEndian, UINT8_MAX,
4774 pAudioCfg->uSwapEndian == 0 || pAudioCfg->uSwapEndian == 1 || pAudioCfg->uSwapEndian == UINT8_MAX,
4775 "Must be either 0, 1, or 255");
4776
4777 QUERY_VAL_RET(32, "PeriodSizeMs", &pAudioCfg->uPeriodSizeMs, 0,
4778 pAudioCfg->uPeriodSizeMs <= RT_MS_1SEC, "Max 1000");
4779
4780 QUERY_VAL_RET(32, "BufferSizeMs", &pAudioCfg->uBufferSizeMs, 0,
4781 pAudioCfg->uBufferSizeMs <= RT_MS_5SEC, "Max 5000");
4782
4783 QUERY_VAL_RET(32, "PreBufferSizeMs", &pAudioCfg->uPreBufSizeMs, UINT32_MAX,
4784 pAudioCfg->uPreBufSizeMs <= RT_MS_1SEC || pAudioCfg->uPreBufSizeMs == UINT32_MAX,
4785 "Max 1000, or 0xffffffff");
4786#undef QUERY_VAL_RET
4787 }
4788
4789 /*
4790 * Init the rest of the driver instance data.
4791 */
4792 rc = RTCritSectRwInit(&pThis->CritSectHotPlug);
4793 AssertRCReturn(rc, rc);
4794 rc = RTCritSectRwInit(&pThis->CritSectGlobals);
4795 AssertRCReturn(rc, rc);
4796#ifdef VBOX_STRICT
4797 /* Define locking order: */
4798 RTCritSectRwEnterExcl(&pThis->CritSectGlobals);
4799 RTCritSectRwEnterExcl(&pThis->CritSectHotPlug);
4800 RTCritSectRwLeaveExcl(&pThis->CritSectHotPlug);
4801 RTCritSectRwLeaveExcl(&pThis->CritSectGlobals);
4802#endif
4803
4804 pThis->pDrvIns = pDrvIns;
4805 /* IBase. */
4806 pDrvIns->IBase.pfnQueryInterface = drvAudioQueryInterface;
4807 /* IAudioConnector. */
4808 pThis->IAudioConnector.pfnEnable = drvAudioEnable;
4809 pThis->IAudioConnector.pfnIsEnabled = drvAudioIsEnabled;
4810 pThis->IAudioConnector.pfnGetConfig = drvAudioGetConfig;
4811 pThis->IAudioConnector.pfnGetStatus = drvAudioGetStatus;
4812 pThis->IAudioConnector.pfnStreamConfigHint = drvAudioStreamConfigHint;
4813 pThis->IAudioConnector.pfnStreamCreate = drvAudioStreamCreate;
4814 pThis->IAudioConnector.pfnStreamDestroy = drvAudioStreamDestroy;
4815 pThis->IAudioConnector.pfnStreamReInit = drvAudioStreamReInit;
4816 pThis->IAudioConnector.pfnStreamRetain = drvAudioStreamRetain;
4817 pThis->IAudioConnector.pfnStreamRelease = drvAudioStreamRelease;
4818 pThis->IAudioConnector.pfnStreamControl = drvAudioStreamControl;
4819 pThis->IAudioConnector.pfnStreamIterate = drvAudioStreamIterate;
4820 pThis->IAudioConnector.pfnStreamGetState = drvAudioStreamGetState;
4821 pThis->IAudioConnector.pfnStreamGetWritable = drvAudioStreamGetWritable;
4822 pThis->IAudioConnector.pfnStreamPlay = drvAudioStreamPlay;
4823 pThis->IAudioConnector.pfnStreamGetReadable = drvAudioStreamGetReadable;
4824 pThis->IAudioConnector.pfnStreamCapture = drvAudioStreamCapture;
4825 /* IHostAudioPort */
4826 pThis->IHostAudioPort.pfnDoOnWorkerThread = drvAudioHostPort_DoOnWorkerThread;
4827 pThis->IHostAudioPort.pfnNotifyDeviceChanged = drvAudioHostPort_NotifyDeviceChanged;
4828 pThis->IHostAudioPort.pfnStreamNotifyPreparingDeviceSwitch = drvAudioHostPort_StreamNotifyPreparingDeviceSwitch;
4829 pThis->IHostAudioPort.pfnStreamNotifyDeviceChanged = drvAudioHostPort_StreamNotifyDeviceChanged;
4830 pThis->IHostAudioPort.pfnNotifyDevicesChanged = drvAudioHostPort_NotifyDevicesChanged;
4831
4832 /*
4833 * Statistics.
4834 */
4835#ifdef VBOX_WITH_STATISTICS
4836 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalStreamsActive, "TotalStreamsActive",
4837 STAMUNIT_COUNT, "Total active audio streams.");
4838 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalStreamsCreated, "TotalStreamsCreated",
4839 STAMUNIT_COUNT, "Total created audio streams.");
4840 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesRead, "TotalFramesRead",
4841 STAMUNIT_COUNT, "Total frames read by device emulation.");
4842 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesIn, "TotalFramesIn",
4843 STAMUNIT_COUNT, "Total frames captured by backend.");
4844 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalBytesRead, "TotalBytesRead",
4845 STAMUNIT_BYTES, "Total bytes read.");
4846#endif
4847
4848#ifdef VBOX_WITH_AUDIO_ENUM
4849 /*
4850 * Create a timer to trigger delayed device enumeration on device changes.
4851 */
4852 RTStrPrintf(pThis->szEnumTimerName, sizeof(pThis->szEnumTimerName), "AudioEnum-%u", pDrvIns->iInstance);
4853 rc = PDMDrvHlpTMTimerCreate(pDrvIns, TMCLOCK_REAL, drvAudioEnumerateTimer, NULL /*pvUser*/,
4854 0 /*fFlags*/, pThis->szEnumTimerName, &pThis->hEnumTimer);
4855 AssertRCReturn(rc, rc);
4856#endif
4857
4858 /*
4859 * Attach the host driver, if present.
4860 */
4861 rc = drvAudioDoAttachInternal(pDrvIns, pThis, fFlags);
4862 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
4863 rc = VINF_SUCCESS;
4864
4865 LogFlowFuncLeaveRC(rc);
4866 return rc;
4867}
4868
4869/**
4870 * Audio driver registration record.
4871 */
4872const PDMDRVREG g_DrvAUDIO =
4873{
4874 /* u32Version */
4875 PDM_DRVREG_VERSION,
4876 /* szName */
4877 "AUDIO",
4878 /* szRCMod */
4879 "",
4880 /* szR0Mod */
4881 "",
4882 /* pszDescription */
4883 "Audio connector driver",
4884 /* fFlags */
4885 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
4886 /* fClass */
4887 PDM_DRVREG_CLASS_AUDIO,
4888 /* cMaxInstances */
4889 UINT32_MAX,
4890 /* cbInstance */
4891 sizeof(DRVAUDIO),
4892 /* pfnConstruct */
4893 drvAudioConstruct,
4894 /* pfnDestruct */
4895 drvAudioDestruct,
4896 /* pfnRelocate */
4897 NULL,
4898 /* pfnIOCtl */
4899 NULL,
4900 /* pfnPowerOn */
4901 NULL,
4902 /* pfnReset */
4903 NULL,
4904 /* pfnSuspend */
4905 drvAudioSuspend,
4906 /* pfnResume */
4907 drvAudioResume,
4908 /* pfnAttach */
4909 drvAudioAttach,
4910 /* pfnDetach */
4911 drvAudioDetach,
4912 /* pfnPowerOff */
4913 drvAudioPowerOff,
4914 /* pfnSoftReset */
4915 NULL,
4916 /* u32EndVersion */
4917 PDM_DRVREG_VERSION
4918};
4919
Note: See TracBrowser for help on using the repository browser.

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