[RFC PATCH 3/4] Add UCM modifier functions into alsa card module

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Feng Wei <feng.wei@xxxxxxxxxx>

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.

Signed-off-by: Feng Wei <feng.wei at linaro.org>
---
 src/modules/alsa/alsa-ucm.c         |   95 ++++++++++++++++++++
 src/modules/alsa/alsa-ucm.h         |    6 ++
 src/modules/alsa/module-alsa-card.c |  168 ++++++++++++++++++++++++++++++++++-
 3 files changed, 267 insertions(+), 2 deletions(-)

diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 452a1c4..b74a3e7 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -1013,6 +1013,18 @@ static int ucm_create_profile(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps,
     return 0;
 }
 
+static pa_alsa_ucm_device *find_ucm_dev(pa_alsa_ucm_verb *verb, const char *dev_name) {
+    pa_alsa_ucm_device *dev;
+
+    PA_LLIST_FOREACH(dev, verb->devices) {
+        const char *name = pa_proplist_gets(dev->proplist, PA_PROP_UCM_NAME);
+        if (pa_streq(name, dev_name))
+            return dev;
+    }
+
+    return NULL;
+}
+
 pa_alsa_profile_set* pa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map) {
     pa_alsa_ucm_verb *verb;
     pa_alsa_profile_set *ps;
@@ -1081,3 +1093,86 @@ void pa_ucm_free(pa_alsa_ucm_config *ucm) {
         ucm->ucm_mgr = NULL;
     }
 }
+
+static pa_bool_t stream_routed_to_mod_intent (pa_alsa_ucm_verb *verb,
+        pa_alsa_ucm_modifier *mod, const char *mapping_name) {
+    int i;
+    const char *dev_name;
+    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 */
+        dev = find_ucm_dev(verb, dev_name);
+        if (dev) {
+            /* 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;
+        }
+    }
+
+    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 pa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm,
+        const char *role, const char *mapping_name, int is_sink) {
+    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 pa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm,
+        const char *role, const char *mapping_name, int is_sink) {
+    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 18b6fe0..8e2dd71 100644
--- a/src/modules/alsa/alsa-ucm.h
+++ b/src/modules/alsa/alsa-ucm.h
@@ -49,6 +49,9 @@ int pa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port,
 
 void pa_ucm_free(pa_alsa_ucm_config *ucm);
 
+void pa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, const char *mapping_name, int is_sink);
+void pa_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,
@@ -82,6 +85,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 7d09938..4802a39 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 {
@@ -286,7 +298,7 @@ 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_alsa_ucm_config *ucm = &u->ucm;
 
     pa_assert(u);
 
@@ -476,7 +488,7 @@ static int card_query_ucm_profiles(struct userdata *u, int card_index)
 
     /* get the properties of each UCM verb */
     for (i = 0; i < num_verbs; i += 2) {
-        struct pa_alsa_ucm_verb *verb;
+        pa_alsa_ucm_verb *verb;
 
         /* Get devices and modifiers for each verb */
         err = pa_ucm_get_verb(u->ucm.ucm_mgr, verb_list[i], verb_list[i+1], &verb);
@@ -512,6 +524,102 @@ 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)
+        pa_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)
+        pa_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)
+        pa_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)
+        pa_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 +671,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 +835,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)
-- 
1.7.9.5




[Index of Archives]     [Linux Audio Users]     [AMD Graphics]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux