[pulseaudio-discuss] [RFC Patch 3/3] Support UCM in Pulseaudio

Feng Wei feng.wei at linaro.org
Tue Nov 15 19:53:30 PST 2011


add ucm branches in module alsa card

diff --git a/src/modules/alsa/module-alsa-card.c
b/src/modules/alsa/module-alsa-card.c
index 6d1a5e1..efde01d 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -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"

 PA_MODULE_AUTHOR("Lennart Poettering");
@@ -66,7 +67,8 @@ PA_MODULE_USAGE(
         "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",
@@ -90,11 +92,20 @@ static const char* const valid_modargs[] = {
     "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;
@@ -106,6 +117,21 @@ struct userdata {
     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 {
@@ -207,6 +233,24 @@ static int card_set_profile(pa_card *c,
pa_card_profile *new_profile) {
             am->source = NULL;
         }

+    /* if UCM is available for this card then update the verb */
+    if (u->use_ucm) {
+        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, TRUE);
+        }
+        PA_LLIST_FOREACH(item, u->source_role_counts) {
+            ucm_new_stream_role(&u->ucm, item->role, FALSE);
+        }
+    }
+
     if (nd->profile && nd->profile->output_mappings)
         PA_IDXSET_FOREACH(am, nd->profile->output_mappings, idx) {

@@ -231,6 +275,11 @@ static int card_set_profile(pa_card *c,
pa_card_profile *new_profile) {
             }
         }

+    /* TODO
+     * re-route the sink-inputs/source-outputs to new sinks/sources
+     * new priority-list mechanism. maybe not constrained in this card
+     */
+
     if (sink_inputs)
         pa_sink_move_all_fail(sink_inputs);

@@ -244,11 +293,23 @@ static void init_profile(struct userdata *u) {
     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);
@@ -284,6 +345,197 @@ static void set_card_name(pa_card_new_data
*data, pa_modargs *ma, const char *de
     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);
+        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, TRUE);
+        }
+    }
+
+    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, FALSE);
+        }
+    }
+
+    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;
@@ -326,18 +578,38 @@ int pa__init(pa_module *m) {
         }
     }

+    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;

@@ -442,12 +714,37 @@ int pa__get_n_used(pa_module *m) {

 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;

@@ -462,6 +759,8 @@ void pa__done(pa_module*m) {
             pa_alsa_source_free(s);
     }

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

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