A fallback mapping or profile will only be considered for probing if all non-fallback profiles fail. If auto-profiles are used, a profile made up of one non-fallback mapping and one fallback mapping will be considered a fallback profile. Signed-off-by: David Henningsson <david.henningsson at canonical.com> --- src/modules/alsa/alsa-mixer.c | 77 ++++++++++++++++++++++-- src/modules/alsa/alsa-mixer.h | 3 + src/modules/alsa/mixer/profile-sets/default.conf | 2 + 3 files changed, 78 insertions(+), 4 deletions(-) diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index 77c3c7e..9aa28a3 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -3611,6 +3611,34 @@ static int mapping_parse_priority(pa_config_parser_state *state) { return 0; } +static int mapping_parse_fallback(pa_config_parser_state *state) { + pa_alsa_profile_set *ps; + pa_alsa_profile *p; + pa_alsa_mapping *m; + int k; + + pa_assert(state); + + ps = state->userdata; + + if ((k = pa_parse_boolean(state->rvalue)) < 0) { + pa_log("[%s:%u] Fallback invalid of '%s'", state->filename, state->lineno, state->section); + return -1; + } + + if ((m = pa_alsa_mapping_get(ps, state->section))) + m->fallback = k; + else if ((p = profile_get(ps, state->section))) + p->fallback_input = p->fallback_output = k; + else { + pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section); + return -1; + } + + return 0; +} + + static int profile_parse_mappings(pa_config_parser_state *state) { pa_alsa_profile_set *ps; pa_alsa_profile *p; @@ -3939,12 +3967,14 @@ static void profile_set_add_auto_pair( p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); pa_idxset_put(p->output_mappings, m, NULL); p->priority += m->priority * 100; + p->fallback_output = m->fallback; } if (n) { p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); pa_idxset_put(p->input_mappings, n, NULL); p->priority += n->priority; + p->fallback_input = n->fallback; } pa_hashmap_put(ps->profiles, p->name, p); @@ -4186,6 +4216,7 @@ pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel /* Shared by [Mapping ...] and [Profile ...] */ { "description", mapping_parse_description, NULL, NULL }, { "priority", mapping_parse_priority, NULL, NULL }, + { "fallback", mapping_parse_fallback, NULL, NULL }, /* [Profile ...] */ { "input-mappings", profile_parse_mappings, NULL, NULL }, @@ -4337,6 +4368,24 @@ static void paths_drop_unused(pa_hashmap* h, pa_hashmap *keep) { } } +static int add_profiles_to_probe( + pa_alsa_profile **list, + pa_hashmap *profiles, + bool fallback_output, + bool fallback_input) { + + int i = 0; + void *state; + pa_alsa_profile *p; + PA_HASHMAP_FOREACH(p, profiles, state) + if (p->fallback_input == fallback_input && p->fallback_output == fallback_output) { + *list = p; + list++; + i++; + } + return i; +} + void pa_alsa_profile_set_probe( pa_alsa_profile_set *ps, const char *dev_id, @@ -4344,8 +4393,10 @@ void pa_alsa_profile_set_probe( unsigned default_n_fragments, unsigned default_fragment_size_msec) { - void *state; + bool found_output = false, found_input = false; + pa_alsa_profile *p, *last = NULL; + pa_alsa_profile **pp, **probe_order; pa_alsa_mapping *m; pa_hashmap *broken_inputs, *broken_outputs, *used_paths; @@ -4359,9 +4410,22 @@ void pa_alsa_profile_set_probe( broken_inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); broken_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); used_paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + pp = probe_order = pa_xnew0(pa_alsa_profile *, pa_hashmap_size(ps->profiles) + 1); - PA_HASHMAP_FOREACH(p, ps->profiles, state) { + pp += add_profiles_to_probe(pp, ps->profiles, false, false); + pp += add_profiles_to_probe(pp, ps->profiles, false, true); + pp += add_profiles_to_probe(pp, ps->profiles, true, false); + pp += add_profiles_to_probe(pp, ps->profiles, true, true); + + for (pp = probe_order; *pp; pp++) { uint32_t idx; + p = *pp; + + /* Skip if fallback and already found something */ + if (found_input && p->fallback_input) + continue; + if (found_output && p->fallback_output) + continue; /* Skip if this is already marked that it is supported (i.e. from the config file) */ if (!p->supported) { @@ -4445,13 +4509,17 @@ void pa_alsa_profile_set_probe( if (p->output_mappings) PA_IDXSET_FOREACH(m, p->output_mappings, idx) - if (m->output_pcm) + if (m->output_pcm) { + found_output |= !p->fallback_output; mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT, used_paths); + } if (p->input_mappings) PA_IDXSET_FOREACH(m, p->input_mappings, idx) - if (m->input_pcm) + if (m->input_pcm) { + found_input |= !p->fallback_input; mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT, used_paths); + } } /* Clean up */ @@ -4464,6 +4532,7 @@ void pa_alsa_profile_set_probe( pa_hashmap_free(broken_inputs); pa_hashmap_free(broken_outputs); pa_hashmap_free(used_paths); + pa_xfree(probe_order); ps->probed = true; } diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h index d04c2bb..4949560 100644 --- a/src/modules/alsa/alsa-mixer.h +++ b/src/modules/alsa/alsa-mixer.h @@ -265,6 +265,7 @@ struct pa_alsa_mapping { unsigned supported; bool exact_channels:1; + bool fallback:1; /* Temporarily used during probing */ snd_pcm_t *input_pcm; @@ -285,6 +286,8 @@ struct pa_alsa_profile { unsigned priority; bool supported:1; + bool fallback_input:1; + bool fallback_output:1; char **input_mapping_names; char **output_mapping_names; diff --git a/src/modules/alsa/mixer/profile-sets/default.conf b/src/modules/alsa/mixer/profile-sets/default.conf index 77a4d76..feede52 100644 --- a/src/modules/alsa/mixer/profile-sets/default.conf +++ b/src/modules/alsa/mixer/profile-sets/default.conf @@ -57,6 +57,7 @@ ; ; exact-channels = yes | no # If no, and the exact number of channels is not supported, ; # allow device to be opened with another channel count +; fallback = no | yes # This mapping will only be considered if all non-fallback mappings fail ; [Profile id] ; input-mappings = ... # Lists mappings for sources on this profile, those mapping must be ; # defined in this file too @@ -68,6 +69,7 @@ ; # will be assumed as working without probing. Makes initialization ; # a bit faster but only works if the card is really known well. ; +; fallback = no | yes # This profile will only be considered if all non-fallback profiles fail ; [DecibelFix element] # Decibel fixes can be used to work around missing or incorrect dB ; # information from alsa. A decibel fix is a table that maps volume steps ; # to decibel values for one volume element. The "element" part in the -- 1.9.1