From patchwork Thu Mar 15 06:26:56 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Feng Wei X-Patchwork-Id: 7306 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 A319B23E14 for ; Thu, 15 Mar 2012 06:27:53 +0000 (UTC) Received: from mail-iy0-f180.google.com (mail-iy0-f180.google.com [209.85.210.180]) by fiordland.canonical.com (Postfix) with ESMTP id 5CCE4A187E2 for ; Thu, 15 Mar 2012 06:27:53 +0000 (UTC) Received: by mail-iy0-f180.google.com with SMTP id e36so4682556iag.11 for ; Wed, 14 Mar 2012 23:27:53 -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-spamscore:x-bigfish:x-forefront-antispam-report :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=0GI5CHx2aKKeln4XRNI5Xvwv0gSUku2yrnJR/te3KVo=; b=LLS5bwTIOuXKoyE/1ono80q992+1mQWFg+r2bqxNdTQ8RtoOycvVhN/ayUjhHCtg+y M+Jz49Uwi7azLHbd7GRwlhpVjqW03NJGeq6jk70srqeswncwH9oksRnkD9ZYF1UpjH0/ WCzONH//EuPTTJJyF238yvPOMFTbSMG/EwzN0SvkbdKrM96XMmbr+TnQW7gx/Ji8XOUY 4Mtqe3iOJRx/z2lwP9/j5osoXFd0GUUxAMAByLztDBppe7RLZVfUoWBIUa3Zq6puSy1t CjzIj36hFiVhMTj8rHXESyHS45XR1Gi0647fi4N0bHB+fa7a0hS+6b03dNMyRpkcMECO 1rhg== Received: by 10.50.184.228 with SMTP id ex4mr8089327igc.50.1331792873094; Wed, 14 Mar 2012 23:27:53 -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.53.18 with SMTP id k18csp28357ibg; Wed, 14 Mar 2012 23:27:52 -0700 (PDT) Received: by 10.42.131.10 with SMTP id x10mr6465178ics.17.1331792872677; Wed, 14 Mar 2012 23:27:52 -0700 (PDT) Received: from ch1outboundpool.messaging.microsoft.com (ch1ehsobe005.messaging.microsoft.com. [216.32.181.185]) by mx.google.com with ESMTPS id l8si907622igq.0.2012.03.14.23.27.52 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 14 Mar 2012 23:27:52 -0700 (PDT) Received-SPF: neutral (google.com: 216.32.181.185 is neither permitted nor denied by best guess record for domain of feng.wei@linaro.org) client-ip=216.32.181.185; Authentication-Results: mx.google.com; spf=neutral (google.com: 216.32.181.185 is neither permitted nor denied by best guess record for domain of feng.wei@linaro.org) smtp.mail=feng.wei@linaro.org Received: from mail153-ch1-R.bigfish.com (10.43.68.242) by CH1EHSOBE009.bigfish.com (10.43.70.59) with Microsoft SMTP Server id 14.1.225.23; Thu, 15 Mar 2012 06:27:52 +0000 Received: from mail153-ch1 (localhost [127.0.0.1]) by mail153-ch1-R.bigfish.com (Postfix) with ESMTP id A8C4D36009E; Thu, 15 Mar 2012 06:27:52 +0000 (UTC) X-SpamScore: 0 X-BigFish: VS0(zzzz1202hzzz2dh87h2a8h668h839hd24h) X-Forefront-Antispam-Report: CIP:70.37.183.190; KIP:(null); UIP:(null); IPV:NLI; H:mail.freescale.net; RD:none; EFVD:NLI X-FB-DOMAIN-IP-MATCH: fail Received: from mail153-ch1 (localhost.localdomain [127.0.0.1]) by mail153-ch1 (MessageSwitch) id 1331792868795151_25982; Thu, 15 Mar 2012 06:27:48 +0000 (UTC) Received: from CH1EHSMHS028.bigfish.com (snatpool3.int.messaging.microsoft.com [10.43.68.225]) by mail153-ch1.bigfish.com (Postfix) with ESMTP id BEF31140048; Thu, 15 Mar 2012 06:27:48 +0000 (UTC) Received: from mail.freescale.net (70.37.183.190) by CH1EHSMHS028.bigfish.com (10.43.70.28) with Microsoft SMTP Server (TLS) id 14.1.225.23; Thu, 15 Mar 2012 06:27:48 +0000 Received: from tx30smr01.am.freescale.net (10.81.153.31) by 039-SN1MMR1-002.039d.mgd.msft.net (10.84.1.15) with Microsoft SMTP Server (TLS) id 14.1.355.3; Thu, 15 Mar 2012 01:27:46 -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 q2F6RLVb014574; Wed, 14 Mar 2012 23:27:44 -0700 From: Feng Wei To: CC: , , , Feng Wei Subject: [pulseaudio-discuss][RFC PATCH v2 3/3] Add UCM modifier functions into alsa card module Date: Thu, 15 Mar 2012 14:26:56 +0800 Message-ID: <1331792816-18971-4-git-send-email-feng.wei@linaro.org> X-Mailer: git-send-email 1.7.4.1 In-Reply-To: <1331792816-18971-1-git-send-email-feng.wei@linaro.org> References: <1331792816-18971-1-git-send-email-feng.wei@linaro.org> MIME-Version: 1.0 X-OriginatorOrg: sigmatel.com X-Gm-Message-State: ALoCoQkeDF/BhrRog64eE7tcAP/enjdLgWrgqzMEiHcFdqj238wA0BZPtiOufofItIJdO0I72a4V 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. --- src/modules/alsa/alsa-ucm.c | 97 ++++++++++++++++++++- src/modules/alsa/alsa-ucm.h | 10 ++ src/modules/alsa/module-alsa-card.c | 165 +++++++++++++++++++++++++++++++++++ 3 files changed, 271 insertions(+), 1 deletions(-) diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c index 6b9be62..2d299e3 100644 --- a/src/modules/alsa/alsa-ucm.c +++ b/src/modules/alsa/alsa-ucm.c @@ -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; iucm_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; in_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; + } + } +} diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h index c0ebfe9..680be5a 100644 --- a/src/modules/alsa/alsa-ucm.h +++ b/src/modules/alsa/alsa-ucm.h @@ -27,6 +27,8 @@ #include #include +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 { diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c index 7433ecf..d7f1043 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 { @@ -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)