[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