From: Tanu Kaskinen Date: Mon, 4 May 2015 18:03:45 +0000 (+0300) Subject: ucm: Add support for "JackHWMute" X-Git-Tag: v6.99.1~4 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=38e81f4c3dd1751fa78480daee3c71b73b45f7a7;p=platform%2Fupstream%2Fpulseaudio.git ucm: Add support for "JackHWMute" JackHWMute is used to list devices that get forcibly muted by a particular jack. We mark ports unavailable based on that information. --- diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index c6d6212..a0b2493 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -119,6 +119,7 @@ pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *name) { jack->state_unplugged = PA_AVAILABLE_NO; jack->state_plugged = PA_AVAILABLE_YES; jack->ucm_devices = pa_dynarray_new(NULL); + jack->ucm_hw_mute_devices = pa_dynarray_new(NULL); return jack; } @@ -126,6 +127,9 @@ pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *name) { void pa_alsa_jack_free(pa_alsa_jack *jack) { pa_assert(jack); + if (jack->ucm_hw_mute_devices) + pa_dynarray_free(jack->ucm_hw_mute_devices); + if (jack->ucm_devices) pa_dynarray_free(jack->ucm_devices); @@ -145,6 +149,9 @@ void pa_alsa_jack_set_has_control(pa_alsa_jack *jack, bool has_control) { jack->has_control = has_control; + PA_DYNARRAY_FOREACH(device, jack->ucm_hw_mute_devices, idx) + pa_alsa_ucm_device_update_available(device); + PA_DYNARRAY_FOREACH(device, jack->ucm_devices, idx) pa_alsa_ucm_device_update_available(device); } @@ -160,6 +167,44 @@ void pa_alsa_jack_set_plugged_in(pa_alsa_jack *jack, bool plugged_in) { jack->plugged_in = plugged_in; + /* XXX: If this is a headphone jack that mutes speakers when plugged in, + * and the headphones get unplugged, then the headphone device must be set + * to unavailable and the speaker device must be set to unknown. So far so + * good. But there's an ugly detail: we must first set the availability of + * the speakers and then the headphones. We shouldn't need to care about + * the order, but we have to, because module-switch-on-port-available gets + * separate events for the two devices, and the intermediate state between + * the two events is such that the second event doesn't trigger the desired + * port switch, if the event order is "wrong". + * + * These are the transitions when the event order is "right": + * + * speakers: 1) unavailable -> 2) unknown -> 3) unknown + * headphones: 1) available -> 2) available -> 3) unavailable + * + * In the 2 -> 3 transition, headphones become unavailable, and + * module-switch-on-port-available sees that speakers can be used, so the + * port gets changed as it should. + * + * These are the transitions when the event order is "wrong": + * + * speakers: 1) unavailable -> 2) unavailable -> 3) unknown + * headphones: 1) available -> 2) unavailable -> 3) unavailable + * + * In the 1 -> 2 transition, headphones become unavailable, and there are + * no available ports to use, so no port change happens. In the 2 -> 3 + * transition, speaker availability becomes unknown, but that's not + * a strong enough signal for module-switch-on-port-available, so it still + * doesn't do the port switch. + * + * We should somehow merge the two events so that + * module-switch-on-port-available would handle both transitions in one go. + * If module-switch-on-port-available used a defer event to delay + * the port availability processing, that would probably do the trick. */ + + PA_DYNARRAY_FOREACH(device, jack->ucm_hw_mute_devices, idx) + pa_alsa_ucm_device_update_available(device); + PA_DYNARRAY_FOREACH(device, jack->ucm_devices, idx) pa_alsa_ucm_device_update_available(device); } @@ -171,6 +216,13 @@ void pa_alsa_jack_add_ucm_device(pa_alsa_jack *jack, pa_alsa_ucm_device *device) pa_dynarray_append(jack->ucm_devices, device); } +void pa_alsa_jack_add_ucm_hw_mute_device(pa_alsa_jack *jack, pa_alsa_ucm_device *device) { + pa_assert(jack); + pa_assert(device); + + pa_dynarray_append(jack->ucm_hw_mute_devices, device); +} + static const char *lookup_description(const char *key, const struct description_map dm[], unsigned n) { unsigned i; diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h index 96a7a5e..621a71b 100644 --- a/src/modules/alsa/alsa-mixer.h +++ b/src/modules/alsa/alsa-mixer.h @@ -170,6 +170,7 @@ struct pa_alsa_jack { pa_alsa_required_t required_absent; pa_dynarray *ucm_devices; /* pa_alsa_ucm_device */ + pa_dynarray *ucm_hw_mute_devices; /* pa_alsa_ucm_device */ }; pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *name); @@ -177,6 +178,7 @@ void pa_alsa_jack_free(pa_alsa_jack *jack); void pa_alsa_jack_set_has_control(pa_alsa_jack *jack, bool has_control); void pa_alsa_jack_set_plugged_in(pa_alsa_jack *jack, bool plugged_in); void pa_alsa_jack_add_ucm_device(pa_alsa_jack *jack, pa_alsa_ucm_device *device); +void pa_alsa_jack_add_ucm_hw_mute_device(pa_alsa_jack *jack, pa_alsa_ucm_device *device); /* A path wraps a series of elements into a single entity which can be * used to control it as if it had a single volume slider, a single diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c index d2c7140..c231bb4 100644 --- a/src/modules/alsa/alsa-ucm.c +++ b/src/modules/alsa/alsa-ucm.c @@ -77,6 +77,9 @@ struct ucm_info { }; static void device_set_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack); +static void device_add_hw_mute_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack); + +static pa_alsa_ucm_device *verb_find_device(pa_alsa_ucm_verb *verb, const char *device_name); struct ucm_port { pa_alsa_ucm_config *ucm; @@ -107,6 +110,7 @@ static struct ucm_items item[] = { {"CaptureChannels", PA_ALSA_PROP_UCM_CAPTURE_CHANNELS}, {"TQ", PA_ALSA_PROP_UCM_QOS}, {"JackControl", PA_ALSA_PROP_UCM_JACK_CONTROL}, + {"JackHWMute", PA_ALSA_PROP_UCM_JACK_HW_MUTE}, {NULL, NULL}, }; @@ -414,6 +418,7 @@ static int ucm_get_devices(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) { pa_proplist_sets(d->proplist, PA_ALSA_PROP_UCM_NAME, pa_strnull(dev_list[i])); pa_proplist_sets(d->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(dev_list[i + 1])); d->ucm_ports = pa_dynarray_new(NULL); + d->hw_mute_jacks = pa_dynarray_new(NULL); d->available = PA_AVAILABLE_UNKNOWN; PA_LLIST_PREPEND(pa_alsa_ucm_device, verb->devices, d); @@ -1381,6 +1386,7 @@ static int ucm_create_profile( PA_LLIST_FOREACH(dev, verb->devices) { pa_alsa_jack *jack; + const char *jack_hw_mute; name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); @@ -1391,6 +1397,37 @@ static int ucm_create_profile( jack = ucm_get_jack(ucm, dev); device_set_jack(dev, jack); + + /* JackHWMute contains a list of device names. Each listed device must + * be associated with the jack object that we just created. */ + jack_hw_mute = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_JACK_HW_MUTE); + if (jack_hw_mute) { + char *hw_mute_device_name; + const char *state = NULL; + + while ((hw_mute_device_name = pa_split_spaces(jack_hw_mute, &state))) { + pa_alsa_ucm_verb *verb2; + bool device_found = false; + + /* Search the referenced device from all verbs. If there are + * multiple verbs that have a device with this name, we add the + * hw mute association to each of those devices. */ + PA_LLIST_FOREACH(verb2, ucm->verbs) { + pa_alsa_ucm_device *hw_mute_device; + + hw_mute_device = verb_find_device(verb2, hw_mute_device_name); + if (hw_mute_device) { + device_found = true; + device_add_hw_mute_jack(hw_mute_device, jack); + } + } + + if (!device_found) + pa_log("[%s] JackHWMute references an unknown device: %s", name, hw_mute_device_name); + + pa_xfree(hw_mute_device_name); + } + } } /* Now find modifiers that have their own PlaybackPCM and create @@ -1596,6 +1633,9 @@ static void free_verb(pa_alsa_ucm_verb *verb) { PA_LLIST_FOREACH_SAFE(di, dn, verb->devices) { PA_LLIST_REMOVE(pa_alsa_ucm_device, verb->devices, di); + if (di->hw_mute_jacks) + pa_dynarray_free(di->hw_mute_jacks); + if (di->ucm_ports) pa_dynarray_free(di->ucm_ports); @@ -1621,6 +1661,23 @@ static void free_verb(pa_alsa_ucm_verb *verb) { pa_xfree(verb); } +static pa_alsa_ucm_device *verb_find_device(pa_alsa_ucm_verb *verb, const char *device_name) { + pa_alsa_ucm_device *device; + + pa_assert(verb); + pa_assert(device_name); + + PA_LLIST_FOREACH(device, verb->devices) { + const char *name; + + name = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_NAME); + if (pa_streq(name, device_name)) + return device; + } + + return NULL; +} + void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) { pa_alsa_ucm_verb *vi, *vn; pa_alsa_jack *ji, *jn; @@ -1737,6 +1794,16 @@ static void device_set_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack) { pa_alsa_ucm_device_update_available(device); } +static void device_add_hw_mute_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack) { + pa_assert(device); + pa_assert(jack); + + pa_dynarray_append(device->hw_mute_jacks, jack); + pa_alsa_jack_add_ucm_hw_mute_device(jack, device); + + pa_alsa_ucm_device_update_available(device); +} + static void device_set_available(pa_alsa_ucm_device *device, pa_available_t available) { struct ucm_port *port; unsigned idx; @@ -1754,12 +1821,21 @@ static void device_set_available(pa_alsa_ucm_device *device, pa_available_t avai void pa_alsa_ucm_device_update_available(pa_alsa_ucm_device *device) { pa_available_t available = PA_AVAILABLE_UNKNOWN; + pa_alsa_jack *jack; + unsigned idx; pa_assert(device); if (device->jack && device->jack->has_control) available = device->jack->plugged_in ? PA_AVAILABLE_YES : PA_AVAILABLE_NO; + PA_DYNARRAY_FOREACH(jack, device->hw_mute_jacks, idx) { + if (jack->plugged_in) { + available = PA_AVAILABLE_NO; + break; + } + } + device_set_available(device, available); } diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h index 5ccc466..0930303 100644 --- a/src/modules/alsa/alsa-ucm.h +++ b/src/modules/alsa/alsa-ucm.h @@ -87,6 +87,9 @@ typedef void snd_use_case_mgr_t; /* Corresponds to the "JackControl" UCM value. */ #define PA_ALSA_PROP_UCM_JACK_CONTROL "alsa.ucm.jack_control" +/* Corresponds to the "JackHWMute" UCM value. */ +#define PA_ALSA_PROP_UCM_JACK_HW_MUTE "alsa.ucm.jack_hw_mute" + 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; @@ -148,6 +151,7 @@ struct pa_alsa_ucm_device { pa_dynarray *ucm_ports; /* struct ucm_port */ pa_alsa_jack *jack; + pa_dynarray *hw_mute_jacks; /* pa_alsa_jack */ pa_available_t available; };