[pulseaudio-discuss] [PATCH] UCM patches on ubuntu 1:1.1-0ubuntu4

Feng Wei feng.wei at linaro.org
Sun Feb 19 23:34:01 PST 2012


Somewhat verified on Panda board and i.mx53 board.

Integrate UCM in alsa module
Index: pulseaudio-1.1/src/Makefile.am
===================================================================
--- pulseaudio-1.1.orig/src/Makefile.am	2012-02-17 13:19:52.600542570 +0800
+++ pulseaudio-1.1/src/Makefile.am	2012-02-17 13:19:52.648542570 +0800
@@ -1561,6 +1561,7 @@

 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-jack-inputdev.c modules/alsa/alsa-jack-inputdev.h \
 		modules/alsa/alsa-sink.c modules/alsa/alsa-sink.h \
Index: pulseaudio-1.1/src/modules/alsa/alsa-mixer.c
===================================================================
--- pulseaudio-1.1.orig/src/modules/alsa/alsa-mixer.c	2012-02-17
13:19:52.548542570 +0800
+++ pulseaudio-1.1/src/modules/alsa/alsa-mixer.c	2012-02-17
13:19:52.652542570 +0800
@@ -3180,6 +3180,8 @@
     pa_xstrfreev(m->input_element);
     pa_xstrfreev(m->output_element);

+    pa_xfree(m->ucm_context.ucm_devices);
+
     pa_assert(!m->input_pcm);
     pa_assert(!m->output_pcm);

@@ -3255,7 +3257,7 @@
     pa_xfree(ps);
 }

-static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const
char *name) {
+pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
     pa_alsa_mapping *m;

     if (!pa_startswith(name, "Mapping "))
Index: pulseaudio-1.1/src/modules/alsa/alsa-mixer.h
===================================================================
--- pulseaudio-1.1.orig/src/modules/alsa/alsa-mixer.h	2012-02-17
13:19:52.528542570 +0800
+++ pulseaudio-1.1/src/modules/alsa/alsa-mixer.h	2012-02-17
13:19:52.652542570 +0800
@@ -49,6 +49,7 @@
 typedef struct pa_alsa_jack_inputdev pa_alsa_jack_inputdev;

 #include "alsa-util.h"
+#include "alsa-ucm.h"

 typedef enum pa_alsa_switch_use {
     PA_ALSA_SWITCH_IGNORE,
@@ -225,6 +226,8 @@
 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t
*m, snd_mixer_elem_callback_t cb, void *userdata);
 void pa_alsa_path_set_free(pa_alsa_path_set *s);

+pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name);
+
 struct pa_alsa_mapping {
     pa_alsa_profile_set *profile_set;

@@ -252,6 +255,9 @@

     pa_sink *sink;
     pa_source *source;
+
+    /* ucm device context*/
+    pa_alsa_ucm_mapping_context ucm_context;
 };

 struct pa_alsa_profile {
Index: pulseaudio-1.1/src/modules/alsa/alsa-sink.c
===================================================================
--- pulseaudio-1.1.orig/src/modules/alsa/alsa-sink.c	2012-02-17
13:19:52.532542570 +0800
+++ pulseaudio-1.1/src/modules/alsa/alsa-sink.c	2012-02-17
13:19:52.652542570 +0800
@@ -151,6 +151,9 @@
     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,14 @@
     }
 }

+static int sink_set_port_ucm_cb(pa_sink *s, pa_device_port *p) {
+    struct userdata *u = s->userdata;
+
+    pa_assert(p);
+
+    return ucm_set_port(u->ucm_context, p, 1);
+}
+
 static int sink_set_port_cb(pa_sink *s, pa_device_port *p) {
     struct userdata *u = s->userdata;
     pa_alsa_port_data *data;
@@ -1840,6 +1851,16 @@
     }
 }

+/* FIXME: hardware volume/mute ??? */
+static int ucm_setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
+    pa_assert(u && u->sink);
+
+    if (u->sink->active_port) {
+        return ucm_set_port(u->ucm_context, u->sink->active_port, 1);
+    }
+
+    return 0;
+}

 static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
     pa_bool_t need_mixer_callback = FALSE;
@@ -2020,6 +2041,8 @@
             TRUE);
     u->smoother_interval = SMOOTHER_MIN_INTERVAL;

+    u->ucm_context = &mapping->ucm_context;
+
     dev_id = pa_modargs_get_value(
             ma, "device_id",
             pa_modargs_get_value(ma, "device", DEFAULT_DEVICE));
@@ -2108,7 +2131,8 @@
     /* 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 (!mapping->ucm_context.ucm)
+        find_mixer(u, mapping, pa_modargs_get_value(ma, "control",
NULL), ignore_dB);

     pa_sink_new_data_init(&data);
     data.driver = driver;
@@ -2153,7 +2177,9 @@
         goto fail;
     }

-    if (u->mixer_path_set)
+    if (mapping->ucm_context.ucm)
+        ucm_add_ports(&data.ports, data.proplist, mapping, 1, card);
+    else if (u->mixer_path_set)
         pa_alsa_add_ports(&data.ports, 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) |
@@ -2181,7 +2207,10 @@
     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 (mapping->ucm_context.ucm)
+        u->sink->set_port = sink_set_port_ucm_cb;
+    else
+        u->sink->set_port = sink_set_port_cb;
     u->sink->userdata = u;

     pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
@@ -2233,7 +2262,11 @@
     if (update_sw_params(u) < 0)
         goto fail;

-    if (setup_mixer(u, ignore_dB) < 0)
+    if (mapping->ucm_context.ucm) {
+        if (ucm_setup_mixer(u, ignore_dB) < 0)
+            goto fail;
+    }
+    else if (setup_mixer(u, ignore_dB) < 0)
         goto fail;

     pa_alsa_dump(PA_LOG_DEBUG, u->pcm_handle);
Index: pulseaudio-1.1/src/modules/alsa/alsa-source.c
===================================================================
--- pulseaudio-1.1.orig/src/modules/alsa/alsa-source.c	2012-02-17
13:19:52.532542570 +0800
+++ pulseaudio-1.1/src/modules/alsa/alsa-source.c	2012-02-17
13:19:52.656542570 +0800
@@ -133,6 +133,9 @@
     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);
@@ -1301,6 +1304,14 @@
     }
 }

+static int source_set_port_ucm_cb(pa_source *s, pa_device_port *p) {
+    struct userdata *u = s->userdata;
+
+    pa_assert(p);
+
+    return ucm_set_port(u->ucm_context, p, 0);
+}
+
 static int source_set_port_cb(pa_source *s, pa_device_port *p) {
     struct userdata *u = s->userdata;
     pa_alsa_port_data *data;
@@ -1540,6 +1551,17 @@
     }
 }

+/* FIXME: hardware volume/mute ??? */
+static int ucm_setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
+    pa_assert(u && u->source);
+
+    if (u->source->active_port) {
+        return ucm_set_port(u->ucm_context, u->source->active_port, 0);
+    }
+
+    return 0;
+}
+
 static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
     pa_bool_t need_mixer_callback = FALSE;

@@ -1712,6 +1734,8 @@
             TRUE);
     u->smoother_interval = SMOOTHER_MIN_INTERVAL;

+    u->ucm_context = &mapping->ucm_context;
+
     dev_id = pa_modargs_get_value(
             ma, "device_id",
             pa_modargs_get_value(ma, "device", DEFAULT_DEVICE));
@@ -1797,7 +1821,8 @@
     /* 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 (!mapping->ucm_context.ucm)
+        find_mixer(u, mapping, pa_modargs_get_value(ma, "control",
NULL), ignore_dB);

     pa_source_new_data_init(&data);
     data.driver = driver;
@@ -1842,7 +1867,9 @@
         goto fail;
     }

-    if (u->mixer_path_set)
+    if (mapping->ucm_context.ucm)
+        ucm_add_ports(&data.ports, data.proplist, mapping, 0, card);
+    else if (u->mixer_path_set)
         pa_alsa_add_ports(&data.ports, 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));
@@ -1869,7 +1896,10 @@
     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 (mapping->ucm_context.ucm)
+        u->source->set_port = source_set_port_ucm_cb;
+    else
+        u->source->set_port = source_set_port_cb;
     u->source->userdata = u;

     pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
@@ -1913,7 +1943,11 @@
     if (update_sw_params(u) < 0)
         goto fail;

-    if (setup_mixer(u, ignore_dB) < 0)
+    if (mapping->ucm_context.ucm) {
+        if (ucm_setup_mixer(u, ignore_dB) < 0)
+            goto fail;
+    }
+    else if (setup_mixer(u, ignore_dB) < 0)
         goto fail;

     pa_alsa_dump(PA_LOG_DEBUG, u->pcm_handle);
Index: pulseaudio-1.1/src/modules/alsa/alsa-ucm.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ pulseaudio-1.1/src/modules/alsa/alsa-ucm.c	2012-02-17
13:20:30.016542590 +0800
@@ -0,0 +1,1129 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2011 Wolfson Microelectronics PLC
+ Author Margarita Olaya <magi at slimlogic.co.uk>
+
+ 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 <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_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_PROP_UCM_SINK},
+    {"CapturePCM", PA_PROP_UCM_SOURCE},
+    {"PlaybackVolume", PA_PROP_UCM_PLAYBACK_VOLUME},
+    {"PlaybackSwitch", PA_PROP_UCM_PLAYBACK_SWITCH},
+    {"PlaybackPriority", PA_PROP_UCM_PLAYBACK_PRIORITY},
+    {"PlaybackChannels", PA_PROP_UCM_PLAYBACK_CHANNELS},
+    {"CaptureVolume", PA_PROP_UCM_CAPTURE_VOLUME},
+    {"CaptureSwitch", PA_PROP_UCM_CAPTURE_SWITCH},
+    {"CapturePriority", PA_PROP_UCM_CAPTURE_PRIORITY},
+    {"CaptureChannels", PA_PROP_UCM_CAPTURE_CHANNELS},
+    {"TQ", PA_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 */
+
+int ucm_get_property(struct pa_alsa_ucm_verb *verb,
snd_use_case_mgr_t *uc_mgr, const char *verb_name) {
+    const char *value;
+    char *id;
+    int i = 0;
+
+    do {
+        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 ) {
+            pa_log_info("No %s for verb %s", item[i].id, verb_name);
+            continue;
+        }
+
+        pa_log_info("Got %s for verb %s: %s", item[i].id, verb_name, value);
+        pa_proplist_sets(verb->proplist, item[i].property, value);
+        free((void*)value);
+    } while (item[++i].id);
+
+    return 0;
+};
+
+static char **dup_strv(const char **src, int n) {
+    char **dest = pa_xnew0(char *, n+1);
+    int i;
+    for (i=0; i<n; i++) {
+        dest[i] = pa_xstrdup(src[i]);
+    }
+    return dest;
+}
+
+static int ucm_device_in(char **device_names, int num,
pa_alsa_ucm_device *dev) {
+    int i;
+    const char *dev_name = pa_proplist_gets(dev->proplist, PA_PROP_UCM_NAME);
+
+    for (i=0; i<num; i++)
+    {
+        if (!strcmp(dev_name, device_names[i]))
+            return 1;
+    }
+
+    return 0;
+}
+
+/* Create a property list for this ucm device */
+static int ucm_get_device_property(struct pa_alsa_ucm_device *device,
+        snd_use_case_mgr_t *uc_mgr, struct pa_alsa_ucm_verb *verb,
const char *device_name) {
+    const char *value;
+    const char **devices;
+    char *id;
+    int i = 0;
+    int err;
+    uint32_t ui;
+
+    do {
+        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) {
+            pa_log_info("No %s for device %s", item[i].id, device_name);
+            continue;
+        }
+
+        pa_log_info("Got %s for device %s: %s", item[i].id,
device_name, value);
+        pa_proplist_sets(device->proplist, item[i].property, value);
+        free((void*)value);
+    }  while (item[++i].id);
+
+    /* get direction and channels */
+    value = pa_proplist_gets(device->proplist, PA_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_PROP_UCM_SINK);
+        if (!value) { /* take pcm from verb playback default */
+            value = pa_proplist_gets(verb->proplist, PA_PROP_UCM_SINK);
+            if (value) {
+                pa_log_info("UCM playback device %s fetch pcm from
verb default %s", device_name, value);
+                pa_proplist_sets(device->proplist, PA_PROP_UCM_SINK, value);
+            }
+            else {
+                pa_log("UCM playback device %s fetch pcm failed", device_name);
+            }
+        }
+    }
+    value = pa_proplist_gets(device->proplist, PA_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_PROP_UCM_SOURCE);
+        if (!value) { /* take pcm from verb capture default */
+            value = pa_proplist_gets(verb->proplist, PA_PROP_UCM_SOURCE);
+            if (value) {
+                pa_log_info("UCM capture device %s fetch pcm from
verb default %s", device_name, value);
+                pa_proplist_sets(device->proplist, PA_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_PROP_UCM_PLAYBACK_PRIORITY);
+        if (value) {
+            /* get priority from ucm config */
+            if (pa_atou(value, &ui) == 0)
+                device->playback_priority = ui;
+            else
+                pa_log("UCM playback priority %s for device %s
error", value, device_name);
+        }
+    }
+    if (device->capture_channels) { /* source device */
+        value = pa_proplist_gets(device->proplist,
PA_PROP_UCM_CAPTURE_PRIORITY);
+        if (value) {
+            /* get priority from ucm config */
+            if (pa_atou(value, &ui) == 0)
+                device->capture_priority = ui;
+            else
+                pa_log("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 */
+        i = 0;
+        do {
+            if (strcasecmp(dev_info[i].id, device_name) == 0) {
+                PA_UCM_DEVICE_PRIORITY_SET(device, dev_info[i].priority);
+                break;
+            }
+        } while (dev_info[++i].id);
+    }
+    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);
+    device->n_confdev = snd_use_case_get_list(uc_mgr, id, &devices);
+    pa_xfree(id);
+    if (device->n_confdev <= 0)
+        pa_log_info("No %s for device %s", "_conflictingdevs", device_name);
+    else {
+        device->conflicting_devices = dup_strv(devices, device->n_confdev);
+        snd_use_case_free_list(devices, device->n_confdev);
+    }
+
+    id = pa_sprintf_malloc("%s/%s", "_supporteddevs", device_name);
+    device->n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices);
+    pa_xfree(id);
+    if (device->n_suppdev <= 0)
+        pa_log_info("No %s for device %s", "_supporteddevs", device_name);
+    else {
+        device->supported_devices = dup_strv(devices, device->n_suppdev);
+        snd_use_case_free_list(devices, device->n_suppdev);
+    }
+
+    return 0;
+};
+
+/* Create a property list for this ucm modifier */
+static int ucm_get_modifier_property(struct pa_alsa_ucm_modifier
*modifier, snd_use_case_mgr_t *uc_mgr, const char *modifier_name) {
+    const char *value;
+    char *id;
+    int i = 0;
+
+    do {
+        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 ) {
+            pa_log_info("No %s for modifier %s", item[i].id, modifier_name);
+            continue;
+        }
+
+        pa_log_info("Got %s for modifier %s: %s", item[i].id,
modifier_name, value);
+        pa_proplist_sets(modifier->proplist, item[i].property, value);
+        free((void*)value);
+    } while (item[++i].id);
+
+    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_info("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_info("No %s for modifier %s", "_supporteddevs", modifier_name);
+
+    return 0;
+};
+
+/* Create a list of devices for this verb */
+static int ucm_get_devices(struct 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;
+        d = pa_xnew0(pa_alsa_ucm_device, 1);
+        d->proplist = pa_proplist_new();
+        pa_proplist_sets(d->proplist, PA_PROP_UCM_NAME,
pa_strnull(dev_list[i]));
+        pa_proplist_sets(d->proplist, PA_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(struct 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;
+        m = pa_xnew0(pa_alsa_ucm_modifier, 1);
+        m->proplist = pa_proplist_new();
+        pa_proplist_sets(m->proplist, PA_PROP_UCM_NAME,
pa_strnull(mod_list[i]));
+        pa_proplist_sets(m->proplist, PA_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 pa_bool_t role_match(const char *cur, const char *role) {
+    char *r;
+    const char *state=NULL;
+
+    while ((r = pa_split(cur, ",", &state))) {
+        if (!strcasecmp(role, r)) {
+            pa_xfree(r);
+            return TRUE;
+        }
+        pa_xfree(r);
+    }
+
+    return FALSE;
+}
+
+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 (!role_match(cur, role)) /* not exists */
+    {
+        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, int is_sink) {
+    pa_alsa_ucm_device *d;
+
+    PA_LLIST_FOREACH(d, list) {
+        const char *dev_name = pa_proplist_gets(d->proplist, PA_PROP_UCM_NAME);
+        if (!strcmp(dev_name, name)) {
+            const char *sink = pa_proplist_gets(d->proplist, PA_PROP_UCM_SINK);
+            if (((is_sink && sink) || (!is_sink && !sink))) {
+                add_role_to_device (d, dev_name, role_name, role);
+            }
+            break;
+        }
+    }
+}
+
+static void ucm_set_media_roles(struct pa_alsa_ucm_modifier *modifier,
+        pa_alsa_ucm_device *list, const char *mod_name) {
+    int i;
+    int is_sink=0;
+    const char *sub = NULL;
+
+    if ((sub = strcasestr(mod_name, "Play")) != NULL) {
+        is_sink = 1;
+        sub += 4;
+    }
+    else if ((sub = strcasestr(mod_name, "Capture")) != NULL) {
+        sub += 7;
+    }
+
+    if (!sub || !*sub || !*(sub+1)) {
+        pa_log_warn("Can't match media roles for modifer %s", mod_name);
+        return;
+    }
+
+    modifier->action_direct = is_sink ?
+        PA_ALSA_UCM_DIRECT_SINK : PA_ALSA_UCM_DIRECT_SOURCE;
+    modifier->media_role = pa_xstrdup(sub);
+
+    for (i=0; i<modifier->n_suppdev; i++)
+    {
+        add_media_role(modifier->supported_devices[i],
+                list, PA_PROP_DEVICE_INTENDED_ROLES, sub, is_sink);
+    }
+/*
+    for (i=0; i<modifier->n_confdev; i++)
+    {
+        add_media_role(modifier->conflicting_devices[i],
+                list, PA_PROP_DEVICE_DENYED_ROLES, sub, is_sink);
+    }
+*/
+}
+
+static void append_me_to_device(pa_alsa_ucm_device *devices,
+        const char *dev_name, pa_alsa_ucm_device *me, const char
*my_name, int is_conflicting) {
+    pa_alsa_ucm_device *d;
+    char ***pdevices;
+    int *pnum;
+    PA_LLIST_FOREACH(d, devices) {
+        const char *name = pa_proplist_gets(d->proplist, PA_PROP_UCM_NAME);
+        if (!strcmp(name, dev_name)) {
+            pdevices = is_conflicting ? &d->conflicting_devices :
&d->supported_devices;
+            pnum = is_conflicting ? &d->n_confdev : &d->n_suppdev;
+            if (!ucm_device_in(*pdevices, *pnum, me)) {
+                /* append my name */
+                *pdevices = pa_xrealloc(*pdevices, sizeof(char *) * (*pnum+2));
+                (*pdevices)[*pnum] = pa_xstrdup(my_name);
+                (*pdevices)[*pnum+1] = NULL;
+                (*pnum)++;
+                pa_log_info("== Device %s complemented to %s's %s list",
+                        my_name, name, is_conflicting ? "conflicting"
: "supported");
+            }
+            break;
+        }
+    }
+}
+
+static void append_lost_relationship(pa_alsa_ucm_device *devices,
+        pa_alsa_ucm_device *dev, const char *dev_name) {
+    int i;
+    for (i=0; i<dev->n_confdev; i++) {
+        append_me_to_device(devices, dev->conflicting_devices[i],
dev, dev_name, 1);
+    }
+    for (i=0; i<dev->n_suppdev; i++) {
+        append_me_to_device(devices, dev->supported_devices[i], dev,
dev_name, 0);
+    }
+}
+
+int ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name,
+        const char *verb_desc, struct pa_alsa_ucm_verb **p_verb) {
+    struct pa_alsa_ucm_device *d;
+    struct pa_alsa_ucm_modifier *mod;
+    struct pa_alsa_ucm_verb *verb;
+    int err=0;
+
+    *p_verb = NULL;
+    pa_log_info("ucm_get_verb: 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_PROP_UCM_NAME, pa_strnull(verb_name));
+    pa_proplist_sets(verb->proplist, PA_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_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) {
+        const char *dev_name = pa_proplist_gets(d->proplist, PA_PROP_UCM_NAME);
+        append_lost_relationship(verb->devices, d, dev_name);
+    }
+
+    PA_LLIST_FOREACH(mod, verb->modifiers) {
+        const char *mod_name = pa_proplist_gets(mod->proplist,
PA_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_info("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_mapping *mapping,
+        int is_sink, int *dev_indices, int num, pa_hashmap *ports,
pa_card_profile *cp) {
+    pa_device_port *port;
+    int i;
+    unsigned priority;
+    char *name, *desc;
+    const char *dev_name;
+    const char *direction;
+    pa_alsa_ucm_device *dev;
+
+    dev = mapping->ucm_context.ucm_devices[dev_indices[0]];
+    dev_name = pa_proplist_gets(dev->proplist, PA_PROP_UCM_NAME);
+    name = pa_xstrdup(dev_name);
+    desc = num == 1 ? pa_xstrdup(pa_proplist_gets(dev->proplist,
PA_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 = mapping->ucm_context.ucm_devices[dev_indices[i]];
+        dev_name = pa_proplist_gets(dev->proplist, PA_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 it true? */
+        priority += (is_sink ? dev->playback_priority : dev->capture_priority);
+    }
+
+    port = pa_hashmap_get(ports, name);
+    if (!port) {
+        port = pa_device_port_new(pa_strna(name), desc,
sizeof(pa_alsa_port_data_ucm));
+        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);
+        /* TODO: jack detection */
+    }
+    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_device_contain(pa_alsa_mapping *mapping, int *dev_indices,
+        int dev_num, const char *device_name) {
+    int i;
+    const char *dev_name;
+    pa_alsa_ucm_device *dev;
+
+    for (i=0; i<dev_num; i++)
+    {
+        dev = mapping->ucm_context.ucm_devices[dev_indices[i]];
+        dev_name = pa_proplist_gets(dev->proplist, PA_PROP_UCM_NAME);
+        if (!strcmp(dev_name, device_name))
+            return 1;
+    }
+
+    return 0;
+}
+
+static int ucm_port_contain(const char *port_name, const char *dev_name) {
+    int ret=0;
+    char *r;
+    const char *state=NULL;
+    while ((r = pa_split(port_name, "+", &state))) {
+        if (!strcmp(r, dev_name)) {
+            pa_xfree(r);
+            ret = 1;
+            break;
+        }
+        pa_xfree(r);
+    }
+    return ret;
+}
+
+static int ucm_check_conformance(pa_alsa_mapping *mapping, int *dev_indices,
+        int dev_num, int map_index) {
+    int i;
+    pa_alsa_ucm_device *dev = mapping->ucm_context.ucm_devices[map_index];
+
+    pa_log_debug("Check device %s conformance with %d other devices",
+            pa_proplist_gets(dev->proplist, PA_PROP_UCM_NAME), dev_num);
+    if (dev_num == 0) {
+        pa_log_debug("First device in combination, number 1");
+        return 1;
+    }
+
+    if (dev->n_confdev > 0)
+    { /* the device defines conflicting devices */
+        for (i=0; i<dev->n_confdev; i++)
+        {
+            if (ucm_device_contain(mapping, dev_indices,
+                        dev_num, dev->conflicting_devices[i])) {
+                pa_log_debug("Conflicting device found");
+                return 0;
+            }
+        }
+    }
+    else if (dev->n_suppdev >= dev_num)
+    { /* the device defines supported devices */
+        for (i=0; i<dev_num; i++)
+        {
+            if (!ucm_device_in(dev->supported_devices,
+                dev->n_suppdev,
mapping->ucm_context.ucm_devices[dev_indices[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;
+}
+
+void ucm_add_ports_combination(pa_hashmap *hash,
+        pa_alsa_mapping *mapping, int is_sink, int *dev_indices, int dev_num,
+        int map_index, pa_hashmap *ports, pa_card_profile *cp) {
+
+    if (map_index >= mapping->ucm_context.ucm_devices_num)
+        return;
+
+    /* check if device at map_index can combine with existing devices
combination */
+    if (ucm_check_conformance(mapping, dev_indices, dev_num, map_index))
+    {
+        /* add device at map_index to devices combination */
+        dev_indices[dev_num] = map_index;
+        /* add current devices combination as a new port */
+        ucm_add_port_combination(hash, mapping, is_sink, dev_indices,
dev_num+1, ports, cp);
+        /* try more elements combination */
+        ucm_add_ports_combination(hash, mapping, is_sink,
dev_indices, dev_num+1, map_index+1, ports, cp);
+    }
+    /* try other device with current elements number */
+    ucm_add_ports_combination(hash, mapping, is_sink, dev_indices,
dev_num, map_index+1, ports, cp);
+}
+
+static char* merge_roles(const char *cur, const char *add) {
+    char *r, *ret;
+    const char *state=NULL;
+
+    ret = pa_xstrdup(cur);
+
+    if (add == NULL)
+        return ret;
+
+    while ((r = pa_split(add, ",", &state))) {
+        char *value;
+        if (!ret)
+            value = pa_xstrdup(r);
+        else if (!role_match (cur, 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 ucm_add_ports(pa_hashmap **p, pa_proplist *proplist,
pa_alsa_mapping *mapping, int is_sink, pa_card *card) {
+    int *dev_indices = pa_xnew(int, mapping->ucm_context.ucm_devices_num);
+    int i;
+    char *merged_roles;
+
+    pa_assert(p);
+    pa_assert(!*p);
+    pa_assert(mapping->ucm_context.ucm_devices_num > 0);
+
+    /* add ports first */
+    *p = pa_hashmap_new(pa_idxset_string_hash_func,
pa_idxset_string_compare_func);
+    ucm_add_ports_combination(*p, mapping, is_sink, dev_indices, 0,
0, card->ports, NULL);
+    pa_xfree(dev_indices);
+
+    /* then set property PA_PROP_DEVICE_INTENDED_ROLES */
+    merged_roles = pa_xstrdup(pa_proplist_gets(proplist,
PA_PROP_DEVICE_INTENDED_ROLES));
+    for (i=0; i<mapping->ucm_context.ucm_devices_num; i++)
+    {
+        const char *roles = pa_proplist_gets(
+                mapping->ucm_context.ucm_devices[i]->proplist,
PA_PROP_DEVICE_INTENDED_ROLES);
+        char *tmp;
+        tmp = merge_roles(merged_roles, roles);
+        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",
mapping->device_strings[0], pa_strnull(merged_roles));
+    pa_xfree(merged_roles);
+}
+
+/* Change UCM verb and device to match selected card profile */
+int ucm_set_profile(struct 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 (strcmp(new_profile, old_profile) != 0)
+        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_PROP_UCM_NAME);
+        if (!strcmp(verb_name, profile)) {
+            ucm->active_verb = verb;
+            break;
+        }
+    }
+
+    return ret;
+}
+
+int ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port
*port, int is_sink) {
+    int i, ret=0;
+    pa_alsa_ucm_config *ucm;
+    char *enable_devs=NULL;
+    char *r;
+    const char *state=NULL;
+
+    pa_assert(context && context->ucm);
+
+    ucm = context->ucm;
+    pa_assert(ucm->ucm_mgr);
+
+    /* first disable then enable */
+    for (i=0; i<context->ucm_devices_num; i++)
+    {
+        const char *dev_name =
pa_proplist_gets(context->ucm_devices[i]->proplist, PA_PROP_UCM_NAME);
+        if (ucm_port_contain(port->name, dev_name))
+        {
+            char *tmp = enable_devs ? pa_sprintf_malloc("%s,%s",
+                    enable_devs, dev_name) : pa_xstrdup(dev_name);
+            pa_xfree(enable_devs);
+            enable_devs = tmp;
+        }
+        else
+        {
+            pa_log_info("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;
+            }
+        }
+    }
+    if (enable_devs) {
+        while ((r = pa_split(enable_devs, ",", &state))) {
+            pa_log_info("Enable ucm device %s", r);
+            if (snd_use_case_set(ucm->ucm_mgr, "_enadev", r) < 0) {
+                pa_log("failed to enable ucm device %s", r);
+                pa_xfree(r);
+                ret = -1;
+                break;
+            }
+            pa_xfree(r);
+        }
+        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;
+
+    /* we expand 8 entries each time */
+    if ((m->ucm_context.ucm_devices_num & 7) == 0)
+        m->ucm_context.ucm_devices = pa_xrealloc(m->ucm_context.ucm_devices,
+                sizeof(pa_alsa_ucm_device *) *
(m->ucm_context.ucm_devices_num + 8));
+    m->ucm_context.ucm_devices[m->ucm_context.ucm_devices_num++] = device;
+
+    new_desc = pa_proplist_gets(device->proplist, PA_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("");
+}
+
+static int ucm_create_mapping_direction(struct pa_alsa_ucm_config *ucm,
+        pa_alsa_profile_set *ps, struct pa_alsa_profile *p,
+        struct pa_alsa_ucm_device *device, const char *verb_name,
+        const char *device_name, const char *device_str, int 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 = 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 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_num == 0)
+    {   /* new mapping */
+        m->supported = TRUE;
+        m->ucm_context.ucm = ucm;
+
+        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(struct pa_alsa_ucm_config *ucm,
+        pa_alsa_profile_set *ps, struct pa_alsa_profile *p,
+        struct 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, 1);
+    if (ret == 0 && source)
+        ret = ucm_create_mapping_direction(ucm, ps, p, device,
verb_name, device_name, source, 0);
+
+    return ret;
+}
+
+static int ucm_create_profile(struct pa_alsa_ucm_config *ucm,
pa_alsa_profile_set *ps,
+        struct pa_alsa_ucm_verb *verb, const char *verb_name, const
char *verb_desc) {
+    struct pa_alsa_profile *p;
+    struct pa_alsa_ucm_device *dev;
+    int i=0;
+
+    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);
+
+    ps->probed = TRUE;
+    p->supported = 1;
+    pa_hashmap_put(ps->profiles, p->name, p);
+
+    /* TODO: get profile priority from ucm info or policy management */
+    do {
+        char *verb_cmp = pa_xstrdup(verb_name);
+        char *c = verb_cmp;
+        while (*c) {
+            if (*c == '_') *c = ' ';
+            c++;
+        }
+        if (strcasecmp(verb_info[i].id, verb_cmp) == 0)
+        {
+            p->priority = verb_info[i].priority;
+            pa_xfree(verb_cmp);
+            break;
+        }
+        pa_xfree(verb_cmp);
+    } while (verb_info[++i].id);
+
+    if (verb_info[++i].id == NULL)
+        p->priority = 1000;
+
+    PA_LLIST_FOREACH(dev, verb->devices) {
+        const char *dev_name, *sink, *source;
+
+        dev_name = pa_proplist_gets(dev->proplist, PA_PROP_UCM_NAME);
+
+        sink = pa_proplist_gets(dev->proplist, PA_PROP_UCM_SINK);
+        source = pa_proplist_gets(dev->proplist, PA_PROP_UCM_SOURCE);
+
+        ucm_create_mapping(ucm, ps, p, dev, verb_name, dev_name, sink, source);
+    }
+    pa_alsa_profile_dump(p);
+
+    return 0;
+}
+
+pa_alsa_profile_set* add_ucm_profile_set(struct pa_alsa_ucm_config
*ucm, pa_channel_map *default_channel_map) {
+    struct 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_PROP_UCM_NAME);
+        verb_desc = pa_proplist_gets(verb->proplist, PA_PROP_UCM_DESCRIPTION);
+        if (verb_name == NULL) {
+            pa_log("verb with no name");
+            continue;
+        }
+
+	    ucm_create_profile(ucm, ps, verb, verb_name, verb_desc);
+    }
+
+    return ps;
+}
+
+void free_verb(struct pa_alsa_ucm_verb *verb) {
+    struct pa_alsa_ucm_device *di, *dn;
+    struct 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->n_suppdev > 0)
+            pa_xstrfreev(di->supported_devices);
+        if (di->n_confdev > 0)
+            pa_xstrfreev(di->conflicting_devices);
+        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 free_ucm(struct pa_alsa_ucm_config *ucm) {
+    struct 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 ucm_new_stream_role(pa_alsa_ucm_config *ucm,
+        const char *role, int is_sink) {
+    struct pa_alsa_ucm_modifier *mod;
+
+    if (!ucm->active_verb)
+        return;
+
+    PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) {
+        if (((mod->action_direct == PA_ALSA_UCM_DIRECT_SINK && is_sink) ||
+            (mod->action_direct == PA_ALSA_UCM_DIRECT_SOURCE && !is_sink)) &&
+            (!strcasecmp(mod->media_role, role))) {
+            const char *mod_name = pa_proplist_gets(mod->proplist,
PA_PROP_UCM_NAME);
+            pa_log_info("Enable ucm modifiers %s", mod_name);
+            if (snd_use_case_set(ucm->ucm_mgr, "_enamod", mod_name) < 0) {
+                pa_log("failed to enable ucm modifier %s", mod_name);
+            }
+            break;
+        }
+    }
+}
+
+void ucm_del_stream_role(pa_alsa_ucm_config *ucm,
+        const char *role, int is_sink) {
+    struct pa_alsa_ucm_modifier *mod;
+
+    if (!ucm->active_verb)
+        return;
+
+    PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) {
+        if (((mod->action_direct == PA_ALSA_UCM_DIRECT_SINK && is_sink) ||
+            (mod->action_direct == PA_ALSA_UCM_DIRECT_SOURCE && !is_sink)) &&
+            (!strcasecmp(mod->media_role, role))) {
+            const char *mod_name = pa_proplist_gets(mod->proplist,
PA_PROP_UCM_NAME);
+            pa_log_info("Disable ucm modifiers %s", mod_name);
+            if (snd_use_case_set(ucm->ucm_mgr, "_dismod", mod_name) < 0) {
+                pa_log("failed to disable ucm modifier %s", mod_name);
+            }
+            break;
+        }
+    }
+}
Index: pulseaudio-1.1/src/modules/alsa/alsa-ucm.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ pulseaudio-1.1/src/modules/alsa/alsa-ucm.h	2012-02-17
13:19:57.948542572 +0800
@@ -0,0 +1,106 @@
+#ifndef foopulseucmhfoo
+#define foopulseucmhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2011 Wolfson Microelectronics PLC
+  Author Margarita Olaya <magi at slimlogic.co.uk>
+
+  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 <asoundlib.h>
+#include <use-case.h>
+
+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;
+typedef struct pa_alsa_port_data_ucm pa_alsa_port_data_ucm;
+
+int ucm_set_profile(struct pa_alsa_ucm_config *ucm, const char
*new_profile, const char *old_profile);
+void free_ucm(struct pa_alsa_ucm_config *ucm);
+void free_verb(struct pa_alsa_ucm_verb *verb);
+pa_alsa_profile_set* add_ucm_profile_set(struct pa_alsa_ucm_config
*ucm, pa_channel_map *default_channel_map);
+int ucm_get_property(struct pa_alsa_ucm_verb *verb,
snd_use_case_mgr_t *uc_mgr, const char *verb_name);
+int ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name,
const char *verb_desc, struct pa_alsa_ucm_verb ** p_verb);
+void ucm_add_ports(pa_hashmap **p, pa_proplist *proplist,
pa_alsa_mapping *mapping, int is_sink, pa_card *card);
+void ucm_add_ports_combination(pa_hashmap *hash, pa_alsa_mapping
*mapping, int is_sink, int *dev_indices,
+        int dev_num, int map_index, pa_hashmap *ports, pa_card_profile *cp);
+int ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port
*port, int is_sink);
+void ucm_new_stream_role(pa_alsa_ucm_config *ucm, const char *role,
int is_sink);
+void ucm_del_stream_role(pa_alsa_ucm_config *ucm, const char *role,
int is_sink);
+
+/* UCM modifier action direction */
+enum {
+    PA_ALSA_UCM_DIRECT_NONE = 0,
+    PA_ALSA_UCM_DIRECT_SINK,
+    PA_ALSA_UCM_DIRECT_SOURCE
+};
+
+/* 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;
+    int n_confdev;
+    int n_suppdev;
+    char **conflicting_devices;
+    char **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;
+    int action_direct;
+    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 {
+    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;
+    int ucm_devices_num;
+    pa_alsa_ucm_device **ucm_devices;
+};
+
+struct pa_alsa_port_data_ucm {
+    int dummy;
+};
+
+#endif
Index: pulseaudio-1.1/src/modules/alsa/module-alsa-card.c
===================================================================
--- pulseaudio-1.1.orig/src/modules/alsa/module-alsa-card.c	2012-02-17
13:19:52.616542570 +0800
+++ pulseaudio-1.1/src/modules/alsa/module-alsa-card.c	2012-02-17
13:19:57.960542572 +0800
@@ -39,6 +39,7 @@
 #include "alsa-util.h"
 #include "alsa-sink.h"
 #include "alsa-source.h"
+#include "alsa-ucm.h"
 #include "module-alsa-card-symdef.h"
 #include "alsa-jack-inputdev.h"

@@ -67,7 +68,8 @@
         "profile=<profile name> "
         "ignore_dB=<ignore dB information from the device?> "
         "deferred_volume=<Synchronize software and hardware volume
changes to avoid momentary jumps?> "
-        "profile_set=<profile set configuration file> ");
+        "profile_set=<profile set configuration file> "
+        "use_ucm=<Load Use Case Manager> ");

 static const char* const valid_modargs[] = {
     "name",
@@ -91,11 +93,20 @@
     "ignore_dB",
     "deferred_volume",
     "profile_set",
+    "use_ucm",
     NULL
 };

 #define DEFAULT_DEVICE_ID "0"

+typedef struct pa_media_role_count pa_media_role_count;
+
+struct pa_media_role_count {
+    PA_LLIST_FIELDS(pa_media_role_count);
+    char *role;
+    int   num;
+};
+
 struct userdata {
     pa_core *core;
     pa_module *module;
@@ -107,6 +118,21 @@
     pa_modargs *modargs;

     pa_alsa_profile_set *profile_set;
+
+    /* ucm stuffs */
+    pa_bool_t use_ucm;
+    pa_alsa_ucm_config ucm;
+
+    /* hooks for modifier */
+    pa_hook_slot
+        *sink_input_put_hook_slot,
+        *sink_input_unlink_hook_slot,
+        *source_output_put_hook_slot,
+        *source_output_unlink_hook_slot;
+
+    /* hashmap records numbers of certain media role */
+    PA_LLIST_HEAD(pa_media_role_count, sink_role_counts);
+    PA_LLIST_HEAD(pa_media_role_count, source_role_counts);
 };

 struct profile_data {
@@ -116,6 +142,7 @@
 static void add_profiles(struct userdata *u, pa_hashmap *h,
pa_hashmap *ports) {
     pa_alsa_profile *ap;
     void *state;
+    int *dev_indices;

     pa_assert(u);
     pa_assert(h);
@@ -133,7 +160,13 @@
             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);
+                if (u->use_ucm) {
+                    dev_indices = pa_xnew(int, m->ucm_context.ucm_devices_num);
+                    ucm_add_ports_combination(NULL, m, 1,
dev_indices, 0, 0, ports, cp);
+                    pa_xfree(dev_indices);
+                }
+                else
+                    pa_alsa_path_set_add_ports(m->output_path_set,
cp, ports, NULL);
                 if (m->channel_map.channels > cp->max_sink_channels)
                     cp->max_sink_channels = m->channel_map.channels;
             }
@@ -143,7 +176,13 @@
             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);
+                if (u->use_ucm) {
+                    dev_indices = pa_xnew(int, m->ucm_context.ucm_devices_num);
+                    ucm_add_ports_combination(NULL, m, 0,
dev_indices, 0, 0, ports, cp);
+                    pa_xfree(dev_indices);
+                }
+                else
+                    pa_alsa_path_set_add_ports(m->input_path_set, cp,
ports, NULL);
                 if (m->channel_map.channels > cp->max_source_channels)
                     cp->max_source_channels = m->channel_map.channels;
             }
@@ -212,6 +251,24 @@
             am->source = NULL;
         }

+    /* if UCM is available for this card then update the verb */
+    if (u->use_ucm) {
+        pa_media_role_count *item;
+        if (ucm_set_profile(&u->ucm, nd->profile ? nd->profile->name : NULL,
+                    od->profile ? od->profile->name : NULL) < 0)
+            return -1;
+        /*
+         * enable modifier matching the role in new profile,
+         * modifier in old profile was automaticly disabled
+         */
+        PA_LLIST_FOREACH(item, u->sink_role_counts) {
+            ucm_new_stream_role(&u->ucm, item->role, 1);
+        }
+        PA_LLIST_FOREACH(item, u->source_role_counts) {
+            ucm_new_stream_role(&u->ucm, item->role, 0);
+        }
+    }
+
     if (nd->profile && nd->profile->output_mappings)
         PA_IDXSET_FOREACH(am, nd->profile->output_mappings, idx) {

@@ -236,6 +293,11 @@
             }
         }

+    /* TODO: route policy
+     * re-route the sink-inputs/source-outputs to new sinks/sources,
+     * take modifier's device into consideration
+     */
+
     if (sink_inputs)
         pa_sink_move_all_fail(sink_inputs);

@@ -249,11 +311,23 @@
     uint32_t idx;
     pa_alsa_mapping *am;
     struct profile_data *d;
+    struct pa_alsa_ucm_config *ucm = &u->ucm;

     pa_assert(u);

     d = PA_CARD_PROFILE_DATA(u->card->active_profile);

+    if (!d || !d->profile)
+        return;
+
+    if (u->use_ucm) {
+        /* Set initial verb */
+        if (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);
@@ -289,6 +363,197 @@
     pa_xfree(t);
 }

+static int card_query_ucm_profiles(struct userdata *u, int card_index)
+{
+    char *card_name;
+    const char **verb_list;
+    int num_verbs, i, err=0;
+
+    /* is UCM available for this card ? */
+    if(snd_card_get_name(card_index, &card_name) < 0)
+    {
+        pa_log("Card can't get card_name from card_index %d", card_index);
+        err = -1;
+        goto name_fail;
+    }
+    err = snd_use_case_mgr_open(&u->ucm.ucm_mgr, card_name);
+    if (err < 0) {
+        pa_log("UCM not available for card %s", card_name);
+        err = -1;
+        goto ucm_mgr_fail;
+    }
+
+    pa_log("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(u->ucm.ucm_mgr, &verb_list);
+    if (num_verbs <= 0) {
+        pa_log("UCM verb list not found for %s", card_name);
+        err = -1;
+        goto ucm_verb_fail;
+    }
+
+    /* get the properties of each UCM verb */
+    for (i = 0; i < num_verbs; i += 2) {
+        struct pa_alsa_ucm_verb *verb;
+
+        /* Get devices and modifiers for each verb */
+        err = ucm_get_verb(u->ucm.ucm_mgr, verb_list[i],
verb_list[i+1], &verb);
+        if (err < 0) {
+            pa_log("Failed to set the verb %s", verb_list[i]);
+            continue;
+        }
+        PA_LLIST_PREPEND(pa_alsa_ucm_verb, u->ucm.verbs, verb);
+    }
+
+    if(u->ucm.verbs)
+    {
+        /* create the profile set for the UCM card */
+        u->profile_set = add_ucm_profile_set(&u->ucm,
&u->core->default_channel_map);
+        pa_alsa_profile_set_dump(u->profile_set);
+        err = 0;
+    }
+    else
+    {
+        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(u->ucm.ucm_mgr);
+        u->ucm.ucm_mgr = NULL;
+    }
+ucm_mgr_fail:
+    free(card_name);
+name_fail:
+    return err;
+}
+
+static int add_role_number (pa_media_role_count **head, const char *role) {
+
+    pa_media_role_count *item;
+
+    PA_LLIST_FOREACH(item, *head) {
+        if (!strcasecmp(role, item->role)) {
+            item->num++;
+            return item->num;
+        }
+    }
+
+    /* not found */
+    item = pa_xnew0(pa_media_role_count, 1);
+    item->role = pa_xstrdup(role);
+    item->num = 1;
+
+    if ((item->next = *head))
+        item->next->prev = item;
+    *head = item;
+
+    return item->num;
+}
+
+static int minus_role_number (pa_media_role_count **head, const char *role) {
+
+    pa_media_role_count *item;
+    int num;
+
+    PA_LLIST_FOREACH(item, *head) {
+        if (!strcasecmp(role, item->role)) {
+            item->num--;
+            break;
+        }
+    }
+
+    pa_assert (item);
+
+    num = item->num;
+    if (num == 0) { /* last one */
+        if (item->next)
+            item->next->prev = item->prev;
+        if (item->prev)
+            item->prev->next = item->next;
+        else {
+            *head = item->next;
+        }
+        pa_xfree(item->role);
+        pa_xfree(item);
+    }
+
+    return num;
+}
+
+static pa_hook_result_t sink_input_put_hook_callback(
+        pa_core *c, pa_sink_input *sink_input, struct userdata *u) {
+
+    const char *role = pa_proplist_gets(sink_input->proplist,
PA_PROP_MEDIA_ROLE);
+
+    /* FIXME: check if sink_input link to our card? */
+    if (role)
+    {
+        int num = add_role_number(&u->sink_role_counts, role);
+        if (num == 1) { /* first stream of certain role */
+            /* enable modifier matching the role */
+            ucm_new_stream_role(&u->ucm, role, 1);
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_unlink_hook_callback(pa_core *c,
pa_sink_input *sink_input, struct userdata *u) {
+
+    const char *role = pa_proplist_gets(sink_input->proplist,
PA_PROP_MEDIA_ROLE);
+
+    /* FIXME: check if sink_input link to our card? */
+    if (role)
+    {
+        int num = minus_role_number(&u->sink_role_counts, role);
+        if (num == 0) { /* last stream of certain role */
+            /* enable modifier matching the role */
+            ucm_del_stream_role(&u->ucm, role, TRUE);
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_put_hook_callback(
+        pa_core *c, pa_source_output *source_output, struct userdata *u) {
+
+    const char *role = pa_proplist_gets(source_output->proplist,
PA_PROP_MEDIA_ROLE);
+
+    /* FIXME: check if source_output link to our card? */
+    if (role)
+    {
+        int num = add_role_number(&u->source_role_counts, role);
+        if (num == 1) { /* first stream of certain role */
+            /* enable modifier matching the role */
+            ucm_new_stream_role(&u->ucm, role, 0);
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_unlink_hook_callback(pa_core
*c, pa_source_output *source_output, struct userdata *u) {
+
+    const char *role = pa_proplist_gets(source_output->proplist,
PA_PROP_MEDIA_ROLE);
+
+    /* FIXME: check if source_output link to our card? */
+    if (role)
+    {
+        int num = minus_role_number(&u->source_role_counts, role);
+        if (num == 0) { /* last stream of certain role */
+            /* enable modifier matching the role */
+            ucm_del_stream_role(&u->ucm, role, FALSE);
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
 int pa__init(pa_module *m) {
     pa_card_new_data data;
     pa_modargs *ma;
@@ -337,18 +602,39 @@
         }
     }

+    pa_modargs_get_value_boolean(ma, "use_ucm", &u->use_ucm);
+    if (u->use_ucm && !card_query_ucm_profiles(u, alsa_card_index)) {
+        pa_log_info("Found UCM profiles");
+        /* hook sink input/source output to enable/disable modifiers */
+        u->sink_input_put_hook_slot = pa_hook_connect(
+                &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_EARLY,
+                (pa_hook_cb_t) sink_input_put_hook_callback, u);
+        u->sink_input_unlink_hook_slot = pa_hook_connect(
+                &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK],
+                PA_HOOK_LATE, (pa_hook_cb_t)
sink_input_unlink_hook_callback, u);
+        u->source_output_put_hook_slot = pa_hook_connect(
+                &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], PA_HOOK_EARLY,
+                (pa_hook_cb_t) source_output_put_hook_callback, u);
+        u->source_output_unlink_hook_slot = pa_hook_connect(
+                &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK],
+                PA_HOOK_LATE, (pa_hook_cb_t)
source_output_unlink_hook_callback, u);
+    }
+    else {
+        u->use_ucm = FALSE;
+
 #ifdef HAVE_UDEV
-    fn = pa_udev_get_property(alsa_card_index, "PULSE_PROFILE_SET");
+        fn = pa_udev_get_property(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);
-
     if (!u->profile_set)
         goto fail;

@@ -456,12 +742,37 @@

 void pa__done(pa_module*m) {
     struct userdata *u;
+    pa_media_role_count *item;

     pa_assert(m);

     if (!(u = m->userdata))
         goto finish;

+    if (u->sink_input_put_hook_slot)
+        pa_hook_slot_free(u->sink_input_put_hook_slot);
+
+    if (u->sink_input_unlink_hook_slot)
+        pa_hook_slot_free(u->sink_input_unlink_hook_slot);
+
+    if (u->source_output_put_hook_slot)
+        pa_hook_slot_free(u->source_output_put_hook_slot);
+
+    if (u->source_output_unlink_hook_slot)
+        pa_hook_slot_free(u->source_output_unlink_hook_slot);
+
+    while ((item = u->sink_role_counts)) {
+        PA_LLIST_REMOVE(pa_media_role_count, u->sink_role_counts, item);
+        pa_xfree(item->role);
+        pa_xfree(item);
+    }
+
+    while ((item = u->source_role_counts)) {
+        PA_LLIST_REMOVE(pa_media_role_count, u->source_role_counts, item);
+        pa_xfree(item->role);
+        pa_xfree(item);
+    }
+
     if (u->card && u->card->sinks) {
         pa_sink *s;

@@ -481,6 +792,8 @@
         u->profile_set->jack_inputdevs = NULL;
     }

+    free_ucm(&u->ucm);
+
     if (u->card)
         pa_card_free(u->card);

Index: pulseaudio-1.1/src/modules/module-udev-detect.c
===================================================================
--- pulseaudio-1.1.orig/src/modules/module-udev-detect.c	2012-02-17
13:11:50.688542312 +0800
+++ pulseaudio-1.1/src/modules/module-udev-detect.c	2012-02-17
13:19:52.660542570 +0800
@@ -390,6 +390,7 @@
                                 "tsched=%s "
                                 "ignore_dB=%s "
                                 "deferred_volume=%s "
+                                "use_ucm=1 "

"card_properties=\"module-udev-detect.discovered=1\"",
                                 path_get_card_id(path),
                                 n,
Index: pulseaudio-1.1/src/pulse/proplist.h
===================================================================
--- pulseaudio-1.1.orig/src/pulse/proplist.h	2012-02-17 13:11:50.780542312 +0800
+++ pulseaudio-1.1/src/pulse/proplist.h	2012-02-17 13:19:52.660542570 +0800
@@ -254,6 +254,45 @@
 /** For modules: a version string for the module. e.g. "0.9.15" */
 #define PA_PROP_MODULE_VERSION                 "module.version"

+/** For devices: List of verbs, devices or modifiers availables */
+#define PA_PROP_UCM_NAME                       "ucm.name"
+
+/** For devices: List of supported devices per verb*/
+#define PA_PROP_UCM_DESCRIPTION                "ucm.description"
+
+/** For devices: Playback device name e.g PlaybackPCM */
+#define PA_PROP_UCM_SINK                       "ucm.sink"
+
+/** For devices: Capture device name e.g CapturePCM*/
+#define PA_PROP_UCM_SOURCE                     "ucm.source"
+
+/** For devices: Playback control volume ID string. e.g PlaybackVolume */
+#define PA_PROP_UCM_PLAYBACK_VOLUME            "ucm.playback.volume"
+
+/** For devices: Playback switch e.g PlaybackSwitch */
+#define PA_PROP_UCM_PLAYBACK_SWITCH            "ucm.playback.switch"
+
+/** For devices: Playback priority */
+#define PA_PROP_UCM_PLAYBACK_PRIORITY          "ucm.playback.priority"
+
+/** For devices: Playback channels */
+#define PA_PROP_UCM_PLAYBACK_CHANNELS          "ucm.playback.channels"
+
+/** For devices: Capture controls volume ID string. e.g CaptureVolume */
+#define PA_PROP_UCM_CAPTURE_VOLUME             "ucm.capture.volume"
+
+/** For devices: Capture switch e.g CaptureSwitch */
+#define PA_PROP_UCM_CAPTURE_SWITCH             "ucm.capture.switch"
+
+/** For devices: Capture priority */
+#define PA_PROP_UCM_CAPTURE_PRIORITY           "ucm.capture.priority"
+
+/** For devices: Capture channels */
+#define PA_PROP_UCM_CAPTURE_CHANNELS           "ucm.capture.channels"
+
+/** For devices: Quality of Service */
+#define PA_PROP_UCM_QOS                        "ucm.qos"
+
 /** For PCM formats: the sample format used as returned by
pa_sample_format_to_string() \since 1.0 */
 #define PA_PROP_FORMAT_SAMPLE_FORMAT           "format.sample_format"


-- 
Wei.Feng (irc wei_feng)
Linaro Multimedia Team
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog


More information about the pulseaudio-discuss mailing list