From 03cc7c5cb7e9ea227af43cedf58dc60febb7b745 Mon Sep 17 00:00:00 2001 From: Ricardo Biehl Pasquali Date: Fri, 4 Jan 2019 00:20:14 -0200 Subject: pcm: Create generic transfer Group read/write transfer into pcm_rw_transfer(). Create pcm_generic_transfer() for read/write and mmap transfer modes. This recovers from xrun and suspended. Signed-off-by: Ricardo Biehl Pasquali --- src/pcm.c | 120 ++++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 66 insertions(+), 54 deletions(-) (limited to 'src') diff --git a/src/pcm.c b/src/pcm.c index d20a954..eb2f4b8 100644 --- a/src/pcm.c +++ b/src/pcm.c @@ -1357,6 +1357,70 @@ int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count) return pcm_mmap_transfer(pcm, data, pcm_bytes_to_frames(pcm, count)); } +static int pcm_rw_transfer(struct pcm *pcm, void *data, unsigned int frames) +{ + int is_playback; + + struct snd_xferi transfer; + int res; + + is_playback = !(pcm->flags & PCM_IN); + + transfer.buf = data; + transfer.frames = frames; + transfer.result = 0; + + res = ioctl(pcm->fd, is_playback + ? SNDRV_PCM_IOCTL_WRITEI_FRAMES + : SNDRV_PCM_IOCTL_READI_FRAMES, &transfer); + + return res == 0 ? (int) transfer.result : -1; +} + +static int pcm_generic_transfer(struct pcm *pcm, void *data, + unsigned int frames) +{ + int res; + +#if UINT_MAX > TINYALSA_FRAMES_MAX + if (frames > TINYALSA_FRAMES_MAX) + return -EINVAL; +#endif + if (frames > INT_MAX) + return -EINVAL; + +again: + + if (pcm->flags & PCM_MMAP) + res = pcm_mmap_transfer(pcm, data, frames); + else + res = pcm_rw_transfer(pcm, data, frames); + + if (res < 0) { + switch (errno) { + case EPIPE: + pcm->underruns++; + /* fallthrough */ + case ESTRPIPE: + /* + * Try to restart if we are allowed to do so. + * Otherwise, return error. + */ + if (pcm->flags & PCM_NORESTART || pcm_prepare(pcm)) + return -1; + goto again; + case EAGAIN: + if (pcm->flags & PCM_NONBLOCK) + return -1; + /* fallthrough */ + default: + return oops(pcm, errno, "cannot read/write stream data"); + } + } + + return res; +} + /** Writes audio samples to PCM. * If the PCM has not been started, it is started in this function. * This function is only valid for PCMs opened with the @ref PCM_OUT flag. @@ -1370,37 +1434,10 @@ int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count) */ int pcm_writei(struct pcm *pcm, const void *data, unsigned int frame_count) { - struct snd_xferi x; - if (pcm->flags & PCM_IN) return -EINVAL; -#if UINT_MAX > TINYALSA_FRAMES_MAX - if (frame_count > TINYALSA_FRAMES_MAX) - return -EINVAL; -#endif - if (frame_count > INT_MAX) - return -EINVAL; - x.buf = (void*)data; - x.frames = frame_count; - x.result = 0; - for (;;) { - if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) { - if (errno == EPIPE) { - /* we failed to make our window -- try to restart if we are - * allowed to do so. Otherwise, simply allow the EPIPE error to - * propagate up to the app level */ - pcm->underruns++; - if (pcm->flags & PCM_NORESTART) - return -EPIPE; - if (pcm_prepare(pcm)) - return -EPIPE; - continue; - } - return oops(pcm, errno, "cannot write stream data"); - } - return x.result; - } + return pcm_generic_transfer(pcm, (void*) data, frame_count); } /** Reads audio samples from PCM. @@ -1416,35 +1453,10 @@ int pcm_writei(struct pcm *pcm, const void *data, unsigned int frame_count) */ int pcm_readi(struct pcm *pcm, void *data, unsigned int frame_count) { - struct snd_xferi x; - if (!(pcm->flags & PCM_IN)) return -EINVAL; -#if UINT_MAX > TINYALSA_FRAMES_MAX - if (frame_count > TINYALSA_FRAMES_MAX) - return -EINVAL; -#endif - if (frame_count > INT_MAX) - return -EINVAL; - x.buf = data; - x.frames = frame_count; - x.result = 0; - for (;;) { - if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) { - if (errno == EPIPE) { - /* we failed to make our window -- try to restart */ - pcm->underruns++; - if (pcm->flags & PCM_NORESTART) - return -EPIPE; - if (pcm_prepare(pcm)) - return -EPIPE; - continue; - } - return oops(pcm, errno, "cannot read stream data"); - } - return x.result; - } + return pcm_generic_transfer(pcm, data, frame_count); } /** Writes audio samples to PCM. -- cgit v1.2.3