Be more careful checking for ENODEV conditions in audio_alsa.c. Still a few places where it is not done...

This commit is contained in:
Mike Brady
2022-12-31 17:16:02 +00:00
parent dcc994ab77
commit 76387f8291
3 changed files with 96 additions and 150 deletions
+68 -122
View File
@@ -166,6 +166,22 @@ int volume_based_mute_is_active =
// use this to allow the use of snd_pcm_writei or snd_pcm_mmap_writei
snd_pcm_sframes_t (*alsa_pcm_write)(snd_pcm_t *, const void *, snd_pcm_uframes_t) = snd_pcm_writei;
void handle_unfixable_error(int errorCode) {
if (config.unfixable_error_reported == 0) {
config.unfixable_error_reported = 1;
char messageString[1024];
messageString[0] = '\0';
snprintf(messageString, sizeof(messageString), "output_device_error_%d", errorCode);
if (config.cmd_unfixable) {
command_execute(config.cmd_unfixable, messageString, 1);
} else {
die("An unrecoverable error, \"output_device_error_%d\", has been "
"detected. Doing an emergency exit, as no run_this_if_an_unfixable_error_is_detected program.",
errorCode);
}
}
}
static int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
yndk_type *using_update_timestamps);
static int standard_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
@@ -277,40 +293,33 @@ static void help(void) {
void set_alsa_out_dev(char *dev) { alsa_out_dev = dev; } // ugh -- not static!
// assuming pthread cancellation is disabled
// returns zero of all is okay, a Unx error code if there's a problem
static int open_mixer() {
int response = 0;
if (alsa_mix_ctrl != NULL) {
debug(3, "Open Mixer");
int ret = 0;
snd_mixer_selem_id_alloca(&alsa_mix_sid);
snd_mixer_selem_id_set_index(alsa_mix_sid, alsa_mix_index);
snd_mixer_selem_id_set_name(alsa_mix_sid, alsa_mix_ctrl);
if ((snd_mixer_open(&alsa_mix_handle, 0)) < 0) {
if ((response = snd_mixer_open(&alsa_mix_handle, 0)) < 0) {
debug(1, "Failed to open mixer");
response = -1;
} else {
debug(3, "Mixer device name is \"%s\".", alsa_mix_dev);
if ((snd_mixer_attach(alsa_mix_handle, alsa_mix_dev)) < 0) {
if ((response = snd_mixer_attach(alsa_mix_handle, alsa_mix_dev)) < 0) {
debug(1, "Failed to attach mixer");
response = -2;
} else {
if ((snd_mixer_selem_register(alsa_mix_handle, NULL, NULL)) < 0) {
if ((response = snd_mixer_selem_register(alsa_mix_handle, NULL, NULL)) < 0) {
debug(1, "Failed to register mixer element");
response = -3;
} else {
ret = snd_mixer_load(alsa_mix_handle);
if (ret < 0) {
if ((response = snd_mixer_load(alsa_mix_handle)) < 0) {
debug(1, "Failed to load mixer element");
response = -4;
} else {
debug(3, "Mixer control is \"%s\",%d.", alsa_mix_ctrl, alsa_mix_index);
alsa_mix_elem = snd_mixer_find_selem(alsa_mix_handle, alsa_mix_sid);
if (!alsa_mix_elem) {
warn("failed to find mixer control \"%s\",%d.", alsa_mix_ctrl, alsa_mix_index);
response = -5;
} else {
response = 1; // we found a hardware mixer and successfully opened it
response = -ENXIO; // don't let this be ENODEV!
}
}
}
@@ -321,21 +330,25 @@ static int open_mixer() {
}
// assuming pthread cancellation is disabled
static void close_mixer() {
static int close_mixer() {
int ret = 0;
if (alsa_mix_handle) {
snd_mixer_close(alsa_mix_handle);
ret = snd_mixer_close(alsa_mix_handle);
alsa_mix_handle = NULL;
}
return ret;
}
// assuming pthread cancellation is disabled
static void do_snd_mixer_selem_set_playback_dB_all(snd_mixer_elem_t *mix_elem, double vol) {
if (snd_mixer_selem_set_playback_dB_all(mix_elem, vol, 0) != 0) {
static int do_snd_mixer_selem_set_playback_dB_all(snd_mixer_elem_t *mix_elem, double vol) {
int response = 0;
if ((response = snd_mixer_selem_set_playback_dB_all(mix_elem, vol, 0)) != 0) {
debug(1, "Can't set playback volume accurately to %f dB.", vol);
if (snd_mixer_selem_set_playback_dB_all(mix_elem, vol, -1) != 0)
if (snd_mixer_selem_set_playback_dB_all(mix_elem, vol, 1) != 0)
if ((response = snd_mixer_selem_set_playback_dB_all(mix_elem, vol, -1)) != 0)
if ((response = snd_mixer_selem_set_playback_dB_all(mix_elem, vol, 1)) != 0)
debug(1, "Could not set playback dB volume on the mixer.");
}
return response;
}
// This array is a sequence of the output rates to be tried if automatic speed selection is
@@ -444,19 +457,6 @@ static int actual_open_alsa_device(int do_auto_setup) {
warn("The output device \"%s\" is busy and can't be used by Shairport Sync at present.",
alsa_out_dev);
debug(2, "the alsa output_device \"%s\" is busy.", alsa_out_dev);
} else {
if (config.unfixable_error_reported == 0) {
config.unfixable_error_reported = 1;
char messageString[1024];
messageString[0] = '\0';
snprintf(messageString, sizeof(messageString), "output_device_error_%d", -ret);
if (config.cmd_unfixable) {
command_execute(config.cmd_unfixable, messageString, 1);
} else {
die("an unrecoverable error, \"output_device_error_%d\", has been "
"detected.", -ret);
}
}
}
alsa_handle_status = ret;
frames_sent_break_occurred = 1;
@@ -906,11 +906,11 @@ static int prepare_mixer() {
// Now, start trying to initialise the alsa device with the settings
// obtained
pthread_cleanup_debug_mutex_lock(&alsa_mixer_mutex, 1000, 1);
if (open_mixer() == 1) {
if (open_mixer() == 0) {
if (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv, &alsa_mix_maxv) <
0)
0) {
debug(1, "Can't read mixer's [linear] min and max volumes.");
else {
} else {
if (snd_mixer_selem_get_playback_dB_range(alsa_mix_elem, &alsa_mix_mindb,
&alsa_mix_maxdb) == 0) {
@@ -940,14 +940,12 @@ static int prepare_mixer() {
"a dB volume scale.",
alsa_mix_ctrl);
if (snd_ctl_open(&ctl, alsa_mix_dev, 0) < 0) {
if ((response = snd_ctl_open(&ctl, alsa_mix_dev, 0)) < 0) {
warn("Cannot open control \"%s\"", alsa_mix_dev);
response = -1;
}
if (snd_ctl_elem_id_malloc(&elem_id) < 0) {
if ((response = snd_ctl_elem_id_malloc(&elem_id)) < 0) {
debug(1, "Cannot allocate memory for control \"%s\"", alsa_mix_dev);
elem_id = NULL;
response = -2;
} else {
snd_ctl_elem_id_set_interface(elem_id, SND_CTL_ELEM_IFACE_MIXER);
snd_ctl_elem_id_set_name(elem_id, alsa_mix_ctrl);
@@ -965,22 +963,6 @@ static int prepare_mixer() {
debug(1, "Cannot get the dB range from the volume control \"%s\"", alsa_mix_ctrl);
}
}
/*
debug(1, "Min and max volumes are %d and
%d.",alsa_mix_minv,alsa_mix_maxv);
alsa_mix_maxdb = 0;
if ((alsa_mix_maxv!=0) && (alsa_mix_minv!=0))
alsa_mix_mindb =
-20*100*(log10(alsa_mix_maxv*1.0)-log10(alsa_mix_minv*1.0));
else if (alsa_mix_maxv!=0)
alsa_mix_mindb = -20*100*log10(alsa_mix_maxv*1.0);
audio_alsa.volume = &linear_volume; // insert the linear volume
function
audio_alsa.parameters = &parameters; // likewise the parameters
stuff
debug(1,"Max and min dB calculated are %d and
%d.",alsa_mix_maxdb,alsa_mix_mindb);
*/
}
}
if (((config.alsa_use_hardware_mute == 1) &&
@@ -992,7 +974,8 @@ static int prepare_mixer() {
} else {
// debug(1, "Has mixer but not using hardware mute.");
}
close_mixer();
if (response == 0)
response = close_mixer();
}
debug_mutex_unlock(&alsa_mixer_mutex, 3); // release the mutex
pthread_cleanup_pop(0);
@@ -1399,7 +1382,7 @@ static int set_mute_state() {
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
pthread_cleanup_debug_mutex_lock(&alsa_mixer_mutex, 10000, 0);
if ((alsa_backend_state != abm_disconnected) && (config.alsa_use_hardware_mute == 1) &&
(open_mixer() == 1)) {
(open_mixer() == 0)) {
response = 0; // okay if actually using the mute facility
debug(2, "alsa: actually set_mute_state");
int mute = 0;
@@ -1718,36 +1701,20 @@ static int stats(uint64_t *raw_measurement_time, uint64_t *corrected_measurement
*the_delay = hd;
return ret;
}
/*
static int get_rate_information(uint64_t *elapsed_time, uint64_t *frames_played) {
// elapsed_time is in nanoseconds
int response = 0; // zero means okay
if (measurement_data_is_valid) {
*elapsed_time = measurement_time - measurement_start_time;
*frames_played = frames_played_at_measurement_time - frames_played_at_measurement_start_time;
} else {
*elapsed_time = 0;
*frames_played = 0;
response = -1;
}
return response;
}
*/
static int do_play(void *buf, int samples) {
// assuming the alsa_mutex has been acquired
// debug(3,"audio_alsa play called.");
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
int ret = 0;
if ((samples != 0) && (buf != NULL)) {
snd_pcm_state_t state;
snd_pcm_sframes_t my_delay;
int ret = delay_and_status(&state, &my_delay, NULL);
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
if (ret == 0) { // will be non-zero if an error or a stall
if ((samples != 0) && (buf != NULL)) {
snd_pcm_state_t state;
snd_pcm_sframes_t my_delay;
ret = delay_and_status(&state, &my_delay, NULL);
if (ret == 0) { // will be non-zero if an error or a stall
// just check the state of the DAC
if ((state != SND_PCM_STATE_PREPARED) && (state != SND_PCM_STATE_RUNNING) &&
@@ -1784,51 +1751,27 @@ static int do_play(void *buf, int samples) {
debug(1, "alsa: recovering from a previous underrun.");
else
debug(1, "alsa: underrun while writing %d samples to alsa device.", samples);
int tret = snd_pcm_recover(alsa_handle, ret, 1);
if (tret < 0) {
warn("alsa: can't recover from SND_PCM_STATE_XRUN: %s.", snd_strerror(tret));
}
ret = snd_pcm_recover(alsa_handle, ret, 1);
} else if (ret == -ESTRPIPE) { /* suspended */
debug(1, "alsa: suspended while writing %d samples to alsa device.", samples);
int tret;
while ((tret = snd_pcm_resume(alsa_handle)) == -EAGAIN) {
sleep(1); /* wait until the suspend flag is released */
if (tret < 0) {
warn("alsa: can't recover from SND_PCM_STATE_SUSPENDED state, "
"snd_pcm_prepare() "
"failed: %s.",
snd_strerror(tret));
}
}
} else {
if (ret == -ENODEV) {
if (config.unfixable_error_reported == 0) {
config.unfixable_error_reported = 1;
if (config.cmd_unfixable) {
command_execute(config.cmd_unfixable, "output_device_error_19", 1);
} else {
die("an unrecoverable error, \"output_device_error_19\", has been "
"detected.");
}
}
} else {
char errorstring[1024];
strerror_r(-ret, (char *)errorstring, sizeof(errorstring));
debug(1, "alsa: error %d (\"%s\") writing %d samples to alsa device.", ret,
(char *)errorstring, samples);
}
if (state != prior_state)
debug(1, "alsa: suspended while writing %d samples to alsa device.", samples);
if ((ret = snd_pcm_resume(alsa_handle)) == -ENOSYS)
ret = snd_pcm_prepare(alsa_handle);
} else if (ret >= 0) {
debug(1, "alsa: only %d of %d samples output.", ret, samples);
}
}
}
} else {
debug(1,
"alsa: device status returns fault status %d and SND_PCM_STATE_* "
"%d for play.",
ret, state);
pthread_setcancelstate(oldState, NULL);
if (ret < 0) {
char errorstring[1024];
strerror_r(-ret, (char *)errorstring, sizeof(errorstring));
debug(1, "alsa: SND_PCM_STATE_* %d, error %d (\"%s\") writing %d samples to alsa device.",
state, ret, (char *)errorstring, samples);
}
if (ret == -ENODEV) // if the device isn't there...
handle_unfixable_error(-ret);
}
pthread_setcancelstate(oldState, NULL);
return ret;
}
@@ -1854,6 +1797,9 @@ static int do_open(int do_auto_setup) {
// any previously-reported frame count
frames_sent_for_playing = 0;
alsa_backend_state = abm_connected; // only do this if it really opened it.
} else {
if (ret == -ENODEV) // if the device isn't there...
handle_unfixable_error(-ret);
}
} else {
debug(1, "alsa: do_open() -- output device already open.");
@@ -2006,7 +1952,7 @@ static void do_volume(double vol) { // caller is assumed to have the alsa_mutex
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
set_volume = vol;
pthread_cleanup_debug_mutex_lock(&alsa_mixer_mutex, 1000, 1);
if (volume_set_request && (open_mixer() == 1)) {
if (volume_set_request && (open_mixer() == 0)) {
if (has_softvol) {
if (ctl && elem_id) {
snd_ctl_elem_value_t *value;