From 0796f5a2d6bf8e175a16d7f58cd0a18783fb4590 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Oct 2008 20:00:36 +0200 Subject: Try to catch certain driver errors ... by verifying return values of snd_pcm_avail_update() and snd_pcm_begin_mmap() for their sanenness. --- src/modules/alsa-util.c | 60 ++++++++++++++++++++++++++++++++++++++++ src/modules/alsa-util.h | 3 ++ src/modules/module-alsa-sink.c | 6 ++-- src/modules/module-alsa-source.c | 6 ++-- 4 files changed, 69 insertions(+), 6 deletions(-) diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c index ffe7795e..39cea494 100644 --- a/src/modules/alsa-util.c +++ b/src/modules/alsa-util.c @@ -30,6 +30,7 @@ #include #include +#include #include #include @@ -1109,3 +1110,62 @@ pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll) { return item; } + +snd_pcm_sframes_t pa_alsa_safe_avail_update(snd_pcm_t *pcm, size_t hwbuf_size, const pa_sample_spec *ss) { + snd_pcm_sframes_t n; + size_t k; + + pa_assert(pcm); + pa_assert(hwbuf_size > 0); + pa_assert(ss); + + /* Some ALSA driver expose weird bugs, let's inform the user about + * what is going on */ + + n = snd_pcm_avail_update(pcm); + + if (n <= 0) + return n; + + k = (size_t) n * pa_frame_size(ss); + + if (k >= hwbuf_size * 3 || + k >= pa_bytes_per_second(ss)*10) + pa_log("snd_pcm_avail_update() returned a value that is exceptionally large: %lu bytes (%lu ms) " + "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers.", + (unsigned long) k, (unsigned long) pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC); + + return n; +} + +int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames, size_t hwbuf_size, const pa_sample_spec *ss) { + int r; + snd_pcm_uframes_t before; + size_t k; + + pa_assert(pcm); + pa_assert(areas); + pa_assert(offset); + pa_assert(frames); + pa_assert(hwbuf_size > 0); + pa_assert(ss); + + before = *frames; + + r = snd_pcm_mmap_begin(pcm, areas, offset, frames); + + if (r < 0) + return r; + + k = (size_t) *frames * pa_frame_size(ss); + + if (*frames > before || + k >= hwbuf_size * 3 || + k >= pa_bytes_per_second(ss)*10) + + pa_log("snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes (%lu ms) " + "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers.", + (unsigned long) k, (unsigned long) pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC); + + return r; +} diff --git a/src/modules/alsa-util.h b/src/modules/alsa-util.h index b66adc13..aaa01c78 100644 --- a/src/modules/alsa-util.h +++ b/src/modules/alsa-util.h @@ -92,4 +92,7 @@ int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents); pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll); +snd_pcm_sframes_t pa_alsa_safe_avail_update(snd_pcm_t *pcm, size_t hwbuf_size, const pa_sample_spec *ss); +int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames, size_t hwbuf_size, const pa_sample_spec *ss); + #endif diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index 4044de13..af83103d 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -261,7 +261,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) { /* First we determine how many samples are missing to fill the * buffer up to 100% */ - if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) { + if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->sink->sample_spec)) < 0)) { if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0) continue; @@ -299,7 +299,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) { /* pa_log_debug("%lu frames to write", (unsigned long) frames); */ - if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) { + if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->sink->sample_spec)) < 0)) { if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0) continue; @@ -374,7 +374,7 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec) { snd_pcm_hwsync(u->pcm_handle); - if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) { + if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->sink->sample_spec)) < 0)) { if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0) continue; diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index a7437767..dd6ca978 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -255,7 +255,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) { snd_pcm_hwsync(u->pcm_handle); - if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) { + if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) { if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0) continue; @@ -282,7 +282,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) { /* pa_log_debug("%lu frames to read", (unsigned long) frames); */ - if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) { + if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->source->sample_spec)) < 0)) { if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0) continue; @@ -353,7 +353,7 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec) { snd_pcm_hwsync(u->pcm_handle); - if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) { + if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) { if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0) continue; -- cgit