From patchwork Fri May 18 10:06:47 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Feng Wei X-Patchwork-Id: 8807 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id 852F424009 for ; Fri, 18 May 2012 10:07:54 +0000 (UTC) Received: from mail-yx0-f180.google.com (mail-yx0-f180.google.com [209.85.213.180]) by fiordland.canonical.com (Postfix) with ESMTP id 54C1BA187C3 for ; Fri, 18 May 2012 10:07:54 +0000 (UTC) Received: by mail-yx0-f180.google.com with SMTP id q6so3215818yen.11 for ; Fri, 18 May 2012 03:07:54 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-forwarded-to:x-forwarded-for:delivered-to:received-spf :x-forefront-antispam-report:x-spamscore:x-bigfish :x-fb-domain-ip-match:from:to:cc:subject:date:message-id:x-mailer :in-reply-to:references:mime-version:content-type:x-originatororg :x-gm-message-state; bh=cXkRrp8ecXfbjQUu+oj+S6VOUUZTB6CWGteT74v7XVQ=; b=iBN3d2CeSQ/N5M7+sj+m45m33vJ8OpGNVonhXbxAB0a1/8VDEYI8irhdy5l4YkLPEA zJ+gwA/k7q9fAIVl+J5pKjowjTUClnqxwYqNRReb+X/CVC+GQgqKEDjFck6lIKAkqgUL dVBb03zJz+ma3+M86ECCFdSPaOOOODcbc+oI4O903/pVHGp9Eii0NKIEfFZzqlkzHOZ2 XHOq+16fQnXfh130Lb9PS7dAlnu2JpuWqKkxGX8R6LEPI09PCi4kdHUndxboibop+c3C ZNvqRzS0nbASU5o2S7uyEgvGzcYHFYYDub6YfLwa9CItwolOZHdALdyS3y4yUyeRlxKo 4nkQ== Received: by 10.50.87.227 with SMTP id bb3mr7833970igb.57.1337335672581; Fri, 18 May 2012 03:07:52 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.231.35.72 with SMTP id o8csp92187ibd; Fri, 18 May 2012 03:07:52 -0700 (PDT) Received: by 10.50.187.137 with SMTP id fs9mr16045681igc.50.1337335672101; Fri, 18 May 2012 03:07:52 -0700 (PDT) Received: from va3outboundpool.messaging.microsoft.com (va3ehsobe002.messaging.microsoft.com. [216.32.180.12]) by mx.google.com with ESMTPS id c8si9821839igj.60.2012.05.18.03.07.51 (version=TLSv1/SSLv3 cipher=OTHER); Fri, 18 May 2012 03:07:52 -0700 (PDT) Received-SPF: neutral (google.com: 216.32.180.12 is neither permitted nor denied by best guess record for domain of feng.wei@linaro.org) client-ip=216.32.180.12; Authentication-Results: mx.google.com; spf=neutral (google.com: 216.32.180.12 is neither permitted nor denied by best guess record for domain of feng.wei@linaro.org) smtp.mail=feng.wei@linaro.org Received: from mail189-va3-R.bigfish.com (10.7.14.237) by VA3EHSOBE008.bigfish.com (10.7.40.28) with Microsoft SMTP Server id 14.1.225.23; Fri, 18 May 2012 10:07:42 +0000 Received: from mail189-va3 (localhost [127.0.0.1]) by mail189-va3-R.bigfish.com (Postfix) with ESMTP id 58213E0060; Fri, 18 May 2012 10:07:42 +0000 (UTC) X-Forefront-Antispam-Report: CIP:70.37.183.190; KIP:(null); UIP:(null); IPV:NLI; H:mail.freescale.net; RD:none; EFVD:NLI X-SpamScore: 0 X-BigFish: VS0(zzzz1202hzz8275dhz2dh87h2a8h668h839hd24he5bhe96hf0ah) X-FB-DOMAIN-IP-MATCH: fail Received: from mail189-va3 (localhost.localdomain [127.0.0.1]) by mail189-va3 (MessageSwitch) id 1337335659953197_20563; Fri, 18 May 2012 10:07:39 +0000 (UTC) Received: from VA3EHSMHS012.bigfish.com (unknown [10.7.14.244]) by mail189-va3.bigfish.com (Postfix) with ESMTP id E512840004A; Fri, 18 May 2012 10:07:39 +0000 (UTC) Received: from mail.freescale.net (70.37.183.190) by VA3EHSMHS012.bigfish.com (10.7.99.22) with Microsoft SMTP Server (TLS) id 14.1.225.23; Fri, 18 May 2012 10:07:38 +0000 Received: from tx30smr01.am.freescale.net (10.81.153.31) by 039-SN1MMR1-003.039d.mgd.msft.net (10.84.1.16) with Microsoft SMTP Server (TLS) id 14.2.298.5; Fri, 18 May 2012 05:07:42 -0500 Received: from wayne-Latitude-E6410.ap.freescale.net ([10.213.130.145]) by tx30smr01.am.freescale.net (8.14.3/8.14.0) with ESMTP id q4IA7IVd016083; Fri, 18 May 2012 03:07:40 -0700 From: To: CC: , , , Feng Wei Subject: [RFC PATCH 3/4] Add UCM modifier functions into alsa card module Date: Fri, 18 May 2012 18:06:47 +0800 Message-ID: <1337335608-6901-4-git-send-email-feng.wei@linaro.org> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1337335608-6901-1-git-send-email-feng.wei@linaro.org> References: <1337335608-6901-1-git-send-email-feng.wei@linaro.org> MIME-Version: 1.0 X-OriginatorOrg: sigmatel.com X-Gm-Message-State: ALoCoQlVok6KtEG2nNSYeDYEVAnP+O5E5aQPcGb7nLhPCGmuK2G6fn3VgG3fY5ydDRujk90bbOvr From: Feng Wei 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 --- 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; in_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)