VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostALSAAudio.cpp@ 82450

Last change on this file since 82450 was 82255, checked in by vboxsync, 5 years ago

vmm/pdmaudioifs.h: More of the same. bugref:9218

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 45.4 KB
Line 
1/* $Id: DrvHostALSAAudio.cpp 82255 2019-11-27 23:20:26Z vboxsync $ */
2/** @file
3 * ALSA audio driver.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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
53RT_C_DECLS_BEGIN
54 #include "alsa_stubs.h"
55 #include "alsa_mangling.h"
56RT_C_DECLS_END
57
58#include <alsa/asoundlib.h>
59#include <alsa/control.h> /* For device enumeration. */
60
61#include "DrvAudio.h"
62#include "VBoxDD.h"
63
64
65/*********************************************************************************************************************************
66* Defines *
67*********************************************************************************************************************************/
68
69/** Makes DRVHOSTALSAAUDIO out of PDMIHOSTAUDIO. */
70#define PDMIHOSTAUDIO_2_DRVHOSTALSAAUDIO(pInterface) \
71 ( (PDRVHOSTALSAAUDIO)((uintptr_t)pInterface - RT_UOFFSETOF(DRVHOSTALSAAUDIO, IHostAudio)) )
72
73
74/*********************************************************************************************************************************
75* Structures *
76*********************************************************************************************************************************/
77
78typedef struct ALSAAUDIOSTREAM
79{
80 /** The stream's acquired configuration. */
81 PPDMAUDIOSTREAMCFG pCfg;
82 union
83 {
84 struct
85 {
86 } In;
87 struct
88 {
89 } Out;
90 };
91 snd_pcm_t *phPCM;
92 void *pvBuf;
93 size_t cbBuf;
94} ALSAAUDIOSTREAM, *PALSAAUDIOSTREAM;
95
96/* latency = period_size * periods / (rate * bytes_per_frame) */
97
98static int alsaStreamRecover(snd_pcm_t *phPCM);
99
100/**
101 * Host Alsa audio driver instance data.
102 * @implements PDMIAUDIOCONNECTOR
103 */
104typedef struct DRVHOSTALSAAUDIO
105{
106 /** Pointer to the driver instance structure. */
107 PPDMDRVINS pDrvIns;
108 /** Pointer to host audio interface. */
109 PDMIHOSTAUDIO IHostAudio;
110 /** Error count for not flooding the release log.
111 * UINT32_MAX for unlimited logging. */
112 uint32_t cLogErrors;
113} DRVHOSTALSAAUDIO, *PDRVHOSTALSAAUDIO;
114
115/** Maximum number of tries to recover a broken pipe. */
116#define ALSA_RECOVERY_TRIES_MAX 5
117
118typedef struct ALSAAUDIOSTREAMCFG
119{
120 unsigned int freq;
121 /** PCM sound format. */
122 snd_pcm_format_t fmt;
123 /** PCM data access type. */
124 snd_pcm_access_t access;
125 /** Whether resampling should be performed by alsalib or not. */
126 int resample;
127 int nchannels;
128 /** Buffer size (in audio frames). */
129 unsigned long buffer_size;
130 /** Periods (in audio frames). */
131 unsigned long period_size;
132 /** For playback: Starting to play threshold (in audio frames).
133 * For Capturing: Starting to capture threshold (in audio frames). */
134 unsigned long threshold;
135} ALSAAUDIOSTREAMCFG, *PALSAAUDIOSTREAMCFG;
136
137
138
139static snd_pcm_format_t alsaAudioPropsToALSA(PPDMAUDIOPCMPROPS pProps)
140{
141 switch (pProps->cbSample)
142 {
143 case 1:
144 return pProps->fSigned ? SND_PCM_FORMAT_S8 : SND_PCM_FORMAT_U8;
145
146 case 2:
147 return pProps->fSigned ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_U16_LE;
148
149 case 4:
150 return pProps->fSigned ? SND_PCM_FORMAT_S32_LE : SND_PCM_FORMAT_U32_LE;
151
152 default:
153 break;
154 }
155
156 AssertMsgFailed(("%RU8 bytes not supported\n", pProps->cbSample));
157 return SND_PCM_FORMAT_U8;
158}
159
160
161static int alsaALSAToAudioProps(snd_pcm_format_t fmt, PPDMAUDIOPCMPROPS pProps)
162{
163 /** @todo r=bird: Why isn't this code setting fSwapEndian for every case? It
164 * seems to make some UNDOCUMENT ASSUMPTIONS about pProps. */
165 switch (fmt)
166 {
167 case SND_PCM_FORMAT_S8:
168 pProps->cbSample = 1;
169 pProps->fSigned = true;
170 break;
171
172 case SND_PCM_FORMAT_U8:
173 pProps->cbSample = 1;
174 pProps->fSigned = false;
175 break;
176
177 case SND_PCM_FORMAT_S16_LE:
178 pProps->cbSample = 2;
179 pProps->fSigned = true;
180 break;
181
182 case SND_PCM_FORMAT_U16_LE:
183 pProps->cbSample = 2;
184 pProps->fSigned = false;
185 break;
186
187 case SND_PCM_FORMAT_S16_BE:
188 pProps->cbSample = 2;
189 pProps->fSigned = true;
190#ifdef RT_LITTLE_ENDIAN
191 pProps->fSwapEndian = true;
192#endif
193 break;
194
195 case SND_PCM_FORMAT_U16_BE:
196 pProps->cbSample = 2;
197 pProps->fSigned = false;
198#ifdef RT_LITTLE_ENDIAN
199 pProps->fSwapEndian = true;
200#endif
201 break;
202
203 case SND_PCM_FORMAT_S32_LE:
204 pProps->cbSample = 4;
205 pProps->fSigned = true;
206 break;
207
208 case SND_PCM_FORMAT_U32_LE:
209 pProps->cbSample = 4;
210 pProps->fSigned = false;
211 break;
212
213 case SND_PCM_FORMAT_S32_BE:
214 pProps->cbSample = 4;
215 pProps->fSigned = true;
216#ifdef RT_LITTLE_ENDIAN
217 pProps->fSwapEndian = true;
218#endif
219 break;
220
221 case SND_PCM_FORMAT_U32_BE:
222 pProps->cbSample = 4;
223 pProps->fSigned = false;
224#ifdef RT_LITTLE_ENDIAN
225 pProps->fSwapEndian = true;
226#endif
227 break;
228
229 default:
230 AssertMsgFailedReturn(("Format %d not supported\n", fmt), VERR_NOT_SUPPORTED);
231 }
232
233 Assert(pProps->cbSample > 0);
234 Assert(pProps->cChannels > 0);
235 pProps->cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pProps->cbSample, pProps->cChannels);
236
237 return VINF_SUCCESS;
238}
239
240
241static int alsaStreamSetSWParams(snd_pcm_t *phPCM, bool fIn, PALSAAUDIOSTREAMCFG pCfgReq, PALSAAUDIOSTREAMCFG pCfgObt)
242{
243 if (fIn) /* For input streams there's nothing to do in here right now. */
244 return VINF_SUCCESS;
245
246 snd_pcm_sw_params_t *pSWParms = NULL;
247 snd_pcm_sw_params_alloca(&pSWParms);
248 if (!pSWParms)
249 return VERR_NO_MEMORY;
250
251 int rc;
252 do
253 {
254 int err = snd_pcm_sw_params_current(phPCM, pSWParms);
255 if (err < 0)
256 {
257 LogRel(("ALSA: Failed to get current software parameters: %s\n", snd_strerror(err)));
258 rc = VERR_ACCESS_DENIED;
259 break;
260 }
261
262 err = snd_pcm_sw_params_set_start_threshold(phPCM, pSWParms, pCfgReq->threshold);
263 if (err < 0)
264 {
265 LogRel(("ALSA: Failed to set software threshold to %ld: %s\n", pCfgReq->threshold, snd_strerror(err)));
266 rc = VERR_ACCESS_DENIED;
267 break;
268 }
269
270 err = snd_pcm_sw_params_set_avail_min(phPCM, pSWParms, pCfgReq->period_size);
271 if (err < 0)
272 {
273 LogRel(("ALSA: Failed to set available minimum to %ld: %s\n", pCfgReq->threshold, snd_strerror(err)));
274 rc = VERR_ACCESS_DENIED;
275 break;
276 }
277
278 err = snd_pcm_sw_params(phPCM, pSWParms);
279 if (err < 0)
280 {
281 LogRel(("ALSA: Failed to set new software parameters: %s\n", snd_strerror(err)));
282 rc = VERR_ACCESS_DENIED;
283 break;
284 }
285
286 err = snd_pcm_sw_params_get_start_threshold(pSWParms, &pCfgObt->threshold);
287 if (err < 0)
288 {
289 LogRel(("ALSA: Failed to get start threshold\n"));
290 rc = VERR_ACCESS_DENIED;
291 break;
292 }
293
294 LogFunc(("Setting threshold to %RU32 frames\n", pCfgObt->threshold));
295 rc = VINF_SUCCESS;
296 }
297 while (0);
298
299 return rc;
300}
301
302
303static int alsaStreamClose(snd_pcm_t **pphPCM)
304{
305 if (!pphPCM || !*pphPCM)
306 return VINF_SUCCESS;
307
308 int rc;
309 int rc2 = snd_pcm_close(*pphPCM);
310 if (rc2)
311 {
312 LogRel(("ALSA: Closing PCM descriptor failed: %s\n", snd_strerror(rc2)));
313 rc = VERR_GENERAL_FAILURE; /** @todo */
314 }
315 else
316 {
317 *pphPCM = NULL;
318 rc = VINF_SUCCESS;
319 }
320
321 LogFlowFuncLeaveRC(rc);
322 return rc;
323}
324
325
326static int alsaStreamOpen(bool fIn, PALSAAUDIOSTREAMCFG pCfgReq, PALSAAUDIOSTREAMCFG pCfgObt, snd_pcm_t **pphPCM)
327{
328 snd_pcm_t *phPCM = NULL;
329
330 int rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
331
332 unsigned int cChannels = pCfgReq->nchannels;
333 unsigned int uFreq = pCfgReq->freq;
334 snd_pcm_uframes_t obt_buffer_size;
335
336 do
337 {
338 const char *pszDev = "default"; /** @todo Make this configurable through PALSAAUDIOSTREAMCFG. */
339 if (!pszDev)
340 {
341 LogRel(("ALSA: Invalid or no %s device name set\n", fIn ? "input" : "output"));
342 rc = VERR_INVALID_PARAMETER;
343 break;
344 }
345
346 LogRel(("ALSA: Using %s device \"%s\"\n", fIn ? "input" : "output", pszDev));
347
348 int err = snd_pcm_open(&phPCM, pszDev,
349 fIn ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
350 SND_PCM_NONBLOCK);
351 if (err < 0)
352 {
353 LogRel(("ALSA: Failed to open \"%s\" as %s device: %s\n", pszDev, fIn ? "input" : "output", snd_strerror(err)));
354 break;
355 }
356
357 err = snd_pcm_nonblock(phPCM, 1);
358 if (err < 0)
359 {
360 LogRel(("ALSA: Error setting output non-blocking mode: %s\n", snd_strerror(err)));
361 break;
362 }
363
364 snd_pcm_hw_params_t *pHWParms;
365 snd_pcm_hw_params_alloca(&pHWParms); /** @todo Check for successful allocation? */
366 err = snd_pcm_hw_params_any(phPCM, pHWParms);
367 if (err < 0)
368 {
369 LogRel(("ALSA: Failed to initialize hardware parameters: %s\n", snd_strerror(err)));
370 break;
371 }
372
373 err = snd_pcm_hw_params_set_access(phPCM, pHWParms, SND_PCM_ACCESS_RW_INTERLEAVED);
374 if (err < 0)
375 {
376 LogRel(("ALSA: Failed to set access type: %s\n", snd_strerror(err)));
377 break;
378 }
379
380 err = snd_pcm_hw_params_set_format(phPCM, pHWParms, pCfgReq->fmt);
381 if (err < 0)
382 {
383 LogRel(("ALSA: Failed to set audio format to %d: %s\n", pCfgReq->fmt, snd_strerror(err)));
384 break;
385 }
386
387 err = snd_pcm_hw_params_set_rate_near(phPCM, pHWParms, &uFreq, 0);
388 if (err < 0)
389 {
390 LogRel(("ALSA: Failed to set frequency to %uHz: %s\n", pCfgReq->freq, snd_strerror(err)));
391 break;
392 }
393
394 err = snd_pcm_hw_params_set_channels_near(phPCM, pHWParms, &cChannels);
395 if (err < 0)
396 {
397 LogRel(("ALSA: Failed to set number of channels to %d\n", pCfgReq->nchannels));
398 break;
399 }
400
401 if ( cChannels != 1
402 && cChannels != 2)
403 {
404 LogRel(("ALSA: Number of audio channels (%u) not supported\n", cChannels));
405 break;
406 }
407
408 snd_pcm_uframes_t period_size_f = pCfgReq->period_size;
409 snd_pcm_uframes_t buffer_size_f = pCfgReq->buffer_size;
410
411 snd_pcm_uframes_t minval = period_size_f;
412
413 int dir = 0;
414 err = snd_pcm_hw_params_get_period_size_min(pHWParms, &minval, &dir);
415 if (err < 0)
416 {
417 LogRel(("ALSA: Could not determine minimal period size\n"));
418 break;
419 }
420 else
421 {
422 LogFunc(("Minimal period size is: %ld\n", minval));
423 if (period_size_f < minval)
424 period_size_f = minval;
425 }
426
427 err = snd_pcm_hw_params_set_period_size_near(phPCM, pHWParms, &period_size_f, 0);
428 LogFunc(("Period size is: %RU32\n", period_size_f));
429 if (err < 0)
430 {
431 LogRel(("ALSA: Failed to set period size %d (%s)\n", period_size_f, snd_strerror(err)));
432 break;
433 }
434
435 minval = buffer_size_f;
436 err = snd_pcm_hw_params_get_buffer_size_min(pHWParms, &minval);
437 if (err < 0)
438 {
439 LogRel(("ALSA: Could not retrieve minimal buffer size\n"));
440 break;
441 }
442 else
443 LogFunc(("Minimal buffer size is: %RU32\n", minval));
444
445 err = snd_pcm_hw_params_set_buffer_size_near(phPCM, pHWParms, &buffer_size_f);
446 if (err < 0)
447 {
448 LogRel(("ALSA: Failed to set near buffer size %RU32: %s\n", buffer_size_f, snd_strerror(err)));
449 break;
450 }
451
452 err = snd_pcm_hw_params(phPCM, pHWParms);
453 if (err < 0)
454 {
455 LogRel(("ALSA: Failed to apply audio parameters\n"));
456 break;
457 }
458
459 err = snd_pcm_hw_params_get_buffer_size(pHWParms, &obt_buffer_size);
460 if (err < 0)
461 {
462 LogRel(("ALSA: Failed to get buffer size\n"));
463 break;
464 }
465
466 snd_pcm_uframes_t obt_period_size;
467 err = snd_pcm_hw_params_get_period_size(pHWParms, &obt_period_size, &dir);
468 if (err < 0)
469 {
470 LogRel(("ALSA: Failed to get period size\n"));
471 break;
472 }
473
474 LogRel2(("ALSA: Frequency is %dHz, period size is %RU32 frames, buffer size is %RU32 frames\n",
475 pCfgReq->freq, obt_period_size, obt_buffer_size));
476
477 err = snd_pcm_prepare(phPCM);
478 if (err < 0)
479 {
480 LogRel(("ALSA: Could not prepare hPCM %p\n", (void *)phPCM));
481 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
482 break;
483 }
484
485 rc = alsaStreamSetSWParams(phPCM, fIn, pCfgReq, pCfgObt);
486 if (RT_FAILURE(rc))
487 break;
488
489 pCfgObt->fmt = pCfgReq->fmt;
490 pCfgObt->nchannels = cChannels;
491 pCfgObt->freq = uFreq;
492 pCfgObt->period_size = obt_period_size;
493 pCfgObt->buffer_size = obt_buffer_size;
494
495 rc = VINF_SUCCESS;
496 }
497 while (0);
498
499 if (RT_SUCCESS(rc))
500 {
501 *pphPCM = phPCM;
502 }
503 else
504 alsaStreamClose(&phPCM);
505
506 LogFlowFuncLeaveRC(rc);
507 return rc;
508}
509
510
511#ifdef DEBUG
512static void alsaDbgErrorHandler(const char *file, int line, const char *function,
513 int err, const char *fmt, ...)
514{
515 /** @todo Implement me! */
516 RT_NOREF(file, line, function, err, fmt);
517}
518#endif
519
520
521static int alsaStreamGetAvail(snd_pcm_t *phPCM, snd_pcm_sframes_t *pFramesAvail)
522{
523 AssertPtrReturn(phPCM, VERR_INVALID_POINTER);
524 /* pFramesAvail is optional. */
525
526 int rc;
527
528 snd_pcm_sframes_t framesAvail = snd_pcm_avail_update(phPCM);
529 if (framesAvail < 0)
530 {
531 if (framesAvail == -EPIPE)
532 {
533 rc = alsaStreamRecover(phPCM);
534 if (RT_SUCCESS(rc))
535 framesAvail = snd_pcm_avail_update(phPCM);
536 }
537 else
538 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
539 }
540 else
541 rc = VINF_SUCCESS;
542
543 if (RT_SUCCESS(rc))
544 {
545 if (pFramesAvail)
546 *pFramesAvail = framesAvail;
547 }
548
549 LogFunc(("cFrames=%ld, rc=%Rrc\n", framesAvail, rc));
550 return rc;
551}
552
553
554static int alsaStreamRecover(snd_pcm_t *phPCM)
555{
556 AssertPtrReturn(phPCM, VERR_INVALID_POINTER);
557
558 int err = snd_pcm_prepare(phPCM);
559 if (err < 0)
560 {
561 LogFunc(("Failed to recover stream %p: %s\n", phPCM, snd_strerror(err)));
562 return VERR_ACCESS_DENIED; /** @todo Find a better rc. */
563 }
564
565 return VINF_SUCCESS;
566}
567
568
569static int alsaStreamResume(snd_pcm_t *phPCM)
570{
571 AssertPtrReturn(phPCM, VERR_INVALID_POINTER);
572
573 int err = snd_pcm_resume(phPCM);
574 if (err < 0)
575 {
576 LogFunc(("Failed to resume stream %p: %s\n", phPCM, snd_strerror(err)));
577 return VERR_ACCESS_DENIED; /** @todo Find a better rc. */
578 }
579
580 return VINF_SUCCESS;
581}
582
583
584/**
585 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
586 */
587static DECLCALLBACK(int) drvHostAlsaAudioHA_Init(PPDMIHOSTAUDIO pInterface)
588{
589 RT_NOREF(pInterface);
590
591 LogFlowFuncEnter();
592
593 int rc = audioLoadAlsaLib();
594 if (RT_FAILURE(rc))
595 LogRel(("ALSA: Failed to load the ALSA shared library, rc=%Rrc\n", rc));
596 else
597 {
598#ifdef DEBUG
599 snd_lib_error_set_handler(alsaDbgErrorHandler);
600#endif
601 }
602
603 return rc;
604}
605
606
607/**
608 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
609 */
610static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
611 void *pvBuf, uint32_t uBufSize, uint32_t *puRead)
612{
613 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
614 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
615 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
616 AssertReturn(uBufSize, VERR_INVALID_PARAMETER);
617 /* pcbRead is optional. */
618
619 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
620
621 snd_pcm_sframes_t cAvail;
622 int rc = alsaStreamGetAvail(pStreamALSA->phPCM, &cAvail);
623 if (RT_FAILURE(rc))
624 {
625 LogFunc(("Error getting number of captured frames, rc=%Rrc\n", rc));
626 return rc;
627 }
628
629 PPDMAUDIOSTREAMCFG pCfg = pStreamALSA->pCfg;
630 AssertPtr(pCfg);
631
632 if (!cAvail) /* No data yet? */
633 {
634 snd_pcm_state_t state = snd_pcm_state(pStreamALSA->phPCM);
635 switch (state)
636 {
637 case SND_PCM_STATE_PREPARED:
638 cAvail = PDMAUDIOSTREAMCFG_B2F(pCfg, uBufSize);
639 break;
640
641 case SND_PCM_STATE_SUSPENDED:
642 {
643 rc = alsaStreamResume(pStreamALSA->phPCM);
644 if (RT_FAILURE(rc))
645 break;
646
647 LogFlow(("Resuming suspended input stream\n"));
648 break;
649 }
650
651 default:
652 LogFlow(("No frames available, state=%d\n", state));
653 break;
654 }
655
656 if (!cAvail)
657 {
658 if (puRead)
659 *puRead = 0;
660 return VINF_SUCCESS;
661 }
662 }
663
664 /*
665 * Check how much we can read from the capture device without overflowing
666 * the mixer buffer.
667 */
668 size_t cbToRead = RT_MIN((size_t)PDMAUDIOSTREAMCFG_F2B(pCfg, cAvail), uBufSize);
669
670 LogFlowFunc(("cbToRead=%zu, cAvail=%RI32\n", cbToRead, cAvail));
671
672 uint32_t cbReadTotal = 0;
673
674 snd_pcm_uframes_t cToRead;
675 snd_pcm_sframes_t cRead;
676
677 while ( cbToRead
678 && RT_SUCCESS(rc))
679 {
680 cToRead = RT_MIN(PDMAUDIOSTREAMCFG_B2F(pCfg, cbToRead),
681 PDMAUDIOSTREAMCFG_B2F(pCfg, pStreamALSA->cbBuf));
682 AssertBreakStmt(cToRead, rc = VERR_NO_DATA);
683 cRead = snd_pcm_readi(pStreamALSA->phPCM, pStreamALSA->pvBuf, cToRead);
684 if (cRead <= 0)
685 {
686 switch (cRead)
687 {
688 case 0:
689 {
690 LogFunc(("No input frames available\n"));
691 rc = VERR_ACCESS_DENIED;
692 break;
693 }
694
695 case -EAGAIN:
696 {
697 /*
698 * Don't set error here because EAGAIN means there are no further frames
699 * available at the moment, try later. As we might have read some frames
700 * already these need to be processed instead.
701 */
702 cbToRead = 0;
703 break;
704 }
705
706 case -EPIPE:
707 {
708 rc = alsaStreamRecover(pStreamALSA->phPCM);
709 if (RT_FAILURE(rc))
710 break;
711
712 LogFlowFunc(("Recovered from capturing\n"));
713 continue;
714 }
715
716 default:
717 {
718 LogFunc(("Failed to read input frames: %s\n", snd_strerror(cRead)));
719 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
720 break;
721 }
722 }
723 }
724 else
725 {
726 /*
727 * We should not run into a full mixer buffer or we loose samples and
728 * run into an endless loop if ALSA keeps producing samples ("null"
729 * capture device for example).
730 */
731 uint32_t cbRead = PDMAUDIOSTREAMCFG_F2B(pCfg, cRead);
732
733 memcpy(pvBuf, pStreamALSA->pvBuf, cbRead);
734
735 Assert(cbToRead >= cbRead);
736 cbToRead -= cbRead;
737 cbReadTotal += cbRead;
738 }
739 }
740
741 if (RT_SUCCESS(rc))
742 {
743 if (puRead)
744 *puRead = cbReadTotal;
745 }
746
747 return rc;
748}
749
750/**
751 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
752 */
753static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
754 const void *pvBuf, uint32_t uBufSize, uint32_t *puWritten)
755{
756 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
757 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
758 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
759 AssertReturn(uBufSize, VERR_INVALID_PARAMETER);
760 /* puWritten is optional. */
761
762 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
763
764 PPDMAUDIOSTREAMCFG pCfg = pStreamALSA->pCfg;
765 AssertPtr(pCfg);
766
767 int rc;
768
769 uint32_t cbWrittenTotal = 0;
770
771 do
772 {
773 snd_pcm_sframes_t csAvail;
774 rc = alsaStreamGetAvail(pStreamALSA->phPCM, &csAvail);
775 if (RT_FAILURE(rc))
776 {
777 LogFunc(("Error getting number of playback frames, rc=%Rrc\n", rc));
778 break;
779 }
780
781 if (!csAvail)
782 break;
783
784 size_t cbToWrite = RT_MIN((unsigned)PDMAUDIOSTREAMCFG_F2B(pCfg, csAvail), pStreamALSA->cbBuf);
785 if (!cbToWrite)
786 break;
787
788 /* Do not write more than available. */
789 if (cbToWrite > uBufSize)
790 cbToWrite = uBufSize;
791
792 memcpy(pStreamALSA->pvBuf, pvBuf, cbToWrite);
793
794 snd_pcm_sframes_t csWritten = 0;
795
796 /* Don't try infinitely on recoverable errors. */
797 unsigned iTry;
798 for (iTry = 0; iTry < ALSA_RECOVERY_TRIES_MAX; iTry++)
799 {
800 csWritten = snd_pcm_writei(pStreamALSA->phPCM, pStreamALSA->pvBuf,
801 PDMAUDIOSTREAMCFG_B2F(pCfg, cbToWrite));
802 if (csWritten <= 0)
803 {
804 switch (csWritten)
805 {
806 case 0:
807 {
808 LogFunc(("Failed to write %zu bytes\n", cbToWrite));
809 rc = VERR_ACCESS_DENIED;
810 break;
811 }
812
813 case -EPIPE:
814 {
815 rc = alsaStreamRecover(pStreamALSA->phPCM);
816 if (RT_FAILURE(rc))
817 break;
818
819 LogFlowFunc(("Recovered from playback\n"));
820 continue;
821 }
822
823 case -ESTRPIPE:
824 {
825 /* Stream was suspended and waiting for a recovery. */
826 rc = alsaStreamResume(pStreamALSA->phPCM);
827 if (RT_FAILURE(rc))
828 {
829 LogRel(("ALSA: Failed to resume output stream\n"));
830 break;
831 }
832
833 LogFlowFunc(("Resumed suspended output stream\n"));
834 continue;
835 }
836
837 default:
838 LogFlowFunc(("Failed to write %RU32 bytes, error unknown\n", cbToWrite));
839 rc = VERR_GENERAL_FAILURE; /** @todo */
840 break;
841 }
842 }
843 else
844 break;
845 } /* For number of tries. */
846
847 if ( iTry == ALSA_RECOVERY_TRIES_MAX
848 && csWritten <= 0)
849 rc = VERR_BROKEN_PIPE;
850
851 if (RT_FAILURE(rc))
852 break;
853
854 cbWrittenTotal = PDMAUDIOSTREAMCFG_F2B(pCfg, csWritten);
855
856 } while (0);
857
858 if (RT_SUCCESS(rc))
859 {
860 if (puWritten)
861 *puWritten = cbWrittenTotal;
862 }
863
864 return rc;
865}
866
867
868static int alsaDestroyStreamIn(PALSAAUDIOSTREAM pStreamALSA)
869{
870 alsaStreamClose(&pStreamALSA->phPCM);
871
872 if (pStreamALSA->pvBuf)
873 {
874 RTMemFree(pStreamALSA->pvBuf);
875 pStreamALSA->pvBuf = NULL;
876 }
877
878 return VINF_SUCCESS;
879}
880
881
882static int alsaDestroyStreamOut(PALSAAUDIOSTREAM pStreamALSA)
883{
884 alsaStreamClose(&pStreamALSA->phPCM);
885
886 if (pStreamALSA->pvBuf)
887 {
888 RTMemFree(pStreamALSA->pvBuf);
889 pStreamALSA->pvBuf = NULL;
890 }
891
892 return VINF_SUCCESS;
893}
894
895
896static int alsaCreateStreamOut(PALSAAUDIOSTREAM pStreamALSA, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
897{
898 snd_pcm_t *phPCM = NULL;
899
900 int rc;
901
902 do
903 {
904 ALSAAUDIOSTREAMCFG req;
905 req.fmt = alsaAudioPropsToALSA(&pCfgReq->Props);
906 req.freq = pCfgReq->Props.uHz;
907 req.nchannels = pCfgReq->Props.cChannels;
908 req.period_size = pCfgReq->Backend.cFramesPeriod;
909 req.buffer_size = pCfgReq->Backend.cFramesBufferSize;
910 req.threshold = pCfgReq->Backend.cFramesPreBuffering;
911
912 ALSAAUDIOSTREAMCFG obt;
913 rc = alsaStreamOpen(false /* fIn */, &req, &obt, &phPCM);
914 if (RT_FAILURE(rc))
915 break;
916
917 pCfgAcq->Props.uHz = obt.freq;
918 pCfgAcq->Props.cChannels = obt.nchannels;
919
920 rc = alsaALSAToAudioProps(obt.fmt, &pCfgAcq->Props);
921 if (RT_FAILURE(rc))
922 break;
923
924 pCfgAcq->Backend.cFramesPeriod = obt.period_size;
925 pCfgAcq->Backend.cFramesBufferSize = obt.buffer_size;
926 pCfgAcq->Backend.cFramesPreBuffering = obt.threshold;
927
928 pStreamALSA->cbBuf = pCfgAcq->Backend.cFramesBufferSize * DrvAudioHlpPCMPropsBytesPerFrame(&pCfgAcq->Props);
929 pStreamALSA->pvBuf = RTMemAllocZ(pStreamALSA->cbBuf);
930 if (!pStreamALSA->pvBuf)
931 {
932 LogRel(("ALSA: Not enough memory for output DAC buffer (%zu frames)\n", pCfgAcq->Backend.cFramesBufferSize));
933 rc = VERR_NO_MEMORY;
934 break;
935 }
936
937 pStreamALSA->phPCM = phPCM;
938 }
939 while (0);
940
941 if (RT_FAILURE(rc))
942 alsaStreamClose(&phPCM);
943
944 LogFlowFuncLeaveRC(rc);
945 return rc;
946}
947
948
949static int alsaCreateStreamIn(PALSAAUDIOSTREAM pStreamALSA, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
950{
951 int rc;
952
953 snd_pcm_t *phPCM = NULL;
954
955 do
956 {
957 ALSAAUDIOSTREAMCFG req;
958 req.fmt = alsaAudioPropsToALSA(&pCfgReq->Props);
959 req.freq = pCfgReq->Props.uHz;
960 req.nchannels = pCfgReq->Props.cChannels;
961 req.period_size = DrvAudioHlpMilliToFrames(50 /* ms */, &pCfgReq->Props); /** @todo Make this configurable. */
962 req.buffer_size = req.period_size * 2; /** @todo Make this configurable. */
963 req.threshold = req.period_size;
964
965 ALSAAUDIOSTREAMCFG obt;
966 rc = alsaStreamOpen(true /* fIn */, &req, &obt, &phPCM);
967 if (RT_FAILURE(rc))
968 break;
969
970 pCfgAcq->Props.uHz = obt.freq;
971 pCfgAcq->Props.cChannels = obt.nchannels;
972
973 rc = alsaALSAToAudioProps(obt.fmt, &pCfgAcq->Props);
974 if (RT_FAILURE(rc))
975 break;
976
977 pCfgAcq->Backend.cFramesPeriod = obt.period_size;
978 pCfgAcq->Backend.cFramesBufferSize = obt.buffer_size;
979 /* No pre-buffering. */
980
981 pStreamALSA->cbBuf = pCfgAcq->Backend.cFramesBufferSize * DrvAudioHlpPCMPropsBytesPerFrame(&pCfgAcq->Props);
982 pStreamALSA->pvBuf = RTMemAlloc(pStreamALSA->cbBuf);
983 if (!pStreamALSA->pvBuf)
984 {
985 LogRel(("ALSA: Not enough memory for input ADC buffer (%zu frames)\n", pCfgAcq->Backend.cFramesBufferSize));
986 rc = VERR_NO_MEMORY;
987 break;
988 }
989
990 pStreamALSA->phPCM = phPCM;
991 }
992 while (0);
993
994 if (RT_FAILURE(rc))
995 alsaStreamClose(&phPCM);
996
997 LogFlowFuncLeaveRC(rc);
998 return rc;
999}
1000
1001
1002static int alsaControlStreamIn(PALSAAUDIOSTREAM pStreamALSA, PDMAUDIOSTREAMCMD enmStreamCmd)
1003{
1004 int rc = VINF_SUCCESS;
1005
1006 int err;
1007
1008 switch (enmStreamCmd)
1009 {
1010 case PDMAUDIOSTREAMCMD_ENABLE:
1011 case PDMAUDIOSTREAMCMD_RESUME:
1012 {
1013 err = snd_pcm_prepare(pStreamALSA->phPCM);
1014 if (err < 0)
1015 {
1016 LogRel(("ALSA: Error preparing input stream: %s\n", snd_strerror(err)));
1017 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1018 }
1019 else
1020 {
1021 Assert(snd_pcm_state(pStreamALSA->phPCM) == SND_PCM_STATE_PREPARED);
1022
1023 /* Only start the PCM stream for input streams. */
1024 err = snd_pcm_start(pStreamALSA->phPCM);
1025 if (err < 0)
1026 {
1027 LogRel(("ALSA: Error starting input stream: %s\n", snd_strerror(err)));
1028 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1029 }
1030 }
1031
1032 break;
1033 }
1034
1035 case PDMAUDIOSTREAMCMD_DISABLE:
1036 {
1037 err = snd_pcm_drop(pStreamALSA->phPCM);
1038 if (err < 0)
1039 {
1040 LogRel(("ALSA: Error disabling input stream: %s\n", snd_strerror(err)));
1041 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1042 }
1043 break;
1044 }
1045
1046 case PDMAUDIOSTREAMCMD_PAUSE:
1047 {
1048 err = snd_pcm_drop(pStreamALSA->phPCM);
1049 if (err < 0)
1050 {
1051 LogRel(("ALSA: Error pausing input stream: %s\n", snd_strerror(err)));
1052 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1053 }
1054 break;
1055 }
1056
1057 default:
1058 rc = VERR_NOT_SUPPORTED;
1059 break;
1060 }
1061
1062 LogFlowFuncLeaveRC(rc);
1063 return rc;
1064}
1065
1066
1067static int alsaControlStreamOut(PALSAAUDIOSTREAM pStreamALSA, PDMAUDIOSTREAMCMD enmStreamCmd)
1068{
1069 int rc = VINF_SUCCESS;
1070
1071 int err;
1072
1073 switch (enmStreamCmd)
1074 {
1075 case PDMAUDIOSTREAMCMD_ENABLE:
1076 case PDMAUDIOSTREAMCMD_RESUME:
1077 {
1078 err = snd_pcm_prepare(pStreamALSA->phPCM);
1079 if (err < 0)
1080 {
1081 LogRel(("ALSA: Error preparing output stream: %s\n", snd_strerror(err)));
1082 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1083 }
1084 else
1085 {
1086 Assert(snd_pcm_state(pStreamALSA->phPCM) == SND_PCM_STATE_PREPARED);
1087 }
1088
1089 break;
1090 }
1091
1092 case PDMAUDIOSTREAMCMD_DISABLE:
1093 {
1094 err = snd_pcm_drop(pStreamALSA->phPCM);
1095 if (err < 0)
1096 {
1097 LogRel(("ALSA: Error disabling output stream: %s\n", snd_strerror(err)));
1098 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1099 }
1100 break;
1101 }
1102
1103 case PDMAUDIOSTREAMCMD_PAUSE:
1104 {
1105 err = snd_pcm_drop(pStreamALSA->phPCM);
1106 if (err < 0)
1107 {
1108 LogRel(("ALSA: Error pausing output stream: %s\n", snd_strerror(err)));
1109 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1110 }
1111 break;
1112 }
1113
1114 case PDMAUDIOSTREAMCMD_DRAIN:
1115 {
1116 snd_pcm_state_t streamState = snd_pcm_state(pStreamALSA->phPCM);
1117 Log2Func(("Stream state is: %d\n", streamState));
1118
1119 if ( streamState == SND_PCM_STATE_PREPARED
1120 || streamState == SND_PCM_STATE_RUNNING)
1121 {
1122 err = snd_pcm_nonblock(pStreamALSA->phPCM, 0);
1123 if (err < 0)
1124 {
1125 LogRel(("ALSA: Error disabling output non-blocking mode: %s\n", snd_strerror(err)));
1126 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1127 break;
1128 }
1129
1130 err = snd_pcm_drain(pStreamALSA->phPCM);
1131 if (err < 0)
1132 {
1133 LogRel(("ALSA: Error draining output: %s\n", snd_strerror(err)));
1134 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1135 break;
1136 }
1137
1138 err = snd_pcm_nonblock(pStreamALSA->phPCM, 1);
1139 if (err < 0)
1140 {
1141 LogRel(("ALSA: Error re-enabling output non-blocking mode: %s\n", snd_strerror(err)));
1142 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1143 }
1144 }
1145 break;
1146 }
1147
1148 default:
1149 rc = VERR_NOT_SUPPORTED;
1150 break;
1151 }
1152
1153 LogFlowFuncLeaveRC(rc);
1154 return rc;
1155}
1156
1157
1158/**
1159 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
1160 */
1161static DECLCALLBACK(int) drvHostAlsaAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
1162{
1163 RT_NOREF(pInterface);
1164 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
1165
1166 RTStrPrintf2(pBackendCfg->szName, sizeof(pBackendCfg->szName), "ALSA audio driver");
1167
1168 pBackendCfg->cbStreamIn = sizeof(ALSAAUDIOSTREAM);
1169 pBackendCfg->cbStreamOut = sizeof(ALSAAUDIOSTREAM);
1170
1171 /* Enumerate sound devices. */
1172 char **pszHints;
1173 int err = snd_device_name_hint(-1 /* All cards */, "pcm", (void***)&pszHints);
1174 if (err == 0)
1175 {
1176 char** pszHintCur = pszHints;
1177 while (*pszHintCur != NULL)
1178 {
1179 char *pszDev = snd_device_name_get_hint(*pszHintCur, "NAME");
1180 bool fSkip = !pszDev
1181 || !RTStrICmp("null", pszDev);
1182 if (fSkip)
1183 {
1184 if (pszDev)
1185 free(pszDev);
1186 pszHintCur++;
1187 continue;
1188 }
1189
1190 char *pszIOID = snd_device_name_get_hint(*pszHintCur, "IOID");
1191 if (pszIOID)
1192 {
1193#if 0
1194 if (!RTStrICmp("input", pszIOID))
1195
1196 else if (!RTStrICmp("output", pszIOID))
1197#endif
1198 }
1199 else /* NULL means bidirectional, input + output. */
1200 {
1201 }
1202
1203 LogRel2(("ALSA: Found %s device: %s\n", pszIOID ? RTStrToLower(pszIOID) : "bidirectional", pszDev));
1204
1205 /* Special case for ALSAAudio. */
1206 if ( pszDev
1207 && RTStrIStr("pulse", pszDev) != NULL)
1208 LogRel2(("ALSA: ALSAAudio plugin in use\n"));
1209
1210 if (pszIOID)
1211 free(pszIOID);
1212
1213 if (pszDev)
1214 free(pszDev);
1215
1216 pszHintCur++;
1217 }
1218
1219 snd_device_name_free_hint((void **)pszHints);
1220 pszHints = NULL;
1221 }
1222 else
1223 LogRel2(("ALSA: Error enumerating PCM devices: %Rrc (%d)\n", RTErrConvertFromErrno(err), err));
1224
1225 /* ALSA allows exactly one input and one output used at a time for the selected device(s). */
1226 pBackendCfg->cMaxStreamsIn = 1;
1227 pBackendCfg->cMaxStreamsOut = 1;
1228
1229 return VINF_SUCCESS;
1230}
1231
1232
1233/**
1234 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
1235 */
1236static DECLCALLBACK(void) drvHostAlsaAudioHA_Shutdown(PPDMIHOSTAUDIO pInterface)
1237{
1238 RT_NOREF(pInterface);
1239}
1240
1241
1242/**
1243 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
1244 */
1245static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostAlsaAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
1246{
1247 RT_NOREF(enmDir);
1248 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
1249
1250 return PDMAUDIOBACKENDSTS_RUNNING;
1251}
1252
1253
1254/**
1255 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
1256 */
1257static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
1258 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
1259{
1260 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1261 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1262 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
1263 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
1264
1265 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
1266
1267 int rc;
1268 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
1269 rc = alsaCreateStreamIn( pStreamALSA, pCfgReq, pCfgAcq);
1270 else
1271 rc = alsaCreateStreamOut(pStreamALSA, pCfgReq, pCfgAcq);
1272
1273 if (RT_SUCCESS(rc))
1274 {
1275 pStreamALSA->pCfg = DrvAudioHlpStreamCfgDup(pCfgAcq);
1276 if (!pStreamALSA->pCfg)
1277 rc = VERR_NO_MEMORY;
1278 }
1279
1280 return rc;
1281}
1282
1283
1284/**
1285 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
1286 */
1287static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1288{
1289 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1290 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1291
1292 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
1293
1294 if (!pStreamALSA->pCfg) /* Not (yet) configured? Skip. */
1295 return VINF_SUCCESS;
1296
1297 int rc;
1298 if (pStreamALSA->pCfg->enmDir == PDMAUDIODIR_IN)
1299 rc = alsaDestroyStreamIn(pStreamALSA);
1300 else
1301 rc = alsaDestroyStreamOut(pStreamALSA);
1302
1303 if (RT_SUCCESS(rc))
1304 {
1305 DrvAudioHlpStreamCfgFree(pStreamALSA->pCfg);
1306 pStreamALSA->pCfg = NULL;
1307 }
1308
1309 return rc;
1310}
1311
1312
1313/**
1314 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
1315 */
1316static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamControl(PPDMIHOSTAUDIO pInterface,
1317 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
1318{
1319 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1320 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1321
1322 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
1323
1324 if (!pStreamALSA->pCfg) /* Not (yet) configured? Skip. */
1325 return VINF_SUCCESS;
1326
1327 int rc;
1328 if (pStreamALSA->pCfg->enmDir == PDMAUDIODIR_IN)
1329 rc = alsaControlStreamIn (pStreamALSA, enmStreamCmd);
1330 else
1331 rc = alsaControlStreamOut(pStreamALSA, enmStreamCmd);
1332
1333 return rc;
1334}
1335
1336
1337/**
1338 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
1339 */
1340static DECLCALLBACK(uint32_t) drvHostAlsaAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1341{
1342 RT_NOREF(pInterface);
1343
1344 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
1345
1346 uint32_t cbAvail = 0;
1347
1348 snd_pcm_sframes_t cFramesAvail;
1349 int rc = alsaStreamGetAvail(pStreamALSA->phPCM, &cFramesAvail);
1350 if (RT_SUCCESS(rc))
1351 cbAvail = PDMAUDIOSTREAMCFG_F2B(pStreamALSA->pCfg, cFramesAvail);
1352
1353 return cbAvail;
1354}
1355
1356
1357/**
1358 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
1359 */
1360static DECLCALLBACK(uint32_t) drvHostAlsaAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1361{
1362 RT_NOREF(pInterface);
1363
1364 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
1365
1366 uint32_t cbAvail = 0;
1367
1368 snd_pcm_sframes_t cFramesAvail;
1369 int rc = alsaStreamGetAvail(pStreamALSA->phPCM, &cFramesAvail);
1370 if (RT_SUCCESS(rc))
1371 cbAvail = PDMAUDIOSTREAMCFG_F2B(pStreamALSA->pCfg, cFramesAvail);
1372
1373 return cbAvail;
1374}
1375
1376
1377/**
1378 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetPending}
1379 */
1380static DECLCALLBACK(uint32_t) drvHostALSAStreamGetPending(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1381{
1382 RT_NOREF(pInterface);
1383 AssertPtrReturn(pStream, 0);
1384
1385 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
1386
1387 snd_pcm_sframes_t cFramesDelay = 0;
1388 snd_pcm_state_t enmState = snd_pcm_state(pStreamALSA->phPCM);
1389
1390 int rc = VINF_SUCCESS;
1391
1392 AssertPtr(pStreamALSA->pCfg);
1393 if (pStreamALSA->pCfg->enmDir == PDMAUDIODIR_OUT)
1394 {
1395 /* Getting the delay (in audio frames) reports the time it will take
1396 * to hear a new sample after all queued samples have been played out. */
1397 int rc2 = snd_pcm_delay(pStreamALSA->phPCM, &cFramesDelay);
1398 if (RT_SUCCESS(rc))
1399 rc = rc2;
1400
1401 /* Make sure to check the stream's status.
1402 * If it's anything but SND_PCM_STATE_RUNNING, the delay is meaningless and therefore 0. */
1403 if (enmState != SND_PCM_STATE_RUNNING)
1404 cFramesDelay = 0;
1405 }
1406
1407 /* Note: For input streams we never have pending data left. */
1408
1409 Log2Func(("cFramesDelay=%RI32, enmState=%d, rc=%d\n", cFramesDelay, enmState, rc));
1410
1411 return DrvAudioHlpFramesToBytes(cFramesDelay, &pStreamALSA->pCfg->Props);
1412}
1413
1414
1415/**
1416 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
1417 */
1418static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostAlsaAudioHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1419{
1420 RT_NOREF(pInterface, pStream);
1421
1422 return PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
1423}
1424
1425
1426/**
1427 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
1428 */
1429static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1430{
1431 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1432 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1433
1434 LogFlowFuncEnter();
1435
1436 /* Nothing to do here for ALSA. */
1437 return VINF_SUCCESS;
1438}
1439
1440
1441/**
1442 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1443 */
1444static DECLCALLBACK(void *) drvHostAlsaAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1445{
1446 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1447 PDRVHOSTALSAAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTALSAAUDIO);
1448 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1449 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1450
1451 return NULL;
1452}
1453
1454
1455/**
1456 * Construct a DirectSound Audio driver instance.
1457 *
1458 * @copydoc FNPDMDRVCONSTRUCT
1459 */
1460static DECLCALLBACK(int) drvHostAlsaAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1461{
1462 RT_NOREF(pCfg, fFlags);
1463 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1464 PDRVHOSTALSAAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTALSAAUDIO);
1465 LogRel(("Audio: Initializing ALSA driver\n"));
1466
1467 /*
1468 * Init the static parts.
1469 */
1470 pThis->pDrvIns = pDrvIns;
1471 /* IBase */
1472 pDrvIns->IBase.pfnQueryInterface = drvHostAlsaAudioQueryInterface;
1473 /* IHostAudio */
1474 pThis->IHostAudio.pfnInit = drvHostAlsaAudioHA_Init;
1475 pThis->IHostAudio.pfnShutdown = drvHostAlsaAudioHA_Shutdown;
1476 pThis->IHostAudio.pfnGetConfig = drvHostAlsaAudioHA_GetConfig;
1477 pThis->IHostAudio.pfnGetStatus = drvHostAlsaAudioHA_GetStatus;
1478 pThis->IHostAudio.pfnStreamCreate = drvHostAlsaAudioHA_StreamCreate;
1479 pThis->IHostAudio.pfnStreamDestroy = drvHostAlsaAudioHA_StreamDestroy;
1480 pThis->IHostAudio.pfnStreamControl = drvHostAlsaAudioHA_StreamControl;
1481 pThis->IHostAudio.pfnStreamGetReadable = drvHostAlsaAudioHA_StreamGetReadable;
1482 pThis->IHostAudio.pfnStreamGetWritable = drvHostAlsaAudioHA_StreamGetWritable;
1483 pThis->IHostAudio.pfnStreamGetStatus = drvHostAlsaAudioHA_StreamGetStatus;
1484 pThis->IHostAudio.pfnStreamIterate = drvHostAlsaAudioHA_StreamIterate;
1485 pThis->IHostAudio.pfnStreamPlay = drvHostAlsaAudioHA_StreamPlay;
1486 pThis->IHostAudio.pfnStreamCapture = drvHostAlsaAudioHA_StreamCapture;
1487 pThis->IHostAudio.pfnSetCallback = NULL;
1488 pThis->IHostAudio.pfnGetDevices = NULL;
1489 pThis->IHostAudio.pfnStreamGetPending = drvHostALSAStreamGetPending;
1490 pThis->IHostAudio.pfnStreamPlayBegin = NULL;
1491 pThis->IHostAudio.pfnStreamPlayEnd = NULL;
1492 pThis->IHostAudio.pfnStreamCaptureBegin = NULL;
1493 pThis->IHostAudio.pfnStreamCaptureEnd = NULL;
1494
1495
1496 return VINF_SUCCESS;
1497}
1498
1499
1500/**
1501 * Char driver registration record.
1502 */
1503const PDMDRVREG g_DrvHostALSAAudio =
1504{
1505 /* u32Version */
1506 PDM_DRVREG_VERSION,
1507 /* szName */
1508 "ALSAAudio",
1509 /* szRCMod */
1510 "",
1511 /* szR0Mod */
1512 "",
1513 /* pszDescription */
1514 "ALSA host audio driver",
1515 /* fFlags */
1516 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1517 /* fClass. */
1518 PDM_DRVREG_CLASS_AUDIO,
1519 /* cMaxInstances */
1520 ~0U,
1521 /* cbInstance */
1522 sizeof(DRVHOSTALSAAUDIO),
1523 /* pfnConstruct */
1524 drvHostAlsaAudioConstruct,
1525 /* pfnDestruct */
1526 NULL,
1527 /* pfnRelocate */
1528 NULL,
1529 /* pfnIOCtl */
1530 NULL,
1531 /* pfnPowerOn */
1532 NULL,
1533 /* pfnReset */
1534 NULL,
1535 /* pfnSuspend */
1536 NULL,
1537 /* pfnResume */
1538 NULL,
1539 /* pfnAttach */
1540 NULL,
1541 /* pfnDetach */
1542 NULL,
1543 /* pfnPowerOff */
1544 NULL,
1545 /* pfnSoftReset */
1546 NULL,
1547 /* u32EndVersion */
1548 PDM_DRVREG_VERSION
1549};
1550
Note: See TracBrowser for help on using the repository browser.

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