[v2,1/2] alsa-mixer: Add alsa index number support for Element and Jack

Submitted by Hui Wang on Jan. 14, 2019, 4:24 a.m.

Details

Message ID 20190114042414.1234-2-hui.wang@canonical.com
State New
Headers show
Series "Handle two Headphone Jacks with the same name" ( rev: 2 ) in PulseAudio

Not browsing as part of any series.

Commit Message

Hui Wang Jan. 14, 2019, 4:24 a.m.
We have met a couple of machines which have two Kcontrols with the
exact same names and with the different index numbers, for example:
  (1 physical headphone jack + 1 physical headset jack on front panel)
  - Simple mixer control 'Headphone',0 (volume and switch)
  - Simple mixer control 'Headphone',1 (volume and switch)
  (1 physical mic jack + 1 physical headset jack on front panel)
  - iface=CARD,name='Mic Jack' index 0
  - iface=CARD,name='Mic Jack' index 1

To let pulseaudio treat them as different Jacks or differnt Elements,
we introduce index to the pa_alsa_jack and pa_alsa_element.

When probing jacks or elements, we add index as a parameter.

Signed-off-by: Hui Wang <hui.wang@canonical.com>
---
 src/modules/alsa/alsa-mixer.c                 | 59 ++++++++++++++-----
 src/modules/alsa/alsa-mixer.h                 |  2 +
 src/modules/alsa/alsa-ucm.c                   |  2 +-
 src/modules/alsa/alsa-util.c                  |  4 +-
 src/modules/alsa/alsa-util.h                  |  2 +-
 .../mixer/paths/analog-output.conf.common     |  5 ++
 src/modules/alsa/module-alsa-card.c           |  4 +-
 7 files changed, 59 insertions(+), 19 deletions(-)

Patch hide | download patch | download mbox

diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index 91dfc66ee..e32ec5cd7 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -718,11 +718,11 @@  static pa_volume_t from_alsa_volume(long v, long min, long max) {
     return (pa_volume_t) round(((double) (v - min) * PA_VOLUME_NORM) / (double) (max - min));
 }
 
-#define SELEM_INIT(sid, name)                           \
+#define SELEM_INIT(sid, name, index)			\
     do {                                                \
         snd_mixer_selem_id_alloca(&(sid));              \
         snd_mixer_selem_id_set_name((sid), (name));     \
-        snd_mixer_selem_id_set_index((sid), 0);         \
+        snd_mixer_selem_id_set_index((sid), index);     \
     } while(false)
 
 static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
@@ -737,7 +737,7 @@  static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
     pa_assert(cm);
     pa_assert(v);
 
-    SELEM_INIT(sid, e->alsa_name);
+    SELEM_INIT(sid, e->alsa_name, e->index);
     if (!(me = snd_mixer_find_selem(m, sid))) {
         pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
         return -1;
@@ -902,7 +902,7 @@  static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, bool *b) {
     pa_assert(e);
     pa_assert(b);
 
-    SELEM_INIT(sid, e->alsa_name);
+    SELEM_INIT(sid, e->alsa_name, e->index);
     if (!(me = snd_mixer_find_selem(m, sid))) {
         pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
         return -1;
@@ -1066,7 +1066,7 @@  static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
     pa_assert(v);
     pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
 
-    SELEM_INIT(sid, e->alsa_name);
+    SELEM_INIT(sid, e->alsa_name, e->index);
     if (!(me = snd_mixer_find_selem(m, sid))) {
         pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
         return -1;
@@ -1256,7 +1256,7 @@  static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, bool b) {
     pa_assert(m);
     pa_assert(e);
 
-    SELEM_INIT(sid, e->alsa_name);
+    SELEM_INIT(sid, e->alsa_name, e->index);
     if (!(me = snd_mixer_find_selem(m, sid))) {
         pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
         return -1;
@@ -1307,7 +1307,7 @@  static int element_set_constant_volume(pa_alsa_element *e, snd_mixer_t *m) {
     pa_assert(m);
     pa_assert(e);
 
-    SELEM_INIT(sid, e->alsa_name);
+    SELEM_INIT(sid, e->alsa_name, e->index);
     if (!(me = snd_mixer_find_selem(m, sid))) {
         pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
         return -1;
@@ -1734,7 +1734,7 @@  static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
     pa_assert(e);
     pa_assert(e->path);
 
-    SELEM_INIT(sid, e->alsa_name);
+    SELEM_INIT(sid, e->alsa_name, e->index);
 
     if (!(me = snd_mixer_find_selem(m, sid))) {
 
@@ -1837,7 +1837,7 @@  static int jack_probe(pa_alsa_jack *j, pa_alsa_mapping *mapping, snd_mixer_t *m)
         j->append_pcm_to_name = false;
     }
 
-    has_control = pa_alsa_mixer_find(m, j->alsa_name, 0) != NULL;
+    has_control = pa_alsa_mixer_find(m, j->alsa_name, 0, j->index) != NULL;
     pa_alsa_jack_set_has_control(j, has_control);
 
     if (j->has_control) {
@@ -1959,6 +1959,36 @@  finish:
     return o;
 }
 
+static int element_parse_index(pa_config_parser_state *state) {
+    pa_alsa_path *p;
+    pa_alsa_element *e;
+    pa_alsa_jack *j;
+    uint32_t ind;
+
+    pa_assert(state);
+
+    p = state->userdata;
+
+    e = element_get(p, state->section, true);
+    j = jack_get(p, state->section);
+    if (!e && !j)
+	goto error;
+
+    if (pa_atou(state->rvalue, &ind) < 0)
+	goto error;
+
+    if (e)
+	e->index = ind;
+    else
+	j->index = ind;
+
+    return 0;
+
+error:
+    pa_log("[%s:%u] Required makes no sense in '%s'", state->filename, state->lineno, state->section);
+    return -1;
+}
+
 static int element_parse_switch(pa_config_parser_state *state) {
     pa_alsa_path *p;
     pa_alsa_element *e;
@@ -2399,7 +2429,7 @@  static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx)
     pa_assert(e);
     pa_assert(m);
 
-    SELEM_INIT(sid, e->alsa_name);
+    SELEM_INIT(sid, e->alsa_name, e->index);
     if (!(me = snd_mixer_find_selem(m, sid))) {
         pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
         return -1;
@@ -2606,6 +2636,7 @@  pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa
         { "append-pcm-to-name",  jack_parse_append_pcm_to_name,     NULL, NULL },
 
         /* [Element ...] */
+        { "index",               element_parse_index,               NULL, NULL },
         { "switch",              element_parse_switch,              NULL, NULL },
         { "volume",              element_parse_volume,              NULL, NULL },
         { "enumeration",         element_parse_enumeration,         NULL, NULL },
@@ -3014,7 +3045,7 @@  static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_e
     pa_assert(m);
     pa_assert(cb);
 
-    SELEM_INIT(sid, e->alsa_name);
+    SELEM_INIT(sid, e->alsa_name, e->index);
     if (!(me = snd_mixer_find_selem(m, sid))) {
         pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
         return;
@@ -3294,7 +3325,7 @@  static bool element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_
                     snd_mixer_selem_id_t *sid;
                     snd_mixer_elem_t *me;
 
-                    SELEM_INIT(sid, a->alsa_name);
+                    SELEM_INIT(sid, a->alsa_name, a->index);
                     if (!(me = snd_mixer_find_selem(m, sid))) {
                         pa_log_warn("Element %s seems to have disappeared.", a->alsa_name);
                         return false;
@@ -3402,7 +3433,7 @@  static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
                     continue;
 
                 PA_LLIST_FOREACH(jb, p2->jacks) {
-                    if (jb->has_control && pa_streq(jb->alsa_name, ja->alsa_name) &&
+                    if (jb->has_control && pa_streq(jb->alsa_name, ja->alsa_name) && (jb->index == ja->index) &&
                        (ja->state_plugged == jb->state_plugged) &&
                        (ja->state_unplugged == jb->state_unplugged)) {
                         exists = true;
@@ -3424,7 +3455,7 @@  static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
                     break;
 
                 PA_LLIST_FOREACH(eb, p2->elements) {
-                    if (pa_streq(ea->alsa_name, eb->alsa_name)) {
+                    if (pa_streq(ea->alsa_name, eb->alsa_name) && (ea->index == eb->index)) {
                         found_matching_element = true;
                         is_subset = element_is_subset(ea, eb, m);
                         break;
diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
index 3ea4d7329..5a8ed03b6 100644
--- a/src/modules/alsa/alsa-mixer.h
+++ b/src/modules/alsa/alsa-mixer.h
@@ -124,6 +124,7 @@  struct pa_alsa_element {
     PA_LLIST_FIELDS(pa_alsa_element);
 
     char *alsa_name;
+    uint32_t index;
     pa_alsa_direction_t direction;
 
     pa_alsa_switch_use_t switch_use;
@@ -160,6 +161,7 @@  struct pa_alsa_jack {
 
     char *name; /* E g "Headphone" */
     char *alsa_name; /* E g "Headphone Jack" */
+    uint32_t index;
     bool has_control; /* is the jack itself present? */
     bool plugged_in; /* is this jack currently plugged in? */
     snd_mixer_elem_t *melem; /* Jack detection handle */
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index d2d3ee1eb..bde6e4d0f 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -1515,7 +1515,7 @@  static void ucm_mapping_jack_probe(pa_alsa_mapping *m) {
     PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
         bool has_control;
 
-        has_control = pa_alsa_mixer_find(mixer_handle, dev->jack->alsa_name, 0) != NULL;
+        has_control = pa_alsa_mixer_find(mixer_handle, dev->jack->alsa_name, 0, dev->jack->index) != 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);
     }
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index c5f6188e6..242645c59 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1595,7 +1595,7 @@  bool pa_alsa_may_tsched(bool want) {
 
 #define SND_MIXER_ELEM_PULSEAUDIO (SND_MIXER_ELEM_LAST + 10)
 
-snd_mixer_elem_t *pa_alsa_mixer_find(snd_mixer_t *mixer, const char *name, unsigned int device) {
+snd_mixer_elem_t *pa_alsa_mixer_find(snd_mixer_t *mixer, const char *name, unsigned int device, unsigned int index) {
     snd_mixer_elem_t *elem;
 
     for (elem = snd_mixer_first_elem(mixer); elem; elem = snd_mixer_elem_next(elem)) {
@@ -1607,6 +1607,8 @@  snd_mixer_elem_t *pa_alsa_mixer_find(snd_mixer_t *mixer, const char *name, unsig
             continue;
         if (snd_hctl_elem_get_device(helem) != device)
             continue;
+        if (snd_hctl_elem_get_index(helem) != index)
+            continue;
         return elem;
     }
     return NULL;
diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h
index 6b27339ec..58fc211c0 100644
--- a/src/modules/alsa/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -141,7 +141,7 @@  const char* pa_alsa_strerror(int errnum);
 
 bool pa_alsa_may_tsched(bool want);
 
-snd_mixer_elem_t *pa_alsa_mixer_find(snd_mixer_t *mixer, const char *name, unsigned int device);
+snd_mixer_elem_t *pa_alsa_mixer_find(snd_mixer_t *mixer, const char *name, unsigned int device, unsigned int index);
 
 snd_mixer_t *pa_alsa_open_mixer(int alsa_card_index, char **ctl_device);
 
diff --git a/src/modules/alsa/mixer/paths/analog-output.conf.common b/src/modules/alsa/mixer/paths/analog-output.conf.common
index e52830d99..8730ab5a4 100644
--- a/src/modules/alsa/mixer/paths/analog-output.conf.common
+++ b/src/modules/alsa/mixer/paths/analog-output.conf.common
@@ -86,6 +86,9 @@ 
 ; required-absent = ignore | enumeration | any     # In this element, this option must not exist or the path will be invalid
 ;
 ; [Element ...]                          # For each element that we shall control
+; index = <the index number>             # If the index number of this Element is not 0, we need to set the correct index number here,
+;                                        # if it is 0, don't need to set it here
+;
 ; required = ignore | switch | volume | enumeration | any     # If set, require this element to be of this kind and available,
 ;                                                             # otherwise don't consider this path valid for the card
 ; required-any = ignore | switch | volume | enumeration | any # If set, at least one of the elements or jacks with required-any in this
@@ -120,6 +123,8 @@ 
 ;                                        # channels in a mask
 ; [Jack ...]                           # For each jack that we will use for jack detection
 ;                                      # The name 'Jack Foo' must match ALSA's 'Foo Jack' control.
+; index = <the index number>           # If the index number of this Jack is not 0, we need to set the correct index number here,
+;                                      # if it is 0, don't need to set it here
 ; required = ignore | any              # If not set to ignore, make the path invalid if this jack control is not present.
 ; required-absent = ignore | any       # If not set to ignore, make the path invalid if this jack control is present.
 ; required-any = ignore | any          # If not set to ignore, make the path invalid if no jack controls and no elements with
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index 473248767..656b58278 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -583,7 +583,7 @@  static void init_eld_ctls(struct userdata *u) {
         if (device < 0)
             continue;
 
-        melem = pa_alsa_mixer_find(u->mixer_handle, "ELD", device);
+        melem = pa_alsa_mixer_find(u->mixer_handle, "ELD", device, 0);
         if (melem) {
             snd_mixer_elem_set_callback(melem, hdmi_eld_changed);
             snd_mixer_elem_set_callback_private(melem, u);
@@ -630,7 +630,7 @@  static void init_jacks(struct userdata *u) {
     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(u->mixer_handle, jack->alsa_name, 0);
+            jack->melem = pa_alsa_mixer_find(u->mixer_handle, jack->alsa_name, 0, jack->index);
             if (!jack->melem) {
                 pa_log_warn("Jack '%s' seems to have disappeared.", jack->alsa_name);
                 pa_alsa_jack_set_has_control(jack, false);