@@ -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;
+ }
+ }
+}
@@ -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 {
@@ -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)