diff mbox

UCM patches on ubuntu 1:1.1-0ubuntu4

Message ID CAJ4+uhW3mMoJ4FkP5vPjm5FaKk+Thjjkbr0Db4ob-_y2uRibyQ@mail.gmail.com
State New
Headers show

Commit Message

Feng Wei Feb. 20, 2012, 7:34 a.m. UTC
Somewhat verified on Panda board and i.mx53 board.

Integrate UCM in alsa module

Comments

Arun Raghavan Feb. 21, 2012, 3:20 a.m. UTC | #1
Hi,

On Mon, 2012-02-20 at 15:34 +0800, Feng Wei wrote:
> Somewhat verified on Panda board and i.mx53 board.

Do you mean for these to be merged, or is it an RFC? Also, the best way
to get these actually looked at is either using git send-email or
pointing us to a git tree. Much easier to address each patch
individually, then.

Regards,
Arun
Feng Wei Feb. 21, 2012, 3:34 a.m. UTC | #2
Hi Arun,
I'm not clear what should I do to upstream patches. I tested them on
ubuntu, so that I must follow what David had done in port structure.
In my original mind, I will first upstream to ubuntu, then pulseaudio
community.
My current patches are maintained in bzr according to ubuntu, I want
them to be merged in ubuntu branch.

2012/2/21 Arun Raghavan <arun.raghavan@collabora.co.uk>:
> Hi,
>
> On Mon, 2012-02-20 at 15:34 +0800, Feng Wei wrote:
>> Somewhat verified on Panda board and i.mx53 board.
>
> Do you mean for these to be merged, or is it an RFC? Also, the best way
> to get these actually looked at is either using git send-email or
> pointing us to a git tree. Much easier to address each patch
> individually, then.
>
> Regards,
> Arun
>
> _______________________________________________
> pulseaudio-discuss mailing list
> pulseaudio-discuss@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss
David Henningsson Feb. 24, 2012, 6:17 a.m. UTC | #3
On 02/21/2012 04:34 AM, Feng Wei wrote:
> Hi Arun,
> I'm not clear what should I do to upstream patches. I tested them on
> ubuntu, so that I must follow what David had done in port structure.
> In my original mind, I will first upstream to ubuntu, then pulseaudio
> community.
> My current patches are maintained in bzr according to ubuntu, I want
> them to be merged in ubuntu branch.

Hi Feng,

Sorry I haven't responded earlier, but I'm not sure what to do about 
these patches either. As I see it there are at least three problems that 
need to be resolved:

  * The competing implementation problem: We've had multiple 
implementations posted to the PulseAudio mailinglist, one by Janos and 
Jaska, one by Alejandro and Margarita (probably discontinued?), and one 
by yourself. It would be great if the UCM community could give us a hint 
on why we should choose one over another.

  * The patches are based on an older version of PulseAudio - the one 
that uses the input devices for jack detection. Ubuntu 12.04, as well as 
PulseAudio 2.0, will release with the new kcontrol jack detection 
interface [1]. In essence, your patches do not apply, and should we 
consider these for Ubuntu 12.04 and/or PulseAudio 2.0 we're in quite a 
hurry. Perhaps it's even too late, I don't know.

  * Verification and testing is difficult, for a variety of reasons 
(this point is not a real blocker like the other two, just a little 
cumbersome):
    1) Requires special hardware. I could probably get hold of some 
hardware if that was the only thing keeping me from reviewing it though.
    2) Requires special configuration files, which are usually kept 
private by companies. I assume that the files you have used for imx53 
and omap are public though (where can they be found)?
    3) I *was* going to complain about the lack of UCM documentation, 
but it seems like running "make doc" in alsa-lib 1.0.25 does create a 
UCM page (called "case interface"), and while it does not look perfect 
everywhere it seems like most things can actually be figured out.
Feng Wei Feb. 24, 2012, 7:39 a.m. UTC | #4
Hi David,
I'm appreciated for your comments. UCM really has a long way to go.

2012/2/24 David Henningsson <david.henningsson@canonical.com>:
> On 02/21/2012 04:34 AM, Feng Wei wrote:
>>
>> Hi Arun,
>> I'm not clear what should I do to upstream patches. I tested them on
>> ubuntu, so that I must follow what David had done in port structure.
>> In my original mind, I will first upstream to ubuntu, then pulseaudio
>> community.
>> My current patches are maintained in bzr according to ubuntu, I want
>> them to be merged in ubuntu branch.
>
>
> Hi Feng,
>
> Sorry I haven't responded earlier, but I'm not sure what to do about these
> patches either. As I see it there are at least three problems that need to
> be resolved:
>
>  * The competing implementation problem: We've had multiple implementations
> posted to the PulseAudio mailinglist, one by Janos and Jaska, one by
> Alejandro and Margarita (probably discontinued?), and one by yourself. It
> would be great if the UCM community could give us a hint on why we should
> choose one over another.
Exactly. Maybe one reason is my patch is the only one to implement the
agreed concepts mapping between PA and alsa-lib.
>
>  * The patches are based on an older version of PulseAudio - the one that
> uses the input devices for jack detection. Ubuntu 12.04, as well as
> PulseAudio 2.0, will release with the new kcontrol jack detection interface
> [1]. In essence, your patches do not apply, and should we consider these for
> Ubuntu 12.04 and/or PulseAudio 2.0 we're in quite a hurry. Perhaps it's even
> too late, I don't know.
It's really too late. I'll follow the new jack detection method and
update my patches tho.
>
>  * Verification and testing is difficult, for a variety of reasons (this
> point is not a real blocker like the other two, just a little cumbersome):
>   1) Requires special hardware. I could probably get hold of some hardware
> if that was the only thing keeping me from reviewing it though.
Understandable.
>   2) Requires special configuration files, which are usually kept private by
> companies. I assume that the files you have used for imx53 and omap are
> public though (where can they be found)?
I hold the configs for linaro release at
https://code.launchpad.net/~b34248/+junk/alsa-lib-1.0.24.1
>   3) I *was* going to complain about the lack of UCM documentation, but it
> seems like running "make doc" in alsa-lib 1.0.25 does create a UCM page
> (called "case interface"), and while it does not look perfect everywhere it
> seems like most things can actually be figured out.
>
> --
> David Henningsson, Canonical Ltd.
> http://launchpad.net/~diwic
>
> [1] Assuming my recently posted patches pass the review of my fellow
> developers on this list :-)
Liam Girdwood Feb. 24, 2012, 9 a.m. UTC | #5
On Fri, 2012-02-24 at 15:39 +0800, Feng Wei wrote:
> Hi David,
> I'm appreciated for your comments. UCM really has a long way to go.
> 
> 2012/2/24 David Henningsson <david.henningsson@canonical.com>:
> > On 02/21/2012 04:34 AM, Feng Wei wrote:
> >>
> >> Hi Arun,
> >> I'm not clear what should I do to upstream patches. I tested them on
> >> ubuntu, so that I must follow what David had done in port structure.
> >> In my original mind, I will first upstream to ubuntu, then pulseaudio
> >> community.
> >> My current patches are maintained in bzr according to ubuntu, I want
> >> them to be merged in ubuntu branch.
> >
> >
> > Hi Feng,
> >
> > Sorry I haven't responded earlier, but I'm not sure what to do about these
> > patches either. As I see it there are at least three problems that need to
> > be resolved:
> >
> >  * The competing implementation problem: We've had multiple implementations
> > posted to the PulseAudio mailinglist, one by Janos and Jaska, one by
> > Alejandro and Margarita (probably discontinued?), and one by yourself. It
> > would be great if the UCM community could give us a hint on why we should
> > choose one over another.
> Exactly. Maybe one reason is my patch is the only one to implement the
> agreed concepts mapping between PA and alsa-lib.
> >

The version from Margarita was prior to the ELC-E BOF and is
discontinued. However it was the starting point for the other two
patches in that it did interface with alsa-lib as intended.

I have not seen the patch by Janos and Jaska but I have seen the patch
Feng. I could review the latest patches wrt to alsa-lib/ucm but the PA
parts would need checked by someone else.

> >  * The patches are based on an older version of PulseAudio - the one that
> > uses the input devices for jack detection. Ubuntu 12.04, as well as
> > PulseAudio 2.0, will release with the new kcontrol jack detection interface
> > [1]. In essence, your patches do not apply, and should we consider these for
> > Ubuntu 12.04 and/or PulseAudio 2.0 we're in quite a hurry. Perhaps it's even
> > too late, I don't know.
> It's really too late. I'll follow the new jack detection method and
> update my patches tho.
> >

Fwiw, we could still upstream UCM support without waiting for jack
support. It was originally done together as it was easy for testing UCM
device changes.

> >  * Verification and testing is difficult, for a variety of reasons (this
> > point is not a real blocker like the other two, just a little cumbersome):
> >   1) Requires special hardware. I could probably get hold of some hardware
> > if that was the only thing keeping me from reviewing it though.
> Understandable.

A pandaboard is probably easiest to use for testing. David, I could look
into getting one shipped to you for testing/review if this helps ?

> >   2) Requires special configuration files, which are usually kept private by
> > companies. I assume that the files you have used for imx53 and omap are
> > public though (where can they be found)?
> I hold the configs for linaro release at
> https://code.launchpad.net/~b34248/+junk/alsa-lib-1.0.24.1

I'll push to public alsa git today for Panda. I think they are quite
stable now.

> >   3) I *was* going to complain about the lack of UCM documentation, but it
> > seems like running "make doc" in alsa-lib 1.0.25 does create a UCM page
> > (called "case interface"), and while it does not look perfect everywhere it
> > seems like most things can actually be figured out.
> >

Regards

Liam
Mark Brown Feb. 24, 2012, 2:32 p.m. UTC | #6
On Fri, Feb 24, 2012 at 03:39:44PM +0800, Feng Wei wrote:
> 2012/2/24 David Henningsson <david.henningsson@canonical.com>:

> >  * The competing implementation problem: We've had multiple implementations
> > posted to the PulseAudio mailinglist, one by Janos and Jaska, one by
> > Alejandro and Margarita (probably discontinued?), and one by yourself. It
> > would be great if the UCM community could give us a hint on why we should
> > choose one over another.

> Exactly. Maybe one reason is my patch is the only one to implement the
> agreed concepts mapping between PA and alsa-lib.

At this point I think we just need something merging.  If it's
problematic we can always work on incremental fixes.  I know the one
Marga and Jorge were working on has been abandoned due as things were
taking too long and they moved on to other things, I don't know if Feng
has based his code off that or if he started from scratch yet again :/

Anyway, I think the thing to do is just to go with whatever someone
actually has the time to work on and get it in then iterate
incrementally to deal with the issues that remain (if any).
Arun Raghavan Feb. 25, 2012, 5:15 a.m. UTC | #7
On Fri, 2012-02-24 at 15:39 +0800, Feng Wei wrote:
> Hi David,
> I'm appreciated for your comments. UCM really has a long way to go.

IMO it's not that bad. While I haven't gone over the code at all, I've
used it on an OMAP4 and it does work (which is pretty neat to see!).

> 2012/2/24 David Henningsson <david.henningsson@canonical.com>:
> > On 02/21/2012 04:34 AM, Feng Wei wrote:
> >>
> >> Hi Arun,
> >> I'm not clear what should I do to upstream patches. I tested them on
> >> ubuntu, so that I must follow what David had done in port structure.
> >> In my original mind, I will first upstream to ubuntu, then pulseaudio
> >> community.
> >> My current patches are maintained in bzr according to ubuntu, I want
> >> them to be merged in ubuntu branch.
> >
> >
> > Hi Feng,
> >
> > Sorry I haven't responded earlier, but I'm not sure what to do about these
> > patches either. As I see it there are at least three problems that need to
> > be resolved:
> >
> >  * The competing implementation problem: We've had multiple implementations
> > posted to the PulseAudio mailinglist, one by Janos and Jaska, one by
> > Alejandro and Margarita (probably discontinued?), and one by yourself. It
> > would be great if the UCM community could give us a hint on why we should
> > choose one over another.
> Exactly. Maybe one reason is my patch is the only one to implement the
> agreed concepts mapping between PA and alsa-lib.

Going by Mark's comments, While it'd be nice to hear from Janos and
Jaska as well, since yours is the only actual code that is available and
being taken forward through the merging process, I think we're fine
going ahead with it.

> >  * The patches are based on an older version of PulseAudio - the one that
> > uses the input devices for jack detection. Ubuntu 12.04, as well as
> > PulseAudio 2.0, will release with the new kcontrol jack detection interface
> > [1]. In essence, your patches do not apply, and should we consider these for
> > Ubuntu 12.04 and/or PulseAudio 2.0 we're in quite a hurry. Perhaps it's even
> > too late, I don't know.
> It's really too late. I'll follow the new jack detection method and
> update my patches tho.

We're trying for much quicker releases now, so missing the 2.0 boat
really isn't catastrophic at all.

So if you could rebase and send these in a more review-friendly format
(git send-email or a git tree somewhere), that would help move this
forwards. We might have our hands full for a short while getting the
outstanding patches and bugs done for 2.0, but I'd certainly like to try
to get this in for 3.0 (i.e., the next release) if everything looks
good.

-- Arun
Feng Wei Feb. 27, 2012, 2:21 a.m. UTC | #8
2012/2/24 Mark Brown <broonie@opensource.wolfsonmicro.com>:
> On Fri, Feb 24, 2012 at 03:39:44PM +0800, Feng Wei wrote:
>> 2012/2/24 David Henningsson <david.henningsson@canonical.com>:
>
>> >  * The competing implementation problem: We've had multiple implementations
>> > posted to the PulseAudio mailinglist, one by Janos and Jaska, one by
>> > Alejandro and Margarita (probably discontinued?), and one by yourself. It
>> > would be great if the UCM community could give us a hint on why we should
>> > choose one over another.
>
>> Exactly. Maybe one reason is my patch is the only one to implement the
>> agreed concepts mapping between PA and alsa-lib.
>
> At this point I think we just need something merging.  If it's
> problematic we can always work on incremental fixes.  I know the one
> Marga and Jorge were working on has been abandoned due as things were
> taking too long and they moved on to other things, I don't know if Feng
> has based his code off that or if he started from scratch yet again :/
I based on that one but went too far from it, so you can consider it
as a new one.
>
> Anyway, I think the thing to do is just to go with whatever someone
> actually has the time to work on and get it in then iterate
> incrementally to deal with the issues that remain (if any).
Feng Wei Feb. 27, 2012, 2:24 a.m. UTC | #9
2012/2/25 Arun Raghavan <arun.raghavan@collabora.co.uk>:
> On Fri, 2012-02-24 at 15:39 +0800, Feng Wei wrote:
>> Hi David,
>> I'm appreciated for your comments. UCM really has a long way to go.
>
> IMO it's not that bad. While I haven't gone over the code at all, I've
> used it on an OMAP4 and it does work (which is pretty neat to see!).
>
>> 2012/2/24 David Henningsson <david.henningsson@canonical.com>:
>> > On 02/21/2012 04:34 AM, Feng Wei wrote:
>> >>
>> >> Hi Arun,
>> >> I'm not clear what should I do to upstream patches. I tested them on
>> >> ubuntu, so that I must follow what David had done in port structure.
>> >> In my original mind, I will first upstream to ubuntu, then pulseaudio
>> >> community.
>> >> My current patches are maintained in bzr according to ubuntu, I want
>> >> them to be merged in ubuntu branch.
>> >
>> >
>> > Hi Feng,
>> >
>> > Sorry I haven't responded earlier, but I'm not sure what to do about these
>> > patches either. As I see it there are at least three problems that need to
>> > be resolved:
>> >
>> >  * The competing implementation problem: We've had multiple implementations
>> > posted to the PulseAudio mailinglist, one by Janos and Jaska, one by
>> > Alejandro and Margarita (probably discontinued?), and one by yourself. It
>> > would be great if the UCM community could give us a hint on why we should
>> > choose one over another.
>> Exactly. Maybe one reason is my patch is the only one to implement the
>> agreed concepts mapping between PA and alsa-lib.
>
> Going by Mark's comments, While it'd be nice to hear from Janos and
> Jaska as well, since yours is the only actual code that is available and
> being taken forward through the merging process, I think we're fine
> going ahead with it.
>
>> >  * The patches are based on an older version of PulseAudio - the one that
>> > uses the input devices for jack detection. Ubuntu 12.04, as well as
>> > PulseAudio 2.0, will release with the new kcontrol jack detection interface
>> > [1]. In essence, your patches do not apply, and should we consider these for
>> > Ubuntu 12.04 and/or PulseAudio 2.0 we're in quite a hurry. Perhaps it's even
>> > too late, I don't know.
>> It's really too late. I'll follow the new jack detection method and
>> update my patches tho.
>
> We're trying for much quicker releases now, so missing the 2.0 boat
> really isn't catastrophic at all.
>
> So if you could rebase and send these in a more review-friendly format
> (git send-email or a git tree somewhere), that would help move this
> forwards. We might have our hands full for a short while getting the
> outstanding patches and bugs done for 2.0, but I'd certainly like to try
> to get this in for 3.0 (i.e., the next release) if everything looks
> good.
Surely I will make it in good shape and send the patch again.
>
> -- Arun
>
> _______________________________________________
> pulseaudio-discuss mailing list
> pulseaudio-discuss@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss
diff mbox

Patch

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@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@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"