diff mbox

[pulseaudio-discuss,RFC,v2,3/3] Add UCM modifier functions into alsa card module

Message ID 1331792816-18971-4-git-send-email-feng.wei@linaro.org
State New
Headers show

Commit Message

Feng Wei March 15, 2012, 6:26 a.m. UTC
In UCM basic functions, we only assign intended roles from modifier
to sink/source, but we don't have a chance to set the ucm modifiers.
Here we amend the functions so that when special roled stream start
or stop or moved, we have the following results:
1. stream will be routed to sink/source specified in modifier by
   module-intended-roles
2. at the same time, modifier will be enabled or disabled.
3. when multiple streams with matched roles of modifier start, only
   the first one will enable the modifier, and when they end, the
   last one will disable the modifier.
---
 src/modules/alsa/alsa-ucm.c         |   97 ++++++++++++++++++++-
 src/modules/alsa/alsa-ucm.h         |   10 ++
 src/modules/alsa/module-alsa-card.c |  165 +++++++++++++++++++++++++++++++++++
 3 files changed, 271 insertions(+), 1 deletions(-)
diff mbox

Patch

diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 6b9be62..2d299e3 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -744,6 +744,7 @@  void ucm_add_ports(pa_hashmap **p, pa_proplist *proplist,
     int *dev_indices = pa_xnew(int, context->ucm_devices_num);
     int i;
     char *merged_roles;
+    const char *role_name = is_sink ? PA_PROP_UCM_PLAYBACK_ROLES : PA_PROP_UCM_CAPTURE_ROLES;
 
     pa_assert(p);
     pa_assert(!*p);
@@ -759,7 +760,7 @@  void ucm_add_ports(pa_hashmap **p, pa_proplist *proplist,
     for (i=0; i<context->ucm_devices_num; i++)
     {
         const char *roles = pa_proplist_gets(
-                context->ucm_devices[i]->proplist, PA_PROP_DEVICE_INTENDED_ROLES);
+                context->ucm_devices[i]->proplist, role_name);
         char *tmp;
         tmp = merge_roles(merged_roles, roles);
         pa_xfree(merged_roles);
@@ -894,6 +895,12 @@  static void alsa_mapping_add_ucm_device(pa_alsa_mapping *m, pa_alsa_ucm_device *
 
     /* walk around null case */
     m->description = m->description ? m->description : pa_xstrdup("");
+
+    /* save mapping to ucm device */
+    if (m->direction == PA_ALSA_DIRECTION_OUTPUT)
+        device->playback_mapping = m;
+    else
+        device->capture_mapping = m;
 }
 
 static int ucm_create_mapping_direction(struct pa_alsa_ucm_config *ucm,
@@ -1094,3 +1101,91 @@  void ucm_free(struct pa_alsa_ucm_config *ucm) {
         ucm->ucm_mgr = NULL;
     }
 }
+
+static pa_bool_t stream_routed_to_mod_intent (struct pa_alsa_ucm_verb *verb,
+        struct pa_alsa_ucm_modifier *mod, const char *mapping_name) {
+    int i;
+    const char *dev_name;
+    struct pa_alsa_ucm_device *dev;
+    pa_alsa_mapping *mapping;
+
+    /* check if mapping_name is same as one of the modifier's supported device */
+    for (i=0; i<mod->n_suppdev; i++) {
+        dev_name = mod->supported_devices[i];
+        /* first find the supported device */
+        PA_LLIST_FOREACH(dev, verb->devices) {
+            const char *name = pa_proplist_gets(dev->proplist, PA_PROP_UCM_NAME);
+            if (pa_streq(name, dev_name)) {
+                /* then match the mapping name */
+                mapping = mod->action_direct == PA_ALSA_UCM_DIRECT_SINK ? dev->playback_mapping : dev->capture_mapping;
+                if (mapping && pa_streq(mapping->name, mapping_name))
+                    return TRUE;
+                break;
+            }
+        }
+    }
+
+    return FALSE;
+}
+
+/* enable the modifier when both of the conditions are met
+ * 1. the first stream with matched role starts
+ * 2. the stream is routed to the device of the modifier specifies.
+ */
+void ucm_roled_stream_begin(pa_alsa_ucm_config *ucm,
+        const char *role, const char *mapping_name, 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))) {
+            if (stream_routed_to_mod_intent(ucm->active_verb, mod, mapping_name)) {
+                if (mod->enabled_counter == 0) {
+                    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);
+                    }
+                }
+                mod->enabled_counter++;
+                //TODO: set port of the sink/source to modifier's intent device? */
+            }
+            break;
+        }
+    }
+}
+
+/* disable the modifier when both of the conditions are met
+ * 1. the last stream with matched role ends
+ * 2. the stream is routed to the device of the modifier specifies.
+ */
+void ucm_roled_stream_end(pa_alsa_ucm_config *ucm,
+        const char *role, const char *mapping_name, 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))) {
+            if (stream_routed_to_mod_intent(ucm->active_verb, mod, mapping_name)) {
+                mod->enabled_counter--;
+                if (mod->enabled_counter == 0) {
+                    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);
+                    }
+                }
+                //TODO: set port of the sink/source to modifier's intent device? */
+            }
+            break;
+        }
+    }
+}
diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
index c0ebfe9..680be5a 100644
--- a/src/modules/alsa/alsa-ucm.h
+++ b/src/modules/alsa/alsa-ucm.h
@@ -27,6 +27,8 @@ 
 #include <asoundlib.h>
 #include <use-case.h>
 
+typedef struct pa_alsa_mapping pa_alsa_mapping;
+
 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;
@@ -43,6 +45,9 @@  void ucm_add_ports_combination(pa_hashmap *hash, pa_alsa_ucm_mapping_context *co
         int dev_num, int map_index, pa_hashmap *ports, pa_card_profile *cp, pa_core *core);
 int  ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, int is_sink);
 
+void ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, const char *mapping_name, int is_sink);
+void ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, const char *mapping_name, int is_sink);
+
 /* UCM modifier action direction */
 enum {
     PA_ALSA_UCM_DIRECT_NONE = 0,
@@ -59,6 +64,8 @@  struct pa_alsa_ucm_device {
     unsigned capture_priority;
     unsigned playback_channels;
     unsigned capture_channels;
+    pa_alsa_mapping *playback_mapping;
+    pa_alsa_mapping *capture_mapping;
     int n_confdev;
     int n_suppdev;
     char **conflicting_devices;
@@ -74,6 +81,9 @@  struct pa_alsa_ucm_modifier {
     const char **supported_devices;
     int action_direct;
     char *media_role;
+
+    /* runtime variable */
+    int enabled_counter;
 };
 
 struct pa_alsa_ucm_verb {
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index 7433ecf..d7f1043 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -124,6 +124,18 @@  struct userdata {
     /* ucm stuffs */
     pa_bool_t use_ucm;
     pa_alsa_ucm_config ucm;
+
+    /* hooks for modifier action */
+    pa_hook_slot
+        *sink_input_put_hook_slot,
+        *source_output_put_hook_slot,
+        *sink_input_unlink_hook_slot,
+        *source_output_unlink_hook_slot,
+
+        *sink_input_move_start_hook_slot,
+        *source_output_move_start_hook_slot,
+        *sink_input_move_finish_hook_slot,
+        *source_output_move_finish_hook_slot;
 };
 
 struct profile_data {
@@ -512,6 +524,103 @@  name_fail:
     return err;
 }
 
+static pa_hook_result_t sink_input_put_hook_callback(
+        pa_core *c, pa_sink_input *sink_input, struct userdata *u) {
+    const char *role;
+    const char *mapping_name;
+    pa_sink *sink = sink_input->sink;
+
+    pa_assert(sink);
+
+    role = pa_proplist_gets(sink_input->proplist, PA_PROP_MEDIA_ROLE);
+    mapping_name = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_PROFILE_NAME);
+
+    /* new sink input linked to sink of this card */
+    if (role && sink->card == u->card) {
+        ucm_roled_stream_begin(&u->ucm, role, mapping_name, 1);
+    }
+    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;
+    const char *mapping_name;
+    pa_source *source = source_output->source;
+
+    pa_assert(source);
+
+    role = pa_proplist_gets(source_output->proplist, PA_PROP_MEDIA_ROLE);
+    mapping_name = pa_proplist_gets(source->proplist, PA_PROP_DEVICE_PROFILE_NAME);
+
+    /* new source output linked to source of this card */
+    if (role && source->card == u->card) {
+        ucm_roled_stream_begin(&u->ucm, role, mapping_name, 0);
+    }
+    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;
+    const char *mapping_name;
+    pa_sink *sink = sink_input->sink;
+
+    pa_assert(sink);
+
+    role = pa_proplist_gets(sink_input->proplist, PA_PROP_MEDIA_ROLE);
+    mapping_name = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_PROFILE_NAME);
+
+    /* new sink input unlinked from sink of this card */
+    if (role && sink->card == u->card) {
+        ucm_roled_stream_end(&u->ucm, role, mapping_name, 1);
+    }
+    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;
+    const char *mapping_name;
+    pa_source *source = source_output->source;
+
+    pa_assert(source);
+
+    role = pa_proplist_gets(source_output->proplist, PA_PROP_MEDIA_ROLE);
+    mapping_name = pa_proplist_gets(source->proplist, PA_PROP_DEVICE_PROFILE_NAME);
+
+    /* new source output unlinked from source of this card */
+    if (role && source->card == u->card) {
+        ucm_roled_stream_end(&u->ucm, role, mapping_name, 0);
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_move_start_hook_callback(
+        pa_core *c, pa_sink_input *sink_input, struct userdata *u) {
+    /* same as sink input unlink */
+    return sink_input_unlink_hook_callback (c, sink_input, u);
+}
+
+static pa_hook_result_t source_output_move_start_hook_callback(
+        pa_core *c, pa_source_output *source_output, struct userdata *u) {
+    /* same as source output unlink */
+    return source_output_unlink_hook_callback (c, source_output, u);
+}
+
+static pa_hook_result_t sink_input_move_finish_hook_callback(
+        pa_core *c, pa_sink_input *sink_input, struct userdata *u) {
+    /* same as sink input put */
+    return sink_input_put_hook_callback (c, sink_input, u);
+}
+
+static pa_hook_result_t source_output_move_finish_hook_callback(
+        pa_core *c, pa_source_output *source_output, struct userdata *u) {
+    /* same as source output put */
+    return source_output_put_hook_callback (c, source_output, u);
+}
+
 int pa__init(pa_module *m) {
     pa_card_new_data data;
     pa_modargs *ma;
@@ -563,6 +672,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, u->alsa_card_index)) {
         pa_log_info("Found UCM profiles");
+
+        /* hook start of sink input/source output to enable modifiers */
+        /* A little bit later than module-role-cork */
+        u->sink_input_put_hook_slot = pa_hook_connect(
+                &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE+10,
+                (pa_hook_cb_t) sink_input_put_hook_callback, u);
+        u->source_output_put_hook_slot = pa_hook_connect(
+                &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], PA_HOOK_LATE+10,
+                (pa_hook_cb_t) source_output_put_hook_callback, u);
+
+        /* hook end of sink input/source output to disable modifiers */
+        /* A little bit later than module-role-cork */
+        u->sink_input_unlink_hook_slot = pa_hook_connect(
+                &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK],
+                PA_HOOK_LATE+10, (pa_hook_cb_t) sink_input_unlink_hook_callback, u);
+        u->source_output_unlink_hook_slot = pa_hook_connect(
+                &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK],
+                PA_HOOK_LATE+10, (pa_hook_cb_t) source_output_unlink_hook_callback, u);
+
+        /* hook move start of sink input/source output to disable modifiers */
+        /* A little bit later than module-role-cork */
+        u->sink_input_move_start_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START],
+                PA_HOOK_LATE+10, (pa_hook_cb_t) sink_input_move_start_hook_callback, u);
+        u->source_output_move_start_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START],
+                PA_HOOK_LATE+10, (pa_hook_cb_t) source_output_move_start_hook_callback, u);
+
+        /* hook move finish of sink input/source output to enable modifiers */
+        /* A little bit later than module-role-cork */
+        u->sink_input_move_finish_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH],
+                PA_HOOK_LATE+10, (pa_hook_cb_t) sink_input_move_finish_hook_callback, u);
+        u->source_output_move_finish_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH],
+                PA_HOOK_LATE+10, (pa_hook_cb_t) source_output_move_finish_hook_callback, u);
     }
     else {
         u->use_ucm = FALSE;
@@ -695,6 +836,30 @@  void pa__done(pa_module*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);
+
+    if (u->sink_input_move_start_hook_slot)
+        pa_hook_slot_free(u->sink_input_move_start_hook_slot);
+
+    if (u->source_output_move_start_hook_slot)
+        pa_hook_slot_free(u->source_output_move_start_hook_slot);
+
+    if (u->sink_input_move_finish_hook_slot)
+        pa_hook_slot_free(u->sink_input_move_finish_hook_slot);
+
+    if (u->source_output_move_finish_hook_slot)
+        pa_hook_slot_free(u->source_output_move_finish_hook_slot);
+
     if (u->mixer_fdl)
         pa_alsa_fdlist_free(u->mixer_fdl);
     if (u->mixer_handle)