[pulseaudio-commits] 5 commits - src/Makefile.am src/modules

Arun Raghavan arun at kemper.freedesktop.org
Mon Jul 16 09:56:09 PDT 2012


 src/Makefile.am                     |    1 
 src/modules/alsa/alsa-mixer.c       |   53 -
 src/modules/alsa/alsa-mixer.h       |    8 
 src/modules/alsa/alsa-sink.c        |   65 +
 src/modules/alsa/alsa-source.c      |   64 +
 src/modules/alsa/alsa-ucm.c         | 1563 ++++++++++++++++++++++++++++++++++++
 src/modules/alsa/alsa-ucm.h         |  178 ++++
 src/modules/alsa/alsa-util.c        |    2 
 src/modules/alsa/module-alsa-card.c |  115 ++
 9 files changed, 1993 insertions(+), 56 deletions(-)

New commits:
commit 5c12ce5985990f71eb32bbac4a98649e3236e296
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Mon Jul 16 15:42:53 2012 +0530

    alsa: Add separate sinks/sources for UCM modifiers if needed
    
    Modifiers may have their own PlaybackPCM/CapturePCM and for these, we
    create separate sinks/sources. These are marked with the
    device.intended_roles property to let role-based routing take care if
    streams are tagged appropriately.

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index d873361..8509d8a 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -904,6 +904,8 @@ static int build_pollfd(struct userdata *u) {
 
 /* Called from IO context */
 static int suspend(struct userdata *u) {
+    const char *mod_name;
+
     pa_assert(u);
     pa_assert(u->pcm_handle);
 
@@ -914,6 +916,13 @@ static int suspend(struct userdata *u) {
     snd_pcm_close(u->pcm_handle);
     u->pcm_handle = NULL;
 
+    if ((mod_name = pa_proplist_gets(u->sink->proplist, PA_ALSA_PROP_UCM_MODIFIER))) {
+        pa_log_info("Disable ucm modifier %s", mod_name);
+
+        if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_dismod", mod_name) < 0)
+            pa_log("Failed to disable ucm modifier %s", mod_name);
+    }
+
     if (u->alsa_rtpoll_item) {
         pa_rtpoll_item_free(u->alsa_rtpoll_item);
         u->alsa_rtpoll_item = NULL;
@@ -1036,12 +1045,20 @@ static int unsuspend(struct userdata *u) {
     pa_bool_t b, d;
     snd_pcm_uframes_t period_size, buffer_size;
     char *device_name = NULL;
+    const char *mod_name;
 
     pa_assert(u);
     pa_assert(!u->pcm_handle);
 
     pa_log_info("Trying resume...");
 
+    if ((mod_name = pa_proplist_gets(u->sink->proplist, PA_ALSA_PROP_UCM_MODIFIER))) {
+        pa_log_info("Enable ucm modifier %s", mod_name);
+
+        if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_enamod", mod_name) < 0)
+            pa_log("Failed to enable ucm modifier %s", mod_name);
+    }
+
     if ((is_iec958(u) || is_hdmi(u)) && pa_sink_is_passthrough(u->sink)) {
         /* Need to open device in NONAUDIO mode */
         int len = strlen(u->device_name) + 8;
@@ -1977,7 +1994,7 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
 pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, pa_alsa_mapping *mapping) {
 
     struct userdata *u = NULL;
-    const char *dev_id = NULL, *key;
+    const char *dev_id = NULL, *key, *mod_name;
     pa_sample_spec ss;
     uint32_t alternate_sample_rate;
     pa_channel_map map;
@@ -2110,6 +2127,13 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
             goto fail;
         }
 
+        if ((mod_name = pa_proplist_gets(mapping->proplist, PA_ALSA_PROP_UCM_MODIFIER))) {
+            if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_enamod", mod_name) < 0)
+                pa_log("Failed to enable ucm modifier %s", mod_name);
+            else
+                pa_log_debug("Enabled ucm modifier %s", mod_name);
+        }
+
         if (!(u->pcm_handle = pa_alsa_open_by_device_id_mapping(
                       dev_id,
                       &u->device_name,
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index b544a82..2957892 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -836,6 +836,8 @@ static int build_pollfd(struct userdata *u) {
 
 /* Called from IO context */
 static int suspend(struct userdata *u) {
+    const char *mod_name;
+
     pa_assert(u);
     pa_assert(u->pcm_handle);
 
@@ -845,6 +847,13 @@ static int suspend(struct userdata *u) {
     snd_pcm_close(u->pcm_handle);
     u->pcm_handle = NULL;
 
+    if ((mod_name = pa_proplist_gets(u->source->proplist, PA_ALSA_PROP_UCM_MODIFIER))) {
+        pa_log_info("Disable ucm modifier %s", mod_name);
+
+        if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_dismod", mod_name) < 0)
+            pa_log("Failed to disable ucm modifier %s", mod_name);
+    }
+
     if (u->alsa_rtpoll_item) {
         pa_rtpoll_item_free(u->alsa_rtpoll_item);
         u->alsa_rtpoll_item = NULL;
@@ -949,12 +958,20 @@ static int unsuspend(struct userdata *u) {
     int err;
     pa_bool_t b, d;
     snd_pcm_uframes_t period_size, buffer_size;
+    const char *mod_name;
 
     pa_assert(u);
     pa_assert(!u->pcm_handle);
 
     pa_log_info("Trying resume...");
 
+    if ((mod_name = pa_proplist_gets(u->source->proplist, PA_ALSA_PROP_UCM_MODIFIER))) {
+        pa_log_info("Enable ucm modifier %s", mod_name);
+
+        if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_enamod", mod_name) < 0)
+            pa_log("Failed to enable ucm modifier %s", mod_name);
+    }
+
     if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_CAPTURE,
                             SND_PCM_NONBLOCK|
                             SND_PCM_NO_AUTO_RESAMPLE|
@@ -1714,7 +1731,7 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
 pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, pa_alsa_mapping *mapping) {
 
     struct userdata *u = NULL;
-    const char *dev_id = NULL, *key;
+    const char *dev_id = NULL, *key, *mod_name;
     pa_sample_spec ss;
     uint32_t alternate_sample_rate;
     pa_channel_map map;
@@ -1840,6 +1857,13 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
             goto fail;
         }
 
+        if ((mod_name = pa_proplist_gets(mapping->proplist, PA_ALSA_PROP_UCM_MODIFIER))) {
+            if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_enamod", mod_name) < 0)
+                pa_log("Failed to enable ucm modifier %s", mod_name);
+            else
+                pa_log_debug("Enabled ucm modifier %s", mod_name);
+        }
+
         if (!(u->pcm_handle = pa_alsa_open_by_device_id_mapping(
                       dev_id,
                       &u->device_name,
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 0a63bff..87ec293 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -64,6 +64,7 @@
         if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device)) (device)->playback_priority = (priority);   \
         if (PA_UCM_CAPTURE_PRIORITY_UNSET(device))  (device)->capture_priority = (priority);    \
     } while (0)
+#define PA_UCM_IS_MODIFIER_MAPPING(m) ((pa_proplist_gets((m)->proplist, PA_ALSA_PROP_UCM_MODIFIER)) != NULL)
 
 struct ucm_items {
     const char *id;
@@ -1033,6 +1034,50 @@ static void alsa_mapping_add_ucm_device(pa_alsa_mapping *m, pa_alsa_ucm_device *
         device->capture_mapping = m;
 }
 
+static void alsa_mapping_add_ucm_modifier(pa_alsa_mapping *m, pa_alsa_ucm_modifier *modifier) {
+    char *cur_desc;
+    const char *new_desc, *mod_name, *channel_str;
+    uint32_t channels = 0;
+
+    pa_idxset_put(m->ucm_context.ucm_modifiers, modifier, NULL);
+
+    new_desc = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
+    cur_desc = m->description;
+    if (cur_desc)
+        m->description = pa_sprintf_malloc("%s + %s", cur_desc, new_desc);
+    else
+        m->description = pa_xstrdup(new_desc);
+    pa_xfree(cur_desc);
+
+    if (!m->description)
+        pa_xstrdup("");
+
+    /* Modifier sinks should not be routed to by default */
+    m->priority = 0;
+
+    mod_name = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_NAME);
+    pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_MODIFIER, mod_name);
+
+    /* save mapping to ucm modifier */
+    if (m->direction == PA_ALSA_DIRECTION_OUTPUT) {
+        modifier->playback_mapping = m;
+        channel_str = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS);
+    } else {
+        modifier->capture_mapping = m;
+        channel_str = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS);
+    }
+
+    if (channel_str) {
+        pa_assert_se(pa_atou(channel_str, &channels) == 0 && channels < PA_CHANNELS_MAX);
+        pa_log_debug("Got channel count %" PRIu32 " for modifier", channels);
+    }
+
+    if (channels)
+        pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
+    else
+        pa_channel_map_init(&m->channel_map);
+}
+
 static int ucm_create_mapping_direction(
         pa_alsa_ucm_config *ucm,
         pa_alsa_profile_set *ps,
@@ -1087,6 +1132,51 @@ static int ucm_create_mapping_direction(
     return 0;
 }
 
+static int ucm_create_mapping_for_modifier(
+        pa_alsa_ucm_config *ucm,
+        pa_alsa_profile_set *ps,
+        pa_alsa_profile *p,
+        pa_alsa_ucm_modifier *modifier,
+        const char *verb_name,
+        const char *mod_name,
+        const char *device_str,
+        bool is_sink) {
+
+    pa_alsa_mapping *m;
+    char *mapping_name;
+
+    mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, device_str, is_sink ? "sink" : "source");
+
+    m = pa_alsa_mapping_get(ps, mapping_name);
+    if (!m) {
+        pa_log("no mapping for %s", mapping_name);
+        pa_xfree(mapping_name);
+        return -1;
+    }
+    pa_log_info("ucm mapping: %s modifier %s", mapping_name, mod_name);
+    pa_xfree(mapping_name);
+
+    if (!m->ucm_context.ucm_devices && !m->ucm_context.ucm_modifiers) {   /* new mapping */
+        m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+        m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+        m->ucm_context.ucm = ucm;
+        m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
+
+        m->device_strings = pa_xnew0(char*, 2);
+        m->device_strings[0] = pa_xstrdup(device_str);
+        m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT;
+        /* Modifier sinks should not be routed to by default */
+        m->priority = 0;
+
+        ucm_add_mapping(p, m);
+    } else if (!m->ucm_context.ucm_modifiers) /* share pcm with device */
+        m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    alsa_mapping_add_ucm_modifier(m, modifier);
+
+    return 0;
+}
+
 static int ucm_create_mapping(
         pa_alsa_ucm_config *ucm,
         pa_alsa_profile_set *ps,
@@ -1142,6 +1232,7 @@ static int ucm_create_profile(
 
     pa_alsa_profile *p;
     pa_alsa_ucm_device *dev;
+    pa_alsa_ucm_modifier *mod;
     int i = 0;
     const char *name, *sink, *source;
     char *verb_cmp, *c;
@@ -1197,6 +1288,20 @@ static int ucm_create_profile(
             dev->input_jack = ucm_get_jack(ucm, name, PA_UCM_PRE_TAG_INPUT);
     }
 
+    /* Now find modifiers that have their own PlaybackPCM and create
+     * separate sinks for them. */
+    PA_LLIST_FOREACH(mod, verb->modifiers) {
+        name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
+
+        sink = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SINK);
+        source = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SOURCE);
+
+        if (sink)
+            ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, sink, TRUE);
+        else if (source)
+            ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, source, FALSE);
+    }
+
     pa_alsa_profile_dump(p);
 
     return 0;
@@ -1234,23 +1339,23 @@ static void profile_finalize_probing(pa_alsa_profile *p) {
     uint32_t idx;
 
     PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
-        if (!m->output_pcm)
-            continue;
-
         if (p->supported)
             m->supported++;
 
+        if (!m->output_pcm)
+            continue;
+
         snd_pcm_close(m->output_pcm);
         m->output_pcm = NULL;
     }
 
     PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
-        if (!m->input_pcm)
-            continue;
-
         if (p->supported)
             m->supported++;
 
+        if (!m->input_pcm)
+            continue;
+
         snd_pcm_close(m->input_pcm);
         m->input_pcm = NULL;
     }
@@ -1289,20 +1394,35 @@ static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *
     PA_HASHMAP_FOREACH(p, ps->profiles, state) {
         /* change verb */
         pa_log_info("Set ucm verb to %s", p->name);
+
         if ((snd_use_case_set(ucm->ucm_mgr, "_verb", p->name)) < 0) {
             pa_log("Failed to set verb %s", p->name);
             p->supported = FALSE;
             continue;
         }
+
         PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
+            if (PA_UCM_IS_MODIFIER_MAPPING(m)) {
+                /* Skip jack probing on modifier PCMs since we expect this to
+                 * only be controlled on the main device/verb PCM. */
+                continue;
+            }
+
             m->output_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_PLAYBACK);
             if (!m->output_pcm) {
                 p->supported = FALSE;
                 break;
             }
         }
+
         if (p->supported) {
             PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
+                if (PA_UCM_IS_MODIFIER_MAPPING(m)) {
+                    /* Skip jack probing on modifier PCMs since we expect this to
+                     * only be controlled on the main device/verb PCM. */
+                    continue;
+                }
+
                 m->input_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_CAPTURE);
                 if (!m->input_pcm) {
                     p->supported = FALSE;
@@ -1319,10 +1439,12 @@ static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *
         pa_log_debug("Profile %s supported.", p->name);
 
         PA_IDXSET_FOREACH(m, p->output_mappings, idx)
-            ucm_mapping_jack_probe(m);
+            if (!PA_UCM_IS_MODIFIER_MAPPING(m))
+                ucm_mapping_jack_probe(m);
 
         PA_IDXSET_FOREACH(m, p->input_mappings, idx)
-            ucm_mapping_jack_probe(m);
+            if (!PA_UCM_IS_MODIFIER_MAPPING(m))
+                ucm_mapping_jack_probe(m);
 
         profile_finalize_probing(p);
     }
@@ -1413,6 +1535,7 @@ void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) {
 
 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
     pa_alsa_ucm_device *dev;
+    pa_alsa_ucm_modifier *mod;
     uint32_t idx;
 
     if (context->ucm_devices) {
@@ -1428,6 +1551,13 @@ void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
     }
 
     if (context->ucm_modifiers) {
+        PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) {
+            if (context->direction == PA_DIRECTION_OUTPUT)
+                mod->playback_mapping = NULL;
+            else
+                mod->capture_mapping = NULL;
+        }
+
         pa_idxset_free(context->ucm_modifiers, NULL, NULL);
     }
 }
diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
index 38fb262..4c5167f 100644
--- a/src/modules/alsa/alsa-ucm.h
+++ b/src/modules/alsa/alsa-ucm.h
@@ -73,6 +73,9 @@
 /** For devices: Quality of Service */
 #define PA_ALSA_PROP_UCM_QOS                        "alsa.ucm.qos"
 
+/** For devices: The modifier (if any) that this device corresponds to */
+#define PA_ALSA_PROP_UCM_MODIFIER "alsa.ucm.modifier"
+
 typedef struct pa_alsa_ucm_verb pa_alsa_ucm_verb;
 typedef struct pa_alsa_ucm_modifier pa_alsa_ucm_modifier;
 typedef struct pa_alsa_ucm_device pa_alsa_ucm_device;
@@ -140,6 +143,10 @@ struct pa_alsa_ucm_modifier {
     pa_direction_t action_direction;
 
     char *media_role;
+
+    /* Non-NULL if the modifier has its own PlaybackPCM/CapturePCM */
+    pa_alsa_mapping *playback_mapping;
+    pa_alsa_mapping *capture_mapping;
 };
 
 struct pa_alsa_ucm_verb {

commit a08c85d2993ee070098d6460b5a760bb02d87e80
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Tue Jul 3 11:50:29 2012 +0530

    alsa: Add a proplist to mappings
    
    The proplist isn't used by the conventional alsa-mixer code path, but
    can be used by UCM to transfer properties from UCM data to the
    sinks/sources corresponding to a mapping. These properties could be used
    later in policy, etc.
    
    The specific use for which I'm writing this now is for UCM modifiers
    that have their own PlaybackPCM/CapturePCM field. These will be
    translated to a separate sink/source corresponding to the modifier by
    adding an additional mapping per sink/source. These mappings' proplist
    will be populated with the name of the modifier and corresponding
    "device.intended_role" property. The latter will be used in the usual
    routing-by-role way, and the former will be used during sink/source
    activation and deactivation to know what UCM modifier is to be enabled
    or disabled.

diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index 7a1b0ca..8072fbb 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -3258,6 +3258,8 @@ static void mapping_free(pa_alsa_mapping *m) {
     pa_xfree(m->name);
     pa_xfree(m->description);
 
+    pa_proplist_free(m->proplist);
+
     pa_xstrfreev(m->device_strings);
     pa_xstrfreev(m->input_path_names);
     pa_xstrfreev(m->output_path_names);
@@ -3360,6 +3362,7 @@ pa_alsa_mapping *pa_alsa_mapping_get(pa_alsa_profile_set *ps, const char *name)
     m->profile_set = ps;
     m->name = pa_xstrdup(name);
     pa_channel_map_init(&m->channel_map);
+    m->proplist = pa_proplist_new();
 
     pa_hashmap_put(ps->mappings, m->name, m);
 
diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
index 8648101..2e3c115 100644
--- a/src/modules/alsa/alsa-mixer.h
+++ b/src/modules/alsa/alsa-mixer.h
@@ -246,6 +246,8 @@ struct pa_alsa_mapping {
     char *description;
     unsigned priority;
     pa_alsa_direction_t direction;
+    /* These are copied over to the resultant sink/source */
+    pa_proplist *proplist;
 
     pa_channel_map channel_map;
 
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 753d87a..d873361 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1977,7 +1977,7 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
 pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, pa_alsa_mapping *mapping) {
 
     struct userdata *u = NULL;
-    const char *dev_id = NULL;
+    const char *dev_id = NULL, *key;
     pa_sample_spec ss;
     uint32_t alternate_sample_rate;
     pa_channel_map map;
@@ -1987,6 +1987,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
     pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE, namereg_fail = FALSE, deferred_volume = FALSE, set_formats = FALSE, fixed_latency_range = FALSE;
     pa_sink_new_data data;
     pa_alsa_profile_set *profile_set = NULL;
+    void *state = NULL;
 
     pa_assert(m);
     pa_assert(ma);
@@ -2221,6 +2222,9 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
     if (mapping) {
         pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, mapping->name);
         pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, mapping->description);
+
+        while ((key = pa_proplist_iterate(mapping->proplist, &state)))
+            pa_proplist_sets(data.proplist, key, pa_proplist_gets(mapping->proplist, key));
     }
 
     pa_alsa_init_description(data.proplist);
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index 6748f9d..b544a82 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -1714,7 +1714,7 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
 pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, pa_alsa_mapping *mapping) {
 
     struct userdata *u = NULL;
-    const char *dev_id = NULL;
+    const char *dev_id = NULL, *key;
     pa_sample_spec ss;
     uint32_t alternate_sample_rate;
     pa_channel_map map;
@@ -1724,6 +1724,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
     pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE, namereg_fail = FALSE, deferred_volume = FALSE, fixed_latency_range = FALSE;
     pa_source_new_data data;
     pa_alsa_profile_set *profile_set = NULL;
+    void *state = NULL;
 
     pa_assert(m);
     pa_assert(ma);
@@ -1947,6 +1948,9 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
     if (mapping) {
         pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, mapping->name);
         pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, mapping->description);
+
+        while ((key = pa_proplist_iterate(mapping->proplist, &state)))
+            pa_proplist_sets(data.proplist, key, pa_proplist_gets(mapping->proplist, key));
     }
 
     pa_alsa_init_description(data.proplist);

commit 89024f6f1216c9fa5afe9c831d4204a184594c05
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Mon Jul 16 15:50:10 2012 +0530

    alsa: Allow channel count probe on open by mapping
    
    This allows opening a PCM given a mapping to work even if we don't have
    a channel count for the device up-front.

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index fbdf476..bb4e307 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -627,7 +627,7 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping(
             tsched_size,
             use_mmap,
             use_tsched,
-            TRUE);
+            pa_channel_map_valid(&m->channel_map) /* Query the channel count if we don't know what we want */);
 
     if (!pcm_handle)
         return NULL;

commit dae4b83dd0cfbb0b7aa8096c89eba0b799d1682b
Author: Feng Wei <b34248 at freescale.com>
Date:   Thu Jul 5 13:03:45 2012 +0800

    alsa: Add UCM jack detection
    
    Jack in UCM is decided by UCM device name, although in fact
    not all UCM devices have "jacks". Because port is also mapped
    to UCM device, we can always find target port when some jack
    event happens.
    
    Signed-off-by: Feng Wei <wei.feng at freescale.com>

diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 7b08b1b..0a63bff 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -1112,6 +1112,27 @@ static int ucm_create_mapping(
     return ret;
 }
 
+static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, const char *dev_name, const char *pre_tag) {
+    pa_alsa_jack *j;
+    char *name = pa_sprintf_malloc("%s%s", pre_tag, dev_name);
+
+    PA_LLIST_FOREACH(j, ucm->jacks)
+        if (pa_streq(j->name, name))
+            goto out;
+
+    j = pa_xnew0(pa_alsa_jack, 1);
+    j->state_unplugged = PA_PORT_AVAILABLE_NO;
+    j->state_plugged = PA_PORT_AVAILABLE_YES;
+    j->name = pa_xstrdup(name);
+    j->alsa_name = pa_sprintf_malloc("%s Jack", dev_name);
+
+    PA_LLIST_PREPEND(pa_alsa_jack, ucm->jacks, j);
+
+out:
+    pa_xfree(name);
+    return j;
+}
+
 static int ucm_create_profile(
         pa_alsa_ucm_config *ucm,
         pa_alsa_profile_set *ps,
@@ -1169,6 +1190,11 @@ static int ucm_create_profile(
         source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE);
 
         ucm_create_mapping(ucm, ps, p, dev, verb_name, name, sink, source);
+
+        if (sink)
+            dev->output_jack = ucm_get_jack(ucm, name, PA_UCM_PRE_TAG_OUTPUT);
+        if (source)
+            dev->input_jack = ucm_get_jack(ucm, name, PA_UCM_PRE_TAG_INPUT);
     }
 
     pa_alsa_profile_dump(p);
@@ -1230,6 +1256,30 @@ static void profile_finalize_probing(pa_alsa_profile *p) {
     }
 }
 
+static void ucm_mapping_jack_probe(pa_alsa_mapping *m) {
+    snd_pcm_t *pcm_handle;
+    snd_mixer_t *mixer_handle;
+    snd_hctl_t *hctl_handle;
+    pa_alsa_ucm_mapping_context *context = &m->ucm_context;
+    pa_alsa_ucm_device *dev;
+    uint32_t idx;
+
+    pcm_handle = m->direction == PA_ALSA_DIRECTION_OUTPUT ? m->output_pcm : m->input_pcm;
+    mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL, &hctl_handle);
+    if (!mixer_handle || !hctl_handle)
+        return;
+
+    PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
+        pa_alsa_jack *jack;
+        jack = m->direction == PA_ALSA_DIRECTION_OUTPUT ? dev->output_jack : dev->input_jack;
+        pa_assert (jack);
+        jack->has_control = pa_alsa_find_jack(hctl_handle, jack->alsa_name) != NULL;
+        pa_log_info("UCM jack %s has_control=%d", jack->name, jack->has_control);
+    }
+
+    snd_mixer_close(mixer_handle);
+}
+
 static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps) {
     void *state;
     pa_alsa_profile *p;
@@ -1268,6 +1318,12 @@ static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *
 
         pa_log_debug("Profile %s supported.", p->name);
 
+        PA_IDXSET_FOREACH(m, p->output_mappings, idx)
+            ucm_mapping_jack_probe(m);
+
+        PA_IDXSET_FOREACH(m, p->input_mappings, idx)
+            ucm_mapping_jack_probe(m);
+
         profile_finalize_probing(p);
     }
 
@@ -1337,11 +1393,18 @@ static void free_verb(pa_alsa_ucm_verb *verb) {
 
 void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) {
     pa_alsa_ucm_verb *vi, *vn;
+    pa_alsa_jack *ji, *jn;
 
     PA_LLIST_FOREACH_SAFE(vi, vn, ucm->verbs) {
         PA_LLIST_REMOVE(pa_alsa_ucm_verb, ucm->verbs, vi);
         free_verb(vi);
     }
+    PA_LLIST_FOREACH_SAFE(ji, jn, ucm->jacks) {
+        PA_LLIST_REMOVE(pa_alsa_jack, ucm->jacks, ji);
+        pa_xfree(ji->alsa_name);
+        pa_xfree(ji->name);
+        pa_xfree(ji);
+    }
     if (ucm->ucm_mgr) {
         snd_use_case_mgr_close(ucm->ucm_mgr);
         ucm->ucm_mgr = NULL;
diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
index db21d25..38fb262 100644
--- a/src/modules/alsa/alsa-ucm.h
+++ b/src/modules/alsa/alsa-ucm.h
@@ -121,6 +121,9 @@ struct pa_alsa_ucm_device {
 
     pa_idxset *conflicting_devices;
     pa_idxset *supported_devices;
+
+    pa_alsa_jack *input_jack;
+    pa_alsa_jack *output_jack;
 };
 
 struct pa_alsa_ucm_modifier {
@@ -154,6 +157,7 @@ struct pa_alsa_ucm_config {
     pa_alsa_ucm_verb *active_verb;
 
     PA_LLIST_HEAD(pa_alsa_ucm_verb, verbs);
+    PA_LLIST_HEAD(pa_alsa_jack, jacks);
 };
 
 struct pa_alsa_ucm_mapping_context {
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index 517c8f8..d9c0596 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -307,14 +307,21 @@ static void report_port_state(pa_device_port *p, struct userdata *u)
     void *state;
     pa_alsa_jack *jack;
     pa_port_available_t pa = PA_PORT_AVAILABLE_UNKNOWN;
+    pa_device_port *port;
 
     PA_HASHMAP_FOREACH(jack, u->jacks, state) {
         pa_port_available_t cpa;
 
-        if (!jack->path)
-            continue;
+        if (u->use_ucm)
+            port = pa_hashmap_get(u->card->ports, jack->name);
+        else {
+            if (jack->path)
+                port = jack->path->port;
+            else
+                continue;
+        }
 
-        if (p != jack->path->port)
+        if (p != port)
             continue;
 
         cpa = jack->plugged_in ? jack->state_plugged : jack->state_unplugged;
@@ -340,6 +347,7 @@ static int report_jack_state(snd_hctl_elem_t *elem, unsigned int mask)
     pa_bool_t plugged_in;
     void *state;
     pa_alsa_jack *jack;
+    pa_device_port *port;
 
     pa_assert(u);
 
@@ -359,8 +367,16 @@ static int report_jack_state(snd_hctl_elem_t *elem, unsigned int mask)
     PA_HASHMAP_FOREACH(jack, u->jacks, state)
         if (jack->hctl_elem == elem) {
             jack->plugged_in = plugged_in;
-            pa_assert(jack->path && jack->path->port);
-            report_port_state(jack->path->port, u);
+            if (u->use_ucm) {
+                pa_assert(u->card->ports);
+                port = pa_hashmap_get(u->card->ports, jack->name);
+                pa_assert(port);
+            }
+            else {
+                pa_assert(jack->path && jack->path->port);
+                port = jack->path->port;
+            }
+            report_port_state(port, u);
         }
     return 0;
 }
@@ -372,18 +388,24 @@ static void init_jacks(struct userdata *u) {
 
     u->jacks = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
-    /* See if we have any jacks */
-    if (u->profile_set->output_paths)
-        PA_HASHMAP_FOREACH(path, u->profile_set->output_paths, state)
-            PA_LLIST_FOREACH(jack, path->jacks)
-                if (jack->has_control)
-                    pa_hashmap_put(u->jacks, jack, jack);
-
-    if (u->profile_set->input_paths)
-        PA_HASHMAP_FOREACH(path, u->profile_set->input_paths, state)
-            PA_LLIST_FOREACH(jack, path->jacks)
-                if (jack->has_control)
-                    pa_hashmap_put(u->jacks, jack, jack);
+    if (u->use_ucm) {
+        PA_LLIST_FOREACH(jack, u->ucm.jacks)
+            if (jack->has_control)
+                pa_hashmap_put(u->jacks, jack, jack);
+    } else {
+        /* See if we have any jacks */
+        if (u->profile_set->output_paths)
+            PA_HASHMAP_FOREACH(path, u->profile_set->output_paths, state)
+                PA_LLIST_FOREACH(jack, path->jacks)
+                    if (jack->has_control)
+                        pa_hashmap_put(u->jacks, jack, jack);
+
+        if (u->profile_set->input_paths)
+            PA_HASHMAP_FOREACH(path, u->profile_set->input_paths, state)
+                PA_LLIST_FOREACH(jack, path->jacks)
+                    if (jack->has_control)
+                        pa_hashmap_put(u->jacks, jack, jack);
+    }
 
     pa_log_debug("Found %d jacks.", pa_hashmap_size(u->jacks));
 

commit c19d1082192312bdc8fb8f8a462e2e6843efc9b6
Author: Feng Wei <b34248 at freescale.com>
Date:   Thu Jul 5 12:53:21 2012 +0800

    alsa: Integrate UCM basic functions
    
    UCM basic functions will provide another way to handle the alsa mixer
    and controls. That means alsa card module will make use of alsa ucm
    configurations provided by various audio systems instead of mixer and
    paths configurations provided by PA. PA profiles come from UCM verb, PA
    sinks/sources and ports come from UCM devices.
    
    In case the proper UCM configurations are found, ucm branches are
    activated, or we will still fall through to the original way.
    
    Signed-off-by: Feng Wei <wei.feng at freescale.com>

diff --git a/src/Makefile.am b/src/Makefile.am
index 91b5e30..b181264 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1628,6 +1628,7 @@ module_coreaudio_device_la_LIBADD = $(MODULE_LIBADD)
 
 libalsa_util_la_SOURCES = \
 		modules/alsa/alsa-util.c modules/alsa/alsa-util.h \
+		modules/alsa/alsa-ucm.c modules/alsa/alsa-ucm.h \
 		modules/alsa/alsa-mixer.c modules/alsa/alsa-mixer.h \
 		modules/alsa/alsa-sink.c modules/alsa/alsa-sink.h \
 		modules/alsa/alsa-source.c modules/alsa/alsa-source.h \
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index b46508c..7a1b0ca 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -3271,6 +3271,8 @@ static void mapping_free(pa_alsa_mapping *m) {
     pa_assert(!m->input_pcm);
     pa_assert(!m->output_pcm);
 
+    pa_alsa_ucm_mapping_context_free(&m->ucm_context);
+
     pa_xfree(m);
 }
 
@@ -3343,7 +3345,7 @@ void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
     pa_xfree(ps);
 }
 
-static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
+pa_alsa_mapping *pa_alsa_mapping_get(pa_alsa_profile_set *ps, const char *name) {
     pa_alsa_mapping *m;
 
     if (!pa_startswith(name, "Mapping "))
@@ -3412,7 +3414,7 @@ static int mapping_parse_device_strings(pa_config_parser_state *state) {
 
     ps = state->userdata;
 
-    if (!(m = mapping_get(ps, state->section))) {
+    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
         return -1;
     }
@@ -3434,7 +3436,7 @@ static int mapping_parse_channel_map(pa_config_parser_state *state) {
 
     ps = state->userdata;
 
-    if (!(m = mapping_get(ps, state->section))) {
+    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
         return -1;
     }
@@ -3455,7 +3457,7 @@ static int mapping_parse_paths(pa_config_parser_state *state) {
 
     ps = state->userdata;
 
-    if (!(m = mapping_get(ps, state->section))) {
+    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
         return -1;
     }
@@ -3479,7 +3481,7 @@ static int mapping_parse_element(pa_config_parser_state *state) {
 
     ps = state->userdata;
 
-    if (!(m = mapping_get(ps, state->section))) {
+    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
         return -1;
     }
@@ -3503,7 +3505,7 @@ static int mapping_parse_direction(pa_config_parser_state *state) {
 
     ps = state->userdata;
 
-    if (!(m = mapping_get(ps, state->section))) {
+    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
         pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
         return -1;
     }
@@ -3531,7 +3533,7 @@ static int mapping_parse_description(pa_config_parser_state *state) {
 
     ps = state->userdata;
 
-    if ((m = mapping_get(ps, state->section))) {
+    if ((m = pa_alsa_mapping_get(ps, state->section))) {
         pa_xfree(m->description);
         m->description = pa_xstrdup(state->rvalue);
     } else if ((p = profile_get(ps, state->section))) {
@@ -3560,7 +3562,7 @@ static int mapping_parse_priority(pa_config_parser_state *state) {
         return -1;
     }
 
-    if ((m = mapping_get(ps, state->section)))
+    if ((m = pa_alsa_mapping_get(ps, state->section)))
         m->priority = prio;
     else if ((p = profile_get(ps, state->section)))
         p->priority = prio;
@@ -4361,17 +4363,7 @@ void pa_alsa_profile_set_probe(
     /* Clean up */
     profile_finalize_probing(last, NULL);
 
-    PA_HASHMAP_FOREACH(p, ps->profiles, state)
-        if (!p->supported) {
-            pa_hashmap_remove(ps->profiles, p->name);
-            profile_free(p);
-        }
-
-    PA_HASHMAP_FOREACH(m, ps->mappings, state)
-        if (m->supported <= 0) {
-            pa_hashmap_remove(ps->mappings, m->name);
-            mapping_free(m);
-        }
+    pa_alsa_profile_set_drop_unsupported(ps);
 
     paths_drop_unsupported(ps->input_paths);
     paths_drop_unsupported(ps->output_paths);
@@ -4406,6 +4398,26 @@ void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
         pa_alsa_decibel_fix_dump(db_fix);
 }
 
+void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set *ps) {
+    pa_alsa_profile *p;
+    pa_alsa_mapping *m;
+    void *state;
+
+    PA_HASHMAP_FOREACH(p, ps->profiles, state) {
+        if (!p->supported) {
+            pa_hashmap_remove(ps->profiles, p->name);
+            profile_free(p);
+        }
+    }
+
+    PA_HASHMAP_FOREACH(m, ps->mappings, state) {
+        if (m->supported <= 0) {
+            pa_hashmap_remove(ps->mappings, m->name);
+            mapping_free(m);
+        }
+    }
+}
+
 static pa_device_port* device_port_alsa_init(pa_hashmap *ports,
     const char* name,
     const char* description,
diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
index ef2a02f..8648101 100644
--- a/src/modules/alsa/alsa-mixer.h
+++ b/src/modules/alsa/alsa-mixer.h
@@ -48,6 +48,7 @@ typedef struct pa_alsa_profile_set pa_alsa_profile_set;
 typedef struct pa_alsa_port_data pa_alsa_port_data;
 
 #include "alsa-util.h"
+#include "alsa-ucm.h"
 
 typedef enum pa_alsa_switch_use {
     PA_ALSA_SWITCH_IGNORE,
@@ -265,6 +266,9 @@ struct pa_alsa_mapping {
 
     pa_sink *sink;
     pa_source *source;
+
+    /* ucm device context*/
+    pa_alsa_ucm_mapping_context ucm_context;
 };
 
 struct pa_alsa_profile {
@@ -314,11 +318,13 @@ struct pa_alsa_profile_set {
 void pa_alsa_mapping_dump(pa_alsa_mapping *m);
 void pa_alsa_profile_dump(pa_alsa_profile *p);
 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_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_for_pcm(snd_pcm_t *pcm, char **ctl_device, snd_hctl_t **hctl);
 
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index fcd2b0b..753d87a 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -153,6 +153,9 @@ struct userdata {
     pa_hook_slot *reserve_slot;
     pa_reserve_monitor_wrapper *monitor;
     pa_hook_slot *monitor_slot;
+
+    /* ucm context */
+    pa_alsa_ucm_mapping_context *ucm_context;
 };
 
 static void userdata_free(struct userdata *u);
@@ -1450,6 +1453,16 @@ static void mixer_volume_init(struct userdata *u) {
     }
 }
 
+static int sink_set_port_ucm_cb(pa_sink *s, pa_device_port *p) {
+    struct userdata *u = s->userdata;
+
+    pa_assert(u);
+    pa_assert(p);
+    pa_assert(u->ucm_context);
+
+    return pa_alsa_ucm_set_port(u->ucm_context, p, TRUE);
+}
+
 static int sink_set_port_cb(pa_sink *s, pa_device_port *p) {
     struct userdata *u = s->userdata;
     pa_alsa_port_data *data;
@@ -1884,7 +1897,6 @@ fail:
     }
 }
 
-
 static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
     pa_bool_t need_mixer_callback = FALSE;
 
@@ -2071,6 +2083,10 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
             TRUE);
     u->smoother_interval = SMOOTHER_MIN_INTERVAL;
 
+    /* use ucm */
+    if (mapping && mapping->ucm_context.ucm)
+        u->ucm_context = &mapping->ucm_context;
+
     dev_id = pa_modargs_get_value(
             ma, "device_id",
             pa_modargs_get_value(ma, "device", DEFAULT_DEVICE));
@@ -2171,7 +2187,8 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
     /* ALSA might tweak the sample spec, so recalculate the frame size */
     frame_size = pa_frame_size(&ss);
 
-    find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), ignore_dB);
+    if (!u->ucm_context)
+        find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), ignore_dB);
 
     pa_sink_new_data_init(&data);
     data.driver = driver;
@@ -2217,7 +2234,9 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
         goto fail;
     }
 
-    if (u->mixer_path_set)
+    if (u->ucm_context)
+        pa_alsa_ucm_add_ports(&data.ports, data.proplist, u->ucm_context, TRUE, card);
+    else if (u->mixer_path_set)
         pa_alsa_add_ports(&data, u->mixer_path_set, card);
 
     u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE | PA_SINK_LATENCY | (u->use_tsched ? PA_SINK_DYNAMIC_LATENCY : 0) |
@@ -2245,7 +2264,10 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
     if (u->use_tsched)
         u->sink->update_requested_latency = sink_update_requested_latency_cb;
     u->sink->set_state = sink_set_state_cb;
-    u->sink->set_port = sink_set_port_cb;
+    if (u->ucm_context)
+        u->sink->set_port = sink_set_port_ucm_cb;
+    else
+        u->sink->set_port = sink_set_port_cb;
     if (u->sink->alternate_sample_rate)
         u->sink->update_rate = sink_update_rate_cb;
     u->sink->userdata = u;
@@ -2284,7 +2306,10 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
     if (update_sw_params(u) < 0)
         goto fail;
 
-    if (setup_mixer(u, ignore_dB) < 0)
+    if (u->ucm_context) {
+        if (u->sink->active_port && pa_alsa_ucm_set_port(u->ucm_context, u->sink->active_port, TRUE) < 0)
+            goto fail;
+    } else if (setup_mixer(u, ignore_dB) < 0)
         goto fail;
 
     pa_alsa_dump(PA_LOG_DEBUG, u->pcm_handle);
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index d27cd9e..6748f9d 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -137,6 +137,9 @@ struct userdata {
     pa_hook_slot *reserve_slot;
     pa_reserve_monitor_wrapper *monitor;
     pa_hook_slot *monitor_slot;
+
+    /* ucm context */
+    pa_alsa_ucm_mapping_context *ucm_context;
 };
 
 static void userdata_free(struct userdata *u);
@@ -1353,6 +1356,16 @@ static void mixer_volume_init(struct userdata *u) {
     }
 }
 
+static int source_set_port_ucm_cb(pa_source *s, pa_device_port *p) {
+    struct userdata *u = s->userdata;
+
+    pa_assert(u);
+    pa_assert(p);
+    pa_assert(u->ucm_context);
+
+    return pa_alsa_ucm_set_port(u->ucm_context, p, FALSE);
+}
+
 static int source_set_port_cb(pa_source *s, pa_device_port *p) {
     struct userdata *u = s->userdata;
     pa_alsa_port_data *data;
@@ -1800,6 +1813,10 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
             TRUE);
     u->smoother_interval = SMOOTHER_MIN_INTERVAL;
 
+    /* use ucm */
+    if (mapping && mapping->ucm_context.ucm)
+        u->ucm_context = &mapping->ucm_context;
+
     dev_id = pa_modargs_get_value(
             ma, "device_id",
             pa_modargs_get_value(ma, "device", DEFAULT_DEVICE));
@@ -1896,7 +1913,8 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
     /* ALSA might tweak the sample spec, so recalculate the frame size */
     frame_size = pa_frame_size(&ss);
 
-    find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), ignore_dB);
+    if (!u->ucm_context)
+        find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), ignore_dB);
 
     pa_source_new_data_init(&data);
     data.driver = driver;
@@ -1942,7 +1960,9 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
         goto fail;
     }
 
-    if (u->mixer_path_set)
+    if (u->ucm_context)
+        pa_alsa_ucm_add_ports(&data.ports, data.proplist, u->ucm_context, FALSE, card);
+    else if (u->mixer_path_set)
         pa_alsa_add_ports(&data, u->mixer_path_set, card);
 
     u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY|(u->use_tsched ? PA_SOURCE_DYNAMIC_LATENCY : 0));
@@ -1969,7 +1989,10 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
     if (u->use_tsched)
         u->source->update_requested_latency = source_update_requested_latency_cb;
     u->source->set_state = source_set_state_cb;
-    u->source->set_port = source_set_port_cb;
+    if (u->ucm_context)
+        u->source->set_port = source_set_port_ucm_cb;
+    else
+        u->source->set_port = source_set_port_cb;
     if (u->source->alternate_sample_rate)
         u->source->update_rate = source_update_rate_cb;
     u->source->userdata = u;
@@ -2001,7 +2024,10 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
     if (update_sw_params(u) < 0)
         goto fail;
 
-    if (setup_mixer(u, ignore_dB) < 0)
+    if (u->ucm_context) {
+        if (u->source->active_port && pa_alsa_ucm_set_port(u->ucm_context, u->source->active_port, FALSE) < 0)
+            goto fail;
+    } else if (setup_mixer(u, ignore_dB) < 0)
         goto fail;
 
     pa_alsa_dump(PA_LOG_DEBUG, u->pcm_handle);
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
new file mode 100644
index 0000000..7b08b1b
--- /dev/null
+++ b/src/modules/alsa/alsa-ucm.c
@@ -0,0 +1,1370 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2011 Wolfson Microelectronics PLC
+ Author Margarita Olaya <magi at slimlogic.co.uk>
+ Copyright 2012 Feng Wei <wei.feng at freescale.com>, Freescale Ltd.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <sys/types.h>
+#include <limits.h>
+#include <asoundlib.h>
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
+#include <pulse/sample.h>
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+#include <pulse/util.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/once.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/conf-parser.h>
+#include <pulsecore/strbuf.h>
+
+#include "alsa-mixer.h"
+#include "alsa-util.h"
+#include "alsa-ucm.h"
+
+#define PA_UCM_PRE_TAG_OUTPUT                       "[Out] "
+#define PA_UCM_PRE_TAG_INPUT                        "[In] "
+
+#define PA_UCM_PLAYBACK_PRIORITY_UNSET(device)      ((device)->playback_channels && !(device)->playback_priority)
+#define PA_UCM_CAPTURE_PRIORITY_UNSET(device)       ((device)->capture_channels && !(device)->capture_priority)
+#define PA_UCM_DEVICE_PRIORITY_SET(device, priority) \
+    do { \
+        if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device)) (device)->playback_priority = (priority);   \
+        if (PA_UCM_CAPTURE_PRIORITY_UNSET(device))  (device)->capture_priority = (priority);    \
+    } while (0)
+
+struct ucm_items {
+    const char *id;
+    const char *property;
+};
+
+struct ucm_info {
+    const char *id;
+    unsigned priority;
+};
+
+static struct ucm_items item[] = {
+    {"PlaybackPCM", PA_ALSA_PROP_UCM_SINK},
+    {"CapturePCM", PA_ALSA_PROP_UCM_SOURCE},
+    {"PlaybackVolume", PA_ALSA_PROP_UCM_PLAYBACK_VOLUME},
+    {"PlaybackSwitch", PA_ALSA_PROP_UCM_PLAYBACK_SWITCH},
+    {"PlaybackPriority", PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY},
+    {"PlaybackChannels", PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS},
+    {"CaptureVolume", PA_ALSA_PROP_UCM_CAPTURE_VOLUME},
+    {"CaptureSwitch", PA_ALSA_PROP_UCM_CAPTURE_SWITCH},
+    {"CapturePriority", PA_ALSA_PROP_UCM_CAPTURE_PRIORITY},
+    {"CaptureChannels", PA_ALSA_PROP_UCM_CAPTURE_CHANNELS},
+    {"TQ", PA_ALSA_PROP_UCM_QOS},
+    {NULL, NULL},
+};
+
+/* UCM verb info - this should eventually be part of policy manangement */
+static struct ucm_info verb_info[] = {
+    {SND_USE_CASE_VERB_INACTIVE, 0},
+    {SND_USE_CASE_VERB_HIFI, 8000},
+    {SND_USE_CASE_VERB_HIFI_LOW_POWER, 7000},
+    {SND_USE_CASE_VERB_VOICE, 6000},
+    {SND_USE_CASE_VERB_VOICE_LOW_POWER, 5000},
+    {SND_USE_CASE_VERB_VOICECALL, 4000},
+    {SND_USE_CASE_VERB_IP_VOICECALL, 4000},
+    {SND_USE_CASE_VERB_ANALOG_RADIO, 3000},
+    {SND_USE_CASE_VERB_DIGITAL_RADIO, 3000},
+    {NULL, 0}
+};
+
+/* UCM device info - should be overwritten by ucm property */
+static struct ucm_info dev_info[] = {
+    {SND_USE_CASE_DEV_SPEAKER, 100},
+    {SND_USE_CASE_DEV_LINE, 100},
+    {SND_USE_CASE_DEV_HEADPHONES, 100},
+    {SND_USE_CASE_DEV_HEADSET, 300},
+    {SND_USE_CASE_DEV_HANDSET, 200},
+    {SND_USE_CASE_DEV_BLUETOOTH, 400},
+    {SND_USE_CASE_DEV_EARPIECE, 100},
+    {SND_USE_CASE_DEV_SPDIF, 100},
+    {SND_USE_CASE_DEV_HDMI, 100},
+    {SND_USE_CASE_DEV_NONE, 100},
+    {NULL, 0}
+};
+
+/* UCM profile properties - The verb data is store so it can be used to fill
+ * the new profiles properties */
+static int ucm_get_property(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr, const char *verb_name) {
+    const char *value;
+    char *id;
+    int i;
+
+    for (i = 0; item[i].id; i++) {
+        int err;
+
+        id = pa_sprintf_malloc("=%s//%s", item[i].id, verb_name);
+        err = snd_use_case_get(uc_mgr, id, &value);
+        pa_xfree(id);
+        if (err < 0 )
+            continue;
+
+        pa_log_debug("Got %s for verb %s: %s", item[i].id, verb_name, value);
+        pa_proplist_sets(verb->proplist, item[i].property, value);
+        free((void*)value);
+    }
+
+    return 0;
+};
+
+static int ucm_device_exists(pa_idxset *idxset, pa_alsa_ucm_device *dev) {
+    pa_alsa_ucm_device *d;
+    uint32_t idx;
+
+    PA_IDXSET_FOREACH(d, idxset, idx)
+        if (d == dev)
+            return 1;
+
+    return 0;
+}
+
+static void ucm_add_devices_to_idxset(
+        pa_idxset *idxset,
+        pa_alsa_ucm_device *me,
+        pa_alsa_ucm_device *devices,
+        const char **dev_names,
+        int n) {
+
+    pa_alsa_ucm_device *d;
+
+    PA_LLIST_FOREACH(d, devices) {
+        const char *name;
+        int i;
+
+        if (d == me)
+            continue;
+
+        name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
+
+        for (i = 0; i < n; i++)
+            if (pa_streq(dev_names[i], name))
+                pa_idxset_put(idxset, d, NULL);
+    }
+}
+
+/* Create a property list for this ucm device */
+static int ucm_get_device_property(
+        pa_alsa_ucm_device *device,
+        snd_use_case_mgr_t *uc_mgr,
+        pa_alsa_ucm_verb *verb,
+        const char *device_name) {
+
+    const char *value;
+    const char **devices;
+    char *id;
+    int i;
+    int err;
+    uint32_t ui;
+    int n_confdev, n_suppdev;
+
+    for (i = 0; item[i].id; i++) {
+        id = pa_sprintf_malloc("=%s/%s", item[i].id, device_name);
+        err = snd_use_case_get(uc_mgr, id, &value);
+        pa_xfree(id);
+        if (err < 0)
+            continue;
+
+        pa_log_debug("Got %s for device %s: %s", item[i].id, device_name, value);
+        pa_proplist_sets(device->proplist, item[i].property, value);
+        free((void*)value);
+    }
+
+    /* get direction and channels */
+    value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS);
+    if (value) { /* output */
+        /* get channels */
+        if (pa_atou(value, &ui) == 0 && ui < PA_CHANNELS_MAX)
+            device->playback_channels = ui;
+        else
+            pa_log("UCM playback channels %s for device %s out of range", value, device_name);
+
+        /* get pcm */
+        value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SINK);
+        if (!value) { /* take pcm from verb playback default */
+            value = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_SINK);
+            if (value) {
+                pa_log_debug("UCM playback device %s fetch pcm from verb default %s", device_name, value);
+                pa_proplist_sets(device->proplist, PA_ALSA_PROP_UCM_SINK, value);
+            } else
+                pa_log("UCM playback device %s fetch pcm failed", device_name);
+        }
+    }
+
+    value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS);
+    if (value) { /* input */
+        /* get channels */
+        if (pa_atou(value, &ui) == 0 && ui < PA_CHANNELS_MAX)
+            device->capture_channels = ui;
+        else
+            pa_log("UCM capture channels %s for device %s out of range", value, device_name);
+
+        /* get pcm */
+        value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SOURCE);
+        if (!value) { /* take pcm from verb capture default */
+            value = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_SOURCE);
+            if (value) {
+                pa_log_debug("UCM capture device %s fetch pcm from verb default %s", device_name, value);
+                pa_proplist_sets(device->proplist, PA_ALSA_PROP_UCM_SOURCE, value);
+            } else
+                pa_log("UCM capture device %s fetch pcm failed", device_name);
+        }
+    }
+
+    pa_assert(device->playback_channels || device->capture_channels);
+
+    /* get priority of device */
+    if (device->playback_channels) { /* sink device */
+        value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY);
+        if (value) {
+            /* get priority from ucm config */
+            if (pa_atou(value, &ui) == 0)
+                device->playback_priority = ui;
+            else
+                pa_log_debug("UCM playback priority %s for device %s error", value, device_name);
+        }
+    }
+
+    if (device->capture_channels) { /* source device */
+        value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_PRIORITY);
+        if (value) {
+            /* get priority from ucm config */
+            if (pa_atou(value, &ui) == 0)
+                device->capture_priority = ui;
+            else
+                pa_log_debug("UCM capture priority %s for device %s error", value, device_name);
+        }
+    }
+
+    if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device) || PA_UCM_CAPTURE_PRIORITY_UNSET(device)) {
+        /* get priority from static table */
+        for (i = 0; dev_info[i].id; i++) {
+            if (strcasecmp(dev_info[i].id, device_name) == 0) {
+                PA_UCM_DEVICE_PRIORITY_SET(device, dev_info[i].priority);
+                break;
+            }
+        }
+    }
+
+    if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device)) {
+        /* fall through to default priority */
+        device->playback_priority = 100;
+    }
+
+    if (PA_UCM_CAPTURE_PRIORITY_UNSET(device)) {
+        /* fall through to default priority */
+        device->capture_priority = 100;
+    }
+
+    id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", device_name);
+    n_confdev = snd_use_case_get_list(uc_mgr, id, &devices);
+    pa_xfree(id);
+
+    if (n_confdev <= 0)
+        pa_log_debug("No %s for device %s", "_conflictingdevs", device_name);
+    else {
+        device->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+        ucm_add_devices_to_idxset(device->conflicting_devices, device, verb->devices, devices, n_confdev);
+        snd_use_case_free_list(devices, n_confdev);
+    }
+
+    id = pa_sprintf_malloc("%s/%s", "_supporteddevs", device_name);
+    n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices);
+    pa_xfree(id);
+
+    if (n_suppdev <= 0)
+        pa_log_debug("No %s for device %s", "_supporteddevs", device_name);
+    else {
+        device->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+        ucm_add_devices_to_idxset(device->supported_devices, device, verb->devices, devices, n_suppdev);
+        snd_use_case_free_list(devices, n_suppdev);
+    }
+
+    return 0;
+};
+
+/* Create a property list for this ucm modifier */
+static int ucm_get_modifier_property(pa_alsa_ucm_modifier *modifier, snd_use_case_mgr_t *uc_mgr, const char *modifier_name) {
+    const char *value;
+    char *id;
+    int i;
+
+    for (i = 0; item[i].id; i++) {
+        int err;
+
+        id = pa_sprintf_malloc("=%s/%s", item[i].id, modifier_name);
+        err = snd_use_case_get(uc_mgr, id, &value);
+        pa_xfree(id);
+        if (err < 0 )
+            continue;
+
+        pa_log_debug("Got %s for modifier %s: %s", item[i].id, modifier_name, value);
+        pa_proplist_sets(modifier->proplist, item[i].property, value);
+        free((void*)value);
+    }
+
+    id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", modifier_name);
+    modifier->n_confdev = snd_use_case_get_list(uc_mgr, id, &modifier->conflicting_devices);
+    pa_xfree(id);
+    if (modifier->n_confdev < 0)
+        pa_log_debug("No %s for modifier %s", "_conflictingdevs", modifier_name);
+
+    id = pa_sprintf_malloc("%s/%s", "_supporteddevs", modifier_name);
+    modifier->n_suppdev = snd_use_case_get_list(uc_mgr, id, &modifier->supported_devices);
+    pa_xfree(id);
+    if (modifier->n_suppdev < 0)
+        pa_log_debug("No %s for modifier %s", "_supporteddevs", modifier_name);
+
+    return 0;
+};
+
+/* Create a list of devices for this verb */
+static int ucm_get_devices(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) {
+    const char **dev_list;
+    int num_dev, i;
+
+    num_dev = snd_use_case_get_list(uc_mgr, "_devices", &dev_list);
+    if (num_dev < 0)
+        return num_dev;
+
+    for (i = 0; i < num_dev; i += 2) {
+        pa_alsa_ucm_device *d = pa_xnew0(pa_alsa_ucm_device, 1);
+
+        d->proplist = pa_proplist_new();
+        pa_proplist_sets(d->proplist, PA_ALSA_PROP_UCM_NAME, pa_strnull(dev_list[i]));
+        pa_proplist_sets(d->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(dev_list[i + 1]));
+
+        PA_LLIST_PREPEND(pa_alsa_ucm_device, verb->devices, d);
+    }
+
+    snd_use_case_free_list(dev_list, num_dev);
+
+    return 0;
+};
+
+static int ucm_get_modifiers(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) {
+    const char **mod_list;
+    int num_mod, i;
+
+    num_mod = snd_use_case_get_list(uc_mgr, "_modifiers", &mod_list);
+    if (num_mod < 0)
+        return num_mod;
+
+    for (i = 0; i < num_mod; i += 2) {
+        pa_alsa_ucm_modifier *m;
+
+        if (!mod_list[i]) {
+            pa_log_warn("Got a modifier with a null name. Skipping.");
+            continue;
+        }
+
+        m = pa_xnew0(pa_alsa_ucm_modifier, 1);
+        m->proplist = pa_proplist_new();
+
+        pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_NAME, mod_list[i]);
+        pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(mod_list[i + 1]));
+
+        PA_LLIST_PREPEND(pa_alsa_ucm_modifier, verb->modifiers, m);
+    }
+
+    snd_use_case_free_list(mod_list, num_mod);
+
+    return 0;
+};
+
+static void add_role_to_device(pa_alsa_ucm_device *dev, const char *dev_name, const char *role_name, const char *role) {
+    const char *cur = pa_proplist_gets(dev->proplist, role_name);
+
+    if (!cur)
+        pa_proplist_sets(dev->proplist, role_name, role);
+    else if (!pa_str_in_list_spaces(cur, role)) { /* does not exist */
+        char *value = pa_sprintf_malloc("%s %s", cur, role);
+
+        pa_proplist_sets(dev->proplist, role_name, value);
+        pa_xfree(value);
+    }
+
+    pa_log_info("Add role %s to device %s(%s), result %s", role, dev_name, role_name, pa_proplist_gets(dev->proplist,
+                role_name));
+}
+
+static void add_media_role(const char *name, pa_alsa_ucm_device *list, const char *role_name, const char *role, bool is_sink) {
+    pa_alsa_ucm_device *d;
+
+    PA_LLIST_FOREACH(d, list) {
+        const char *dev_name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
+
+        if (pa_streq(dev_name, name)) {
+            const char *sink = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SINK);
+            const char *source = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SOURCE);
+
+            if (is_sink && sink)
+                add_role_to_device(d, dev_name, role_name, role);
+            else if (!is_sink && source)
+                add_role_to_device(d, dev_name, role_name, role);
+            break;
+        }
+    }
+}
+
+static char *modifier_name_to_role(const char *mod_name, bool *is_sink) {
+    char *sub = NULL, *tmp;
+
+    *is_sink = FALSE;
+
+    if (pa_startswith(mod_name, "Play")) {
+        *is_sink = TRUE;
+        sub = pa_xstrdup(mod_name + 4);
+    } else if (pa_startswith(mod_name, "Capture"))
+        sub = pa_xstrdup(mod_name + 7);
+
+    if (!sub || !*sub) {
+        pa_xfree(sub);
+        pa_log_warn("Can't match media roles for modifer %s", mod_name);
+        return NULL;
+    }
+
+    tmp = sub;
+
+    do {
+        *tmp = tolower(*tmp);
+    } while (*(++tmp));
+
+    return sub;
+}
+
+static void ucm_set_media_roles(pa_alsa_ucm_modifier *modifier, pa_alsa_ucm_device *list, const char *mod_name) {
+    int i;
+    bool is_sink = FALSE;
+    char *sub = NULL;
+    const char *role_name;
+
+    sub = modifier_name_to_role(mod_name, &is_sink);
+    if (!sub)
+        return;
+
+    modifier->action_direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
+    modifier->media_role = sub;
+
+    role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES;
+    for (i = 0; i < modifier->n_suppdev; i++) {
+        /* if modifier has no specific pcm, we add role intent to its supported devices */
+        if (!pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SINK) &&
+                !pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SOURCE))
+            add_media_role(modifier->supported_devices[i], list, role_name, sub, is_sink);
+    }
+}
+
+static void append_lost_relationship(pa_alsa_ucm_device *dev) {
+    uint32_t idx;
+    pa_alsa_ucm_device *d;
+
+    if (dev->conflicting_devices) {
+        PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) {
+            if (!d->conflicting_devices)
+                d->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+            if (pa_idxset_put(d->conflicting_devices, dev, NULL) == 0)
+                pa_log_warn("Add lost conflicting device %s to %s",
+                        pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME),
+                        pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME));
+        }
+    }
+
+    if (dev->supported_devices) {
+        PA_IDXSET_FOREACH(d, dev->supported_devices, idx) {
+            if (!d->supported_devices)
+                d->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+            if (pa_idxset_put(d->supported_devices, dev, NULL) == 0)
+                pa_log_warn("Add lost supported device %s to %s",
+                        pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME),
+                        pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME));
+        }
+    }
+}
+
+int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index)
+{
+    char *card_name;
+    const char **verb_list;
+    int num_verbs, i, err = 0;
+
+    /* is UCM available for this card ? */
+    err = snd_card_get_name(card_index, &card_name);
+    if (err < 0) {
+        pa_log("Card can't get card_name from card_index %d", card_index);
+        goto name_fail;
+    }
+
+    err = snd_use_case_mgr_open(&ucm->ucm_mgr, card_name);
+    if (err < 0) {
+        pa_log("UCM not available for card %s", card_name);
+        goto ucm_mgr_fail;
+    }
+
+    pa_log_info("UCM available for card %s", card_name);
+
+    /* get a list of all UCM verbs (profiles) for this card */
+    num_verbs = snd_use_case_verb_list(ucm->ucm_mgr, &verb_list);
+    if (num_verbs < 0) {
+        pa_log("UCM verb list not found for %s", card_name);
+        goto ucm_verb_fail;
+    }
+
+    /* get the properties of each UCM verb */
+    for (i = 0; i < num_verbs; i += 2) {
+        pa_alsa_ucm_verb *verb;
+
+        /* Get devices and modifiers for each verb */
+        err = pa_alsa_ucm_get_verb(ucm->ucm_mgr, verb_list[i], verb_list[i+1], &verb);
+        if (err < 0) {
+            pa_log("Failed to get the verb %s", verb_list[i]);
+            continue;
+        }
+
+        PA_LLIST_PREPEND(pa_alsa_ucm_verb, ucm->verbs, verb);
+    }
+
+    if (!ucm->verbs) {
+        pa_log("No UCM verb is valid for %s", card_name);
+        err = -1;
+    }
+
+    snd_use_case_free_list(verb_list, num_verbs);
+
+ucm_verb_fail:
+    if (err < 0) {
+        snd_use_case_mgr_close(ucm->ucm_mgr);
+        ucm->ucm_mgr = NULL;
+    }
+
+ucm_mgr_fail:
+    free(card_name);
+
+name_fail:
+    return err;
+}
+
+int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name, const char *verb_desc, pa_alsa_ucm_verb **p_verb) {
+    pa_alsa_ucm_device *d;
+    pa_alsa_ucm_modifier *mod;
+    pa_alsa_ucm_verb *verb;
+    int err = 0;
+
+    *p_verb = NULL;
+    pa_log_info("Set UCM verb to %s", verb_name);
+    err = snd_use_case_set(uc_mgr, "_verb", verb_name);
+    if (err < 0)
+        return err;
+
+    verb = pa_xnew0(pa_alsa_ucm_verb, 1);
+    verb->proplist = pa_proplist_new();
+
+    pa_proplist_sets(verb->proplist, PA_ALSA_PROP_UCM_NAME, pa_strnull(verb_name));
+    pa_proplist_sets(verb->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(verb_desc));
+
+    err = ucm_get_devices(verb, uc_mgr);
+    if (err < 0)
+        pa_log("No UCM devices for verb %s", verb_name);
+
+    err = ucm_get_modifiers(verb, uc_mgr);
+    if (err < 0)
+        pa_log("No UCM modifiers for verb %s", verb_name);
+
+    /* Verb properties */
+    ucm_get_property(verb, uc_mgr, verb_name);
+
+    PA_LLIST_FOREACH(d, verb->devices) {
+        const char *dev_name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
+
+        /* Devices properties */
+        ucm_get_device_property(d, uc_mgr, verb, dev_name);
+    }
+    /* make conflicting or supported device mutual */
+    PA_LLIST_FOREACH(d, verb->devices)
+        append_lost_relationship(d);
+
+    PA_LLIST_FOREACH(mod, verb->modifiers) {
+        const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
+
+        /* Modifier properties */
+        ucm_get_modifier_property(mod, uc_mgr, mod_name);
+
+        /* Set PA_PROP_DEVICE_INTENDED_ROLES property to devices */
+        pa_log_debug("Set media roles for verb %s, modifier %s", verb_name, mod_name);
+        ucm_set_media_roles(mod, verb->devices, mod_name);
+    }
+
+    *p_verb = verb;
+    return 0;
+}
+
+static void ucm_add_port_combination(
+        pa_hashmap *hash,
+        pa_alsa_ucm_mapping_context *context,
+        bool is_sink,
+        pa_alsa_ucm_device **pdevices,
+        int num,
+        pa_hashmap *ports,
+        pa_card_profile *cp,
+        pa_core *core) {
+
+    pa_device_port *port;
+    int i;
+    unsigned priority;
+    char *name, *desc;
+    const char *dev_name;
+    const char *direction;
+    pa_alsa_ucm_device *dev;
+
+    dev = pdevices[0];
+    dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
+
+    name = pa_sprintf_malloc("%s%s", is_sink ? PA_UCM_PRE_TAG_OUTPUT : PA_UCM_PRE_TAG_INPUT, dev_name);
+    desc = num == 1 ? pa_xstrdup(pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION))
+            : pa_sprintf_malloc("Combination port for %s", dev_name);
+
+    priority = is_sink ? dev->playback_priority : dev->capture_priority;
+
+    for (i = 1; i < num; i++) {
+        char *tmp;
+
+        dev = pdevices[i];
+        dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
+
+        tmp = pa_sprintf_malloc("%s+%s", name, dev_name);
+        pa_xfree(name);
+        name = tmp;
+
+        tmp = pa_sprintf_malloc("%s,%s", desc, dev_name);
+        pa_xfree(desc);
+        desc = tmp;
+
+        /* FIXME: Is this true? */
+        priority += (is_sink ? dev->playback_priority : dev->capture_priority);
+    }
+
+    port = pa_hashmap_get(ports, name);
+    if (!port) {
+        port = pa_device_port_new(core, pa_strna(name), desc, 0);
+        pa_assert(port);
+
+        pa_hashmap_put(ports, port->name, port);
+        pa_log_debug("Add port %s: %s", port->name, port->description);
+        port->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    }
+
+    port->priority = priority;
+    if (is_sink)
+        port->is_output = TRUE;
+    else
+        port->is_input = TRUE;
+
+    pa_xfree(name);
+    pa_xfree(desc);
+
+    direction = is_sink ? "output" : "input";
+    pa_log_debug("Port %s direction %s, priority %d", port->name, direction, priority);
+
+    if (cp) {
+        pa_log_debug("Adding port %s to profile %s", port->name, cp->name);
+        pa_hashmap_put(port->profiles, cp->name, cp);
+    }
+
+    if (hash) {
+        pa_hashmap_put(hash, port->name, port);
+        pa_device_port_ref(port);
+    }
+}
+
+static int ucm_port_contains(const char *port_name, const char *dev_name, bool is_sink) {
+    int ret = 0;
+    const char *r;
+    const char *state = NULL;
+    int len;
+
+    if (!port_name || !dev_name)
+        return FALSE;
+
+    port_name += is_sink ? strlen(PA_UCM_PRE_TAG_OUTPUT) : strlen(PA_UCM_PRE_TAG_INPUT);
+
+    while ((r = pa_split_in_place(port_name, "+", &len, &state))) {
+        if (!strncmp(r, dev_name, len)) {
+            ret = 1;
+            break;
+        }
+    }
+
+    return ret;
+}
+
+static int ucm_check_conformance(
+        pa_alsa_ucm_mapping_context *context,
+        pa_alsa_ucm_device **pdevices,
+        int dev_num,
+        pa_alsa_ucm_device *dev) {
+
+    uint32_t idx;
+    pa_alsa_ucm_device *d;
+    int i;
+
+    pa_assert(dev);
+
+    pa_log_debug("Check device %s conformance with %d other devices",
+            pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME), dev_num);
+    if (dev_num == 0) {
+        pa_log_debug("First device in combination, number 1");
+        return 1;
+    }
+
+    if (dev->conflicting_devices) { /* the device defines conflicting devices */
+        PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) {
+            for (i = 0; i < dev_num; i++) {
+                if (pdevices[i] == d) {
+                    pa_log_debug("Conflicting device found");
+                    return 0;
+                }
+            }
+        }
+    } else if (dev->supported_devices) { /* the device defines supported devices */
+        for (i = 0; i < dev_num; i++) {
+            if (!ucm_device_exists(dev->supported_devices, pdevices[i])) {
+                pa_log_debug("Supported device not found");
+                return 0;
+            }
+        }
+    } else { /* not support any other devices */
+        pa_log_debug("Not support any other devices");
+        return 0;
+    }
+
+    pa_log_debug("Device added to combination, number %d", dev_num + 1);
+    return 1;
+}
+
+static inline pa_alsa_ucm_device *get_next_device(pa_idxset *idxset, uint32_t *idx) {
+    pa_alsa_ucm_device *dev;
+
+    if (*idx == PA_IDXSET_INVALID)
+        dev = pa_idxset_first(idxset, idx);
+    else
+        dev = pa_idxset_next(idxset, idx);
+
+    return dev;
+}
+
+static void ucm_add_ports_combination(
+        pa_hashmap *hash,
+        pa_alsa_ucm_mapping_context *context,
+        bool is_sink,
+        pa_alsa_ucm_device **pdevices,
+        int dev_num,
+        uint32_t map_index,
+        pa_hashmap *ports,
+        pa_card_profile *cp,
+        pa_core *core) {
+
+    pa_alsa_ucm_device *dev;
+    uint32_t idx = map_index;
+
+    if ((dev = get_next_device(context->ucm_devices, &idx)) == NULL)
+        return;
+
+    /* check if device at map_index can combine with existing devices combination */
+    if (ucm_check_conformance(context, pdevices, dev_num, dev)) {
+        /* add device at map_index to devices combination */
+        pdevices[dev_num] = dev;
+        /* add current devices combination as a new port */
+        ucm_add_port_combination(hash, context, is_sink, pdevices, dev_num + 1, ports, cp, core);
+        /* try more elements combination */
+        ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num + 1, idx, ports, cp, core);
+    }
+
+    /* try other device with current elements number */
+    ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num, idx, ports, cp, core);
+}
+
+static char* merge_roles(const char *cur, const char *add) {
+    char *r, *ret;
+    const char *state = NULL;
+
+    if (add == NULL)
+        return pa_xstrdup(cur);
+    else if (cur == NULL)
+        return pa_xstrdup(add);
+
+    ret = pa_xstrdup(cur);
+
+    while ((r = pa_split_spaces(add, &state))) {
+        char *value;
+
+        if (!pa_str_in_list_spaces(ret, r))
+            value = pa_sprintf_malloc("%s %s", ret, r);
+        else {
+            pa_xfree(r);
+            continue;
+        }
+
+        pa_xfree(ret);
+        ret = value;
+        pa_xfree(r);
+    }
+
+    return ret;
+}
+
+void pa_alsa_ucm_add_ports_combination(
+        pa_hashmap *p,
+        pa_alsa_ucm_mapping_context *context,
+        bool is_sink,
+        pa_hashmap *ports,
+        pa_card_profile *cp,
+        pa_core *core) {
+
+    pa_alsa_ucm_device **pdevices;
+
+    pa_assert(context->ucm_devices);
+
+    if (pa_idxset_size(context->ucm_devices) > 0) {
+        pdevices = pa_xnew(pa_alsa_ucm_device *, pa_idxset_size(context->ucm_devices));
+        ucm_add_ports_combination(p, context, is_sink, pdevices, 0, PA_IDXSET_INVALID, ports, cp, core);
+        pa_xfree(pdevices);
+    }
+}
+
+void pa_alsa_ucm_add_ports(
+        pa_hashmap **p,
+        pa_proplist *proplist,
+        pa_alsa_ucm_mapping_context *context,
+        bool is_sink,
+        pa_card *card) {
+
+    uint32_t idx;
+    char *merged_roles;
+    const char *role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES;
+    pa_alsa_ucm_device *dev;
+    pa_alsa_ucm_modifier *mod;
+    char *tmp;
+
+    pa_assert(p);
+    pa_assert(*p);
+
+    /* add ports first */
+    pa_alsa_ucm_add_ports_combination(*p, context, is_sink, card->ports, NULL, card->core);
+
+    /* then set property PA_PROP_DEVICE_INTENDED_ROLES */
+    merged_roles = pa_xstrdup(pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES));
+    PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
+        const char *roles = pa_proplist_gets(dev->proplist, role_name);
+        tmp = merge_roles(merged_roles, roles);
+        pa_xfree(merged_roles);
+        merged_roles = tmp;
+    }
+
+    if (context->ucm_modifiers)
+        PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) {
+            tmp = merge_roles(merged_roles, mod->media_role);
+            pa_xfree(merged_roles);
+            merged_roles = tmp;
+        }
+
+    if (merged_roles)
+        pa_proplist_sets(proplist, PA_PROP_DEVICE_INTENDED_ROLES, merged_roles);
+
+    pa_log_info("ALSA device %s roles: %s", pa_proplist_gets(proplist, PA_PROP_DEVICE_STRING), pa_strnull(merged_roles));
+    pa_xfree(merged_roles);
+}
+
+/* Change UCM verb and device to match selected card profile */
+int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, const char *new_profile, const char *old_profile) {
+    int ret = 0;
+    const char *profile;
+    pa_alsa_ucm_verb *verb;
+
+    if (new_profile == old_profile)
+        return ret;
+    else if (new_profile == NULL || old_profile == NULL)
+        profile = new_profile ? new_profile : SND_USE_CASE_VERB_INACTIVE;
+    else if (!pa_streq(new_profile, old_profile))
+        profile = new_profile;
+    else
+        return ret;
+
+    /* change verb */
+    pa_log_info("Set UCM verb to %s", profile);
+    if ((snd_use_case_set(ucm->ucm_mgr, "_verb", profile)) < 0) {
+        pa_log("Failed to set verb %s", profile);
+        ret = -1;
+    }
+
+    /* find active verb */
+    ucm->active_verb = NULL;
+    PA_LLIST_FOREACH(verb, ucm->verbs) {
+        const char *verb_name;
+        verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME);
+        if (pa_streq(verb_name, profile)) {
+            ucm->active_verb = verb;
+            break;
+        }
+    }
+
+    return ret;
+}
+
+int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) {
+    int i;
+    int ret = 0;
+    pa_alsa_ucm_config *ucm;
+    const char **enable_devs;
+    int enable_num = 0;
+    uint32_t idx;
+    pa_alsa_ucm_device *dev;
+
+    pa_assert(context && context->ucm);
+
+    ucm = context->ucm;
+    pa_assert(ucm->ucm_mgr);
+
+    enable_devs = pa_xnew(const char *, pa_idxset_size(context->ucm_devices));
+
+    /* first disable then enable */
+    PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
+        const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
+
+        if (ucm_port_contains(port->name, dev_name, is_sink))
+            enable_devs[enable_num++] = dev_name;
+        else {
+            pa_log_debug("Disable ucm device %s", dev_name);
+            if (snd_use_case_set(ucm->ucm_mgr, "_disdev", dev_name) > 0) {
+                pa_log("Failed to disable ucm device %s", dev_name);
+                ret = -1;
+                break;
+            }
+        }
+    }
+
+    for (i = 0; i < enable_num; i++) {
+        pa_log_debug("Enable ucm device %s", enable_devs[i]);
+        if (snd_use_case_set(ucm->ucm_mgr, "_enadev", enable_devs[i]) < 0) {
+            pa_log("Failed to enable ucm device %s", enable_devs[i]);
+            ret = -1;
+            break;
+        }
+    }
+
+    pa_xfree(enable_devs);
+
+    return ret;
+}
+
+static void ucm_add_mapping(pa_alsa_profile *p, pa_alsa_mapping *m) {
+
+    switch (m->direction) {
+        case PA_ALSA_DIRECTION_ANY:
+            pa_idxset_put(p->output_mappings, m, NULL);
+            pa_idxset_put(p->input_mappings, m, NULL);
+            break;
+        case PA_ALSA_DIRECTION_OUTPUT:
+            pa_idxset_put(p->output_mappings, m, NULL);
+            break;
+        case PA_ALSA_DIRECTION_INPUT:
+            pa_idxset_put(p->input_mappings, m, NULL);
+            break;
+    }
+}
+
+static void alsa_mapping_add_ucm_device(pa_alsa_mapping *m, pa_alsa_ucm_device *device) {
+    char *cur_desc;
+    const char *new_desc;
+
+    pa_idxset_put(m->ucm_context.ucm_devices, device, NULL);
+
+    new_desc = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
+    cur_desc = m->description;
+    if (cur_desc)
+        m->description = pa_sprintf_malloc("%s + %s", cur_desc, new_desc);
+    else
+        m->description = pa_xstrdup(new_desc);
+    pa_xfree(cur_desc);
+
+    /* walk around null case */
+    m->description = m->description ? m->description : pa_xstrdup("");
+
+    /* save mapping to ucm device */
+    if (m->direction == PA_ALSA_DIRECTION_OUTPUT)
+        device->playback_mapping = m;
+    else
+        device->capture_mapping = m;
+}
+
+static int ucm_create_mapping_direction(
+        pa_alsa_ucm_config *ucm,
+        pa_alsa_profile_set *ps,
+        pa_alsa_profile *p,
+        pa_alsa_ucm_device *device,
+        const char *verb_name,
+        const char *device_name,
+        const char *device_str,
+        bool is_sink) {
+
+    pa_alsa_mapping *m;
+    char *mapping_name;
+    unsigned priority, channels;
+
+    mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, device_str, is_sink ? "sink" : "source");
+
+    m = pa_alsa_mapping_get(ps, mapping_name);
+    if (!m) {
+        pa_log("No mapping for %s", mapping_name);
+        pa_xfree(mapping_name);
+        return -1;
+    }
+    pa_log_debug("UCM mapping: %s dev %s", mapping_name, device_name);
+    pa_xfree(mapping_name);
+
+    priority = is_sink ? device->playback_priority : device->capture_priority;
+    channels = is_sink ? device->playback_channels : device->capture_channels;
+
+    if (!m->ucm_context.ucm_devices) {   /* new mapping */
+        m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+        m->ucm_context.ucm = ucm;
+        m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
+
+        m->device_strings = pa_xnew0(char*, 2);
+        m->device_strings[0] = pa_xstrdup(device_str);
+        m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT;
+
+        ucm_add_mapping(p, m);
+        pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
+    }
+
+    /* mapping priority is the highest one of ucm devices */
+    if (priority > m->priority)
+        m->priority = priority;
+
+    /* mapping channels is the lowest one of ucm devices */
+    if (channels < m->channel_map.channels)
+        pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
+
+    alsa_mapping_add_ucm_device(m, device);
+
+    return 0;
+}
+
+static int ucm_create_mapping(
+        pa_alsa_ucm_config *ucm,
+        pa_alsa_profile_set *ps,
+        pa_alsa_profile *p,
+        pa_alsa_ucm_device *device,
+        const char *verb_name,
+        const char *device_name,
+        const char *sink,
+        const char *source) {
+
+    int ret = 0;
+
+    if (!sink && !source) {
+        pa_log("No sink and source at %s: %s", verb_name, device_name);
+        return -1;
+    }
+
+    if (sink)
+        ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, sink, TRUE);
+    if (ret == 0 && source)
+        ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, source, FALSE);
+
+    return ret;
+}
+
+static int ucm_create_profile(
+        pa_alsa_ucm_config *ucm,
+        pa_alsa_profile_set *ps,
+        pa_alsa_ucm_verb *verb,
+        const char *verb_name,
+        const char *verb_desc) {
+
+    pa_alsa_profile *p;
+    pa_alsa_ucm_device *dev;
+    int i = 0;
+    const char *name, *sink, *source;
+    char *verb_cmp, *c;
+
+    pa_assert(ps);
+
+    if (pa_hashmap_get(ps->profiles, verb_name)) {
+        pa_log("Verb %s already exists", verb_name);
+        return -1;
+    }
+
+    p = pa_xnew0(pa_alsa_profile, 1);
+    p->profile_set = ps;
+    p->name = pa_xstrdup(verb_name);
+    p->description = pa_xstrdup(verb_desc);
+
+    p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    p->supported = TRUE;
+    pa_hashmap_put(ps->profiles, p->name, p);
+
+    /* TODO: get profile priority from ucm info or policy management */
+    c = verb_cmp = pa_xstrdup(verb_name);
+    while (*c) {
+        if (*c == '_') *c = ' ';
+        c++;
+    }
+
+    for (i = 0; verb_info[i].id; i++) {
+        if (strcasecmp(verb_info[i].id, verb_cmp) == 0) {
+            p->priority = verb_info[i].priority;
+            break;
+        }
+    }
+
+    pa_xfree(verb_cmp);
+
+    if (verb_info[i].id == NULL)
+        p->priority = 1000;
+
+    PA_LLIST_FOREACH(dev, verb->devices) {
+        name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
+
+        sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK);
+        source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE);
+
+        ucm_create_mapping(ucm, ps, p, dev, verb_name, name, sink, source);
+    }
+
+    pa_alsa_profile_dump(p);
+
+    return 0;
+}
+
+static snd_pcm_t* mapping_open_pcm(pa_alsa_ucm_config *ucm, pa_alsa_mapping *m, int mode) {
+    snd_pcm_t* pcm;
+    pa_sample_spec try_ss = ucm->core->default_sample_spec;
+    pa_channel_map try_map;
+    snd_pcm_uframes_t try_period_size, try_buffer_size;
+    bool exact_channels = m->channel_map.channels > 0;
+
+    if (exact_channels) {
+        try_map = m->channel_map;
+        try_ss.channels = try_map.channels;
+    } else
+        pa_channel_map_init_extend(&try_map, try_ss.channels, PA_CHANNEL_MAP_ALSA);
+
+    try_period_size =
+        pa_usec_to_bytes(ucm->core->default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
+        pa_frame_size(&try_ss);
+    try_buffer_size = ucm->core->default_n_fragments * try_period_size;
+
+    pcm = pa_alsa_open_by_device_string(m->device_strings[0], NULL, &try_ss,
+            &try_map, mode, &try_period_size, &try_buffer_size, 0, NULL, NULL, exact_channels);
+
+    if (pcm && !exact_channels)
+        m->channel_map = try_map;
+
+    return pcm;
+}
+
+static void profile_finalize_probing(pa_alsa_profile *p) {
+    pa_alsa_mapping *m;
+    uint32_t idx;
+
+    PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
+        if (!m->output_pcm)
+            continue;
+
+        if (p->supported)
+            m->supported++;
+
+        snd_pcm_close(m->output_pcm);
+        m->output_pcm = NULL;
+    }
+
+    PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
+        if (!m->input_pcm)
+            continue;
+
+        if (p->supported)
+            m->supported++;
+
+        snd_pcm_close(m->input_pcm);
+        m->input_pcm = NULL;
+    }
+}
+
+static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps) {
+    void *state;
+    pa_alsa_profile *p;
+    pa_alsa_mapping *m;
+    uint32_t idx;
+
+    PA_HASHMAP_FOREACH(p, ps->profiles, state) {
+        /* change verb */
+        pa_log_info("Set ucm verb to %s", p->name);
+        if ((snd_use_case_set(ucm->ucm_mgr, "_verb", p->name)) < 0) {
+            pa_log("Failed to set verb %s", p->name);
+            p->supported = FALSE;
+            continue;
+        }
+        PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
+            m->output_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_PLAYBACK);
+            if (!m->output_pcm) {
+                p->supported = FALSE;
+                break;
+            }
+        }
+        if (p->supported) {
+            PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
+                m->input_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_CAPTURE);
+                if (!m->input_pcm) {
+                    p->supported = FALSE;
+                    break;
+                }
+            }
+        }
+
+        if (!p->supported) {
+            profile_finalize_probing(p);
+            continue;
+        }
+
+        pa_log_debug("Profile %s supported.", p->name);
+
+        profile_finalize_probing(p);
+    }
+
+    /* restore ucm state */
+    snd_use_case_set(ucm->ucm_mgr, "_verb", SND_USE_CASE_VERB_INACTIVE);
+
+    pa_alsa_profile_set_drop_unsupported(ps);
+}
+
+pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map) {
+    pa_alsa_ucm_verb *verb;
+    pa_alsa_profile_set *ps;
+
+    ps = pa_xnew0(pa_alsa_profile_set, 1);
+    ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+    /* create a profile for each verb */
+    PA_LLIST_FOREACH(verb, ucm->verbs) {
+        const char *verb_name;
+        const char *verb_desc;
+
+        verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME);
+        verb_desc = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
+        if (verb_name == NULL) {
+            pa_log("Verb with no name");
+            continue;
+        }
+
+	    ucm_create_profile(ucm, ps, verb, verb_name, verb_desc);
+    }
+
+    ucm_probe_profile_set(ucm, ps);
+    ps->probed = TRUE;
+
+    return ps;
+}
+
+static void free_verb(pa_alsa_ucm_verb *verb) {
+    pa_alsa_ucm_device *di, *dn;
+    pa_alsa_ucm_modifier *mi, *mn;
+
+    PA_LLIST_FOREACH_SAFE(di, dn, verb->devices) {
+        PA_LLIST_REMOVE(pa_alsa_ucm_device, verb->devices, di);
+        pa_proplist_free(di->proplist);
+        if (di->conflicting_devices)
+            pa_idxset_free(di->conflicting_devices, NULL, NULL);
+        if (di->supported_devices)
+            pa_idxset_free(di->supported_devices, NULL, NULL);
+        pa_xfree(di);
+    }
+
+    PA_LLIST_FOREACH_SAFE(mi, mn, verb->modifiers) {
+        PA_LLIST_REMOVE(pa_alsa_ucm_modifier, verb->modifiers, mi);
+        pa_proplist_free(mi->proplist);
+        if (mi->n_suppdev > 0)
+            snd_use_case_free_list(mi->supported_devices, mi->n_suppdev);
+        if (mi->n_confdev > 0)
+            snd_use_case_free_list(mi->conflicting_devices, mi->n_confdev);
+        pa_xfree(mi->media_role);
+        pa_xfree(mi);
+    }
+    pa_proplist_free(verb->proplist);
+    pa_xfree(verb);
+}
+
+void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) {
+    pa_alsa_ucm_verb *vi, *vn;
+
+    PA_LLIST_FOREACH_SAFE(vi, vn, ucm->verbs) {
+        PA_LLIST_REMOVE(pa_alsa_ucm_verb, ucm->verbs, vi);
+        free_verb(vi);
+    }
+    if (ucm->ucm_mgr) {
+        snd_use_case_mgr_close(ucm->ucm_mgr);
+        ucm->ucm_mgr = NULL;
+    }
+}
+
+void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
+    pa_alsa_ucm_device *dev;
+    uint32_t idx;
+
+    if (context->ucm_devices) {
+        /* clear ucm device pointer to mapping */
+        PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
+            if (context->direction == PA_DIRECTION_OUTPUT)
+                dev->playback_mapping = NULL;
+            else
+                dev->capture_mapping = NULL;
+        }
+
+        pa_idxset_free(context->ucm_devices, NULL, NULL);
+    }
+
+    if (context->ucm_modifiers) {
+        pa_idxset_free(context->ucm_modifiers, NULL, NULL);
+    }
+}
diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
new file mode 100644
index 0000000..db21d25
--- /dev/null
+++ b/src/modules/alsa/alsa-ucm.h
@@ -0,0 +1,167 @@
+#ifndef fooalsaucmhfoo
+#define fooalsaucmhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2011 Wolfson Microelectronics PLC
+  Author Margarita Olaya <magi at slimlogic.co.uk>
+  Copyright 2012 Feng Wei <wei.feng at freescale.com>, Freescale Ltd.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <use-case.h>
+
+#include "alsa-mixer.h"
+
+/** For devices: List of verbs, devices or modifiers available */
+#define PA_ALSA_PROP_UCM_NAME                       "alsa.ucm.name"
+
+/** For devices: List of supported devices per verb*/
+#define PA_ALSA_PROP_UCM_DESCRIPTION                "alsa.ucm.description"
+
+/** For devices: Playback device name e.g PlaybackPCM */
+#define PA_ALSA_PROP_UCM_SINK                       "alsa.ucm.sink"
+
+/** For devices: Capture device name e.g CapturePCM*/
+#define PA_ALSA_PROP_UCM_SOURCE                     "alsa.ucm.source"
+
+/** For devices: Playback roles */
+#define PA_ALSA_PROP_UCM_PLAYBACK_ROLES             "alsa.ucm.playback.roles"
+
+/** For devices: Playback control volume ID string. e.g PlaybackVolume */
+#define PA_ALSA_PROP_UCM_PLAYBACK_VOLUME            "alsa.ucm.playback.volume"
+
+/** For devices: Playback switch e.g PlaybackSwitch */
+#define PA_ALSA_PROP_UCM_PLAYBACK_SWITCH            "alsa.ucm.playback.switch"
+
+/** For devices: Playback priority */
+#define PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY          "alsa.ucm.playback.priority"
+
+/** For devices: Playback channels */
+#define PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS          "alsa.ucm.playback.channels"
+
+/** For devices: Capture roles */
+#define PA_ALSA_PROP_UCM_CAPTURE_ROLES              "alsa.ucm.capture.roles"
+
+/** For devices: Capture controls volume ID string. e.g CaptureVolume */
+#define PA_ALSA_PROP_UCM_CAPTURE_VOLUME             "alsa.ucm.capture.volume"
+
+/** For devices: Capture switch e.g CaptureSwitch */
+#define PA_ALSA_PROP_UCM_CAPTURE_SWITCH             "alsa.ucm.capture.switch"
+
+/** For devices: Capture priority */
+#define PA_ALSA_PROP_UCM_CAPTURE_PRIORITY           "alsa.ucm.capture.priority"
+
+/** For devices: Capture channels */
+#define PA_ALSA_PROP_UCM_CAPTURE_CHANNELS           "alsa.ucm.capture.channels"
+
+/** For devices: Quality of Service */
+#define PA_ALSA_PROP_UCM_QOS                        "alsa.ucm.qos"
+
+typedef struct pa_alsa_ucm_verb pa_alsa_ucm_verb;
+typedef struct pa_alsa_ucm_modifier pa_alsa_ucm_modifier;
+typedef struct pa_alsa_ucm_device pa_alsa_ucm_device;
+typedef struct pa_alsa_ucm_config pa_alsa_ucm_config;
+typedef struct pa_alsa_ucm_mapping_context pa_alsa_ucm_mapping_context;
+
+int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index);
+pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map);
+int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, const char *new_profile, const char *old_profile);
+
+int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name, const char *verb_desc, pa_alsa_ucm_verb **p_verb);
+
+void pa_alsa_ucm_add_ports(
+        pa_hashmap **hash,
+        pa_proplist *proplist,
+        pa_alsa_ucm_mapping_context *context,
+        bool is_sink,
+        pa_card *card);
+void pa_alsa_ucm_add_ports_combination(
+        pa_hashmap *hash,
+        pa_alsa_ucm_mapping_context *context,
+        bool is_sink,
+        pa_hashmap *ports,
+        pa_card_profile *cp,
+        pa_core *core);
+int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink);
+
+void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm);
+void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context);
+
+/* UCM - Use Case Manager is available on some audio cards */
+
+struct pa_alsa_ucm_device {
+    PA_LLIST_FIELDS(pa_alsa_ucm_device);
+
+    pa_proplist *proplist;
+
+    unsigned playback_priority;
+    unsigned capture_priority;
+
+    unsigned playback_channels;
+    unsigned capture_channels;
+
+    pa_alsa_mapping *playback_mapping;
+    pa_alsa_mapping *capture_mapping;
+
+    pa_idxset *conflicting_devices;
+    pa_idxset *supported_devices;
+};
+
+struct pa_alsa_ucm_modifier {
+    PA_LLIST_FIELDS(pa_alsa_ucm_modifier);
+
+    pa_proplist *proplist;
+
+    int n_confdev;
+    int n_suppdev;
+
+    const char **conflicting_devices;
+    const char **supported_devices;
+
+    pa_direction_t action_direction;
+
+    char *media_role;
+};
+
+struct pa_alsa_ucm_verb {
+    PA_LLIST_FIELDS(pa_alsa_ucm_verb);
+
+    pa_proplist *proplist;
+
+    PA_LLIST_HEAD(pa_alsa_ucm_device, devices);
+    PA_LLIST_HEAD(pa_alsa_ucm_modifier, modifiers);
+};
+
+struct pa_alsa_ucm_config {
+    pa_core *core;
+    snd_use_case_mgr_t *ucm_mgr;
+    pa_alsa_ucm_verb *active_verb;
+
+    PA_LLIST_HEAD(pa_alsa_ucm_verb, verbs);
+};
+
+struct pa_alsa_ucm_mapping_context {
+    pa_alsa_ucm_config *ucm;
+    pa_direction_t direction;
+
+    pa_idxset *ucm_devices;
+    pa_idxset *ucm_modifiers;
+};
+
+#endif
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index 7e7e551..517c8f8 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -37,6 +37,7 @@
 #endif
 
 #include "alsa-util.h"
+#include "alsa-ucm.h"
 #include "alsa-sink.h"
 #include "alsa-source.h"
 #include "module-alsa-card-symdef.h"
@@ -69,6 +70,7 @@ PA_MODULE_USAGE(
         "deferred_volume=<Synchronize software and hardware volume changes to avoid momentary jumps?> "
         "profile_set=<profile set configuration file> "
         "paths_dir=<directory containing the path configuration files> "
+        "use_ucm=<load use case manager> "
 );
 
 static const char* const valid_modargs[] = {
@@ -95,6 +97,7 @@ static const char* const valid_modargs[] = {
     "deferred_volume",
     "profile_set",
     "paths_dir",
+    "use_ucm",
     NULL
 };
 
@@ -117,6 +120,10 @@ struct userdata {
     pa_modargs *modargs;
 
     pa_alsa_profile_set *profile_set;
+
+    /* ucm stuffs */
+    pa_bool_t use_ucm;
+    pa_alsa_ucm_config ucm;
 };
 
 struct profile_data {
@@ -143,7 +150,10 @@ static void add_profiles(struct userdata *u, pa_hashmap *h, pa_hashmap *ports) {
             cp->n_sinks = pa_idxset_size(ap->output_mappings);
 
             PA_IDXSET_FOREACH(m, ap->output_mappings, idx) {
-                pa_alsa_path_set_add_ports(m->output_path_set, cp, ports, NULL, u->core);
+                if (u->use_ucm)
+                    pa_alsa_ucm_add_ports_combination(NULL, &m->ucm_context, TRUE, ports, cp, u->core);
+                else
+                    pa_alsa_path_set_add_ports(m->output_path_set, cp, ports, NULL, u->core);
                 if (m->channel_map.channels > cp->max_sink_channels)
                     cp->max_sink_channels = m->channel_map.channels;
             }
@@ -153,7 +163,10 @@ static void add_profiles(struct userdata *u, pa_hashmap *h, pa_hashmap *ports) {
             cp->n_sources = pa_idxset_size(ap->input_mappings);
 
             PA_IDXSET_FOREACH(m, ap->input_mappings, idx) {
-                pa_alsa_path_set_add_ports(m->input_path_set, cp, ports, NULL, u->core);
+                if (u->use_ucm)
+                    pa_alsa_ucm_add_ports_combination(NULL, &m->ucm_context, FALSE, ports, cp, u->core);
+                else
+                    pa_alsa_path_set_add_ports(m->input_path_set, cp, ports, NULL, u->core);
                 if (m->channel_map.channels > cp->max_source_channels)
                     cp->max_source_channels = m->channel_map.channels;
             }
@@ -222,6 +235,13 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
             am->source = NULL;
         }
 
+    /* if UCM is available for this card then update the verb */
+    if (u->use_ucm) {
+        if (pa_alsa_ucm_set_profile(&u->ucm, nd->profile ? nd->profile->name : NULL,
+                    od->profile ? od->profile->name : NULL) < 0)
+            return -1;
+    }
+
     if (nd->profile && nd->profile->output_mappings)
         PA_IDXSET_FOREACH(am, nd->profile->output_mappings, idx) {
 
@@ -259,11 +279,20 @@ static void init_profile(struct userdata *u) {
     uint32_t idx;
     pa_alsa_mapping *am;
     struct profile_data *d;
+    pa_alsa_ucm_config *ucm = &u->ucm;
 
     pa_assert(u);
 
     d = PA_CARD_PROFILE_DATA(u->card->active_profile);
 
+    if (d->profile && u->use_ucm) {
+        /* Set initial verb */
+        if (pa_alsa_ucm_set_profile(ucm, d->profile->name, NULL) < 0) {
+            pa_log("Failed to set ucm profile %s", d->profile->name);
+            return;
+        }
+    }
+
     if (d->profile && d->profile->output_mappings)
         PA_IDXSET_FOREACH(am, d->profile->output_mappings, idx)
             am->sink = pa_alsa_sink_new(u->module, u->modargs, __FILE__, u->card, am);
@@ -439,6 +468,9 @@ int pa__init(pa_module *m) {
     u->device_id = pa_xstrdup(pa_modargs_get_value(ma, "device_id", DEFAULT_DEVICE_ID));
     u->modargs = ma;
 
+    u->use_ucm = TRUE;
+    u->ucm.core = m->core;
+
     if ((u->alsa_card_index = snd_card_get_index(u->device_id)) < 0) {
         pa_log("Card '%s' doesn't exist: %s", u->device_id, pa_alsa_strerror(u->alsa_card_index));
         goto fail;
@@ -456,18 +488,27 @@ int pa__init(pa_module *m) {
         }
     }
 
+    pa_modargs_get_value_boolean(ma, "use_ucm", &u->use_ucm);
+    if (u->use_ucm && !pa_alsa_ucm_query_profiles(&u->ucm, u->alsa_card_index)) {
+        pa_log_info("Found UCM profiles");
+
+        u->profile_set = pa_alsa_ucm_add_profile_set(&u->ucm, &u->core->default_channel_map);
+    }
+    else {
+        u->use_ucm = FALSE;
 #ifdef HAVE_UDEV
-    fn = pa_udev_get_property(u->alsa_card_index, "PULSE_PROFILE_SET");
+        fn = pa_udev_get_property(u->alsa_card_index, "PULSE_PROFILE_SET");
 #endif
 
-    if (pa_modargs_get_value(ma, "profile_set", NULL)) {
+        if (pa_modargs_get_value(ma, "profile_set", NULL)) {
+            pa_xfree(fn);
+            fn = pa_xstrdup(pa_modargs_get_value(ma, "profile_set", NULL));
+        }
+
+        u->profile_set = pa_alsa_profile_set_new(fn, &u->core->default_channel_map);
         pa_xfree(fn);
-        fn = pa_xstrdup(pa_modargs_get_value(ma, "profile_set", NULL));
     }
 
-    u->profile_set = pa_alsa_profile_set_new(fn, &u->core->default_channel_map);
-    pa_xfree(fn);
-
     u->profile_set->ignore_dB = ignore_dB;
 
     if (!u->profile_set)
@@ -613,6 +654,8 @@ void pa__done(pa_module*m) {
     if (u->profile_set)
         pa_alsa_profile_set_free(u->profile_set);
 
+    pa_alsa_ucm_free(&u->ucm);
+
     pa_xfree(u->device_id);
     pa_xfree(u);
 



More information about the pulseaudio-commits mailing list