summaryrefslogtreecommitdiffstats
path: root/src/modules/alsa-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/alsa-util.c')
-rw-r--r--src/modules/alsa-util.c178
1 files changed, 164 insertions, 14 deletions
diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c
index ffe7795e..ff3af19d 100644
--- a/src/modules/alsa-util.c
+++ b/src/modules/alsa-util.c
@@ -30,6 +30,7 @@
#include <pulse/sample.h>
#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
@@ -555,6 +556,7 @@ snd_pcm_t *pa_alsa_open_by_device_id(
for (i = 0;; i += direction) {
pa_sample_spec try_ss;
+ pa_bool_t reformat;
if (i < 0) {
pa_assert(direction == -1);
@@ -579,8 +581,9 @@ snd_pcm_t *pa_alsa_open_by_device_id(
d = pa_sprintf_malloc("%s:%s", device_table[i].name, dev_id);
+ reformat = FALSE;
for (;;) {
- pa_log_debug("Trying %s...", d);
+ pa_log_debug("Trying %s %s SND_PCM_NO_AUTO_FORMAT ...", d, reformat ? "without" : "with");
/* We don't pass SND_PCM_NONBLOCK here, since alsa-lib <=
* 1.0.17a would then ignore the SND_PCM_NO_xxx
@@ -592,7 +595,7 @@ snd_pcm_t *pa_alsa_open_by_device_id(
/* SND_PCM_NONBLOCK| */
SND_PCM_NO_AUTO_RESAMPLE|
SND_PCM_NO_AUTO_CHANNELS|
- SND_PCM_NO_AUTO_FORMAT)) < 0) {
+ (reformat ? 0 : SND_PCM_NO_AUTO_FORMAT))) < 0) {
pa_log_info("Couldn't open PCM device %s: %s", d, snd_strerror(err));
break;
}
@@ -603,6 +606,12 @@ snd_pcm_t *pa_alsa_open_by_device_id(
if ((err = pa_alsa_set_hw_params(pcm_handle, &try_ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, TRUE)) < 0) {
+ if (!reformat) {
+ reformat = TRUE;
+ snd_pcm_close(pcm_handle);
+ continue;
+ }
+
if (!pa_startswith(d, "plug:") && !pa_startswith(d, "plughw:")) {
char *t;
@@ -610,6 +619,8 @@ snd_pcm_t *pa_alsa_open_by_device_id(
pa_xfree(d);
d = t;
+ reformat = FALSE;
+
snd_pcm_close(pcm_handle);
continue;
}
@@ -654,6 +665,7 @@ snd_pcm_t *pa_alsa_open_by_device_string(
int err;
char *d;
snd_pcm_t *pcm_handle;
+ pa_bool_t reformat = FALSE;
pa_assert(device);
pa_assert(dev);
@@ -665,7 +677,7 @@ snd_pcm_t *pa_alsa_open_by_device_string(
d = pa_xstrdup(device);
for (;;) {
- pa_log_debug("Trying %s...", d);
+ pa_log_debug("Trying %s %s SND_PCM_NO_AUTO_FORMAT ...", d, reformat ? "without" : "with");
/* We don't pass SND_PCM_NONBLOCK here, since alsa-lib <=
* 1.0.17a would then ignore the SND_PCM_NO_xxx flags. Instead
@@ -677,7 +689,7 @@ snd_pcm_t *pa_alsa_open_by_device_string(
/*SND_PCM_NONBLOCK|*/
SND_PCM_NO_AUTO_RESAMPLE|
SND_PCM_NO_AUTO_CHANNELS|
- SND_PCM_NO_AUTO_FORMAT)) < 0) {
+ (reformat ? 0 : SND_PCM_NO_AUTO_FORMAT))) < 0) {
pa_log("Error opening PCM device %s: %s", d, snd_strerror(err));
pa_xfree(d);
return NULL;
@@ -685,6 +697,13 @@ snd_pcm_t *pa_alsa_open_by_device_string(
if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, FALSE)) < 0) {
+ if (!reformat) {
+ reformat = TRUE;
+
+ snd_pcm_close(pcm_handle);
+ continue;
+ }
+
/* Hmm, some hw is very exotic, so we retry with plug, if without it didn't work */
if (!pa_startswith(d, "plug:") && !pa_startswith(d, "plughw:")) {
@@ -694,6 +713,8 @@ snd_pcm_t *pa_alsa_open_by_device_string(
pa_xfree(d);
d = t;
+ reformat = FALSE;
+
snd_pcm_close(pcm_handle);
continue;
}
@@ -739,8 +760,32 @@ int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev) {
return 0;
}
-snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback) {
- snd_mixer_elem_t *elem;
+static pa_bool_t elem_has_volume(snd_mixer_elem_t *elem, pa_bool_t playback) {
+ pa_assert(elem);
+
+ if (playback && snd_mixer_selem_has_playback_volume(elem))
+ return TRUE;
+
+ if (!playback && snd_mixer_selem_has_capture_volume(elem))
+ return TRUE;
+
+ return FALSE;
+}
+
+static pa_bool_t elem_has_switch(snd_mixer_elem_t *elem, pa_bool_t playback) {
+ pa_assert(elem);
+
+ if (playback && snd_mixer_selem_has_playback_switch(elem))
+ return TRUE;
+
+ if (!playback && snd_mixer_selem_has_capture_switch(elem))
+ return TRUE;
+
+ return FALSE;
+}
+
+snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback, pa_bool_t playback) {
+ snd_mixer_elem_t *elem = NULL, *fallback_elem = NULL;
snd_mixer_selem_id_t *sid = NULL;
snd_mixer_selem_id_alloca(&sid);
@@ -750,17 +795,57 @@ snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const
snd_mixer_selem_id_set_name(sid, name);
- if (!(elem = snd_mixer_find_selem(mixer, sid))) {
- pa_log_info("Cannot find mixer control \"%s\".", snd_mixer_selem_id_get_name(sid));
+ if ((elem = snd_mixer_find_selem(mixer, sid))) {
+
+ if (elem_has_volume(elem, playback) &&
+ elem_has_switch(elem, playback))
+ goto success;
- if (fallback) {
- snd_mixer_selem_id_set_name(sid, fallback);
+ if (!elem_has_volume(elem, playback) &&
+ !elem_has_switch(elem, playback))
+ elem = NULL;
+ }
+
+ pa_log_info("Cannot find mixer control \"%s\" or mixer control is no combination of switch/volume.", snd_mixer_selem_id_get_name(sid));
+
+ if (fallback) {
+ snd_mixer_selem_id_set_name(sid, fallback);
+
+ if ((fallback_elem = snd_mixer_find_selem(mixer, sid))) {
- if (!(elem = snd_mixer_find_selem(mixer, sid)))
- pa_log_warn("Cannot find fallback mixer control \"%s\".", snd_mixer_selem_id_get_name(sid));
+ if (elem_has_volume(fallback_elem, playback) &&
+ elem_has_switch(fallback_elem, playback)) {
+ elem = fallback_elem;
+ goto success;
+ }
+
+ if (!elem_has_volume(fallback_elem, playback) &&
+ !elem_has_switch(fallback_elem, playback))
+ fallback_elem = NULL;
}
+
+ pa_log_warn("Cannot find fallback mixer control \"%s\" or mixer control is no combination of switch/volume.", snd_mixer_selem_id_get_name(sid));
}
+ if (elem && fallback_elem) {
+
+ /* Hmm, so we have both elements, but neither has both mute
+ * and volume. Let's prefer the one with the volume */
+
+ if (elem_has_volume(elem, playback))
+ goto success;
+
+ if (elem_has_volume(fallback_elem, playback)) {
+ elem = fallback_elem;
+ goto success;
+ }
+ }
+
+ if (!elem && fallback_elem)
+ elem = fallback_elem;
+
+success:
+
if (elem)
pa_log_info("Using mixer control \"%s\".", snd_mixer_selem_id_get_name(sid));
@@ -1045,14 +1130,20 @@ int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) {
pa_assert(pcm);
if (revents & POLLERR)
- pa_log_warn("Got POLLERR from ALSA");
+ pa_log_debug("Got POLLERR from ALSA");
if (revents & POLLNVAL)
pa_log_warn("Got POLLNVAL from ALSA");
if (revents & POLLHUP)
pa_log_warn("Got POLLHUP from ALSA");
+ if (revents & POLLPRI)
+ pa_log_warn("Got POLLPRI from ALSA");
+ if (revents & POLLIN)
+ pa_log_debug("Got POLLIN from ALSA");
+ if (revents & POLLOUT)
+ pa_log_debug("Got POLLOUT from ALSA");
state = snd_pcm_state(pcm);
- pa_log_warn("PCM state is %s", snd_pcm_state_name(state));
+ pa_log_debug("PCM state is %s", snd_pcm_state_name(state));
/* Try to recover from this error */
@@ -1109,3 +1200,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;
+}