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