diff options
author | Hui Wang <hui.wang@canonical.com> | 2020-07-09 10:48:33 +0800 |
---|---|---|
committer | Hui Wang <hui.wang@canonical.com> | 2020-07-09 16:43:17 +0800 |
commit | ede8cbb131f7493f14f5717aefd5b4e0603a05d1 (patch) | |
tree | 084e6934c9904d019be0198a7de044f5fc6413a9 | |
parent | f12795330be2a3d6f12dd15680f3baf20f6f8f61 (diff) |
alsa: make the unsuspend more robust
We met a weird situation on a couple of Lenovo machines and at least
on one Dell machine. First we open the gnome-sound-setting, then
suspend and resume the system, after the system resume back, the audio
devices change to dummy, the audio doesn't work anymore. And pacmd
list-cards shows no available sound card.
Through debugging I found after resume, the alsa receives POLLERR
events and it will call unsuspend to recover the pcm, but at that
moment, the device nodes in /dev/snd/ is not accessible, so the
snd_pcm_open() fails and the pulseaudio unload the module-alsa-card.
Here I add retry and pa_msleep if snd_pcm_open fails, I tested it on
all machines which have this problem, pa_msleep(25) is ok for most of
them, there is only one machine which needs to call pa_msleep(25)
twice, so for safety reason, I set the max retry times to 4.
Signed-off-by: Hui Wang <hui.wang@canonical.com>
-rw-r--r-- | src/modules/alsa/alsa-sink.c | 29 | ||||
-rw-r--r-- | src/modules/alsa/alsa-source.c | 27 |
2 files changed, 41 insertions, 15 deletions
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 67523f1c9..457dc2785 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -1152,7 +1152,7 @@ static void update_size(struct userdata *u, pa_sample_spec *ss) { /* Called from IO context */ static int unsuspend(struct userdata *u, bool recovering) { pa_sample_spec ss; - int err; + int err, i; bool b, d; snd_pcm_uframes_t period_frames, buffer_frames; snd_pcm_uframes_t tsched_frames = 0; @@ -1172,13 +1172,26 @@ static int unsuspend(struct userdata *u, bool recovering) { pa_snprintf(device_name, len, "%s,AES0=6", u->device_name); } - if ((err = snd_pcm_open(&u->pcm_handle, device_name ? device_name : u->device_name, SND_PCM_STREAM_PLAYBACK, - SND_PCM_NONBLOCK| - SND_PCM_NO_AUTO_RESAMPLE| - SND_PCM_NO_AUTO_CHANNELS| - SND_PCM_NO_AUTO_FORMAT)) < 0) { - pa_log("Error opening PCM device %s: %s", u->device_name, pa_alsa_strerror(err)); - goto fail; + /* + * On some machines, during the system suspend and resume, the thread_func could receive + * POLLERR events before the dev nodes in /dev/snd/ are accessible, and thread_func calls + * the unsuspend() to try to recover the PCM, this will make the snd_pcm_open() fail, here + * we add msleep and retry to make sure those nodes are accessible. + */ + for (i = 0; i < 4; i++) { + if ((err = snd_pcm_open(&u->pcm_handle, device_name ? device_name : u->device_name, SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK| + SND_PCM_NO_AUTO_RESAMPLE| + SND_PCM_NO_AUTO_CHANNELS| + SND_PCM_NO_AUTO_FORMAT)) < 0 && recovering) + pa_msleep(25); + else + break; + } + + if (err < 0) { + pa_log("Error opening PCM device %s: %s", u->device_name, pa_alsa_strerror(err)); + goto fail; } if (pa_frame_size(&u->sink->sample_spec) != u->frame_size) { diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 4b134d85c..da99ac610 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -1033,7 +1033,7 @@ static void update_size(struct userdata *u, pa_sample_spec *ss) { /* Called from IO context */ static int unsuspend(struct userdata *u, bool recovering) { pa_sample_spec ss; - int err; + int err, i; bool b, d; snd_pcm_uframes_t period_frames, buffer_frames; snd_pcm_uframes_t tsched_frames = 0; @@ -1044,12 +1044,25 @@ static int unsuspend(struct userdata *u, bool recovering) { pa_log_info("Trying resume..."); - if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_CAPTURE, - SND_PCM_NONBLOCK| - SND_PCM_NO_AUTO_RESAMPLE| - SND_PCM_NO_AUTO_CHANNELS| - SND_PCM_NO_AUTO_FORMAT)) < 0) { - pa_log("Error opening PCM device %s: %s", u->device_name, pa_alsa_strerror(err)); + /* + * On some machines, during the system suspend and resume, the thread_func could receive + * POLLERR events before the dev nodes in /dev/snd/ are accessible, and thread_func calls + * the unsuspend() to try to recover the PCM, this will make the snd_pcm_open() fail, here + * we add msleep and retry to make sure those nodes are accessible. + */ + for (i = 0; i < 4; i++) { + if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_CAPTURE, + SND_PCM_NONBLOCK| + SND_PCM_NO_AUTO_RESAMPLE| + SND_PCM_NO_AUTO_CHANNELS| + SND_PCM_NO_AUTO_FORMAT)) < 0 && recovering) + pa_msleep(25); + else + break; + } + + if (err < 0) { + pa_log("Error opening PCM device %s: %s", u->device_name, pa_alsa_strerror(err)); goto fail; } |