VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostAudioAlsa.cpp@ 90778

Last change on this file since 90778 was 89533, checked in by vboxsync, 3 years ago

DrvHostAudioAlsa: Don't call the output/input device 'default'. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 60.0 KB
Line 
1/* $Id: DrvHostAudioAlsa.cpp 89533 2021-06-07 00:55:08Z vboxsync $ */
2/** @file
3 * Host audio driver - Advanced Linux Sound Architecture (ALSA).
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 * This code is based on: alsaaudio.c
19 *
20 * QEMU ALSA audio driver
21 *
22 * Copyright (c) 2005 Vassili Karpov (malc)
23 *
24 * Permission is hereby granted, free of charge, to any person obtaining a copy
25 * of this software and associated documentation files (the "Software"), to deal
26 * in the Software without restriction, including without limitation the rights
27 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28 * copies of the Software, and to permit persons to whom the Software is
29 * furnished to do so, subject to the following conditions:
30 *
31 * The above copyright notice and this permission notice shall be included in
32 * all copies or substantial portions of the Software.
33 *
34 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
37 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
40 * THE SOFTWARE.
41 */
42
43
44/*********************************************************************************************************************************
45* Header Files *
46*********************************************************************************************************************************/
47#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
48#include <VBox/log.h>
49#include <iprt/alloc.h>
50#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
51#include <VBox/vmm/pdmaudioifs.h>
52#include <VBox/vmm/pdmaudioinline.h>
53#include <VBox/vmm/pdmaudiohostenuminline.h>
54
55#include "DrvHostAudioAlsaStubsMangling.h"
56#include <alsa/asoundlib.h>
57#include <alsa/control.h> /* For device enumeration. */
58#include <alsa/version.h>
59#include "DrvHostAudioAlsaStubs.h"
60
61#include "VBoxDD.h"
62
63
64/*********************************************************************************************************************************
65* Defined Constants And Macros *
66*********************************************************************************************************************************/
67/** Maximum number of tries to recover a broken pipe. */
68#define ALSA_RECOVERY_TRIES_MAX 5
69
70
71/*********************************************************************************************************************************
72* Structures *
73*********************************************************************************************************************************/
74/**
75 * ALSA host audio specific stream data.
76 */
77typedef struct DRVHSTAUDALSASTREAM
78{
79 /** Common part. */
80 PDMAUDIOBACKENDSTREAM Core;
81
82 /** Handle to the ALSA PCM stream. */
83 snd_pcm_t *hPCM;
84 /** Internal stream offset (for debugging). */
85 uint64_t offInternal;
86
87 /** The stream's acquired configuration. */
88 PDMAUDIOSTREAMCFG Cfg;
89} DRVHSTAUDALSASTREAM;
90/** Pointer to the ALSA host audio specific stream data. */
91typedef DRVHSTAUDALSASTREAM *PDRVHSTAUDALSASTREAM;
92
93
94/**
95 * Host Alsa audio driver instance data.
96 * @implements PDMIAUDIOCONNECTOR
97 */
98typedef struct DRVHSTAUDALSA
99{
100 /** Pointer to the driver instance structure. */
101 PPDMDRVINS pDrvIns;
102 /** Pointer to host audio interface. */
103 PDMIHOSTAUDIO IHostAudio;
104 /** Error count for not flooding the release log.
105 * UINT32_MAX for unlimited logging. */
106 uint32_t cLogErrors;
107
108 /** Critical section protecting the default device strings. */
109 RTCRITSECT CritSect;
110 /** Default input device name. */
111 char szInputDev[256];
112 /** Default output device name. */
113 char szOutputDev[256];
114 /** Upwards notification interface. */
115 PPDMIHOSTAUDIOPORT pIHostAudioPort;
116} DRVHSTAUDALSA;
117/** Pointer to the instance data of an ALSA host audio driver. */
118typedef DRVHSTAUDALSA *PDRVHSTAUDALSA;
119
120
121
122/**
123 * Closes an ALSA stream
124 *
125 * @returns VBox status code.
126 * @param phPCM Pointer to the ALSA stream handle to close. Will be set to
127 * NULL.
128 */
129static int drvHstAudAlsaStreamClose(snd_pcm_t **phPCM)
130{
131 if (!phPCM || !*phPCM)
132 return VINF_SUCCESS;
133
134 int rc;
135 int rc2 = snd_pcm_close(*phPCM);
136 if (rc2 == 0)
137 {
138 *phPCM = NULL;
139 rc = VINF_SUCCESS;
140 }
141 else
142 {
143 rc = RTErrConvertFromErrno(-rc2);
144 LogRel(("ALSA: Closing PCM descriptor failed: %s (%d, %Rrc)\n", snd_strerror(rc2), rc2, rc));
145 }
146
147 LogFlowFuncLeaveRC(rc);
148 return rc;
149}
150
151
152#ifdef DEBUG
153static void drvHstAudAlsaDbgErrorHandler(const char *file, int line, const char *function,
154 int err, const char *fmt, ...)
155{
156 /** @todo Implement me! */
157 RT_NOREF(file, line, function, err, fmt);
158}
159#endif
160
161
162/**
163 * Tries to recover an ALSA stream.
164 *
165 * @returns VBox status code.
166 * @param hPCM ALSA stream handle.
167 */
168static int drvHstAudAlsaStreamRecover(snd_pcm_t *hPCM)
169{
170 AssertPtrReturn(hPCM, VERR_INVALID_POINTER);
171
172 int rc = snd_pcm_prepare(hPCM);
173 if (rc >= 0)
174 {
175 LogFlowFunc(("Successfully recovered %p.\n", hPCM));
176 return VINF_SUCCESS;
177 }
178 LogFunc(("Failed to recover stream %p: %s (%d)\n", hPCM, snd_strerror(rc), rc));
179 return RTErrConvertFromErrno(-rc);
180}
181
182
183/**
184 * Resumes an ALSA stream.
185 *
186 * Used by drvHstAudAlsaHA_StreamPlay() and drvHstAudAlsaHA_StreamCapture().
187 *
188 * @returns VBox status code.
189 * @param hPCM ALSA stream to resume.
190 */
191static int drvHstAudAlsaStreamResume(snd_pcm_t *hPCM)
192{
193 AssertPtrReturn(hPCM, VERR_INVALID_POINTER);
194
195 int rc = snd_pcm_resume(hPCM);
196 if (rc >= 0)
197 {
198 LogFlowFunc(("Successfuly resumed %p.\n", hPCM));
199 return VINF_SUCCESS;
200 }
201 LogFunc(("Failed to resume stream %p: %s (%d)\n", hPCM, snd_strerror(rc), rc));
202 return RTErrConvertFromErrno(-rc);
203}
204
205
206/**
207 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
208 */
209static DECLCALLBACK(int) drvHstAudAlsaHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
210{
211 RT_NOREF(pInterface);
212 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
213
214 /*
215 * Fill in the config structure.
216 */
217 RTStrCopy(pBackendCfg->szName, sizeof(pBackendCfg->szName), "ALSA");
218 pBackendCfg->cbStream = sizeof(DRVHSTAUDALSASTREAM);
219 pBackendCfg->fFlags = 0;
220 /* ALSA allows exactly one input and one output used at a time for the selected device(s). */
221 pBackendCfg->cMaxStreamsIn = 1;
222 pBackendCfg->cMaxStreamsOut = 1;
223
224 return VINF_SUCCESS;
225}
226
227
228/**
229 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetDevices}
230 */
231static DECLCALLBACK(int) drvHstAudAlsaHA_GetDevices(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHOSTENUM pDeviceEnum)
232{
233 RT_NOREF(pInterface);
234 PDMAudioHostEnumInit(pDeviceEnum);
235
236 char **papszHints = NULL;
237 int rc = snd_device_name_hint(-1 /* All cards */, "pcm", (void***)&papszHints);
238 if (rc == 0)
239 {
240 rc = VINF_SUCCESS;
241 for (size_t iHint = 0; papszHints[iHint] != NULL && RT_SUCCESS(rc); iHint++)
242 {
243 /*
244 * Retrieve the available info:
245 */
246 const char * const pszHint = papszHints[iHint];
247 char * const pszDev = snd_device_name_get_hint(pszHint, "NAME");
248 char * const pszInOutId = snd_device_name_get_hint(pszHint, "IOID");
249 char * const pszDesc = snd_device_name_get_hint(pszHint, "DESC");
250
251 if (pszDev && RTStrICmpAscii(pszDev, "null") != 0)
252 {
253 /* Detect and log presence of pulse audio plugin. */
254 if (RTStrIStr("pulse", pszDev) != NULL)
255 LogRel(("ALSA: The ALSAAudio plugin for pulse audio is being used (%s).\n", pszDev));
256
257 /*
258 * Add an entry to the enumeration result.
259 * We engage in some trickery here to deal with device names that
260 * are more than 63 characters long.
261 */
262 size_t const cbId = pszDev ? strlen(pszDev) + 1 : 1;
263 size_t const cbName = pszDesc ? strlen(pszDesc) + 2 + 1 : cbId;
264 PPDMAUDIOHOSTDEV pDev = PDMAudioHostDevAlloc(sizeof(*pDev), cbName, cbId);
265 if (pDev)
266 {
267 RTStrCopy(pDev->pszId, cbId, pszDev);
268 if (pDev->pszId)
269 {
270 pDev->fFlags = PDMAUDIOHOSTDEV_F_NONE;
271 pDev->enmType = PDMAUDIODEVICETYPE_UNKNOWN;
272
273 if (pszInOutId == NULL)
274 {
275 pDev->enmUsage = PDMAUDIODIR_DUPLEX;
276 pDev->cMaxInputChannels = 2;
277 pDev->cMaxOutputChannels = 2;
278 }
279 else if (RTStrICmpAscii(pszInOutId, "Input") == 0)
280 {
281 pDev->enmUsage = PDMAUDIODIR_IN;
282 pDev->cMaxInputChannels = 2;
283 pDev->cMaxOutputChannels = 0;
284 }
285 else
286 {
287 AssertMsg(RTStrICmpAscii(pszInOutId, "Output") == 0, ("%s (%s)\n", pszInOutId, pszHint));
288 pDev->enmUsage = PDMAUDIODIR_OUT;
289 pDev->cMaxInputChannels = 0;
290 pDev->cMaxOutputChannels = 2;
291 }
292
293 if (pszDesc && *pszDesc)
294 {
295 char *pszDesc2 = strchr(pszDesc, '\n');
296 if (!pszDesc2)
297 RTStrCopy(pDev->pszName, cbName, pszDesc);
298 else
299 {
300 *pszDesc2++ = '\0';
301 char *psz;
302 while ((psz = strchr(pszDesc2, '\n')) != NULL)
303 *psz = ' ';
304 RTStrPrintf(pDev->pszName, cbName, "%s (%s)", pszDesc2, pszDesc);
305 }
306 }
307 else
308 RTStrCopy(pDev->pszName, cbName, pszDev);
309
310 PDMAudioHostEnumAppend(pDeviceEnum, pDev);
311
312 LogRel2(("ALSA: Device #%u: '%s' enmDir=%s: %s\n", iHint, pszDev,
313 PDMAudioDirGetName(pDev->enmUsage), pszDesc));
314 }
315 else
316 {
317 PDMAudioHostDevFree(pDev);
318 rc = VERR_NO_STR_MEMORY;
319 }
320 }
321 else
322 rc = VERR_NO_MEMORY;
323 }
324
325 /*
326 * Clean up.
327 */
328 if (pszInOutId)
329 free(pszInOutId);
330 if (pszDesc)
331 free(pszDesc);
332 if (pszDev)
333 free(pszDev);
334 }
335
336 snd_device_name_free_hint((void **)papszHints);
337
338 if (RT_FAILURE(rc))
339 {
340 PDMAudioHostEnumDelete(pDeviceEnum);
341 PDMAudioHostEnumInit(pDeviceEnum);
342 }
343 }
344 else
345 {
346 int rc2 = RTErrConvertFromErrno(-rc);
347 LogRel2(("ALSA: Error enumerating PCM devices: %Rrc (%d)\n", rc2, rc));
348 rc = rc2;
349 }
350 return rc;
351}
352
353
354/**
355 * @interface_method_impl{PDMIHOSTAUDIO,pfnSetDevice}
356 */
357static DECLCALLBACK(int) drvHstAudAlsaHA_SetDevice(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir, const char *pszId)
358{
359 PDRVHSTAUDALSA pThis = RT_FROM_MEMBER(pInterface, DRVHSTAUDALSA, IHostAudio);
360
361 /*
362 * Validate and normalize input.
363 */
364 AssertReturn(enmDir == PDMAUDIODIR_IN || enmDir == PDMAUDIODIR_OUT || enmDir == PDMAUDIODIR_DUPLEX, VERR_INVALID_PARAMETER);
365 AssertPtrNullReturn(pszId, VERR_INVALID_POINTER);
366 if (!pszId || !*pszId)
367 pszId = "default";
368 else
369 {
370 size_t cch = strlen(pszId);
371 AssertReturn(cch < sizeof(pThis->szInputDev), VERR_INVALID_NAME);
372 }
373 LogFunc(("enmDir=%d pszId=%s\n", enmDir, pszId));
374
375 /*
376 * Update input.
377 */
378 if (enmDir == PDMAUDIODIR_IN || enmDir == PDMAUDIODIR_DUPLEX)
379 {
380 int rc = RTCritSectEnter(&pThis->CritSect);
381 AssertRCReturn(rc, rc);
382 if (strcmp(pThis->szInputDev, pszId) == 0)
383 RTCritSectLeave(&pThis->CritSect);
384 else
385 {
386 LogRel(("ALSA: Changing input device: '%s' -> '%s'\n", pThis->szInputDev, pszId));
387 RTStrCopy(pThis->szInputDev, sizeof(pThis->szInputDev), pszId);
388 PPDMIHOSTAUDIOPORT pIHostAudioPort = pThis->pIHostAudioPort;
389 RTCritSectLeave(&pThis->CritSect);
390 if (pIHostAudioPort)
391 {
392 LogFlowFunc(("Notifying parent driver about input device change...\n"));
393 pIHostAudioPort->pfnNotifyDeviceChanged(pIHostAudioPort, PDMAUDIODIR_IN, NULL /*pvUser*/);
394 }
395 }
396 }
397
398 /*
399 * Update output.
400 */
401 if (enmDir == PDMAUDIODIR_OUT || enmDir == PDMAUDIODIR_DUPLEX)
402 {
403 int rc = RTCritSectEnter(&pThis->CritSect);
404 AssertRCReturn(rc, rc);
405 if (strcmp(pThis->szOutputDev, pszId) == 0)
406 RTCritSectLeave(&pThis->CritSect);
407 else
408 {
409 LogRel(("ALSA: Changing output device: '%s' -> '%s'\n", pThis->szOutputDev, pszId));
410 RTStrCopy(pThis->szOutputDev, sizeof(pThis->szOutputDev), pszId);
411 PPDMIHOSTAUDIOPORT pIHostAudioPort = pThis->pIHostAudioPort;
412 RTCritSectLeave(&pThis->CritSect);
413 if (pIHostAudioPort)
414 {
415 LogFlowFunc(("Notifying parent driver about output device change...\n"));
416 pIHostAudioPort->pfnNotifyDeviceChanged(pIHostAudioPort, PDMAUDIODIR_OUT, NULL /*pvUser*/);
417 }
418 }
419 }
420
421 return VINF_SUCCESS;
422}
423
424
425/**
426 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
427 */
428static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHstAudAlsaHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
429{
430 RT_NOREF(enmDir);
431 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
432
433 return PDMAUDIOBACKENDSTS_RUNNING;
434}
435
436
437/**
438 * Converts internal audio PCM properties to an ALSA PCM format.
439 *
440 * @returns Converted ALSA PCM format.
441 * @param pProps Internal audio PCM configuration to convert.
442 */
443static snd_pcm_format_t alsaAudioPropsToALSA(PCPDMAUDIOPCMPROPS pProps)
444{
445 switch (PDMAudioPropsSampleSize(pProps))
446 {
447 case 1:
448 return pProps->fSigned ? SND_PCM_FORMAT_S8 : SND_PCM_FORMAT_U8;
449
450 case 2:
451 if (PDMAudioPropsIsLittleEndian(pProps))
452 return pProps->fSigned ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_U16_LE;
453 return pProps->fSigned ? SND_PCM_FORMAT_S16_BE : SND_PCM_FORMAT_U16_BE;
454
455 case 4:
456 if (PDMAudioPropsIsLittleEndian(pProps))
457 return pProps->fSigned ? SND_PCM_FORMAT_S32_LE : SND_PCM_FORMAT_U32_LE;
458 return pProps->fSigned ? SND_PCM_FORMAT_S32_BE : SND_PCM_FORMAT_U32_BE;
459
460 default:
461 AssertLogRelMsgFailed(("%RU8 bytes not supported\n", PDMAudioPropsSampleSize(pProps)));
462 return SND_PCM_FORMAT_UNKNOWN;
463 }
464}
465
466
467/**
468 * Sets the software parameters of an ALSA stream.
469 *
470 * @returns 0 on success, negative errno on failure.
471 * @param hPCM ALSA stream to set software parameters for.
472 * @param pCfgReq Requested stream configuration (PDM).
473 * @param pCfgAcq The actual stream configuration (PDM). Updated as
474 * needed.
475 */
476static int alsaStreamSetSWParams(snd_pcm_t *hPCM, PCPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
477{
478 if (pCfgReq->enmDir == PDMAUDIODIR_IN) /* For input streams there's nothing to do in here right now. */
479 return 0;
480
481 snd_pcm_sw_params_t *pSWParms = NULL;
482 snd_pcm_sw_params_alloca(&pSWParms);
483 AssertReturn(pSWParms, -ENOMEM);
484
485 int err = snd_pcm_sw_params_current(hPCM, pSWParms);
486 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to get current software parameters: %s\n", snd_strerror(err)), err);
487
488 /* Under normal circumstance, we don't need to set a playback threshold
489 because DrvAudio will do the pre-buffering and hand us everything in
490 one continuous chunk when we should start playing. But since it is
491 configurable, we'll set a reasonable minimum of two DMA periods or
492 max 50 milliseconds (the pAlsaCfgReq->threshold value).
493
494 Of course we also have to make sure the threshold is below the buffer
495 size, or ALSA will never start playing. */
496 unsigned long const cFramesMax = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 50);
497 unsigned long cFramesThreshold = RT_MIN(pCfgAcq->Backend.cFramesPeriod * 2, cFramesMax);
498 if (cFramesThreshold >= pCfgAcq->Backend.cFramesBufferSize - pCfgAcq->Backend.cFramesBufferSize / 16)
499 cFramesThreshold = pCfgAcq->Backend.cFramesBufferSize - pCfgAcq->Backend.cFramesBufferSize / 16;
500
501 err = snd_pcm_sw_params_set_start_threshold(hPCM, pSWParms, cFramesThreshold);
502 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to set software threshold to %lu: %s\n", cFramesThreshold, snd_strerror(err)), err);
503
504 err = snd_pcm_sw_params_set_avail_min(hPCM, pSWParms, pCfgReq->Backend.cFramesPeriod);
505 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to set available minimum to %u: %s\n",
506 pCfgReq->Backend.cFramesPeriod, snd_strerror(err)), err);
507
508 /* Commit the software parameters: */
509 err = snd_pcm_sw_params(hPCM, pSWParms);
510 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to set new software parameters: %s\n", snd_strerror(err)), err);
511
512 /* Get the actual parameters: */
513 snd_pcm_uframes_t cFramesThresholdActual = cFramesThreshold;
514 err = snd_pcm_sw_params_get_start_threshold(pSWParms, &cFramesThresholdActual);
515 AssertLogRelMsgStmt(err >= 0, ("ALSA: Failed to get start threshold: %s\n", snd_strerror(err)),
516 cFramesThresholdActual = cFramesThreshold);
517
518 LogRel2(("ALSA: SW params: %lu frames threshold, %u frames avail minimum\n",
519 cFramesThresholdActual, pCfgAcq->Backend.cFramesPeriod));
520 return 0;
521}
522
523
524/**
525 * Maps a PDM channel ID to an ASLA channel map position.
526 */
527static unsigned int drvHstAudAlsaPdmChToAlsa(PDMAUDIOCHANNELID enmId, uint8_t cChannels)
528{
529 switch (enmId)
530 {
531 case PDMAUDIOCHANNELID_UNKNOWN: return SND_CHMAP_UNKNOWN;
532 case PDMAUDIOCHANNELID_UNUSED_ZERO: return SND_CHMAP_NA;
533 case PDMAUDIOCHANNELID_UNUSED_SILENCE: return SND_CHMAP_NA;
534
535 case PDMAUDIOCHANNELID_FRONT_LEFT: return SND_CHMAP_FL;
536 case PDMAUDIOCHANNELID_FRONT_RIGHT: return SND_CHMAP_FR;
537 case PDMAUDIOCHANNELID_FRONT_CENTER: return cChannels == 1 ? SND_CHMAP_MONO : SND_CHMAP_FC;
538 case PDMAUDIOCHANNELID_LFE: return SND_CHMAP_LFE;
539 case PDMAUDIOCHANNELID_REAR_LEFT: return SND_CHMAP_RL;
540 case PDMAUDIOCHANNELID_REAR_RIGHT: return SND_CHMAP_RR;
541 case PDMAUDIOCHANNELID_FRONT_LEFT_OF_CENTER: return SND_CHMAP_FLC;
542 case PDMAUDIOCHANNELID_FRONT_RIGHT_OF_CENTER: return SND_CHMAP_FRC;
543 case PDMAUDIOCHANNELID_REAR_CENTER: return SND_CHMAP_RC;
544 case PDMAUDIOCHANNELID_SIDE_LEFT: return SND_CHMAP_SL;
545 case PDMAUDIOCHANNELID_SIDE_RIGHT: return SND_CHMAP_SR;
546 case PDMAUDIOCHANNELID_TOP_CENTER: return SND_CHMAP_TC;
547 case PDMAUDIOCHANNELID_FRONT_LEFT_HEIGHT: return SND_CHMAP_TFL;
548 case PDMAUDIOCHANNELID_FRONT_CENTER_HEIGHT: return SND_CHMAP_TFC;
549 case PDMAUDIOCHANNELID_FRONT_RIGHT_HEIGHT: return SND_CHMAP_TFR;
550 case PDMAUDIOCHANNELID_REAR_LEFT_HEIGHT: return SND_CHMAP_TRL;
551 case PDMAUDIOCHANNELID_REAR_CENTER_HEIGHT: return SND_CHMAP_TRC;
552 case PDMAUDIOCHANNELID_REAR_RIGHT_HEIGHT: return SND_CHMAP_TRR;
553
554 case PDMAUDIOCHANNELID_INVALID:
555 case PDMAUDIOCHANNELID_END:
556 case PDMAUDIOCHANNELID_32BIT_HACK:
557 break;
558 }
559 AssertFailed();
560 return SND_CHMAP_NA;
561}
562
563
564/**
565 * Sets the hardware parameters of an ALSA stream.
566 *
567 * @returns 0 on success, negative errno on failure.
568 * @param hPCM ALSA stream to set software parameters for.
569 * @param enmAlsaFmt The ALSA format to use.
570 * @param pCfgReq Requested stream configuration (PDM).
571 * @param pCfgAcq The actual stream configuration (PDM). This is assumed
572 * to be a copy of pCfgReq on input, at least for
573 * properties handled here. On output some of the
574 * properties may be updated to match the actual stream
575 * configuration.
576 */
577static int alsaStreamSetHwParams(snd_pcm_t *hPCM, snd_pcm_format_t enmAlsaFmt,
578 PCPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
579{
580 /*
581 * Get the current hardware parameters.
582 */
583 snd_pcm_hw_params_t *pHWParms = NULL;
584 snd_pcm_hw_params_alloca(&pHWParms);
585 AssertReturn(pHWParms, -ENOMEM);
586
587 int err = snd_pcm_hw_params_any(hPCM, pHWParms);
588 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to initialize hardware parameters: %s\n", snd_strerror(err)), err);
589
590 /*
591 * Modify them according to pAlsaCfgReq.
592 * We update pAlsaCfgObt as we go for parameters set by "near" methods.
593 */
594 /* We'll use snd_pcm_writei/snd_pcm_readi: */
595 err = snd_pcm_hw_params_set_access(hPCM, pHWParms, SND_PCM_ACCESS_RW_INTERLEAVED);
596 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to set access type: %s\n", snd_strerror(err)), err);
597
598 /* Set the format and frequency. */
599 err = snd_pcm_hw_params_set_format(hPCM, pHWParms, enmAlsaFmt);
600 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to set audio format to %d: %s\n", enmAlsaFmt, snd_strerror(err)), err);
601
602 unsigned int uFreq = PDMAudioPropsHz(&pCfgReq->Props);
603 err = snd_pcm_hw_params_set_rate_near(hPCM, pHWParms, &uFreq, NULL /*dir*/);
604 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to set frequency to %uHz: %s\n",
605 PDMAudioPropsHz(&pCfgReq->Props), snd_strerror(err)), err);
606 pCfgAcq->Props.uHz = uFreq;
607
608 /* Channel count currently does not change with the mapping translations,
609 as ALSA can express both silent and unknown channel positions. */
610 union
611 {
612 snd_pcm_chmap_t Map;
613 unsigned int padding[1 + PDMAUDIO_MAX_CHANNELS];
614 } u;
615 uint8_t aidSrcChannels[PDMAUDIO_MAX_CHANNELS];
616 unsigned int *aidDstChannels = u.Map.pos;
617 unsigned int cChannels = u.Map.channels = PDMAudioPropsChannels(&pCfgReq->Props);
618 unsigned int iDst = 0;
619 for (unsigned int iSrc = 0; iSrc < cChannels; iSrc++)
620 {
621 uint8_t const idSrc = pCfgReq->Props.aidChannels[iSrc];
622 aidSrcChannels[iDst] = idSrc;
623 aidDstChannels[iDst] = drvHstAudAlsaPdmChToAlsa((PDMAUDIOCHANNELID)idSrc, cChannels);
624 iDst++;
625 }
626 u.Map.channels = cChannels = iDst;
627 for (; iDst < PDMAUDIO_MAX_CHANNELS; iDst++)
628 {
629 aidSrcChannels[iDst] = PDMAUDIOCHANNELID_INVALID;
630 aidDstChannels[iDst] = SND_CHMAP_NA;
631 }
632
633 err = snd_pcm_hw_params_set_channels_near(hPCM, pHWParms, &cChannels);
634 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to set number of channels to %d\n", PDMAudioPropsChannels(&pCfgReq->Props)),
635 err);
636 if (cChannels == PDMAudioPropsChannels(&pCfgReq->Props))
637 memcpy(pCfgAcq->Props.aidChannels, aidSrcChannels, sizeof(pCfgAcq->Props.aidChannels));
638 else
639 {
640 LogRel2(("ALSA: Requested %u channels, got %u\n", u.Map.channels, cChannels));
641 AssertLogRelMsgReturn(cChannels > 0 && cChannels <= PDMAUDIO_MAX_CHANNELS,
642 ("ALSA: Unsupported channel count: %u (requested %d)\n",
643 cChannels, PDMAudioPropsChannels(&pCfgReq->Props)), -ERANGE);
644 PDMAudioPropsSetChannels(&pCfgAcq->Props, (uint8_t)cChannels);
645 /** @todo Can we somehow guess channel IDs? snd_pcm_get_chmap? */
646 }
647
648 /* The period size (reportedly frame count per hw interrupt): */
649 int dir = 0;
650 snd_pcm_uframes_t minval = pCfgReq->Backend.cFramesPeriod;
651 err = snd_pcm_hw_params_get_period_size_min(pHWParms, &minval, &dir);
652 AssertLogRelMsgReturn(err >= 0, ("ALSA: Could not determine minimal period size: %s\n", snd_strerror(err)), err);
653
654 snd_pcm_uframes_t period_size_f = pCfgReq->Backend.cFramesPeriod;
655 if (period_size_f < minval)
656 period_size_f = minval;
657 err = snd_pcm_hw_params_set_period_size_near(hPCM, pHWParms, &period_size_f, 0);
658 LogRel2(("ALSA: Period size is: %lu frames (min %lu, requested %u)\n", period_size_f, minval, pCfgReq->Backend.cFramesPeriod));
659 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to set period size %d (%s)\n", period_size_f, snd_strerror(err)), err);
660
661 /* The buffer size: */
662 minval = pCfgReq->Backend.cFramesBufferSize;
663 err = snd_pcm_hw_params_get_buffer_size_min(pHWParms, &minval);
664 AssertLogRelMsgReturn(err >= 0, ("ALSA: Could not retrieve minimal buffer size: %s\n", snd_strerror(err)), err);
665
666 snd_pcm_uframes_t buffer_size_f = pCfgReq->Backend.cFramesBufferSize;
667 if (buffer_size_f < minval)
668 buffer_size_f = minval;
669 err = snd_pcm_hw_params_set_buffer_size_near(hPCM, pHWParms, &buffer_size_f);
670 LogRel2(("ALSA: Buffer size is: %lu frames (min %lu, requested %u)\n", buffer_size_f, minval, pCfgReq->Backend.cFramesBufferSize));
671 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to set near buffer size %RU32: %s\n", buffer_size_f, snd_strerror(err)), err);
672
673 /*
674 * Set the hardware parameters.
675 */
676 err = snd_pcm_hw_params(hPCM, pHWParms);
677 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to apply audio parameters: %s\n", snd_strerror(err)), err);
678
679 /*
680 * Get relevant parameters and put them in the pAlsaCfgObt structure.
681 */
682 snd_pcm_uframes_t obt_buffer_size = buffer_size_f;
683 err = snd_pcm_hw_params_get_buffer_size(pHWParms, &obt_buffer_size);
684 AssertLogRelMsgStmt(err >= 0, ("ALSA: Failed to get buffer size: %s\n", snd_strerror(err)), obt_buffer_size = buffer_size_f);
685 pCfgAcq->Backend.cFramesBufferSize = obt_buffer_size;
686
687 snd_pcm_uframes_t obt_period_size = period_size_f;
688 err = snd_pcm_hw_params_get_period_size(pHWParms, &obt_period_size, &dir);
689 AssertLogRelMsgStmt(err >= 0, ("ALSA: Failed to get period size: %s\n", snd_strerror(err)), obt_period_size = period_size_f);
690 pCfgAcq->Backend.cFramesPeriod = obt_period_size;
691
692 LogRel2(("ALSA: HW params: %u Hz, %u frames period, %u frames buffer, %u channel(s), enmAlsaFmt=%d\n",
693 PDMAudioPropsHz(&pCfgAcq->Props), pCfgAcq->Backend.cFramesPeriod, pCfgAcq->Backend.cFramesBufferSize,
694 PDMAudioPropsChannels(&pCfgAcq->Props), enmAlsaFmt));
695
696 /*
697 * Channel config (not fatal).
698 */
699 if (PDMAudioPropsChannels(&pCfgAcq->Props) == PDMAudioPropsChannels(&pCfgReq->Props))
700 {
701 err = snd_pcm_set_chmap(hPCM, &u.Map);
702 if (err < 0)
703 LogRel2(("ALSA: snd_pcm_set_chmap failed: %s (%d)\n", snd_strerror(err), err));
704 }
705
706 return 0;
707}
708
709
710/**
711 * Opens (creates) an ALSA stream.
712 *
713 * @returns VBox status code.
714 * @param pThis The alsa driver instance data.
715 * @param enmAlsaFmt The ALSA format to use.
716 * @param pCfgReq Requested configuration to create stream with (PDM).
717 * @param pCfgAcq The actual stream configuration (PDM). This is assumed
718 * to be a copy of pCfgReq on input, at least for
719 * properties handled here. On output some of the
720 * properties may be updated to match the actual stream
721 * configuration.
722 * @param phPCM Where to store the ALSA stream handle on success.
723 */
724static int alsaStreamOpen(PDRVHSTAUDALSA pThis, snd_pcm_format_t enmAlsaFmt, PCPDMAUDIOSTREAMCFG pCfgReq,
725 PPDMAUDIOSTREAMCFG pCfgAcq, snd_pcm_t **phPCM)
726{
727 /*
728 * Open the stream.
729 */
730 int rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
731 const char * const pszType = pCfgReq->enmDir == PDMAUDIODIR_IN ? "input" : "output";
732 const char * const pszDev = pCfgReq->enmDir == PDMAUDIODIR_IN ? pThis->szInputDev : pThis->szOutputDev;
733 snd_pcm_stream_t enmType = pCfgReq->enmDir == PDMAUDIODIR_IN ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK;
734
735 snd_pcm_t *hPCM = NULL;
736 LogRel(("ALSA: Using %s device \"%s\"\n", pszType, pszDev));
737 int err = snd_pcm_open(&hPCM, pszDev, enmType, SND_PCM_NONBLOCK);
738 if (err >= 0)
739 {
740 err = snd_pcm_nonblock(hPCM, 1);
741 if (err >= 0)
742 {
743 /*
744 * Configure hardware stream parameters.
745 */
746 err = alsaStreamSetHwParams(hPCM, enmAlsaFmt, pCfgReq, pCfgAcq);
747 if (err >= 0)
748 {
749 /*
750 * Prepare it.
751 */
752 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
753 err = snd_pcm_prepare(hPCM);
754 if (err >= 0)
755 {
756 /*
757 * Configure software stream parameters.
758 */
759 rc = alsaStreamSetSWParams(hPCM, pCfgReq, pCfgAcq);
760 if (RT_SUCCESS(rc))
761 {
762 *phPCM = hPCM;
763 return VINF_SUCCESS;
764 }
765 }
766 else
767 LogRel(("ALSA: snd_pcm_prepare failed: %s\n", snd_strerror(err)));
768 }
769 }
770 else
771 LogRel(("ALSA: Error setting non-blocking mode for %s stream: %s\n", pszType, snd_strerror(err)));
772 drvHstAudAlsaStreamClose(&hPCM);
773 }
774 else
775 LogRel(("ALSA: Failed to open \"%s\" as %s device: %s\n", pszDev, pszType, snd_strerror(err)));
776 *phPCM = NULL;
777 return rc;
778}
779
780
781/**
782 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
783 */
784static DECLCALLBACK(int) drvHstAudAlsaHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
785 PCPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
786{
787 PDRVHSTAUDALSA pThis = RT_FROM_MEMBER(pInterface, DRVHSTAUDALSA, IHostAudio);
788 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
789 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
790 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
791 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
792
793 PDRVHSTAUDALSASTREAM pStreamALSA = (PDRVHSTAUDALSASTREAM)pStream;
794 PDMAudioStrmCfgCopy(&pStreamALSA->Cfg, pCfgReq);
795
796 int rc;
797 snd_pcm_format_t const enmFmt = alsaAudioPropsToALSA(&pCfgReq->Props);
798 if (enmFmt != SND_PCM_FORMAT_UNKNOWN)
799 {
800 rc = alsaStreamOpen(pThis, enmFmt, pCfgReq, pCfgAcq, &pStreamALSA->hPCM);
801 if (RT_SUCCESS(rc))
802 {
803 /* We have no objections to the pre-buffering that DrvAudio applies,
804 only we need to adjust it relative to the actual buffer size. */
805 pCfgAcq->Backend.cFramesPreBuffering = (uint64_t)pCfgReq->Backend.cFramesPreBuffering
806 * pCfgAcq->Backend.cFramesBufferSize
807 / RT_MAX(pCfgReq->Backend.cFramesBufferSize, 1);
808
809 PDMAudioStrmCfgCopy(&pStreamALSA->Cfg, pCfgAcq);
810 LogFlowFunc(("returns success - hPCM=%p\n", pStreamALSA->hPCM));
811 return rc;
812 }
813 }
814 else
815 rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
816 LogFunc(("returns %Rrc\n", rc));
817 return rc;
818}
819
820
821/**
822 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
823 */
824static DECLCALLBACK(int) drvHstAudAlsaHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, bool fImmediate)
825{
826 RT_NOREF(pInterface);
827 PDRVHSTAUDALSASTREAM pStreamALSA = (PDRVHSTAUDALSASTREAM)pStream;
828 AssertPtrReturn(pStreamALSA, VERR_INVALID_POINTER);
829 RT_NOREF(fImmediate);
830
831 /** @todo r=bird: It's not like we can do much with a bad status... Check
832 * what the caller does... */
833 return drvHstAudAlsaStreamClose(&pStreamALSA->hPCM);
834}
835
836
837/**
838 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamEnable}
839 */
840static DECLCALLBACK(int) drvHstAudAlsaHA_StreamEnable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
841{
842 RT_NOREF(pInterface);
843 PDRVHSTAUDALSASTREAM pStreamALSA = (PDRVHSTAUDALSASTREAM)pStream;
844
845 /*
846 * Prepare the stream.
847 */
848 int rc = snd_pcm_prepare(pStreamALSA->hPCM);
849 if (rc >= 0)
850 {
851 Assert(snd_pcm_state(pStreamALSA->hPCM) == SND_PCM_STATE_PREPARED);
852
853 /*
854 * Input streams should be started now, whereas output streams must
855 * pre-buffer sufficent data before starting.
856 */
857 if (pStreamALSA->Cfg.enmDir == PDMAUDIODIR_IN)
858 {
859 rc = snd_pcm_start(pStreamALSA->hPCM);
860 if (rc >= 0)
861 rc = VINF_SUCCESS;
862 else
863 {
864 LogRel(("ALSA: Error starting input stream '%s': %s (%d)\n", pStreamALSA->Cfg.szName, snd_strerror(rc), rc));
865 rc = RTErrConvertFromErrno(-rc);
866 }
867 }
868 else
869 rc = VINF_SUCCESS;
870 }
871 else
872 {
873 LogRel(("ALSA: Error preparing stream '%s': %s (%d)\n", pStreamALSA->Cfg.szName, snd_strerror(rc), rc));
874 rc = RTErrConvertFromErrno(-rc);
875 }
876 LogFlowFunc(("returns %Rrc (state %s)\n", rc, snd_pcm_state_name(snd_pcm_state(pStreamALSA->hPCM))));
877 return rc;
878}
879
880
881/**
882 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDisable}
883 */
884static DECLCALLBACK(int) drvHstAudAlsaHA_StreamDisable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
885{
886 RT_NOREF(pInterface);
887 PDRVHSTAUDALSASTREAM pStreamALSA = (PDRVHSTAUDALSASTREAM)pStream;
888
889 int rc = snd_pcm_drop(pStreamALSA->hPCM);
890 if (rc >= 0)
891 rc = VINF_SUCCESS;
892 else
893 {
894 LogRel(("ALSA: Error stopping stream '%s': %s (%d)\n", pStreamALSA->Cfg.szName, snd_strerror(rc), rc));
895 rc = RTErrConvertFromErrno(-rc);
896 }
897 LogFlowFunc(("returns %Rrc (state %s)\n", rc, snd_pcm_state_name(snd_pcm_state(pStreamALSA->hPCM))));
898 return rc;
899}
900
901
902/**
903 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPause}
904 */
905static DECLCALLBACK(int) drvHstAudAlsaHA_StreamPause(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
906{
907 /* Same as disable. */
908 /** @todo r=bird: Try use pause and fallback on disable/enable if it isn't
909 * supported or doesn't work. */
910 return drvHstAudAlsaHA_StreamDisable(pInterface, pStream);
911}
912
913
914/**
915 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamResume}
916 */
917static DECLCALLBACK(int) drvHstAudAlsaHA_StreamResume(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
918{
919 /* Same as enable. */
920 return drvHstAudAlsaHA_StreamEnable(pInterface, pStream);
921}
922
923
924/**
925 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain}
926 */
927static DECLCALLBACK(int) drvHstAudAlsaHA_StreamDrain(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
928{
929 RT_NOREF(pInterface);
930 PDRVHSTAUDALSASTREAM pStreamALSA = (PDRVHSTAUDALSASTREAM)pStream;
931
932 snd_pcm_state_t const enmState = snd_pcm_state(pStreamALSA->hPCM);
933 LogFlowFunc(("Stream '%s' input state: %s (%d)\n", pStreamALSA->Cfg.szName, snd_pcm_state_name(enmState), enmState));
934
935 /* Only for output streams. */
936 AssertReturn(pStreamALSA->Cfg.enmDir == PDMAUDIODIR_OUT, VERR_WRONG_ORDER);
937
938 int rc;
939 switch (enmState)
940 {
941 case SND_PCM_STATE_RUNNING:
942 case SND_PCM_STATE_PREPARED: /* not yet started */
943 {
944 /* Do not change to blocking here! */
945 rc = snd_pcm_drain(pStreamALSA->hPCM);
946 if (rc >= 0 || rc == -EAGAIN)
947 rc = VINF_SUCCESS;
948 else
949 {
950 snd_pcm_state_t const enmState2 = snd_pcm_state(pStreamALSA->hPCM);
951 if (rc == -EPIPE && enmState2 == enmState)
952 {
953 /* Not entirely sure, but possibly an underrun, so just disable the stream. */
954 LogFunc(("snd_pcm_drain failed with -EPIPE, stopping stream (%s)\n", pStreamALSA->Cfg.szName));
955 rc = snd_pcm_drop(pStreamALSA->hPCM);
956 if (rc >= 0)
957 rc = VINF_SUCCESS;
958 else
959 {
960 LogRel(("ALSA: Error draining/stopping stream '%s': %s (%d)\n", pStreamALSA->Cfg.szName, snd_strerror(rc), rc));
961 rc = RTErrConvertFromErrno(-rc);
962 }
963 }
964 else
965 {
966 LogRel(("ALSA: Error draining output of '%s': %s (%d; %s -> %s)\n", pStreamALSA->Cfg.szName, snd_strerror(rc),
967 rc, snd_pcm_state_name(enmState), snd_pcm_state_name(enmState2)));
968 rc = RTErrConvertFromErrno(-rc);
969 }
970 }
971 break;
972 }
973
974 default:
975 rc = VINF_SUCCESS;
976 break;
977 }
978 LogFlowFunc(("returns %Rrc (state %s)\n", rc, snd_pcm_state_name(snd_pcm_state(pStreamALSA->hPCM))));
979 return rc;
980}
981
982
983/**
984 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetState}
985 */
986static DECLCALLBACK(PDMHOSTAUDIOSTREAMSTATE) drvHstAudAlsaHA_StreamGetState(PPDMIHOSTAUDIO pInterface,
987 PPDMAUDIOBACKENDSTREAM pStream)
988{
989 RT_NOREF(pInterface);
990 PDRVHSTAUDALSASTREAM pStreamALSA = (PDRVHSTAUDALSASTREAM)pStream;
991 AssertPtrReturn(pStreamALSA, PDMHOSTAUDIOSTREAMSTATE_INVALID);
992
993 PDMHOSTAUDIOSTREAMSTATE enmStreamState = PDMHOSTAUDIOSTREAMSTATE_OKAY;
994 snd_pcm_state_t enmAlsaState = snd_pcm_state(pStreamALSA->hPCM);
995 if (enmAlsaState == SND_PCM_STATE_DRAINING)
996 {
997 /* We're operating in non-blocking mode, so we must (at least for a demux
998 config) call snd_pcm_drain again to drive it forward. Otherwise we
999 might be stuck in the drain state forever. */
1000 Log5Func(("Calling snd_pcm_drain again...\n"));
1001 snd_pcm_drain(pStreamALSA->hPCM);
1002 enmAlsaState = snd_pcm_state(pStreamALSA->hPCM);
1003 }
1004
1005 if (enmAlsaState == SND_PCM_STATE_DRAINING)
1006 enmStreamState = PDMHOSTAUDIOSTREAMSTATE_DRAINING;
1007#if (((SND_LIB_MAJOR) << 16) | ((SND_LIB_MAJOR) << 8) | (SND_LIB_SUBMINOR)) >= 0x10002 /* was added in 1.0.2 */
1008 else if (enmAlsaState == SND_PCM_STATE_DISCONNECTED)
1009 enmStreamState = PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING;
1010#endif
1011
1012 Log5Func(("Stream '%s': ALSA state=%s -> %s\n",
1013 pStreamALSA->Cfg.szName, snd_pcm_state_name(enmAlsaState), PDMHostAudioStreamStateGetName(enmStreamState) ));
1014 return enmStreamState;
1015}
1016
1017
1018/**
1019 * Returns the available audio frames queued.
1020 *
1021 * @returns VBox status code.
1022 * @param hPCM ALSA stream handle.
1023 * @param pcFramesAvail Where to store the available frames.
1024 */
1025static int alsaStreamGetAvail(snd_pcm_t *hPCM, snd_pcm_sframes_t *pcFramesAvail)
1026{
1027 AssertPtr(hPCM);
1028 AssertPtr(pcFramesAvail);
1029
1030 int rc;
1031 snd_pcm_sframes_t cFramesAvail = snd_pcm_avail_update(hPCM);
1032 if (cFramesAvail > 0)
1033 {
1034 LogFunc(("cFramesAvail=%ld\n", cFramesAvail));
1035 *pcFramesAvail = cFramesAvail;
1036 return VINF_SUCCESS;
1037 }
1038
1039 /*
1040 * We can maybe recover from an EPIPE...
1041 */
1042 if (cFramesAvail == -EPIPE)
1043 {
1044 rc = drvHstAudAlsaStreamRecover(hPCM);
1045 if (RT_SUCCESS(rc))
1046 {
1047 cFramesAvail = snd_pcm_avail_update(hPCM);
1048 if (cFramesAvail >= 0)
1049 {
1050 LogFunc(("cFramesAvail=%ld\n", cFramesAvail));
1051 *pcFramesAvail = cFramesAvail;
1052 return VINF_SUCCESS;
1053 }
1054 }
1055 else
1056 {
1057 *pcFramesAvail = 0;
1058 return rc;
1059 }
1060 }
1061
1062 rc = RTErrConvertFromErrno(-(int)cFramesAvail);
1063 LogFunc(("failed - cFramesAvail=%ld rc=%Rrc\n", cFramesAvail, rc));
1064 *pcFramesAvail = 0;
1065 return rc;
1066}
1067
1068
1069/**
1070 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetPending}
1071 */
1072static DECLCALLBACK(uint32_t) drvHstAudAlsaHA_StreamGetPending(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1073{
1074 RT_NOREF(pInterface);
1075 PDRVHSTAUDALSASTREAM pStreamALSA = (PDRVHSTAUDALSASTREAM)pStream;
1076 AssertPtrReturn(pStreamALSA, 0);
1077
1078 /*
1079 * This is only relevant to output streams (input streams can't have
1080 * any pending, unplayed data).
1081 */
1082 uint32_t cbPending = 0;
1083 if (pStreamALSA->Cfg.enmDir == PDMAUDIODIR_OUT)
1084 {
1085 /*
1086 * Getting the delay (in audio frames) reports the time it will take
1087 * to hear a new sample after all queued samples have been played out.
1088 *
1089 * We use snd_pcm_avail_delay instead of snd_pcm_delay here as it will
1090 * update the buffer positions, and we can use the extra value against
1091 * the buffer size to double check since the delay value may include
1092 * fixed built-in delays in the processing chain and hardware.
1093 */
1094 snd_pcm_sframes_t cFramesAvail = 0;
1095 snd_pcm_sframes_t cFramesDelay = 0;
1096 int rc = snd_pcm_avail_delay(pStreamALSA->hPCM, &cFramesAvail, &cFramesDelay);
1097
1098 /*
1099 * We now also get the state as the pending value should be zero when
1100 * we're not in a playing state.
1101 */
1102 snd_pcm_state_t enmState = snd_pcm_state(pStreamALSA->hPCM);
1103 switch (enmState)
1104 {
1105 case SND_PCM_STATE_RUNNING:
1106 case SND_PCM_STATE_DRAINING:
1107 if (rc >= 0)
1108 {
1109 if ((uint32_t)cFramesAvail >= pStreamALSA->Cfg.Backend.cFramesBufferSize)
1110 cbPending = 0;
1111 else
1112 cbPending = PDMAudioPropsFramesToBytes(&pStreamALSA->Cfg.Props, cFramesDelay);
1113 }
1114 break;
1115
1116 default:
1117 break;
1118 }
1119 Log2Func(("returns %u (%#x) - cFramesBufferSize=%RU32 cFramesAvail=%ld cFramesDelay=%ld rc=%d; enmState=%s (%d) \n",
1120 cbPending, cbPending, pStreamALSA->Cfg.Backend.cFramesBufferSize, cFramesAvail, cFramesDelay, rc,
1121 snd_pcm_state_name(enmState), enmState));
1122 }
1123 return cbPending;
1124}
1125
1126
1127/**
1128 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
1129 */
1130static DECLCALLBACK(uint32_t) drvHstAudAlsaHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1131{
1132 RT_NOREF(pInterface);
1133 PDRVHSTAUDALSASTREAM pStreamALSA = (PDRVHSTAUDALSASTREAM)pStream;
1134
1135 uint32_t cbAvail = 0;
1136 snd_pcm_sframes_t cFramesAvail = 0;
1137 int rc = alsaStreamGetAvail(pStreamALSA->hPCM, &cFramesAvail);
1138 if (RT_SUCCESS(rc))
1139 cbAvail = PDMAudioPropsFramesToBytes(&pStreamALSA->Cfg.Props, cFramesAvail);
1140
1141 return cbAvail;
1142}
1143
1144
1145/**
1146 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
1147 */
1148static DECLCALLBACK(int) drvHstAudAlsaHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
1149 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
1150{
1151 PDRVHSTAUDALSASTREAM pStreamALSA = (PDRVHSTAUDALSASTREAM)pStream;
1152 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1153 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1154 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
1155 Log4Func(("@%#RX64: pvBuf=%p cbBuf=%#x (%u) state=%s - %s\n", pStreamALSA->offInternal, pvBuf, cbBuf, cbBuf,
1156 snd_pcm_state_name(snd_pcm_state(pStreamALSA->hPCM)), pStreamALSA->Cfg.szName));
1157 if (cbBuf)
1158 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1159 else
1160 {
1161 /* Fend off draining calls. */
1162 *pcbWritten = 0;
1163 return VINF_SUCCESS;
1164 }
1165
1166 /*
1167 * Determine how much we can write (caller actually did this
1168 * already, but we repeat it just to be sure or something).
1169 */
1170 snd_pcm_sframes_t cFramesAvail;
1171 int rc = alsaStreamGetAvail(pStreamALSA->hPCM, &cFramesAvail);
1172 if (RT_SUCCESS(rc))
1173 {
1174 Assert(cFramesAvail);
1175 if (cFramesAvail)
1176 {
1177 PCPDMAUDIOPCMPROPS pProps = &pStreamALSA->Cfg.Props;
1178 uint32_t cbToWrite = PDMAudioPropsFramesToBytes(pProps, (uint32_t)cFramesAvail);
1179 if (cbToWrite)
1180 {
1181 if (cbToWrite > cbBuf)
1182 cbToWrite = cbBuf;
1183
1184 /*
1185 * Try write the data.
1186 */
1187 uint32_t cFramesToWrite = PDMAudioPropsBytesToFrames(pProps, cbToWrite);
1188 snd_pcm_sframes_t cFramesWritten = snd_pcm_writei(pStreamALSA->hPCM, pvBuf, cFramesToWrite);
1189 if (cFramesWritten > 0)
1190 {
1191 Log4Func(("snd_pcm_writei w/ cbToWrite=%u -> %ld (frames) [cFramesAvail=%ld]\n",
1192 cbToWrite, cFramesWritten, cFramesAvail));
1193 *pcbWritten = PDMAudioPropsFramesToBytes(pProps, cFramesWritten);
1194 pStreamALSA->offInternal += *pcbWritten;
1195 return VINF_SUCCESS;
1196 }
1197 LogFunc(("snd_pcm_writei w/ cbToWrite=%u -> %ld [cFramesAvail=%ld]\n", cbToWrite, cFramesWritten, cFramesAvail));
1198
1199
1200 /*
1201 * There are a couple of error we can recover from, try to do so.
1202 * Only don't try too many times.
1203 */
1204 for (unsigned iTry = 0;
1205 (cFramesWritten == -EPIPE || cFramesWritten == -ESTRPIPE) && iTry < ALSA_RECOVERY_TRIES_MAX;
1206 iTry++)
1207 {
1208 if (cFramesWritten == -EPIPE)
1209 {
1210 /* Underrun occurred. */
1211 rc = drvHstAudAlsaStreamRecover(pStreamALSA->hPCM);
1212 if (RT_FAILURE(rc))
1213 break;
1214 LogFlowFunc(("Recovered from playback (iTry=%u)\n", iTry));
1215 }
1216 else
1217 {
1218 /* An suspended event occurred, needs resuming. */
1219 rc = drvHstAudAlsaStreamResume(pStreamALSA->hPCM);
1220 if (RT_FAILURE(rc))
1221 {
1222 LogRel(("ALSA: Failed to resume output stream (iTry=%u, rc=%Rrc)\n", iTry, rc));
1223 break;
1224 }
1225 LogFlowFunc(("Resumed suspended output stream (iTry=%u)\n", iTry));
1226 }
1227
1228 cFramesWritten = snd_pcm_writei(pStreamALSA->hPCM, pvBuf, cFramesToWrite);
1229 if (cFramesWritten > 0)
1230 {
1231 Log4Func(("snd_pcm_writei w/ cbToWrite=%u -> %ld (frames) [cFramesAvail=%ld]\n",
1232 cbToWrite, cFramesWritten, cFramesAvail));
1233 *pcbWritten = PDMAudioPropsFramesToBytes(pProps, cFramesWritten);
1234 pStreamALSA->offInternal += *pcbWritten;
1235 return VINF_SUCCESS;
1236 }
1237 LogFunc(("snd_pcm_writei w/ cbToWrite=%u -> %ld [cFramesAvail=%ld, iTry=%d]\n", cbToWrite, cFramesWritten, cFramesAvail, iTry));
1238 }
1239
1240 /* Make sure we return with an error status. */
1241 if (RT_SUCCESS_NP(rc))
1242 {
1243 if (cFramesWritten == 0)
1244 rc = VERR_ACCESS_DENIED;
1245 else
1246 {
1247 rc = RTErrConvertFromErrno(-(int)cFramesWritten);
1248 LogFunc(("Failed to write %RU32 bytes: %ld (%Rrc)\n", cbToWrite, cFramesWritten, rc));
1249 }
1250 }
1251 }
1252 }
1253 }
1254 else
1255 LogFunc(("Error getting number of playback frames, rc=%Rrc\n", rc));
1256 *pcbWritten = 0;
1257 return rc;
1258}
1259
1260
1261/**
1262 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
1263 */
1264static DECLCALLBACK(uint32_t) drvHstAudAlsaHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1265{
1266 RT_NOREF(pInterface);
1267 PDRVHSTAUDALSASTREAM pStreamALSA = (PDRVHSTAUDALSASTREAM)pStream;
1268
1269 uint32_t cbAvail = 0;
1270 snd_pcm_sframes_t cFramesAvail = 0;
1271 int rc = alsaStreamGetAvail(pStreamALSA->hPCM, &cFramesAvail);
1272 if (RT_SUCCESS(rc))
1273 cbAvail = PDMAudioPropsFramesToBytes(&pStreamALSA->Cfg.Props, cFramesAvail);
1274
1275 return cbAvail;
1276}
1277
1278
1279/**
1280 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
1281 */
1282static DECLCALLBACK(int) drvHstAudAlsaHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
1283 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1284{
1285 RT_NOREF_PV(pInterface);
1286 PDRVHSTAUDALSASTREAM pStreamALSA = (PDRVHSTAUDALSASTREAM)pStream;
1287 AssertPtrReturn(pStreamALSA, VERR_INVALID_POINTER);
1288 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1289 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1290 AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
1291 Log4Func(("@%#RX64: pvBuf=%p cbBuf=%#x (%u) state=%s - %s\n", pStreamALSA->offInternal, pvBuf, cbBuf, cbBuf,
1292 snd_pcm_state_name(snd_pcm_state(pStreamALSA->hPCM)), pStreamALSA->Cfg.szName));
1293
1294 /*
1295 * Figure out how much we can read without trouble (we're doing
1296 * non-blocking reads, but whatever).
1297 */
1298 snd_pcm_sframes_t cAvail;
1299 int rc = alsaStreamGetAvail(pStreamALSA->hPCM, &cAvail);
1300 if (RT_SUCCESS(rc))
1301 {
1302 if (!cAvail) /* No data yet? */
1303 {
1304 snd_pcm_state_t enmState = snd_pcm_state(pStreamALSA->hPCM);
1305 switch (enmState)
1306 {
1307 case SND_PCM_STATE_PREPARED:
1308 /** @todo r=bird: explain the logic here... */
1309 cAvail = PDMAudioPropsBytesToFrames(&pStreamALSA->Cfg.Props, cbBuf);
1310 break;
1311
1312 case SND_PCM_STATE_SUSPENDED:
1313 rc = drvHstAudAlsaStreamResume(pStreamALSA->hPCM);
1314 if (RT_SUCCESS(rc))
1315 {
1316 LogFlowFunc(("Resumed suspended input stream.\n"));
1317 break;
1318 }
1319 LogFunc(("Failed resuming suspended input stream: %Rrc\n", rc));
1320 return rc;
1321
1322 default:
1323 LogFlow(("No frames available: state=%s (%d)\n", snd_pcm_state_name(enmState), enmState));
1324 break;
1325 }
1326 if (!cAvail)
1327 {
1328 *pcbRead = 0;
1329 return VINF_SUCCESS;
1330 }
1331 }
1332 }
1333 else
1334 {
1335 LogFunc(("Error getting number of captured frames, rc=%Rrc\n", rc));
1336 return rc;
1337 }
1338
1339 size_t cbToRead = PDMAudioPropsFramesToBytes(&pStreamALSA->Cfg.Props, cAvail);
1340 cbToRead = RT_MIN(cbToRead, cbBuf);
1341 LogFlowFunc(("cbToRead=%zu, cAvail=%RI32\n", cbToRead, cAvail));
1342
1343 /*
1344 * Read loop.
1345 */
1346 uint32_t cbReadTotal = 0;
1347 while (cbToRead > 0)
1348 {
1349 /*
1350 * Do the reading.
1351 */
1352 snd_pcm_uframes_t const cFramesToRead = PDMAudioPropsBytesToFrames(&pStreamALSA->Cfg.Props, cbToRead);
1353 AssertBreakStmt(cFramesToRead > 0, rc = VERR_NO_DATA);
1354
1355 snd_pcm_sframes_t cFramesRead = snd_pcm_readi(pStreamALSA->hPCM, pvBuf, cFramesToRead);
1356 if (cFramesRead > 0)
1357 {
1358 /*
1359 * We should not run into a full mixer buffer or we lose samples and
1360 * run into an endless loop if ALSA keeps producing samples ("null"
1361 * capture device for example).
1362 */
1363 uint32_t const cbRead = PDMAudioPropsFramesToBytes(&pStreamALSA->Cfg.Props, cFramesRead);
1364 Assert(cbRead <= cbToRead);
1365
1366 cbToRead -= cbRead;
1367 cbReadTotal += cbRead;
1368 pvBuf = (uint8_t *)pvBuf + cbRead;
1369 pStreamALSA->offInternal += cbRead;
1370 }
1371 else
1372 {
1373 /*
1374 * Try recover from overrun and re-try.
1375 * Other conditions/errors we cannot and will just quit the loop.
1376 */
1377 if (cFramesRead == -EPIPE)
1378 {
1379 rc = drvHstAudAlsaStreamRecover(pStreamALSA->hPCM);
1380 if (RT_SUCCESS(rc))
1381 {
1382 LogFlowFunc(("Successfully recovered from overrun\n"));
1383 continue;
1384 }
1385 LogFunc(("Failed to recover from overrun: %Rrc\n", rc));
1386 }
1387 else if (cFramesRead == -EAGAIN)
1388 LogFunc(("No input frames available (EAGAIN)\n"));
1389 else if (cFramesRead == 0)
1390 LogFunc(("No input frames available (0)\n"));
1391 else
1392 {
1393 rc = RTErrConvertFromErrno(-(int)cFramesRead);
1394 LogFunc(("Failed to read input frames: %s (%ld, %Rrc)\n", snd_strerror(cFramesRead), cFramesRead, rc));
1395 }
1396
1397 /* If we've read anything, suppress the error. */
1398 if (RT_FAILURE(rc) && cbReadTotal > 0)
1399 {
1400 LogFunc(("Suppressing %Rrc because %#x bytes has been read already\n", rc, cbReadTotal));
1401 rc = VINF_SUCCESS;
1402 }
1403 break;
1404 }
1405 }
1406
1407 LogFlowFunc(("returns %Rrc and %#x (%d) bytes (%u bytes left); state %s\n",
1408 rc, cbReadTotal, cbReadTotal, cbToRead, snd_pcm_state_name(snd_pcm_state(pStreamALSA->hPCM))));
1409 *pcbRead = cbReadTotal;
1410 return rc;
1411}
1412
1413
1414/*********************************************************************************************************************************
1415* PDMIBASE *
1416*********************************************************************************************************************************/
1417
1418/**
1419 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1420 */
1421static DECLCALLBACK(void *) drvHstAudAlsaQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1422{
1423 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1424 PDRVHSTAUDALSA pThis = PDMINS_2_DATA(pDrvIns, PDRVHSTAUDALSA);
1425 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1426 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1427 return NULL;
1428}
1429
1430
1431/*********************************************************************************************************************************
1432* PDMDRVREG *
1433*********************************************************************************************************************************/
1434
1435/**
1436 * @interface_method_impl{PDMDRVREG,pfnDestruct,
1437 * Destructs an ALSA host audio driver instance.}
1438 */
1439static DECLCALLBACK(void) drvHstAudAlsaDestruct(PPDMDRVINS pDrvIns)
1440{
1441 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1442 PDRVHSTAUDALSA pThis = PDMINS_2_DATA(pDrvIns, PDRVHSTAUDALSA);
1443 LogFlowFuncEnter();
1444
1445 if (RTCritSectIsInitialized(&pThis->CritSect))
1446 {
1447 RTCritSectEnter(&pThis->CritSect);
1448 pThis->pIHostAudioPort = NULL;
1449 RTCritSectLeave(&pThis->CritSect);
1450 RTCritSectDelete(&pThis->CritSect);
1451 }
1452
1453 LogFlowFuncLeave();
1454}
1455
1456
1457/**
1458 * @interface_method_impl{PDMDRVREG,pfnConstruct,
1459 * Construct an ALSA host audio driver instance.}
1460 */
1461static DECLCALLBACK(int) drvHstAudAlsaConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1462{
1463 RT_NOREF(fFlags);
1464 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1465 PDRVHSTAUDALSA pThis = PDMINS_2_DATA(pDrvIns, PDRVHSTAUDALSA);
1466 LogRel(("Audio: Initializing ALSA driver\n"));
1467
1468 /*
1469 * Init the static parts.
1470 */
1471 pThis->pDrvIns = pDrvIns;
1472 int rc = RTCritSectInit(&pThis->CritSect);
1473 AssertRCReturn(rc, rc);
1474 /* IBase */
1475 pDrvIns->IBase.pfnQueryInterface = drvHstAudAlsaQueryInterface;
1476 /* IHostAudio */
1477 pThis->IHostAudio.pfnGetConfig = drvHstAudAlsaHA_GetConfig;
1478 pThis->IHostAudio.pfnGetDevices = drvHstAudAlsaHA_GetDevices;
1479 pThis->IHostAudio.pfnSetDevice = drvHstAudAlsaHA_SetDevice;
1480 pThis->IHostAudio.pfnGetStatus = drvHstAudAlsaHA_GetStatus;
1481 pThis->IHostAudio.pfnDoOnWorkerThread = NULL;
1482 pThis->IHostAudio.pfnStreamConfigHint = NULL;
1483 pThis->IHostAudio.pfnStreamCreate = drvHstAudAlsaHA_StreamCreate;
1484 pThis->IHostAudio.pfnStreamInitAsync = NULL;
1485 pThis->IHostAudio.pfnStreamDestroy = drvHstAudAlsaHA_StreamDestroy;
1486 pThis->IHostAudio.pfnStreamNotifyDeviceChanged = NULL;
1487 pThis->IHostAudio.pfnStreamEnable = drvHstAudAlsaHA_StreamEnable;
1488 pThis->IHostAudio.pfnStreamDisable = drvHstAudAlsaHA_StreamDisable;
1489 pThis->IHostAudio.pfnStreamPause = drvHstAudAlsaHA_StreamPause;
1490 pThis->IHostAudio.pfnStreamResume = drvHstAudAlsaHA_StreamResume;
1491 pThis->IHostAudio.pfnStreamDrain = drvHstAudAlsaHA_StreamDrain;
1492 pThis->IHostAudio.pfnStreamGetPending = drvHstAudAlsaHA_StreamGetPending;
1493 pThis->IHostAudio.pfnStreamGetState = drvHstAudAlsaHA_StreamGetState;
1494 pThis->IHostAudio.pfnStreamGetWritable = drvHstAudAlsaHA_StreamGetWritable;
1495 pThis->IHostAudio.pfnStreamPlay = drvHstAudAlsaHA_StreamPlay;
1496 pThis->IHostAudio.pfnStreamGetReadable = drvHstAudAlsaHA_StreamGetReadable;
1497 pThis->IHostAudio.pfnStreamCapture = drvHstAudAlsaHA_StreamCapture;
1498
1499 /*
1500 * Read configuration.
1501 */
1502 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "OutputDeviceID|InputDeviceID", "");
1503
1504 rc = CFGMR3QueryStringDef(pCfg, "InputDeviceID", pThis->szInputDev, sizeof(pThis->szInputDev), "default");
1505 AssertRCReturn(rc, rc);
1506 rc = CFGMR3QueryStringDef(pCfg, "OutputDeviceID", pThis->szOutputDev, sizeof(pThis->szOutputDev), "default");
1507 AssertRCReturn(rc, rc);
1508
1509 /*
1510 * Init the alsa library.
1511 */
1512 rc = audioLoadAlsaLib();
1513 if (RT_FAILURE(rc))
1514 {
1515 LogRel(("ALSA: Failed to load the ALSA shared library: %Rrc\n", rc));
1516 return rc;
1517 }
1518
1519 /*
1520 * Query the notification interface from the driver/device above us.
1521 */
1522 pThis->pIHostAudioPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIHOSTAUDIOPORT);
1523 AssertReturn(pThis->pIHostAudioPort, VERR_PDM_MISSING_INTERFACE_ABOVE);
1524
1525#ifdef DEBUG
1526 /*
1527 * Some debug stuff we don't use for anything at all.
1528 */
1529 snd_lib_error_set_handler(drvHstAudAlsaDbgErrorHandler);
1530#endif
1531 return VINF_SUCCESS;
1532}
1533
1534
1535/**
1536 * ALSA audio driver registration record.
1537 */
1538const PDMDRVREG g_DrvHostALSAAudio =
1539{
1540 /* u32Version */
1541 PDM_DRVREG_VERSION,
1542 /* szName */
1543 "ALSAAudio",
1544 /* szRCMod */
1545 "",
1546 /* szR0Mod */
1547 "",
1548 /* pszDescription */
1549 "ALSA host audio driver",
1550 /* fFlags */
1551 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1552 /* fClass. */
1553 PDM_DRVREG_CLASS_AUDIO,
1554 /* cMaxInstances */
1555 ~0U,
1556 /* cbInstance */
1557 sizeof(DRVHSTAUDALSA),
1558 /* pfnConstruct */
1559 drvHstAudAlsaConstruct,
1560 /* pfnDestruct */
1561 drvHstAudAlsaDestruct,
1562 /* pfnRelocate */
1563 NULL,
1564 /* pfnIOCtl */
1565 NULL,
1566 /* pfnPowerOn */
1567 NULL,
1568 /* pfnReset */
1569 NULL,
1570 /* pfnSuspend */
1571 NULL,
1572 /* pfnResume */
1573 NULL,
1574 /* pfnAttach */
1575 NULL,
1576 /* pfnDetach */
1577 NULL,
1578 /* pfnPowerOff */
1579 NULL,
1580 /* pfnSoftReset */
1581 NULL,
1582 /* u32EndVersion */
1583 PDM_DRVREG_VERSION
1584};
1585
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use