From 3bd7c70c518d66707cbfde138ab7dcc505e463ac Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sat, 7 Dec 2019 20:39:21 +0100 Subject: [PATCH] alsa: rewrite mixer open/close, cache mixer accesses in probe The ALSA mixer can be opened multiple times (especially for UCM in the probe). This adds a simple mixer cache to prevent multiple open calls. Signed-off-by: Jaroslav Kysela --- src/modules/alsa/alsa-mixer.c | 19 ++++---- src/modules/alsa/alsa-mixer.h | 19 ++++++-- src/modules/alsa/alsa-sink.c | 21 ++++---- src/modules/alsa/alsa-source.c | 21 ++++---- src/modules/alsa/alsa-ucm.c | 66 ++++++++++---------------- src/modules/alsa/alsa-ucm.h | 1 + src/modules/alsa/alsa-util.c | 95 +++++++++++++++++++------------------ src/modules/alsa/alsa-util.h | 6 ++- src/modules/alsa/module-alsa-card.c | 76 ++++++++++++++++++----------- 9 files changed, 175 insertions(+), 149 deletions(-) diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index 7956371b604b..a3c998b654e9 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -147,13 +147,14 @@ static int alsa_id_decode(const char *src, char *name, int *index) { return 0; } -pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *name) { +pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *mixer_device_name, const char *name) { pa_alsa_jack *jack; pa_assert(name); jack = pa_xnew0(pa_alsa_jack, 1); jack->path = path; + jack->mixer_device_name = pa_xstrdup(mixer_device_name); jack->name = pa_xstrdup(name); jack->alsa_name = pa_sprintf_malloc("%s Jack", name); jack->state_unplugged = PA_AVAILABLE_NO; @@ -172,6 +173,7 @@ void pa_alsa_jack_free(pa_alsa_jack *jack) { pa_xfree(jack->alsa_name); pa_xfree(jack->name); + pa_xfree(jack->mixer_device_name); pa_xfree(jack); } @@ -1982,7 +1984,7 @@ static pa_alsa_jack* jack_get(pa_alsa_path *p, const char *section) { if (pa_streq(j->name, section)) goto finish; - j = pa_alsa_jack_new(p, section); + j = pa_alsa_jack_new(p, NULL, section); PA_LLIST_INSERT_AFTER(pa_alsa_jack, p->jacks, p->last_jack, j); finish: @@ -4160,7 +4162,8 @@ fail: } static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile, - pa_alsa_direction_t direction, pa_hashmap *used_paths) { + pa_alsa_direction_t direction, pa_hashmap *used_paths, + pa_hashmap *mixers) { pa_alsa_path *p; void *state; @@ -4185,7 +4188,7 @@ static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile, pa_assert(pcm_handle); - mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL); + mixer_handle = pa_alsa_open_mixer_for_pcm(mixers, pcm_handle, true); if (!mixer_handle) { /* Cannot open mixer, remove all entries */ pa_hashmap_remove_all(ps->paths); @@ -4203,9 +4206,6 @@ static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile, path_set_condense(ps, mixer_handle); path_set_make_path_descriptions_unique(ps); - if (mixer_handle) - snd_mixer_close(mixer_handle); - PA_HASHMAP_FOREACH(p, ps->paths, state) pa_hashmap_put(used_paths, p, p); @@ -4785,6 +4785,7 @@ static void mapping_query_hw_device(pa_alsa_mapping *mapping, snd_pcm_t *pcm) { void pa_alsa_profile_set_probe( pa_alsa_profile_set *ps, + pa_hashmap *mixers, const char *dev_id, const pa_sample_spec *ss, unsigned default_n_fragments, @@ -4914,14 +4915,14 @@ void pa_alsa_profile_set_probe( PA_IDXSET_FOREACH(m, p->output_mappings, idx) if (m->output_pcm) { found_output |= !p->fallback_output; - mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT, used_paths); + mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT, used_paths, mixers); } if (p->input_mappings) PA_IDXSET_FOREACH(m, p->input_mappings, idx) if (m->input_pcm) { found_input |= !p->fallback_input; - mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT, used_paths); + mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT, used_paths, mixers); } } diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h index 7864a2c1252f..325b0c998b02 100644 --- a/src/modules/alsa/alsa-mixer.h +++ b/src/modules/alsa/alsa-mixer.h @@ -32,6 +32,7 @@ #include typedef struct pa_alsa_fdlist pa_alsa_fdlist; +typedef struct pa_alsa_mixer pa_alsa_mixer; typedef struct pa_alsa_mixer_pdata pa_alsa_mixer_pdata; typedef struct pa_alsa_setting pa_alsa_setting; typedef struct pa_alsa_mixer_id pa_alsa_mixer_id; @@ -98,6 +99,13 @@ struct pa_alsa_setting { unsigned priority; }; +/* An entry for one ALSA mixer */ +struct pa_alsa_mixer { + snd_mixer_t *mixer_handle; + pa_alsa_fdlist *fdl; + bool used_for_probe_only:1; +}; + /* ALSA mixer element identifier */ struct pa_alsa_mixer_id { char *name; @@ -165,6 +173,9 @@ struct pa_alsa_jack { pa_alsa_path *path; PA_LLIST_FIELDS(pa_alsa_jack); + snd_mixer_t *mixer_handle; + char *mixer_device_name; + char *name; /* E g "Headphone" */ char *alsa_name; /* E g "Headphone Jack" */ bool has_control; /* is the jack itself present? */ @@ -182,7 +193,7 @@ struct pa_alsa_jack { bool append_pcm_to_name; }; -pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *name); +pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *mixer_device_name, const char *name); void pa_alsa_jack_free(pa_alsa_jack *jack); void pa_alsa_jack_set_has_control(pa_alsa_jack *jack, bool has_control); void pa_alsa_jack_set_plugged_in(pa_alsa_jack *jack, bool plugged_in); @@ -201,6 +212,7 @@ struct pa_alsa_path { char *description; unsigned priority; bool autodetect_eld_device; + pa_alsa_mixer *eld_mixer_handle; int eld_device; pa_proplist *proplist; @@ -359,14 +371,11 @@ void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix); pa_alsa_mapping *pa_alsa_mapping_get(pa_alsa_profile_set *ps, const char *name); pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus); -void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, const pa_sample_spec *ss, unsigned default_n_fragments, unsigned default_fragment_size_msec); +void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, pa_hashmap *mixers, const char *dev_id, const pa_sample_spec *ss, unsigned default_n_fragments, unsigned default_fragment_size_msec); void pa_alsa_profile_set_free(pa_alsa_profile_set *s); void pa_alsa_profile_set_dump(pa_alsa_profile_set *s); void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set *s); -snd_mixer_t *pa_alsa_open_mixer_by_name(const char *dev); -snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device); - pa_alsa_fdlist *pa_alsa_fdlist_new(void); void pa_alsa_fdlist_free(pa_alsa_fdlist *fdl); int pa_alsa_fdlist_set_handle(pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, snd_hctl_t *hctl_handle, pa_mainloop_api* m); diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 5ea4b2597449..363b4be2fa25 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -105,6 +105,7 @@ struct userdata { char *paths_dir; pa_alsa_fdlist *mixer_fdl; pa_alsa_mixer_pdata *mixer_pd; + pa_hashmap *mixers; snd_mixer_t *mixer_handle; pa_alsa_path_set *mixer_path_set; pa_alsa_path *mixer_path; @@ -2088,13 +2089,16 @@ static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char if (!mapping && !element) return; + u->mixers = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, + NULL, (pa_free_cb_t) pa_alsa_mixer_free); + mdev = pa_proplist_gets(mapping->proplist, "alsa.mixer_device"); if (mdev) { - u->mixer_handle = pa_alsa_open_mixer_by_name(mdev); + u->mixer_handle = pa_alsa_open_mixer_by_name(u->mixers, mdev, true); } else { - u->mixer_handle = pa_alsa_open_mixer_for_pcm(u->pcm_handle, &u->control_device); + u->mixer_handle = pa_alsa_open_mixer_for_pcm(u->mixers, u->pcm_handle, true); } - if (!mdev) { + if (!u->mixer_handle) { pa_log_info("Failed to find a working mixer device."); return; } @@ -2121,10 +2125,9 @@ fail: u->mixer_path = NULL; } - if (u->mixer_handle) { - snd_mixer_close(u->mixer_handle); - u->mixer_handle = NULL; - } + u->mixer_handle = NULL; + pa_hashmap_free(u->mixers); + u->mixers = NULL; } static int setup_mixer(struct userdata *u, bool ignore_dB) { @@ -2759,8 +2762,8 @@ static void userdata_free(struct userdata *u) { if (u->mixer_path && !u->mixer_path_set && !u->ucm_context) pa_alsa_path_free(u->mixer_path); - if (u->mixer_handle) - snd_mixer_close(u->mixer_handle); + if (u->mixers) + pa_hashmap_free(u->mixers); if (u->smoother) pa_smoother_free(u->smoother); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index bd78d45e2a5b..b46e845cc5a7 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -93,6 +93,7 @@ struct userdata { char *paths_dir; pa_alsa_fdlist *mixer_fdl; pa_alsa_mixer_pdata *mixer_pd; + pa_hashmap *mixers; snd_mixer_t *mixer_handle; pa_alsa_path_set *mixer_path_set; pa_alsa_path *mixer_path; @@ -1794,13 +1795,16 @@ static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char if (!mapping && !element) return; + u->mixers = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, + NULL, (pa_free_cb_t) pa_alsa_mixer_free); + mdev = pa_proplist_gets(mapping->proplist, "alsa.mixer_device"); if (mdev) { - u->mixer_handle = pa_alsa_open_mixer_by_name(mdev); + u->mixer_handle = pa_alsa_open_mixer_by_name(u->mixers, mdev, false); } else { - u->mixer_handle = pa_alsa_open_mixer_for_pcm(u->pcm_handle, &u->control_device); + u->mixer_handle = pa_alsa_open_mixer_for_pcm(u->mixers, u->pcm_handle, false); } - if (!mdev) { + if (!u->mixer_handle) { pa_log_info("Failed to find a working mixer device."); return; } @@ -1827,10 +1831,9 @@ fail: u->mixer_path = NULL; } - if (u->mixer_handle) { - snd_mixer_close(u->mixer_handle); - u->mixer_handle = NULL; - } + u->mixer_handle = NULL; + pa_hashmap_free(u->mixers); + u->mixers = NULL; } static int setup_mixer(struct userdata *u, bool ignore_dB) { @@ -2403,8 +2406,8 @@ static void userdata_free(struct userdata *u) { if (u->mixer_path && !u->mixer_path_set && !u->ucm_context) pa_alsa_path_free(u->mixer_path); - if (u->mixer_handle) - snd_mixer_close(u->mixer_handle); + if (u->mixers) + pa_hashmap_free(u->mixers); if (u->smoother) pa_smoother_free(u->smoother); diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c index a64505a0c781..b9a4b8c4b436 100644 --- a/src/modules/alsa/alsa-ucm.c +++ b/src/modules/alsa/alsa-ucm.c @@ -820,13 +820,13 @@ static int pa_alsa_ucm_device_cmp(const void *a, const void *b) { return strcmp(pa_proplist_gets(d1->proplist, PA_ALSA_PROP_UCM_NAME), pa_proplist_gets(d2->proplist, PA_ALSA_PROP_UCM_NAME)); } -static void probe_volumes(pa_hashmap *hash, bool is_sink, snd_pcm_t *pcm_handle, bool ignore_dB) { +static void probe_volumes(pa_hashmap *hash, bool is_sink, snd_pcm_t *pcm_handle, pa_hashmap *mixers, bool ignore_dB) { pa_device_port *port; pa_alsa_path *path; pa_alsa_ucm_port_data *data; pa_alsa_ucm_device *dev; - snd_mixer_t *mixer_handle = NULL; - const char *profile, *mdev_opened = NULL, *mdev, *mdev2; + snd_mixer_t *mixer_handle; + const char *profile, *mdev, *mdev2; void *state, *state2; int idx; @@ -843,16 +843,9 @@ static void probe_volumes(pa_hashmap *hash, bool is_sink, snd_pcm_t *pcm_handle, mdev = mdev2; } - if (!mdev_opened || !pa_streq(mdev_opened, mdev)) { - if (mixer_handle) { - snd_mixer_close(mixer_handle); - mdev_opened = NULL; - } - if (!(mixer_handle = pa_alsa_open_mixer_by_name(mdev))) { - pa_log_error("Failed to find a working mixer device (%s).", mdev); - goto fail; - } - mdev_opened = mdev; + if (!(mixer_handle = pa_alsa_open_mixer_by_name(mixers, mdev, true))) { + pa_log_error("Failed to find a working mixer device (%s).", mdev); + goto fail; } PA_HASHMAP_FOREACH_KV(profile, path, data->paths, state2) { @@ -867,9 +860,6 @@ static void probe_volumes(pa_hashmap *hash, bool is_sink, snd_pcm_t *pcm_handle, } } - if (mixer_handle) - snd_mixer_close(mixer_handle); - return; fail: @@ -1194,7 +1184,7 @@ void pa_alsa_ucm_add_ports( pa_alsa_ucm_add_ports_combination(*p, context, is_sink, card->ports, NULL, card->core); /* now set up volume paths if any */ - probe_volumes(*p, is_sink, pcm_handle, ignore_dB); + probe_volumes(*p, is_sink, pcm_handle, context->ucm->mixers, ignore_dB); /* then set property PA_PROP_DEVICE_INTENDED_ROLES */ merged_roles = pa_xstrdup(pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES)); @@ -1543,6 +1533,7 @@ static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *d pa_alsa_jack *j; const char *device_name; const char *jack_control; + const char *mixer_device_name; char *name; pa_assert(ucm); @@ -1585,7 +1576,14 @@ static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *d if (pa_streq(j->name, name)) goto finish; - j = pa_alsa_jack_new(NULL, name); + mixer_device_name = get_jack_mixer_device(device, true); + if (!mixer_device_name) + mixer_device_name = get_jack_mixer_device(device, false); + if (!mixer_device_name) { + pa_log("[%s] No mixer device name for JackControl \"%s\"", device_name, jack_control); + return NULL; + } + j = pa_alsa_jack_new(NULL, mixer_device_name, name); PA_LLIST_PREPEND(pa_alsa_jack, ucm->jacks, j); finish: @@ -1765,44 +1763,28 @@ static void profile_finalize_probing(pa_alsa_profile *p) { } } -static void ucm_mapping_jack_probe(pa_alsa_mapping *m) { - snd_mixer_t *mixer_handle = NULL; +static void ucm_mapping_jack_probe(pa_alsa_mapping *m, pa_hashmap *mixers) { + snd_mixer_t *mixer_handle; pa_alsa_ucm_mapping_context *context = &m->ucm_context; pa_alsa_ucm_device *dev; - bool is_sink = m->direction == PA_ALSA_DIRECTION_OUTPUT; - const char *mdev_opened = NULL, *mdev; uint32_t idx; PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) { bool has_control; - if (!dev->jack) + if (!dev->jack || !dev->jack->mixer_device_name) continue; - mdev = get_jack_mixer_device(dev, is_sink); - if (mdev == NULL) { - pa_log_error("Unable to determine mixer device for jack %s", dev->jack->name); + mixer_handle = pa_alsa_open_mixer_by_name(mixers, dev->jack->mixer_device_name, true); + if (!mixer_handle) { + pa_log_error("Unable to determine open mixer device '%s' for jack %s", dev->jack->mixer_device_name, dev->jack->name); continue; } - if (!mdev_opened || !pa_streq(mdev_opened, mdev)) { - if (mixer_handle) { - snd_mixer_close(mixer_handle); - mdev_opened = NULL; - } - mixer_handle = pa_alsa_open_mixer_by_name(mdev); - if (!mixer_handle) - continue; - mdev_opened = mdev; - } - has_control = pa_alsa_mixer_find_card(mixer_handle, dev->jack->alsa_name, 0) != NULL; pa_alsa_jack_set_has_control(dev->jack, has_control); pa_log_info("UCM jack %s has_control=%d", dev->jack->name, dev->jack->has_control); } - - if (mixer_handle) - snd_mixer_close(mixer_handle); } static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps) { @@ -1860,11 +1842,11 @@ static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set * PA_IDXSET_FOREACH(m, p->output_mappings, idx) if (!PA_UCM_IS_MODIFIER_MAPPING(m)) - ucm_mapping_jack_probe(m); + ucm_mapping_jack_probe(m, ucm->mixers); PA_IDXSET_FOREACH(m, p->input_mappings, idx) if (!PA_UCM_IS_MODIFIER_MAPPING(m)) - ucm_mapping_jack_probe(m); + ucm_mapping_jack_probe(m, ucm->mixers); profile_finalize_probing(p); } diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h index 014bc334ad67..49362992865b 100644 --- a/src/modules/alsa/alsa-ucm.h +++ b/src/modules/alsa/alsa-ucm.h @@ -248,6 +248,7 @@ struct pa_alsa_ucm_config { snd_use_case_mgr_t *ucm_mgr; pa_alsa_ucm_verb *active_verb; + pa_hashmap *mixers; PA_LLIST_HEAD(pa_alsa_ucm_verb, verbs); PA_LLIST_HEAD(pa_alsa_jack, jacks); }; diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index 08a144782394..34930c9fa155 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -1726,83 +1726,86 @@ static int prepare_mixer(snd_mixer_t *mixer, const char *dev) { return 0; } -snd_mixer_t *pa_alsa_open_mixer(int alsa_card_index, char **ctl_device) { - int err; - snd_mixer_t *m; - char *md; - snd_pcm_info_t* info; - snd_pcm_info_alloca(&info); - - if ((err = snd_mixer_open(&m, 0)) < 0) { - pa_log("Error opening mixer: %s", pa_alsa_strerror(err)); - return NULL; - } - - /* Then, try by card index */ - md = pa_sprintf_malloc("hw:%i", alsa_card_index); - if (prepare_mixer(m, md) >= 0) { - - if (ctl_device) - *ctl_device = md; - else - pa_xfree(md); - - return m; - } - +snd_mixer_t *pa_alsa_open_mixer(pa_hashmap *mixers, int alsa_card_index, bool probe) { + char *md = pa_sprintf_malloc("hw:%i", alsa_card_index); + snd_mixer_t *m = pa_alsa_open_mixer_by_name(mixers, md, probe); pa_xfree(md); - - snd_mixer_close(m); - return NULL; + return m; } -snd_mixer_t *pa_alsa_open_mixer_by_name(const char *dev) { +snd_mixer_t *pa_alsa_open_mixer_by_name(pa_hashmap *mixers, const char *dev, bool probe) { int err; snd_mixer_t *m; + pa_alsa_mixer *pm; + pa_assert(mixers); pa_assert(dev); + pm = pa_hashmap_get(mixers, dev); + if (pm) { + if (!probe) + pm->used_for_probe_only = false; + return pm->mixer_handle; + } + if ((err = snd_mixer_open(&m, 0)) < 0) { pa_log("Error opening mixer: %s", pa_alsa_strerror(err)); return NULL; } - if (prepare_mixer(m, dev) >= 0) - return m; + if (prepare_mixer(m, dev) >= 0) { + pm = pa_xnew0(pa_alsa_mixer, 1); + if (pm) { + pm->used_for_probe_only = probe; + pm->mixer_handle = m; + pa_hashmap_put(mixers, pa_xstrdup(dev), pm); + return m; + } + } snd_mixer_close(m); return NULL; } -snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device) { - snd_mixer_t *m; +snd_mixer_t *pa_alsa_open_mixer_for_pcm(pa_hashmap *mixers, snd_pcm_t *pcm, bool probe) { snd_pcm_info_t* info; snd_pcm_info_alloca(&info); pa_assert(pcm); if (snd_pcm_info(pcm, info) >= 0) { - char *md; int card_idx; - if ((card_idx = snd_pcm_info_get_card(info)) >= 0) { + if ((card_idx = snd_pcm_info_get_card(info)) >= 0) + return pa_alsa_open_mixer(mixers, card_idx, probe); + } - md = pa_sprintf_malloc("hw:%i", card_idx); - m = pa_alsa_open_mixer_by_name(md); - if (m) { - if (ctl_device) - *ctl_device = md; - else - pa_xfree(md); + return NULL; +} - return m; - } +void pa_alsa_mixer_set_fdlist(pa_hashmap *mixers, snd_mixer_t *mixer_handle, pa_mainloop_api *ml) +{ + pa_alsa_mixer *pm; + void *state; - pa_xfree(md); + PA_HASHMAP_FOREACH(pm, mixers, state) + if (pm->mixer_handle == mixer_handle) { + pm->used_for_probe_only = false; + if (!pm->fdl) { + pm->fdl = pa_alsa_fdlist_new(); + if (pm->fdl) + pa_alsa_fdlist_set_handle(pm->fdl, pm->mixer_handle, NULL, ml); + } } - } +} - return NULL; +void pa_alsa_mixer_free(pa_alsa_mixer *mixer) +{ + if (mixer->fdl) + pa_alsa_fdlist_free(mixer->fdl); + if (mixer->mixer_handle) + snd_mixer_close(mixer->mixer_handle); + pa_xfree(mixer); } int pa_alsa_get_hdmi_eld(snd_hctl_elem_t *elem, pa_hdmi_eld *eld) { diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h index ceca48809400..0d3d5af13370 100644 --- a/src/modules/alsa/alsa-util.h +++ b/src/modules/alsa/alsa-util.h @@ -144,7 +144,11 @@ bool pa_alsa_may_tsched(bool want); snd_mixer_elem_t *pa_alsa_mixer_find_card(snd_mixer_t *mixer, const char *name, unsigned int device); snd_mixer_elem_t *pa_alsa_mixer_find_pcm(snd_mixer_t *mixer, const char *name, unsigned int device); -snd_mixer_t *pa_alsa_open_mixer(int alsa_card_index, char **ctl_device); +snd_mixer_t *pa_alsa_open_mixer(pa_hashmap *mixers, int alsa_card_index, bool probe); +snd_mixer_t *pa_alsa_open_mixer_by_name(pa_hashmap *mixers, const char *dev, bool probe); +snd_mixer_t *pa_alsa_open_mixer_for_pcm(pa_hashmap *mixers, snd_pcm_t *pcm, bool probe); +void pa_alsa_mixer_set_fdlist(pa_hashmap *mixers, snd_mixer_t *mixer, pa_mainloop_api *ml); +void pa_alsa_mixer_free(pa_alsa_mixer *mixer); typedef struct pa_hdmi_eld pa_hdmi_eld; struct pa_hdmi_eld { diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c index ba7a19a38983..3b2f99fc4e54 100644 --- a/src/modules/alsa/module-alsa-card.c +++ b/src/modules/alsa/module-alsa-card.c @@ -111,9 +111,8 @@ struct userdata { char *device_id; int alsa_card_index; - snd_mixer_t *mixer_handle; + pa_hashmap *mixers; pa_hashmap *jacks; - pa_alsa_fdlist *mixer_fdl; pa_card *card; @@ -569,9 +568,6 @@ static void init_eld_ctls(struct userdata *u) { void *state; pa_device_port *port; - if (!u->mixer_handle) - return; - /* The code in this function expects ports to have a pa_alsa_port_data * struct as their data, but in UCM mode ports don't have any data. Hence, * the ELD controls can't currently be used in UCM mode. */ @@ -580,6 +576,7 @@ static void init_eld_ctls(struct userdata *u) { PA_HASHMAP_FOREACH(port, u->card->ports, state) { pa_alsa_port_data *data = PA_DEVICE_PORT_DATA(port); + snd_mixer_t *mixer_handle; snd_mixer_elem_t* melem; int device; @@ -588,8 +585,13 @@ static void init_eld_ctls(struct userdata *u) { if (device < 0) continue; - melem = pa_alsa_mixer_find_pcm(u->mixer_handle, "ELD", device); + mixer_handle = pa_alsa_open_mixer(u->mixers, u->alsa_card_index, true); + if (!mixer_handle) + continue; + + melem = pa_alsa_mixer_find_pcm(mixer_handle, "ELD", device); if (melem) { + pa_alsa_mixer_set_fdlist(u->mixers, mixer_handle, u->core->mainloop); snd_mixer_elem_set_callback(melem, hdmi_eld_changed); snd_mixer_elem_set_callback_private(melem, u); hdmi_eld_changed(melem, 0); @@ -630,25 +632,31 @@ static void init_jacks(struct userdata *u) { if (pa_hashmap_size(u->jacks) == 0) return; - u->mixer_fdl = pa_alsa_fdlist_new(); - - u->mixer_handle = pa_alsa_open_mixer(u->alsa_card_index, NULL); - if (u->mixer_handle && pa_alsa_fdlist_set_handle(u->mixer_fdl, u->mixer_handle, NULL, u->core->mainloop) >= 0) { - PA_HASHMAP_FOREACH(jack, u->jacks, state) { - jack->melem = pa_alsa_mixer_find_card(u->mixer_handle, jack->alsa_name, 0); - if (!jack->melem) { - pa_log_warn("Jack '%s' seems to have disappeared.", jack->alsa_name); - pa_alsa_jack_set_has_control(jack, false); - continue; + PA_HASHMAP_FOREACH(jack, u->jacks, state) { + if (!jack->mixer_device_name) { + jack->mixer_handle = pa_alsa_open_mixer(u->mixers, u->alsa_card_index, false); + if (!jack->mixer_handle) { + pa_log("Failed to open mixer for card %d for jack detection", u->alsa_card_index); + continue; + } + } else { + jack->mixer_handle = pa_alsa_open_mixer_by_name(u->mixers, jack->mixer_device_name, false); + if (!jack->mixer_handle) { + pa_log("Failed to open mixer '%s' for jack detection", jack->mixer_device_name); + continue; } - snd_mixer_elem_set_callback(jack->melem, report_jack_state); - snd_mixer_elem_set_callback_private(jack->melem, u); - report_jack_state(jack->melem, 0); } - - } else - pa_log("Failed to open mixer for jack detection"); - + pa_alsa_mixer_set_fdlist(u->mixers, jack->mixer_handle, u->core->mainloop); + jack->melem = pa_alsa_mixer_find_card(jack->mixer_handle, jack->alsa_name, 0); + if (!jack->melem) { + pa_log_warn("Jack '%s' seems to have disappeared.", jack->alsa_name); + pa_alsa_jack_set_has_control(jack, false); + continue; + } + snd_mixer_elem_set_callback(jack->melem, report_jack_state); + snd_mixer_elem_set_callback_private(jack->melem, u); + report_jack_state(jack->melem, 0); + } } static void set_card_name(pa_card_new_data *data, pa_modargs *ma, const char *device_id) { @@ -772,6 +780,10 @@ int pa__init(pa_module *m) { u->use_ucm = true; u->ucm.core = m->core; + u->mixers = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, + pa_xfree, (pa_free_cb_t) pa_alsa_mixer_free); + u->ucm.mixers = u->mixers; /* alias */ + if (!(u->modargs = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments."); goto fail; @@ -852,7 +864,7 @@ int pa__init(pa_module *m) { u->profile_set->ignore_dB = ignore_dB; - pa_alsa_profile_set_probe(u->profile_set, u->device_id, &m->core->default_sample_spec, m->core->default_n_fragments, m->core->default_fragment_size_msec); + pa_alsa_profile_set_probe(u->profile_set, u->mixers, u->device_id, &m->core->default_sample_spec, m->core->default_n_fragments, m->core->default_fragment_size_msec); pa_alsa_profile_set_dump(u->profile_set); pa_card_new_data_init(&data); @@ -952,6 +964,16 @@ int pa__init(pa_module *m) { init_profile(u); init_eld_ctls(u); + /* Remove all probe only mixers */ + if (u->mixers) { + const char *devname; + pa_alsa_mixer *pm; + void *state; + PA_HASHMAP_FOREACH_KV(devname, pm, u->mixers, state) + if (pm->used_for_probe_only) + pa_hashmap_remove_and_free(u->mixers, devname); + } + if (reserve) pa_reserve_wrapper_unref(reserve); @@ -1002,10 +1024,8 @@ void pa__done(pa_module*m) { if (!(u = m->userdata)) goto finish; - if (u->mixer_fdl) - pa_alsa_fdlist_free(u->mixer_fdl); - if (u->mixer_handle) - snd_mixer_close(u->mixer_handle); + if (u->mixers) + pa_hashmap_free(u->mixers); if (u->jacks) pa_hashmap_free(u->jacks); -- 2.16.4