Takashi Iwai
5dabf3bcba
- Backport upstream fixes / enhancements about alsa modules: mainly for UCM support (boo#1160914): 0001-alsa-mixer-path-test-Hide-unused-functions-when-buil.patch 0002-alsa-mixer-recognize-the-Speaker-Jack-control.patch 0003-alsa-mixer-add-support-for-SteelSeries-Arctis-Pro-20.patch 0004-alsa-mixer-Add-support-for-SteelSeries-Arctis-5-2019.patch 0005-alsa-mixer-add-support-for-LucidSound-LS31-and-creat.patch 0006-alsa-ucm-use-ucm2-name-for-the-direct-card-index-ope.patch 0007-alsa-ucm-add-mixer-IDs-to-ucm_items.patch 0008-alsa-mixer-handle-the-index-for-ALSA-mixer-element-i.patch 0009-alsa-mixer-improve-alsa_id_decode-function.patch 0010-alsa-ucm-Support-Playback-CaptureVolume.patch 0011-alsa-ucm-Fix-volume-control-based-on-review.patch 0012-alsa-ucm-use-the-correct-mixer-identifiers-as-first.patch 0013-alsa-ucm-add-support-for-master-volume.patch 0014-alsa-ucm-split-correctly-JackHWMute-device-names.patch 0015-alsa-ucm-fix-parsing-for-JackControl.patch 0016-alsa-ucm-add-comments-to-ucm_get_mixer_id.patch 0017-alsa-ucm-validate-access-to-PA_DEVICE_PORT_DATA.patch 0018-alsa-Skip-resume-PCM-if-hardware-doesn-t-support-it.patch 0019-alsa-ucm-parse-correctly-the-device-values.patch 0020-alsa-ucm-do-not-try-to-use-UCM-device-name-as-jack-n.patch 0021-alsa-util-do-not-try-to-guess-the-mixer-name-from-th.patch 0022-alsa-ucm-add-control-and-mixer-device-items.patch 0023-alsa-ucm-get-the-mixer-names-from-ucm-don-t-guess.patch 0024-alsa-ucm-use-the-proper-mixer-name-for-ucm-pcm-sink-.patch 0025-alsa-mixer-handle-interface-type-CARD-PCM-for-mixer-.patch 0026-alsa-mixer-Add-the-ability-to-pass-the-intended-role.patch 0027-alsa-mixer-Set-the-intended-role-of-Steelseries-Arct.patch 0028-alsa-rewrite-mixer-open-close-cache-mixer-accesses-i.patch OBS-URL: https://build.opensuse.org/request/show/774841 OBS-URL: https://build.opensuse.org/package/show/multimedia:libs/pulseaudio?expand=0&rev=217
762 lines
28 KiB
Diff
762 lines
28 KiB
Diff
From 3bd7c70c518d66707cbfde138ab7dcc505e463ac Mon Sep 17 00:00:00 2001
|
|
From: Jaroslav Kysela <perex@perex.cz>
|
|
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 <perex@perex.cz>
|
|
---
|
|
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 <pulsecore/rtpoll.h>
|
|
|
|
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
|
|
|