From a66d5b90f9a9c291739c83ef273dde708d02da39 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 12 Apr 2023 12:30:56 +0200 Subject: [PATCH] Revert "alsa: UCM conflicting devices changes" If needs some more work This reverts commit aae770f18b7f6c94195fb3f2e2a61308029086af. This reverts commit 2216956e05689079db5ec05f793158e6479060da. This reverts commit 1d3c4501eff3392e0cd858d980c742c7f88c95e7. This reverts commit c7ca02460761273de0d1a8432c5defaafed43664. This reverts commit 8e4a5f5d5ed3d8b3081956f451c85c23aa74fc15. This reverts commit 8b20675ad12bc9daea84f6d4314ec12fccf5882c. This reverts commit ebe6f8b51a31ff7bd71d560991a12283f710b937. This reverts commit 4b97da1277b709e9749b568319f5eb403ff0f362. This reverts commit 7c46a656c734d4688cac229cd9d49af5ad0609fa. This reverts commit 96ed03e1fc5b1f0a287069c42031d7961239c896. This reverts commit 43770c533c71bfaf2c7e82d47d50bfd0533b53e0. This reverts commit ccb2fce05452185858d22de06d659452702f05d8. This reverts commit 93d0ddc8b327f9aaf11d71193f1d50d64bf1144b. This reverts commit 815e8c8fa09a88269b674786a152fe36849b933d. This reverts commit 97b99a0f7c2dd059c08c6ccd143cc1be06111ad1. This reverts commit 7e4d87402b17cd881e9140c37285b175c622ea24. This reverts commit 168b3ee22c1545b738634c4ae89ae4afbaf68cc4. This reverts commit 5128cdba319a2ced0f2e559ad594b8cbeb97a072. This reverts commit 31cde774c54931109da99bc452c858a208d94d6c. This reverts commit b02c8ba153f21459f1a120d4c1ca1bde6a98f4fb. This reverts commit 68ac72d09857901a33be22922669fc8674a4ab9c. This reverts commit 96cfc9bc1ac63414e7b3a6b928be5a29c03e1431. This reverts commit e2d642a20b1bc4e70f7273ddfc3da376a342e10f. This reverts commit 80fc80c34348bdc247bb1a12bcc174db772208a4. This reverts commit 3a68905c7c45677f64213ff563fd68bce6d14a9f. This reverts commit 7e64680a65e4dc2c755e79e55f43f7596b31e1d9. This reverts commit 8d1e38a2816e113121f2355a5827b0a681a3d41b. This reverts commit 591188195c60bb0bba4019d76547ece5386496ac. This reverts commit 973fe0cb79c9ef508778c7a2bb98bdfebeb70dfe. This reverts commit 4d08c9ad609d47c16d03ae3d5230c14d8d52f49e. This reverts commit 3dce92e58090de98e9f1470d9ba69da1f1b590db. This reverts commit 6b5e232c5c4a8e3d2d1c442c87ab03cc40350978. This reverts commit b1286c62c90167d6f6b6c17ad9bdfbb5d0254f60. --- spa/plugins/alsa/acp/acp.c | 41 +- spa/plugins/alsa/acp/alsa-mixer.c | 2 +- spa/plugins/alsa/acp/alsa-mixer.h | 5 +- spa/plugins/alsa/acp/alsa-ucm.c | 1213 +++++++++++++---------------- spa/plugins/alsa/acp/alsa-ucm.h | 30 +- spa/plugins/alsa/acp/alsa-util.c | 176 ++--- spa/plugins/alsa/acp/alsa-util.h | 8 - spa/plugins/alsa/acp/compat.h | 4 +- spa/plugins/alsa/acp/idxset.h | 86 +- 9 files changed, 635 insertions(+), 930 deletions(-) diff --git a/spa/plugins/alsa/acp/acp.c b/spa/plugins/alsa/acp/acp.c index 9078f774f..5e4afb7a3 100644 --- a/spa/plugins/alsa/acp/acp.c +++ b/spa/plugins/alsa/acp/acp.c @@ -362,7 +362,7 @@ static int add_pro_profile(pa_card *impl, uint32_t index) devstr, NULL, &m->sample_spec, &m->channel_map, SND_PCM_STREAM_PLAYBACK, &try_period_size, &try_buffer_size, - 0, NULL, NULL, NULL, NULL, false))) { + 0, NULL, NULL, false))) { pa_alsa_init_proplist_pcm(NULL, m->output_proplist, m->output_pcm); pa_proplist_setf(m->output_proplist, "clock.name", "api.alsa.%u", index); pa_alsa_close(&m->output_pcm); @@ -392,7 +392,7 @@ static int add_pro_profile(pa_card *impl, uint32_t index) devstr, NULL, &m->sample_spec, &m->channel_map, SND_PCM_STREAM_CAPTURE, &try_period_size, &try_buffer_size, - 0, NULL, NULL, NULL, NULL, false))) { + 0, NULL, NULL, false))) { pa_alsa_init_proplist_pcm(NULL, m->input_proplist, m->input_pcm); pa_proplist_setf(m->input_proplist, "clock.name", "api.alsa.%u", index); pa_alsa_close(&m->input_pcm); @@ -449,8 +449,8 @@ static void add_profiles(pa_card *impl) pa_dynarray_append(&impl->out.devices, dev); } if (impl->use_ucm) { - if (m->ucm_context.ucm_device) { - pa_alsa_ucm_add_port(NULL, &m->ucm_context, + if (m->ucm_context.ucm_devices) { + pa_alsa_ucm_add_ports_combination(NULL, &m->ucm_context, true, impl->ports, ap, NULL); pa_alsa_ucm_add_ports(&dev->ports, m->proplist, &m->ucm_context, true, impl, dev->pcm_handle, impl->profile_set->ignore_dB); @@ -473,8 +473,8 @@ static void add_profiles(pa_card *impl) } if (impl->use_ucm) { - if (m->ucm_context.ucm_device) { - pa_alsa_ucm_add_port(NULL, &m->ucm_context, + if (m->ucm_context.ucm_devices) { + pa_alsa_ucm_add_ports_combination(NULL, &m->ucm_context, false, impl->ports, ap, NULL); pa_alsa_ucm_add_ports(&dev->ports, m->proplist, &m->ucm_context, false, impl, dev->pcm_handle, impl->profile_set->ignore_dB); @@ -608,7 +608,7 @@ struct temp_port_avail { static int report_jack_state(snd_mixer_elem_t *melem, unsigned int mask) { pa_card *impl = snd_mixer_elem_get_callback_private(melem); - snd_hctl_elem_t **_elem = snd_mixer_elem_get_private(melem), *elem; + snd_hctl_elem_t *elem = snd_mixer_elem_get_private(melem); snd_ctl_elem_value_t *elem_value; bool plugged_in, any_input_port_available; void *state; @@ -618,8 +618,6 @@ static int report_jack_state(snd_mixer_elem_t *melem, unsigned int mask) enum acp_available active_available = ACP_AVAILABLE_UNKNOWN; size_t size; - pa_assert(_elem); - elem = *_elem; #if 0 /* Changing the jack state may cause a port change, and a port change will * make the sink or source change the mixer settings. If there are multiple @@ -888,17 +886,13 @@ static pa_device_port* find_port_with_eld_device(pa_card *impl, int device) static int hdmi_eld_changed(snd_mixer_elem_t *melem, unsigned int mask) { pa_card *impl = snd_mixer_elem_get_callback_private(melem); - snd_hctl_elem_t **_elem = snd_mixer_elem_get_private(melem), *elem; - int device; + snd_hctl_elem_t *elem = snd_mixer_elem_get_private(melem); + int device = snd_hctl_elem_get_device(elem); const char *old_monitor_name; pa_device_port *p; pa_hdmi_eld eld; bool changed = false; - pa_assert(_elem); - elem = *_elem; - device = snd_hctl_elem_get_device(elem); - if (mask == SND_CTL_EVENT_MASK_REMOVE) return 0; @@ -1259,7 +1253,8 @@ static int setup_mixer(pa_card *impl, pa_alsa_device *dev, bool ignore_dB) * will be NULL, but the UCM device enable sequence will still need to be * executed. */ if (dev->active_port && dev->ucm_context) { - if ((res = pa_alsa_ucm_set_port(dev->ucm_context, dev->active_port)) < 0) + if ((res = pa_alsa_ucm_set_port(dev->ucm_context, dev->active_port, + dev->direction == PA_ALSA_DIRECTION_OUTPUT)) < 0) return res; } @@ -1434,7 +1429,8 @@ int acp_card_set_profile(struct acp_card *card, uint32_t new_index, uint32_t fla /* if UCM is available for this card then update the verb */ if (impl->use_ucm && !(np->profile.flags & ACP_PROFILE_PRO)) { if ((res = pa_alsa_ucm_set_profile(&impl->ucm, impl, - np->profile.flags & ACP_PROFILE_OFF ? NULL : np, op)) < 0) { + np->profile.flags & ACP_PROFILE_OFF ? NULL : np->profile.name, + op ? op->profile.name : NULL)) < 0) { return res; } } @@ -1443,8 +1439,8 @@ int acp_card_set_profile(struct acp_card *card, uint32_t new_index, uint32_t fla PA_IDXSET_FOREACH(am, np->output_mappings, idx) { if (impl->use_ucm) { /* Update ports priorities */ - if (am->ucm_context.ucm_device) { - pa_alsa_ucm_add_port(am->output.ports, &am->ucm_context, + if (am->ucm_context.ucm_devices) { + pa_alsa_ucm_add_ports_combination(am->output.ports, &am->ucm_context, true, impl->ports, np, NULL); } } @@ -1456,8 +1452,8 @@ int acp_card_set_profile(struct acp_card *card, uint32_t new_index, uint32_t fla PA_IDXSET_FOREACH(am, np->input_mappings, idx) { if (impl->use_ucm) { /* Update ports priorities */ - if (am->ucm_context.ucm_device) { - pa_alsa_ucm_add_port(am->input.ports, &am->ucm_context, + if (am->ucm_context.ucm_devices) { + pa_alsa_ucm_add_ports_combination(am->input.ports, &am->ucm_context, false, impl->ports, np, NULL); } } @@ -1848,7 +1844,8 @@ int acp_device_set_port(struct acp_device *dev, uint32_t port_index, uint32_t fl mixer_volume_init(impl, d); sync_mixer(d, p); - res = pa_alsa_ucm_set_port(d->ucm_context, p); + res = pa_alsa_ucm_set_port(d->ucm_context, p, + dev->direction == ACP_DIRECTION_PLAYBACK); } else { pa_alsa_port_data *data; diff --git a/spa/plugins/alsa/acp/alsa-mixer.c b/spa/plugins/alsa/acp/alsa-mixer.c index 7688b030c..8138c6c6e 100644 --- a/spa/plugins/alsa/acp/alsa-mixer.c +++ b/spa/plugins/alsa/acp/alsa-mixer.c @@ -5000,7 +5000,7 @@ static snd_pcm_t* mapping_open_pcm(pa_alsa_mapping *m, handle = pa_alsa_open_by_template( m->device_strings, dev_id, NULL, &try_ss, &try_map, mode, &try_period_size, - &try_buffer_size, 0, NULL, NULL, NULL, NULL, exact_channels); + &try_buffer_size, 0, NULL, NULL, exact_channels); if (handle && !exact_channels && m->channel_map.channels != try_map.channels) { char buf[PA_CHANNEL_MAP_SNPRINT_MAX]; pa_log_debug("Channel map for mapping '%s' permanently changed to '%s'", m->name, diff --git a/spa/plugins/alsa/acp/alsa-mixer.h b/spa/plugins/alsa/acp/alsa-mixer.h index 687e8b53d..643f03dba 100644 --- a/spa/plugins/alsa/acp/alsa-mixer.h +++ b/spa/plugins/alsa/acp/alsa-mixer.h @@ -354,7 +354,7 @@ struct pa_alsa_mapping { pa_alsa_device output; pa_alsa_device input; - /* ucm device context */ + /* ucm device context*/ pa_alsa_ucm_mapping_context ucm_context; }; @@ -381,9 +381,6 @@ struct pa_alsa_profile { pa_idxset *input_mappings; pa_idxset *output_mappings; - /* ucm device context */ - pa_alsa_ucm_profile_context ucm_context; - struct { pa_dynarray devices; } out; diff --git a/spa/plugins/alsa/acp/alsa-ucm.c b/spa/plugins/alsa/acp/alsa-ucm.c index a1af72e43..f66b77199 100644 --- a/spa/plugins/alsa/acp/alsa-ucm.c +++ b/spa/plugins/alsa/acp/alsa-ucm.c @@ -72,8 +72,9 @@ static pa_alsa_ucm_device *verb_find_device(pa_alsa_ucm_verb *verb, const char * static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port, - pa_alsa_ucm_device *device); + pa_alsa_ucm_device **devices, unsigned n_devices); static void ucm_port_data_free(pa_device_port *port); +static void ucm_port_update_available(pa_alsa_ucm_port_data *port); static struct ucm_type types[] = { {"None", PA_DEVICE_PORT_TYPE_UNKNOWN}, @@ -169,6 +170,17 @@ static char *ucm_verb_value( return (char *)value; } +static int ucm_device_exists(pa_idxset *idxset, pa_alsa_ucm_device *dev) { + pa_alsa_ucm_device *d; + uint32_t idx; + + PA_IDXSET_FOREACH(d, idxset, idx) + if (d == dev) + return 1; + + return 0; +} + static void ucm_add_devices_to_idxset( pa_idxset *idxset, pa_alsa_ucm_device *me, @@ -494,10 +506,10 @@ static int ucm_get_device_property( n_confdev = snd_use_case_get_list(uc_mgr, id, &devices); pa_xfree(id); - device->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); if (n_confdev <= 0) pa_log_debug("No %s for device %s", "_conflictingdevs", device_name); else { + device->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); ucm_add_devices_to_idxset(device->conflicting_devices, device, verb->devices, devices, n_confdev); snd_use_case_free_list(devices, n_confdev); } @@ -506,10 +518,10 @@ static int ucm_get_device_property( n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices); pa_xfree(id); - device->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); if (n_suppdev <= 0) pa_log_debug("No %s for device %s", "_supporteddevs", device_name); else { + device->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); ucm_add_devices_to_idxset(device->supported_devices, device, verb->devices, devices, n_suppdev); snd_use_case_free_list(devices, n_suppdev); } @@ -518,16 +530,10 @@ static int ucm_get_device_property( }; /* Create a property list for this ucm modifier */ -static int ucm_get_modifier_property( - pa_alsa_ucm_modifier *modifier, - snd_use_case_mgr_t *uc_mgr, - pa_alsa_ucm_verb *verb, - const char *modifier_name) { +static int ucm_get_modifier_property(pa_alsa_ucm_modifier *modifier, snd_use_case_mgr_t *uc_mgr, const char *modifier_name) { const char *value; char *id; int i; - const char **devices; - int n_confdev, n_suppdev; for (i = 0; item[i].id; i++) { int err; @@ -544,28 +550,16 @@ static int ucm_get_modifier_property( } id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", modifier_name); - n_confdev = snd_use_case_get_list(uc_mgr, id, &devices); + modifier->n_confdev = snd_use_case_get_list(uc_mgr, id, &modifier->conflicting_devices); pa_xfree(id); - - modifier->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - if (n_confdev <= 0) + if (modifier->n_confdev < 0) pa_log_debug("No %s for modifier %s", "_conflictingdevs", modifier_name); - else { - ucm_add_devices_to_idxset(modifier->conflicting_devices, NULL, verb->devices, devices, n_confdev); - snd_use_case_free_list(devices, n_confdev); - } id = pa_sprintf_malloc("%s/%s", "_supporteddevs", modifier_name); - n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices); + modifier->n_suppdev = snd_use_case_get_list(uc_mgr, id, &modifier->supported_devices); pa_xfree(id); - - modifier->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - if (n_suppdev <= 0) + if (modifier->n_suppdev < 0) pa_log_debug("No %s for modifier %s", "_supporteddevs", modifier_name); - else { - ucm_add_devices_to_idxset(modifier->supported_devices, NULL, verb->devices, devices, n_suppdev); - snd_use_case_free_list(devices, n_suppdev); - } return 0; }; @@ -602,59 +596,6 @@ static int ucm_get_devices(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) { return 0; }; -static long ucm_device_status(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *dev) { - const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); - char *devstatus; - long status = 0; - - devstatus = pa_sprintf_malloc("_devstatus/%s", dev_name); - if (snd_use_case_geti(ucm->ucm_mgr, devstatus, &status) < 0) { - pa_log_debug("Failed to get status for UCM device %s", dev_name); - status = -1; - } - pa_xfree(devstatus); - - return status; -} - -static int ucm_device_disable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *dev) { - const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); - - /* If any of dev's conflicting devices is enabled, trying to disable - * dev gives an error despite the fact that it's already disabled. - * Check that dev is enabled to avoid this error. */ - if (ucm_device_status(ucm, dev) == 0) { - pa_log_debug("UCM device %s is already disabled", dev_name); - return 0; - } - - pa_log_debug("Disabling UCM device %s", dev_name); - if (snd_use_case_set(ucm->ucm_mgr, "_disdev", dev_name) < 0) { - pa_log("Failed to disable UCM device %s", dev_name); - return -1; - } - - return 0; -} - -static int ucm_device_enable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *dev) { - const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); - - /* We don't need to enable devices that are already enabled */ - if (ucm_device_status(ucm, dev) > 0) { - pa_log_debug("UCM device %s is already enabled", dev_name); - return 0; - } - - pa_log_debug("Enabling UCM device %s", dev_name); - if (snd_use_case_set(ucm->ucm_mgr, "_enadev", dev_name) < 0) { - pa_log("Failed to enable UCM device %s", dev_name); - return -1; - } - - return 0; -} - static int ucm_get_modifiers(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) { const char **mod_list; int num_mod, i; @@ -685,57 +626,6 @@ static int ucm_get_modifiers(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) return 0; }; -static long ucm_modifier_status(pa_alsa_ucm_config *ucm, pa_alsa_ucm_modifier *mod) { - const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME); - char *modstatus; - long status = 0; - - modstatus = pa_sprintf_malloc("_modstatus/%s", mod_name); - if (snd_use_case_geti(ucm->ucm_mgr, modstatus, &status) < 0) { - pa_log_debug("Failed to get status for UCM modifier %s", mod_name); - status = -1; - } - pa_xfree(modstatus); - - return status; -} - -static int ucm_modifier_disable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_modifier *mod) { - const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME); - - /* We don't need to disable modifiers that are already disabled */ - if (ucm_modifier_status(ucm, mod) == 0) { - pa_log_debug("UCM modifier %s is already disabled", mod_name); - return 0; - } - - pa_log_debug("Disabling UCM modifier %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); - return -1; - } - - return 0; -} - -static int ucm_modifier_enable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_modifier *mod) { - const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME); - - /* We don't need to enable modifiers that are already enabled */ - if (ucm_modifier_status(ucm, mod) > 0) { - pa_log_debug("UCM modifier %s is already enabled", mod_name); - return 0; - } - - pa_log_debug("Enabling UCM modifier %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); - return -1; - } - - return 0; -} - static void add_role_to_device(pa_alsa_ucm_device *dev, const char *dev_name, const char *role_name, const char *role) { const char *cur = pa_proplist_gets(dev->proplist, role_name); @@ -752,19 +642,27 @@ static void add_role_to_device(pa_alsa_ucm_device *dev, const char *dev_name, co role_name)); } -static void add_media_role(pa_alsa_ucm_device *dev, const char *role_name, const char *role, bool is_sink) { - const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); - const char *sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK); - const char *source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE); +static void add_media_role(const char *name, pa_alsa_ucm_device *list, const char *role_name, const char *role, bool is_sink) { + pa_alsa_ucm_device *d; - if (is_sink && sink) - add_role_to_device(dev, dev_name, role_name, role); - else if (!is_sink && source) - add_role_to_device(dev, dev_name, role_name, role); + PA_LLIST_FOREACH(d, list) { + const char *dev_name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME); + + if (pa_streq(dev_name, name)) { + const char *sink = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SINK); + const char *source = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SOURCE); + + if (is_sink && sink) + add_role_to_device(d, dev_name, role_name, role); + else if (!is_sink && source) + add_role_to_device(d, dev_name, role_name, role); + break; + } + } } static char *modifier_name_to_role(const char *mod_name, bool *is_sink) { - char *sub = NULL, *tmp, *pos; + char *sub = NULL, *tmp; *is_sink = false; @@ -774,32 +672,26 @@ static char *modifier_name_to_role(const char *mod_name, bool *is_sink) { } else if (pa_startswith(mod_name, "Capture")) sub = pa_xstrdup(mod_name + 7); - pos = sub; - while (pos && *pos == ' ') pos++; - - if (!pos || !*pos) { + if (!sub || !*sub) { pa_xfree(sub); pa_log_warn("Can't match media roles for modifier %s", mod_name); return NULL; } - tmp = pos; + tmp = sub; do { *tmp = tolower(*tmp); } while (*(++tmp)); - tmp = pa_xstrdup(pos); - pa_xfree(sub); - return tmp; + return sub; } -static void ucm_set_media_roles(pa_alsa_ucm_modifier *modifier, const char *mod_name) { - pa_alsa_ucm_device *dev; +static void ucm_set_media_roles(pa_alsa_ucm_modifier *modifier, pa_alsa_ucm_device *list, const char *mod_name) { + int i; bool is_sink = false; char *sub = NULL; const char *role_name; - uint32_t idx; sub = modifier_name_to_role(mod_name, &is_sink); if (!sub) @@ -809,11 +701,11 @@ static void ucm_set_media_roles(pa_alsa_ucm_modifier *modifier, const char *mod_ modifier->media_role = sub; role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES; - PA_IDXSET_FOREACH(dev, modifier->supported_devices, idx) { + for (i = 0; i < modifier->n_suppdev; i++) { /* if modifier has no specific pcm, we add role intent to its supported devices */ if (!pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SINK) && !pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SOURCE)) - add_media_role(dev, role_name, sub, is_sink); + add_media_role(modifier->supported_devices[i], list, role_name, sub, is_sink); } } @@ -821,17 +713,29 @@ static void append_lost_relationship(pa_alsa_ucm_device *dev) { uint32_t idx; pa_alsa_ucm_device *d; - PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) - if (pa_idxset_put(d->conflicting_devices, dev, NULL) == 0) - pa_log_warn("Add lost conflicting device %s to %s", - pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME), - pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME)); + if (dev->conflicting_devices) { + PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) { + if (!d->conflicting_devices) + d->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - PA_IDXSET_FOREACH(d, dev->supported_devices, idx) - if (pa_idxset_put(d->supported_devices, dev, NULL) == 0) - pa_log_warn("Add lost supported device %s to %s", - pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME), - pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME)); + if (pa_idxset_put(d->conflicting_devices, dev, NULL) == 0) + pa_log_warn("Add lost conflicting device %s to %s", + pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME), + pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME)); + } + } + + if (dev->supported_devices) { + PA_IDXSET_FOREACH(d, dev->supported_devices, idx) { + if (!d->supported_devices) + d->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + + if (pa_idxset_put(d->supported_devices, dev, NULL) == 0) + pa_log_warn("Add lost supported device %s to %s", + pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME), + pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME)); + } + } } int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index) { @@ -883,7 +787,7 @@ int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index) { free((void *)value); } - /* get a list of all UCM verbs for this card */ + /* get a list of all UCM verbs (profiles) for this card */ num_verbs = snd_use_case_verb_list(ucm->ucm_mgr, &verb_list); if (num_verbs < 0) { pa_log("UCM verb list not found for %s", card_name); @@ -972,11 +876,11 @@ int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name, cons const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME); /* Modifier properties */ - ucm_get_modifier_property(mod, uc_mgr, verb, mod_name); + ucm_get_modifier_property(mod, uc_mgr, mod_name); /* Set PA_PROP_DEVICE_INTENDED_ROLES property to devices */ pa_log_debug("Set media roles for verb %s, modifier %s", verb_name, mod_name); - ucm_set_media_roles(mod, mod_name); + ucm_set_media_roles(mod, verb->devices, mod_name); } *p_verb = verb; @@ -995,27 +899,43 @@ static void set_eld_devices(pa_hashmap *hash) pa_device_port *port; pa_alsa_ucm_port_data *data; pa_alsa_ucm_device *dev; + const char *eld_mixer_device_name; void *state; + int idx, eld_device; PA_HASHMAP_FOREACH(port, hash, state) { data = PA_DEVICE_PORT_DATA(port); - dev = data->device; - data->eld_device = dev->eld_device; - pa_xfree(data->eld_mixer_device_name); - data->eld_mixer_device_name = pa_xstrdup(dev->eld_mixer_device_name); + eld_mixer_device_name = NULL; + eld_device = -1; + PA_DYNARRAY_FOREACH(dev, data->devices, idx) { + if (dev->eld_device >= 0 && dev->eld_mixer_device_name) { + if (eld_device >= 0 && eld_device != dev->eld_device) { + pa_log_error("The ELD device is already set!"); + } else if (eld_mixer_device_name && pa_streq(dev->eld_mixer_device_name, eld_mixer_device_name)) { + pa_log_error("The ELD mixer device is already set (%s, %s)!", dev->eld_mixer_device_name, dev->eld_mixer_device_name); + } else { + eld_mixer_device_name = dev->eld_mixer_device_name; + eld_device = dev->eld_device; + } + } + } + data->eld_device = eld_device; + if (data->eld_mixer_device_name) + pa_xfree(data->eld_mixer_device_name); + data->eld_mixer_device_name = pa_xstrdup(eld_mixer_device_name); } } -static void update_mixer_paths(pa_hashmap *ports, const char *verb_name) { +static void update_mixer_paths(pa_hashmap *ports, const char *profile) { pa_device_port *port; pa_alsa_ucm_port_data *data; void *state; /* select volume controls on ports */ PA_HASHMAP_FOREACH(port, ports, state) { - pa_log_info("Updating mixer path for %s: %s", verb_name, port->name); + pa_log_info("Updating mixer path for %s: %s", profile, port->name); data = PA_DEVICE_PORT_DATA(port); - data->path = pa_hashmap_get(data->paths, verb_name); + data->path = pa_hashmap_get(data->paths, profile); } } @@ -1025,29 +945,39 @@ static void probe_volumes(pa_hashmap *hash, bool is_sink, snd_pcm_t *pcm_handle, pa_alsa_ucm_port_data *data; pa_alsa_ucm_device *dev; snd_mixer_t *mixer_handle; - const char *verb_name, *mdev; + const char *profile, *mdev, *mdev2; void *state, *state2; + int idx; PA_HASHMAP_FOREACH(port, hash, state) { data = PA_DEVICE_PORT_DATA(port); - dev = data->device; - mdev = get_mixer_device(dev, is_sink); + mdev = NULL; + PA_DYNARRAY_FOREACH(dev, data->devices, idx) { + mdev2 = get_mixer_device(dev, is_sink); + if (mdev && mdev2 && !pa_streq(mdev, mdev2)) { + pa_log_error("Two mixer device names found ('%s', '%s'), using s/w volume", mdev, mdev2); + goto fail; + } + if (mdev2) + mdev = mdev2; + } + if (mdev == NULL || !(mixer_handle = pa_alsa_open_mixer_by_name(mixers, mdev, true))) { pa_log_error("Failed to find a working mixer device (%s).", mdev); goto fail; } - PA_HASHMAP_FOREACH_KV(verb_name, path, data->paths, state2) { + PA_HASHMAP_FOREACH_KV(profile, path, data->paths, state2) { if (pa_alsa_path_probe(path, NULL, mixer_handle, ignore_dB) < 0) { pa_log_warn("Could not probe path: %s, using s/w volume", path->name); - pa_hashmap_remove(data->paths, verb_name); + pa_hashmap_remove(data->paths, profile); } else if (!path->has_volume && !path->has_mute) { pa_log_warn("Path %s is not a volume or mute control", path->name); - pa_hashmap_remove(data->paths, verb_name); + pa_hashmap_remove(data->paths, profile); } else pa_log_debug("Set up h/w %s using '%s' for %s:%s", path->has_volume ? "volume" : "mute", - path->name, verb_name, port->name); + path->name, profile, port->name); } } @@ -1061,141 +991,91 @@ fail: } } -static char *devset_name(pa_idxset *devices, const char *sep) { - int i = 0; - int num = pa_idxset_size(devices); - pa_alsa_ucm_device *sorted[num], *dev; - char *dev_names = NULL; - char *tmp = NULL; - uint32_t idx; - - PA_IDXSET_FOREACH(dev, devices, idx) { - sorted[i] = dev; - i++; - } - - /* Sort by alphabetical order so as to have a deterministic naming scheme */ - qsort(&sorted[0], num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp); - - for (i = 0; i < num; i++) { - dev = sorted[i]; - const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); - - if (!dev_names) { - dev_names = pa_xstrdup(dev_name); - } else { - tmp = pa_sprintf_malloc("%s%s%s", dev_names, sep, dev_name); - pa_xfree(dev_names); - dev_names = tmp; - } - } - - return dev_names; -} - -PA_UNUSED static char *devset_description(pa_idxset *devices, const char *sep) { - int i = 0; - int num = pa_idxset_size(devices); - pa_alsa_ucm_device *sorted[num], *dev; - char *dev_descs = NULL; - char *tmp = NULL; - uint32_t idx; - - PA_IDXSET_FOREACH(dev, devices, idx) { - sorted[i] = dev; - i++; - } - - /* Sort by alphabetical order to match devset_name() */ - qsort(&sorted[0], num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp); - - for (i = 0; i < num; i++) { - dev = sorted[i]; - const char *dev_desc = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION); - - if (!dev_descs) { - dev_descs = pa_xstrdup(dev_desc); - } else { - tmp = pa_sprintf_malloc("%s%s%s", dev_descs, sep, dev_desc); - pa_xfree(dev_descs); - dev_descs = tmp; - } - } - - return dev_descs; -} - -/* If invert is true, uses the formula 1/p = 1/p1 + 1/p2 + ... 1/pn. - * This way, the result will always be less than the individual components, - * yet higher components will lead to higher result. */ -static unsigned devset_playback_priority(pa_idxset *devices, bool invert) { - pa_alsa_ucm_device *dev; - uint32_t idx; - double priority = 0; - - PA_IDXSET_FOREACH(dev, devices, idx) { - if (dev->playback_priority > 0 && invert) - priority += 1.0 / dev->playback_priority; - else - priority += dev->playback_priority; - } - - if (priority > 0 && invert) - return 1.0 / priority; - - return (unsigned) priority; -} - -static unsigned devset_capture_priority(pa_idxset *devices, bool invert) { - pa_alsa_ucm_device *dev; - uint32_t idx; - double priority = 0; - - PA_IDXSET_FOREACH(dev, devices, idx) { - if (dev->capture_priority > 0 && invert) - priority += 1.0 / dev->capture_priority; - else - priority += dev->capture_priority; - } - - if (priority > 0 && invert) - return 1.0 / priority; - - return (unsigned) priority; -} - -void pa_alsa_ucm_add_port( +static void ucm_add_port_combination( pa_hashmap *hash, pa_alsa_ucm_mapping_context *context, bool is_sink, + pa_alsa_ucm_device **pdevices, + int num, pa_hashmap *ports, pa_card_profile *cp, pa_core *core) { pa_device_port *port; + int i; unsigned priority; + double prio2; char *name, *desc; const char *dev_name; const char *direction; - const char *verb_name; - pa_alsa_ucm_device *dev; + const char *profile; + pa_alsa_ucm_device *sorted[num], *dev; pa_alsa_ucm_port_data *data; pa_alsa_ucm_volume *vol; - pa_alsa_jack *jack; - pa_device_port_type_t type; + pa_alsa_jack *jack, *jack2; + pa_device_port_type_t type, type2; void *state; - dev = context->ucm_device; - if (!dev) - return; + for (i = 0; i < num; i++) + sorted[i] = pdevices[i]; + /* Sort by alphabetical order so as to have a deterministic naming scheme + * for combination ports */ + qsort(&sorted[0], num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp); + + dev = sorted[0]; dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); + name = pa_sprintf_malloc("%s%s", is_sink ? PA_UCM_PRE_TAG_OUTPUT : PA_UCM_PRE_TAG_INPUT, dev_name); - desc = pa_xstrdup(pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION)); + desc = num == 1 ? pa_xstrdup(pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION)) + : pa_sprintf_malloc("Combination port for %s", dev_name); + priority = is_sink ? dev->playback_priority : dev->capture_priority; + prio2 = (priority == 0 ? 0 : 1.0/priority); jack = ucm_get_jack(context->ucm, dev); type = dev->type; + for (i = 1; i < num; i++) { + char *tmp; + + dev = sorted[i]; + dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); + + tmp = pa_sprintf_malloc("%s+%s", name, dev_name); + pa_xfree(name); + name = tmp; + + tmp = pa_sprintf_malloc("%s,%s", desc, dev_name); + pa_xfree(desc); + desc = tmp; + + priority = is_sink ? dev->playback_priority : dev->capture_priority; + if (priority != 0 && prio2 > 0) + prio2 += 1.0/priority; + + jack2 = ucm_get_jack(context->ucm, dev); + if (jack2) { + if (jack && jack != jack2) + pa_log_warn("Multiple jacks per combined device '%s': '%s' '%s'", name, jack->name, jack2->name); + jack = jack2; + } + + type2 = dev->type; + if (type2 != PA_DEVICE_PORT_TYPE_UNKNOWN) { + if (type != PA_DEVICE_PORT_TYPE_UNKNOWN && type != type2) + pa_log_warn("Multiple device types per combined device '%s': %d %d", name, type, type2); + type = type2; + } + } + + /* Make combination ports always have lower priority, and use the formula + 1/p = 1/p1 + 1/p2 + ... 1/pn. + This way, the result will always be less than the individual components, + yet higher components will lead to higher result. */ + + if (num > 1) + priority = prio2 > 0 ? 1.0/prio2 : 0; + port = pa_hashmap_get(ports, name); if (!port) { pa_device_port_new_data port_data; @@ -1212,32 +1092,37 @@ void pa_alsa_ucm_add_port( pa_device_port_new_data_done(&port_data); data = PA_DEVICE_PORT_DATA(port); - ucm_port_data_init(data, context->ucm, port, dev); + ucm_port_data_init(data, context->ucm, port, pdevices, num); port->impl_free = ucm_port_data_free; pa_hashmap_put(ports, port->name, port); pa_log_debug("Add port %s: %s", port->name, port->description); - PA_HASHMAP_FOREACH_KV(verb_name, vol, is_sink ? dev->playback_volumes : dev->capture_volumes, state) { - pa_alsa_path *path = pa_alsa_path_synthesize(vol->mixer_elem, - is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT); + if (num == 1) { + /* To keep things simple and not worry about stacking controls, we only support hardware volumes on non-combination + * ports. */ + PA_HASHMAP_FOREACH_KV(profile, vol, is_sink ? dev->playback_volumes : dev->capture_volumes, state) { + pa_alsa_path *path = pa_alsa_path_synthesize(vol->mixer_elem, + is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT); - if (!path) - pa_log_warn("Failed to set up volume control: %s", vol->mixer_elem); - else { - if (vol->master_elem) { - pa_alsa_element *e = pa_alsa_element_get(path, vol->master_elem, false); - e->switch_use = PA_ALSA_SWITCH_MUTE; - e->volume_use = PA_ALSA_VOLUME_MERGE; + if (!path) + pa_log_warn("Failed to set up volume control: %s", vol->mixer_elem); + else { + if (vol->master_elem) { + pa_alsa_element *e = pa_alsa_element_get(path, vol->master_elem, false); + e->switch_use = PA_ALSA_SWITCH_MUTE; + e->volume_use = PA_ALSA_VOLUME_MERGE; + } + + pa_hashmap_put(data->paths, pa_xstrdup(profile), path); + + /* Add path also to already created empty path set */ + dev = sorted[0]; + if (is_sink) + pa_hashmap_put(dev->playback_mapping->output_path_set->paths, pa_xstrdup(vol->mixer_elem), path); + else + pa_hashmap_put(dev->capture_mapping->input_path_set->paths, pa_xstrdup(vol->mixer_elem), path); } - - pa_hashmap_put(data->paths, pa_xstrdup(verb_name), path); - - /* Add path also to already created empty path set */ - if (is_sink) - pa_hashmap_put(dev->playback_mapping->output_path_set->paths, pa_xstrdup(vol->mixer_elem), path); - else - pa_hashmap_put(dev->capture_mapping->input_path_set->paths, pa_xstrdup(vol->mixer_elem), path); } } } @@ -1258,127 +1143,113 @@ void pa_alsa_ucm_add_port( if (hash) { pa_hashmap_put(hash, port->name, port); } - - /* ELD devices */ - set_eld_devices(ports); } -static bool devset_supports_device(pa_idxset *devices, pa_alsa_ucm_device *dev) { - const char *sink, *sink2, *source, *source2; - pa_alsa_ucm_device *d; - uint32_t idx; +static int ucm_port_contains(const char *port_name, const char *dev_name, bool is_sink) { + int ret = 0; + const char *r; + const char *state = NULL; + size_t len; - pa_assert(devices); - pa_assert(dev); - - /* Can add anything to empty group */ - if (pa_idxset_isempty(devices)) - return true; - - /* Device already selected */ - if (pa_idxset_contains(devices, dev)) - return true; - - /* No conflicting device must already be selected */ - if (!pa_idxset_isdisjoint(devices, dev->conflicting_devices)) + if (!port_name || !dev_name) return false; - /* No already selected device must be unsupported */ - if (!pa_idxset_isempty(dev->supported_devices)) - if (!pa_idxset_issubset(devices, dev->supported_devices)) - return false; + port_name += is_sink ? strlen(PA_UCM_PRE_TAG_OUTPUT) : strlen(PA_UCM_PRE_TAG_INPUT); - sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK); - source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE); - - PA_IDXSET_FOREACH(d, devices, idx) { - /* Must not be unsupported by any selected device */ - if (!pa_idxset_isempty(d->supported_devices)) - if (!pa_idxset_contains(d->supported_devices, dev)) - return false; - - /* PlaybackPCM must not be the same as any selected device */ - sink2 = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SINK); - if (sink && sink2 && pa_streq(sink, sink2)) - return false; - - /* CapturePCM must not be the same as any selected device */ - source2 = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SOURCE); - if (source && source2 && pa_streq(source, source2)) - return false; - } - - return true; -} - -/* Iterates nonempty subsets of UCM devices that can be simultaneously - * used, including subsets of previously returned subsets. At start, - * *state should be NULL. It's not safe to modify the devices argument - * until iteration ends. The returned idxsets must be freed by the - * caller. */ -static pa_idxset *iterate_device_subsets(pa_idxset *devices, void **state) { - uint32_t idx; - pa_alsa_ucm_device *dev; - - pa_assert(devices); - pa_assert(state); - - if (*state == NULL) { - /* First iteration, start adding from first device */ - *state = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - dev = pa_idxset_first(devices, &idx); - - } else { - /* Backtrack the most recent device we added and skip it */ - dev = pa_idxset_steal_last(*state, NULL); - pa_idxset_get_by_data(devices, dev, &idx); - if (dev) - dev = pa_idxset_next(devices, &idx); - } - - /* Try adding devices we haven't decided on yet */ - for (; dev; dev = pa_idxset_next(devices, &idx)) { - if (devset_supports_device(*state, dev)) - pa_idxset_put(*state, dev, NULL); - } - - if (pa_idxset_isempty(*state)) { - /* No more choices to backtrack on, therefore no more subsets to - * return after this. Don't return the empty set, instead clean - * up and end iteration. */ - pa_idxset_free(*state, NULL); - *state = NULL; - return NULL; - } - - return pa_idxset_copy(*state, NULL); -} - -/* This a wrapper around iterate_device_subsets() that only returns the - * biggest possible groups and not any of their subsets. */ -static pa_idxset *iterate_maximal_device_subsets(pa_idxset *devices, void **state) { - uint32_t idx; - pa_alsa_ucm_device *dev; - pa_idxset *subset; - - pa_assert(devices); - pa_assert(state); - - subset = iterate_device_subsets(devices, state); - if (!subset) - return subset; - - /* Skip this group if it's incomplete, by checking if we can add any - * other device. If we can, this iteration is a subset of another - * group that we already returned or eventually return. */ - PA_IDXSET_FOREACH(dev, devices, idx) { - if (!pa_idxset_contains(subset, dev) && devset_supports_device(subset, dev)) { - pa_idxset_free(subset, NULL); - return iterate_maximal_device_subsets(devices, state); + while ((r = pa_split_in_place(port_name, "+", &len, &state))) { + if (strlen(dev_name) == len && !strncmp(r, dev_name, len)) { + ret = 1; + break; } } - return subset; + return ret; +} + +static int ucm_check_conformance( + pa_alsa_ucm_mapping_context *context, + pa_alsa_ucm_device **pdevices, + int dev_num, + pa_alsa_ucm_device *dev) { + + uint32_t idx; + pa_alsa_ucm_device *d; + int i; + + pa_assert(dev); + + pa_log_debug("Check device %s conformance with %d other devices", + pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME), dev_num); + if (dev_num == 0) { + pa_log_debug("First device in combination, number 1"); + return 1; + } + + if (dev->conflicting_devices) { /* the device defines conflicting devices */ + PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) { + for (i = 0; i < dev_num; i++) { + if (pdevices[i] == d) { + pa_log_debug("Conflicting device found"); + return 0; + } + } + } + } else if (dev->supported_devices) { /* the device defines supported devices */ + for (i = 0; i < dev_num; i++) { + if (!ucm_device_exists(dev->supported_devices, pdevices[i])) { + pa_log_debug("Supported device not found"); + return 0; + } + } + } else { /* not support any other devices */ + pa_log_debug("Not support any other devices"); + return 0; + } + + pa_log_debug("Device added to combination, number %d", dev_num + 1); + return 1; +} + +static inline pa_alsa_ucm_device *get_next_device(pa_idxset *idxset, uint32_t *idx) { + pa_alsa_ucm_device *dev; + + if (*idx == PA_IDXSET_INVALID) + dev = pa_idxset_first(idxset, idx); + else + dev = pa_idxset_next(idxset, idx); + + return dev; +} + +static void ucm_add_ports_combination( + pa_hashmap *hash, + pa_alsa_ucm_mapping_context *context, + bool is_sink, + pa_alsa_ucm_device **pdevices, + int dev_num, + uint32_t map_index, + pa_hashmap *ports, + pa_card_profile *cp, + pa_core *core) { + + pa_alsa_ucm_device *dev; + uint32_t idx = map_index; + + if ((dev = get_next_device(context->ucm_devices, &idx)) == NULL) + return; + + /* check if device at map_index can combine with existing devices combination */ + if (ucm_check_conformance(context, pdevices, dev_num, dev)) { + /* add device at map_index to devices combination */ + pdevices[dev_num] = dev; + /* add current devices combination as a new port */ + ucm_add_port_combination(hash, context, is_sink, pdevices, dev_num + 1, ports, cp, core); + /* try more elements combination */ + ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num + 1, idx, ports, cp, core); + } + + /* try other device with current elements number */ + ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num, idx, ports, cp, core); } static char* merge_roles(const char *cur, const char *add) { @@ -1410,6 +1281,28 @@ static char* merge_roles(const char *cur, const char *add) { return ret; } +void pa_alsa_ucm_add_ports_combination( + pa_hashmap *p, + pa_alsa_ucm_mapping_context *context, + bool is_sink, + pa_hashmap *ports, + pa_card_profile *cp, + pa_core *core) { + + pa_alsa_ucm_device **pdevices; + + pa_assert(context->ucm_devices); + + if (pa_idxset_size(context->ucm_devices) > 0) { + pdevices = pa_xnew(pa_alsa_ucm_device *, pa_idxset_size(context->ucm_devices)); + ucm_add_ports_combination(p, context, is_sink, pdevices, 0, PA_IDXSET_INVALID, ports, cp, core); + pa_xfree(pdevices); + } + + /* ELD devices */ + set_eld_devices(ports); +} + void pa_alsa_ucm_add_ports( pa_hashmap **p, pa_proplist *proplist, @@ -1419,6 +1312,7 @@ void pa_alsa_ucm_add_ports( snd_pcm_t *pcm_handle, bool ignore_dB) { + uint32_t idx; char *merged_roles; const char *role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES; pa_alsa_ucm_device *dev; @@ -1429,39 +1323,34 @@ void pa_alsa_ucm_add_ports( pa_assert(*p); /* add ports first */ - pa_alsa_ucm_add_port(*p, context, is_sink, card->ports, NULL, card->core); + pa_alsa_ucm_add_ports_combination(*p, context, is_sink, card->ports, NULL, card->core); /* now set up volume paths if any */ probe_volumes(*p, is_sink, pcm_handle, context->ucm->mixers, ignore_dB); - /* probe_volumes() removes per-verb paths from ports if probing them - * fails. The path for the current verb is cached in + /* probe_volumes() removes per-profile paths from ports if probing them + * fails. The path for the current profile is cached in * pa_alsa_ucm_port_data.path, which is not cleared by probe_volumes() if * the path gets removed, so we have to call update_mixer_paths() here to * unset the cached path if needed. */ - if (context->ucm->active_verb) { - const char *verb_name; - verb_name = pa_proplist_gets(context->ucm->active_verb->proplist, PA_ALSA_PROP_UCM_NAME); - update_mixer_paths(*p, verb_name); - } + if (card->card.active_profile_index < card->card.n_profiles) + update_mixer_paths(*p, card->card.profiles[card->card.active_profile_index]->name); /* then set property PA_PROP_DEVICE_INTENDED_ROLES */ merged_roles = pa_xstrdup(pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES)); - - dev = context->ucm_device; - if (dev) { + PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) { const char *roles = pa_proplist_gets(dev->proplist, role_name); tmp = merge_roles(merged_roles, roles); pa_xfree(merged_roles); merged_roles = tmp; } - mod = context->ucm_modifier; - if (mod) { - tmp = merge_roles(merged_roles, mod->media_role); - pa_xfree(merged_roles); - merged_roles = tmp; - } + if (context->ucm_modifiers) + PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) { + tmp = merge_roles(merged_roles, mod->media_role); + pa_xfree(merged_roles); + merged_roles = tmp; + } if (merged_roles) pa_proplist_sets(proplist, PA_PROP_DEVICE_INTENDED_ROLES, merged_roles); @@ -1471,81 +1360,85 @@ void pa_alsa_ucm_add_ports( } /* Change UCM verb and device to match selected card profile */ -int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, pa_alsa_profile *new_profile, pa_alsa_profile *old_profile) { +int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile) { int ret = 0; - const char *verb_name, *profile_name; + const char *profile; pa_alsa_ucm_verb *verb; - pa_alsa_mapping *map; - uint32_t idx; if (new_profile == old_profile) - return 0; + return ret; + else if (new_profile == NULL || old_profile == NULL) + profile = new_profile ? new_profile : SND_USE_CASE_VERB_INACTIVE; + else if (!pa_streq(new_profile, old_profile)) + profile = new_profile; + else + return ret; - if (new_profile == NULL) { - verb = NULL; - profile_name = SND_USE_CASE_VERB_INACTIVE; - verb_name = SND_USE_CASE_VERB_INACTIVE; - } else { - verb = new_profile->ucm_context.verb; - profile_name = new_profile->name; + /* change verb */ + pa_log_info("Set UCM verb to %s", profile); + if ((ret = snd_use_case_set(ucm->ucm_mgr, "_verb", profile)) < 0) { + pa_log("Failed to set verb %s: %s", profile, snd_strerror(ret)); + } + + /* find active verb */ + ucm->active_verb = NULL; + PA_LLIST_FOREACH(verb, ucm->verbs) { + const char *verb_name; verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME); - } - - pa_log_info("Set profile to %s", profile_name); - if (ucm->active_verb != verb) { - /* change verb */ - pa_log_info("Set UCM verb to %s", verb_name); - if ((snd_use_case_set(ucm->ucm_mgr, "_verb", verb_name)) < 0) { - pa_log("Failed to set verb %s", verb_name); - ret = -1; + if (pa_streq(verb_name, profile)) { + ucm->active_verb = verb; + break; } - - } else if (ucm->active_verb) { - /* Disable modifiers not in new profile. Has to be done before - * devices, because _dismod fails if a modifier's supported - * devices are disabled. */ - PA_IDXSET_FOREACH(map, old_profile->input_mappings, idx) - if (new_profile && !pa_idxset_contains(new_profile->input_mappings, map)) - if (map->ucm_context.ucm_modifier && ucm_modifier_disable(ucm, map->ucm_context.ucm_modifier) < 0) - ret = -1; - - PA_IDXSET_FOREACH(map, old_profile->output_mappings, idx) - if (new_profile && !pa_idxset_contains(new_profile->output_mappings, map)) - if (map->ucm_context.ucm_modifier && ucm_modifier_disable(ucm, map->ucm_context.ucm_modifier) < 0) - ret = -1; - - /* Disable devices not in new profile */ - PA_IDXSET_FOREACH(map, old_profile->input_mappings, idx) - if (new_profile && !pa_idxset_contains(new_profile->input_mappings, map)) - if (map->ucm_context.ucm_device && ucm_device_disable(ucm, map->ucm_context.ucm_device) < 0) - ret = -1; - - PA_IDXSET_FOREACH(map, old_profile->output_mappings, idx) - if (new_profile && !pa_idxset_contains(new_profile->output_mappings, map)) - if (map->ucm_context.ucm_device && ucm_device_disable(ucm, map->ucm_context.ucm_device) < 0) - ret = -1; } - ucm->active_verb = verb; - - update_mixer_paths(card->ports, verb_name); + update_mixer_paths(card->ports, profile); return ret; } -int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port) { +int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) { + int i; + int ret = 0; pa_alsa_ucm_config *ucm; + const char **enable_devs; + int enable_num = 0; + uint32_t idx; pa_alsa_ucm_device *dev; - pa_alsa_ucm_port_data *data; pa_assert(context && context->ucm); ucm = context->ucm; pa_assert(ucm->ucm_mgr); - data = PA_DEVICE_PORT_DATA(port); - dev = data->device; + enable_devs = pa_xnew(const char *, pa_idxset_size(context->ucm_devices)); - return ucm_device_enable(ucm, dev); + /* first disable then enable */ + PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) { + const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); + + if (ucm_port_contains(port->name, dev_name, is_sink)) + enable_devs[enable_num++] = dev_name; + else { + pa_log_debug("Disable ucm device %s", dev_name); + if (snd_use_case_set(ucm->ucm_mgr, "_disdev", dev_name) > 0) { + pa_log("Failed to disable ucm device %s", dev_name); + ret = -1; + break; + } + } + } + + for (i = 0; i < enable_num; i++) { + pa_log_debug("Enable ucm device %s", enable_devs[i]); + if (snd_use_case_set(ucm->ucm_mgr, "_enadev", enable_devs[i]) < 0) { + pa_log("Failed to enable ucm device %s", enable_devs[i]); + ret = -1; + break; + } + } + + pa_xfree(enable_devs); + + return ret; } static void ucm_add_mapping(pa_alsa_profile *p, pa_alsa_mapping *m) { @@ -1581,7 +1474,7 @@ static void alsa_mapping_add_ucm_device(pa_alsa_mapping *m, pa_alsa_ucm_device * const char *new_desc, *mdev; bool is_sink = m->direction == PA_ALSA_DIRECTION_OUTPUT; - m->ucm_context.ucm_device = device; + pa_idxset_put(m->ucm_context.ucm_devices, device, NULL); new_desc = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_DESCRIPTION); cur_desc = m->description; @@ -1610,7 +1503,7 @@ static void alsa_mapping_add_ucm_modifier(pa_alsa_mapping *m, pa_alsa_ucm_modifi const char *new_desc, *mod_name, *channel_str; uint32_t channels = 0; - m->ucm_context.ucm_modifier = modifier; + pa_idxset_put(m->ucm_context.ucm_modifiers, modifier, NULL); new_desc = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_DESCRIPTION); cur_desc = m->description; @@ -1651,11 +1544,17 @@ static void alsa_mapping_add_ucm_modifier(pa_alsa_mapping *m, pa_alsa_ucm_modifi pa_channel_map_init(&m->channel_map); } -static pa_alsa_mapping* ucm_alsa_mapping_get(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps, const char *verb_name, const char *ucm_name, bool is_sink) { +static pa_alsa_mapping* ucm_alsa_mapping_get(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps, const char *verb_name, const char *device_str, bool is_sink) { pa_alsa_mapping *m; char *mapping_name; + size_t ucm_alibpref_len = 0; - mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, ucm_name, is_sink ? "sink" : "source"); + /* find private alsa-lib's configuration device prefix */ + + if (ucm->alib_prefix && pa_startswith(device_str, ucm->alib_prefix)) + ucm_alibpref_len = strlen(ucm->alib_prefix); + + mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, device_str + ucm_alibpref_len, is_sink ? "sink" : "source"); m = pa_alsa_mapping_get(ps, mapping_name); @@ -1670,6 +1569,7 @@ static pa_alsa_mapping* ucm_alsa_mapping_get(pa_alsa_ucm_config *ucm, pa_alsa_pr static int ucm_create_mapping_direction( pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps, + pa_alsa_profile *p, pa_alsa_ucm_device *device, const char *verb_name, const char *device_name, @@ -1679,7 +1579,7 @@ static int ucm_create_mapping_direction( pa_alsa_mapping *m; unsigned priority, rate, channels; - m = ucm_alsa_mapping_get(ucm, ps, verb_name, device_name, is_sink); + m = ucm_alsa_mapping_get(ucm, ps, verb_name, device_str, is_sink); if (!m) return -1; @@ -1690,7 +1590,8 @@ static int ucm_create_mapping_direction( rate = is_sink ? device->playback_rate : device->capture_rate; channels = is_sink ? device->playback_channels : device->capture_channels; - if (!m->ucm_context.ucm_device) { /* new mapping */ + if (!m->ucm_context.ucm_devices) { /* new mapping */ + m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); m->ucm_context.ucm = ucm; m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT; @@ -1698,6 +1599,7 @@ static int ucm_create_mapping_direction( m->device_strings[0] = pa_xstrdup(device_str); m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT; + ucm_add_mapping(p, m); if (rate) m->sample_spec.rate = rate; pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA); @@ -1719,6 +1621,7 @@ static int ucm_create_mapping_direction( static int ucm_create_mapping_for_modifier( pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps, + pa_alsa_profile *p, pa_alsa_ucm_modifier *modifier, const char *verb_name, const char *mod_name, @@ -1727,14 +1630,16 @@ static int ucm_create_mapping_for_modifier( pa_alsa_mapping *m; - m = ucm_alsa_mapping_get(ucm, ps, verb_name, mod_name, is_sink); + m = ucm_alsa_mapping_get(ucm, ps, verb_name, device_str, is_sink); if (!m) return -1; pa_log_info("UCM mapping: %s modifier %s", m->name, mod_name); - if (!m->ucm_context.ucm_device && !m->ucm_context.ucm_modifier) { /* new mapping */ + if (!m->ucm_context.ucm_devices && !m->ucm_context.ucm_modifiers) { /* new mapping */ + m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); m->ucm_context.ucm = ucm; m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT; @@ -1743,7 +1648,10 @@ static int ucm_create_mapping_for_modifier( m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT; /* Modifier sinks should not be routed to by default */ m->priority = 0; - } + + ucm_add_mapping(p, m); + } else if (!m->ucm_context.ucm_modifiers) /* share pcm with device */ + m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); alsa_mapping_add_ucm_modifier(m, modifier); @@ -1753,6 +1661,7 @@ static int ucm_create_mapping_for_modifier( static int ucm_create_mapping( pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps, + pa_alsa_profile *p, pa_alsa_ucm_device *device, const char *verb_name, const char *device_name, @@ -1767,9 +1676,9 @@ static int ucm_create_mapping( } if (sink) - ret = ucm_create_mapping_direction(ucm, ps, device, verb_name, device_name, sink, true); + ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, sink, true); if (ret == 0 && source) - ret = ucm_create_mapping_direction(ucm, ps, device, verb_name, device_name, source, false); + ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, source, false); return ret; } @@ -1842,28 +1751,27 @@ static int ucm_create_profile( pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps, pa_alsa_ucm_verb *verb, - pa_idxset *mappings, - const char *profile_name, - const char *profile_desc, - unsigned int profile_priority) { + const char *verb_name, + const char *verb_desc) { pa_alsa_profile *p; - pa_alsa_mapping *map; - uint32_t idx; + pa_alsa_ucm_device *dev; + pa_alsa_ucm_modifier *mod; + int i = 0; + const char *name, *sink, *source; + unsigned int priority; pa_assert(ps); - if (pa_hashmap_get(ps->profiles, profile_name)) { - pa_log("Profile %s already exists", profile_name); + if (pa_hashmap_get(ps->profiles, verb_name)) { + pa_log("Verb %s already exists", verb_name); return -1; } p = pa_xnew0(pa_alsa_profile, 1); p->profile_set = ps; - p->name = pa_xstrdup(profile_name); - p->description = pa_xstrdup(profile_desc); - p->priority = profile_priority; - p->ucm_context.verb = verb; + p->name = pa_xstrdup(verb_name); + p->description = pa_xstrdup(verb_desc); p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); @@ -1871,36 +1779,10 @@ static int ucm_create_profile( p->supported = true; pa_hashmap_put(ps->profiles, p->name, p); - PA_IDXSET_FOREACH(map, mappings, idx) - ucm_add_mapping(p, map); - - pa_alsa_profile_dump(p); - - return 0; -} - -static int ucm_create_verb_profiles( - pa_alsa_ucm_config *ucm, - pa_alsa_profile_set *ps, - pa_alsa_ucm_verb *verb, - const char *verb_name, - const char *verb_desc) { - - pa_idxset *verb_devices, *p_devices, *p_mappings; - pa_alsa_ucm_device *dev; - pa_alsa_ucm_modifier *mod; - int i = 0; - int n_profiles = 0; - const char *name, *sink, *source; - char *p_name, *p_desc, *tmp; - unsigned int verb_priority, p_priority; - uint32_t idx; - void *state = NULL; - /* TODO: get profile priority from policy management */ - verb_priority = verb->priority; + priority = verb->priority; - if (verb_priority == 0) { + if (priority == 0) { char *verb_cmp, *c; c = verb_cmp = pa_xstrdup(verb_name); while (*c) { @@ -1909,13 +1791,15 @@ static int ucm_create_verb_profiles( } for (i = 0; verb_info[i].id; i++) { if (strcasecmp(verb_info[i].id, verb_cmp) == 0) { - verb_priority = verb_info[i].priority; + priority = verb_info[i].priority; break; } } pa_xfree(verb_cmp); } + p->priority = priority; + PA_LLIST_FOREACH(dev, verb->devices) { pa_alsa_jack *jack; const char *jack_hw_mute; @@ -1925,7 +1809,7 @@ static int ucm_create_verb_profiles( sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK); source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE); - ucm_create_mapping(ucm, ps, dev, verb_name, name, sink, source); + ucm_create_mapping(ucm, ps, p, dev, verb_name, name, sink, source); jack = ucm_get_jack(ucm, dev); if (jack) @@ -1976,74 +1860,12 @@ static int ucm_create_verb_profiles( source = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SOURCE); if (sink) - ucm_create_mapping_for_modifier(ucm, ps, mod, verb_name, name, sink, true); + ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, sink, true); else if (source) - ucm_create_mapping_for_modifier(ucm, ps, mod, verb_name, name, source, false); + ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, source, false); } - verb_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - PA_LLIST_FOREACH(dev, verb->devices) - pa_idxset_put(verb_devices, dev, NULL); - - while ((p_devices = iterate_maximal_device_subsets(verb_devices, &state))) { - p_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - - /* Add the mappings that include our selected devices */ - PA_IDXSET_FOREACH(dev, p_devices, idx) { - if (dev->playback_mapping) - pa_idxset_put(p_mappings, dev->playback_mapping, NULL); - if (dev->capture_mapping) - pa_idxset_put(p_mappings, dev->capture_mapping, NULL); - } - - /* Add mappings only for the modifiers that can work with our - * device selection */ - PA_LLIST_FOREACH(mod, verb->modifiers) - if (pa_idxset_isempty(mod->supported_devices) || pa_idxset_issubset(mod->supported_devices, p_devices)) - if (pa_idxset_isdisjoint(mod->conflicting_devices, p_devices)) { - if (mod->playback_mapping) - pa_idxset_put(p_mappings, mod->playback_mapping, NULL); - if (mod->capture_mapping) - pa_idxset_put(p_mappings, mod->capture_mapping, NULL); - } - - /* If we'll have multiple profiles for this verb, their names - * must be unique. Use a list of chosen devices to disambiguate - * them. If the profile contains all devices of a verb, we'll - * generate only onle profile whose name should be the verb - * name. GUIs usually show the profile description instead of - * the name, add the device names to those as well. */ - tmp = devset_name(p_devices, ", "); - if (pa_idxset_equals(p_devices, verb_devices)) { - p_name = pa_xstrdup(verb_name); - p_desc = pa_xstrdup(verb_desc); - } else { - p_name = pa_sprintf_malloc("%s (%s)", verb_name, tmp); - p_desc = pa_sprintf_malloc("%s (%s)", verb_desc, tmp); - } - - /* Make sure profiles with higher-priority devices are - * prioritized. */ - p_priority = verb_priority + devset_playback_priority(p_devices, false) + devset_capture_priority(p_devices, false); - - if (ucm_create_profile(ucm, ps, verb, p_mappings, p_name, p_desc, p_priority) == 0) { - pa_log_debug("Created profile %s for UCM verb %s", p_name, verb_name); - n_profiles++; - } - - pa_xfree(tmp); - pa_xfree(p_name); - pa_xfree(p_desc); - pa_idxset_free(p_mappings, NULL); - pa_idxset_free(p_devices, NULL); - } - - pa_idxset_free(verb_devices, NULL); - - if (n_profiles == 0) { - pa_log("UCM verb %s created no profiles", verb_name); - return -1; - } + pa_alsa_profile_dump(p); return 0; } @@ -2052,6 +1874,7 @@ static void mapping_init_eld(pa_alsa_mapping *m, snd_pcm_t *pcm) { pa_alsa_ucm_mapping_context *context = &m->ucm_context; pa_alsa_ucm_device *dev; + uint32_t idx; char *mdev, *alib_prefix; snd_pcm_info_t *info; int pcm_card, pcm_device; @@ -2067,12 +1890,13 @@ static void mapping_init_eld(pa_alsa_mapping *m, snd_pcm_t *pcm) alib_prefix = context->ucm->alib_prefix; - dev = context->ucm_device; - mdev = pa_sprintf_malloc("%shw:%i", alib_prefix ? alib_prefix : "", pcm_card); - if (mdev == NULL) - return; - dev->eld_mixer_device_name = mdev; - dev->eld_device = pcm_device; + PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) { + mdev = pa_sprintf_malloc("%shw:%i", alib_prefix ? alib_prefix : "", pcm_card); + if (mdev == NULL) + continue; + dev->eld_mixer_device_name = mdev; + dev->eld_device = pcm_device; + } } static snd_pcm_t* mapping_open_pcm(pa_alsa_ucm_config *ucm, pa_alsa_mapping *m, int mode) { @@ -2094,7 +1918,7 @@ static snd_pcm_t* mapping_open_pcm(pa_alsa_ucm_config *ucm, pa_alsa_mapping *m, try_buffer_size = ucm->default_n_fragments * try_period_size; pcm = pa_alsa_open_by_device_string(m->device_strings[0], NULL, &try_ss, - &try_map, mode, &try_period_size, &try_buffer_size, 0, NULL, NULL, NULL, NULL, exact_channels); + &try_map, mode, &try_period_size, &try_buffer_size, 0, NULL, NULL, exact_channels); if (pcm) { if (!exact_channels) @@ -2136,39 +1960,38 @@ static void ucm_mapping_jack_probe(pa_alsa_mapping *m, pa_hashmap *mixers) { snd_mixer_t *mixer_handle; pa_alsa_ucm_mapping_context *context = &m->ucm_context; pa_alsa_ucm_device *dev; - bool has_control; + uint32_t idx; - dev = context->ucm_device; - if (!dev->jack || !dev->jack->mixer_device_name) - return; + PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) { + bool has_control; - mixer_handle = pa_alsa_open_mixer_by_name(mixers, dev->jack->mixer_device_name, true); - if (!mixer_handle) { - pa_log_error("Unable to determine open mixer device '%s' for jack %s", dev->jack->mixer_device_name, dev->jack->name); - return; + if (!dev->jack || !dev->jack->mixer_device_name) + continue; + + mixer_handle = pa_alsa_open_mixer_by_name(mixers, dev->jack->mixer_device_name, true); + if (!mixer_handle) { + pa_log_error("Unable to determine open mixer device '%s' for jack %s", dev->jack->mixer_device_name, dev->jack->name); + continue; + } + + has_control = pa_alsa_mixer_find_card(mixer_handle, &dev->jack->alsa_id, 0) != NULL; + pa_alsa_jack_set_has_control(dev->jack, has_control); + pa_log_info("UCM jack %s has_control=%d", dev->jack->name, dev->jack->has_control); } - - has_control = pa_alsa_mixer_find_card(mixer_handle, &dev->jack->alsa_id, 0) != NULL; - pa_alsa_jack_set_has_control(dev->jack, has_control); - pa_log_info("UCM jack %s has_control=%d", dev->jack->name, dev->jack->has_control); } static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps) { void *state; pa_alsa_profile *p; pa_alsa_mapping *m; - const char *verb_name; uint32_t idx; PA_HASHMAP_FOREACH(p, ps->profiles, state) { - pa_log_info("Probing profile %s", p->name); - /* change verb */ - verb_name = pa_proplist_gets(p->ucm_context.verb->proplist, PA_ALSA_PROP_UCM_NAME); - pa_log_info("Set ucm verb to %s", verb_name); + pa_log_info("Set ucm verb to %s", p->name); - if ((snd_use_case_set(ucm->ucm_mgr, "_verb", verb_name)) < 0) { - pa_log("Failed to set verb %s", verb_name); + if ((snd_use_case_set(ucm->ucm_mgr, "_verb", p->name)) < 0) { + pa_log("Failed to set verb %s", p->name); p->supported = false; continue; } @@ -2238,7 +2061,7 @@ pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_cha (pa_free_cb_t) pa_alsa_profile_free); ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - /* create profiles for each verb */ + /* create a profile for each verb */ PA_LLIST_FOREACH(verb, ucm->verbs) { const char *verb_name; const char *verb_desc; @@ -2250,7 +2073,7 @@ pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_cha continue; } - ucm_create_verb_profiles(ucm, ps, verb, verb_name, verb_desc); + ucm_create_profile(ucm, ps, verb, verb_name, verb_desc); } ucm_probe_profile_set(ucm, ps); @@ -2279,8 +2102,10 @@ static void free_verb(pa_alsa_ucm_verb *verb) { pa_proplist_free(di->proplist); - pa_idxset_free(di->conflicting_devices, NULL); - pa_idxset_free(di->supported_devices, NULL); + if (di->conflicting_devices) + pa_idxset_free(di->conflicting_devices, NULL); + if (di->supported_devices) + pa_idxset_free(di->supported_devices, NULL); pa_xfree(di->eld_mixer_device_name); @@ -2290,8 +2115,10 @@ static void free_verb(pa_alsa_ucm_verb *verb) { PA_LLIST_FOREACH_SAFE(mi, mn, verb->modifiers) { PA_LLIST_REMOVE(pa_alsa_ucm_modifier, verb->modifiers, mi); pa_proplist_free(mi->proplist); - pa_idxset_free(mi->conflicting_devices, NULL); - pa_idxset_free(mi->supported_devices, NULL); + if (mi->n_suppdev > 0) + snd_use_case_free_list(mi->supported_devices, mi->n_suppdev); + if (mi->n_confdev > 0) + snd_use_case_free_list(mi->conflicting_devices, mi->n_confdev); pa_xfree(mi->media_role); pa_xfree(mi); } @@ -2339,22 +2166,29 @@ void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) { void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) { pa_alsa_ucm_device *dev; pa_alsa_ucm_modifier *mod; + uint32_t idx; - dev = context->ucm_device; - if (dev) { + if (context->ucm_devices) { /* clear ucm device pointer to mapping */ - if (context->direction == PA_DIRECTION_OUTPUT) - dev->playback_mapping = NULL; - else - dev->capture_mapping = NULL; + PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) { + if (context->direction == PA_DIRECTION_OUTPUT) + dev->playback_mapping = NULL; + else + dev->capture_mapping = NULL; + } + + pa_idxset_free(context->ucm_devices, NULL); } - mod = context->ucm_modifier; - if (mod) { - if (context->direction == PA_DIRECTION_OUTPUT) - mod->playback_mapping = NULL; - else - mod->capture_mapping = NULL; + if (context->ucm_modifiers) { + PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) { + if (context->direction == PA_DIRECTION_OUTPUT) + mod->playback_mapping = NULL; + else + mod->capture_mapping = NULL; + } + + pa_idxset_free(context->ucm_modifiers, NULL); } } @@ -2368,7 +2202,12 @@ void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, p PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) { if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) { if (mod->enabled_counter == 0) { - ucm_modifier_enable(ucm, mod); + const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME); + + pa_log_info("Enable ucm modifier %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++; @@ -2388,14 +2227,27 @@ void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_ if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) { mod->enabled_counter--; - if (mod->enabled_counter == 0) - ucm_modifier_disable(ucm, mod); + if (mod->enabled_counter == 0) { + const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME); + + pa_log_info("Disable ucm modifier %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); + } + } break; } } } +static void device_add_ucm_port(pa_alsa_ucm_device *device, pa_alsa_ucm_port_data *port) { + pa_assert(device); + pa_assert(port); + + pa_dynarray_append(device->ucm_ports, port); +} + static void device_set_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack) { pa_assert(device); pa_assert(jack); @@ -2428,7 +2280,7 @@ static void device_set_available(pa_alsa_ucm_device *device, pa_available_t avai device->available = available; PA_DYNARRAY_FOREACH(port, device->ucm_ports, idx) - pa_device_port_set_available(port->core_port, port->device->available); + ucm_port_update_available(port); } void pa_alsa_ucm_device_update_available(pa_alsa_ucm_device *device) { @@ -2452,21 +2304,26 @@ void pa_alsa_ucm_device_update_available(pa_alsa_ucm_device *device) { } static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port, - pa_alsa_ucm_device *device) { + pa_alsa_ucm_device **devices, unsigned n_devices) { + unsigned i; + pa_assert(ucm); pa_assert(core_port); - pa_assert(device); + pa_assert(devices); port->ucm = ucm; port->core_port = core_port; + port->devices = pa_dynarray_new(NULL); port->eld_device = -1; - port->device = device; - pa_dynarray_append(device->ucm_ports, port); + for (i = 0; i < n_devices; i++) { + pa_dynarray_append(port->devices, devices[i]); + device_add_ucm_port(devices[i], port); + } port->paths = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree, NULL); - pa_device_port_set_available(port->core_port, port->device->available); + ucm_port_update_available(port); } static void ucm_port_data_free(pa_device_port *port) { @@ -2476,12 +2333,34 @@ static void ucm_port_data_free(pa_device_port *port) { ucm_port = PA_DEVICE_PORT_DATA(port); + if (ucm_port->devices) + pa_dynarray_free(ucm_port->devices); + if (ucm_port->paths) pa_hashmap_free(ucm_port->paths); pa_xfree(ucm_port->eld_mixer_device_name); } +static void ucm_port_update_available(pa_alsa_ucm_port_data *port) { + pa_alsa_ucm_device *device; + unsigned idx; + pa_available_t available = PA_AVAILABLE_YES; + + pa_assert(port); + + PA_DYNARRAY_FOREACH(device, port->devices, idx) { + if (device->available == PA_AVAILABLE_UNKNOWN) + available = PA_AVAILABLE_UNKNOWN; + else if (device->available == PA_AVAILABLE_NO) { + available = PA_AVAILABLE_NO; + break; + } + } + + pa_device_port_set_available(port->core_port, available); +} + #else /* HAVE_ALSA_UCM */ /* Dummy functions for systems without UCM support */ @@ -2495,7 +2374,7 @@ pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_cha return NULL; } -int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, pa_alsa_profile *new_profile, pa_alsa_profile *old_profile) { +int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile) { return -1; } @@ -2513,7 +2392,7 @@ void pa_alsa_ucm_add_ports( bool ignore_dB) { } -void pa_alsa_ucm_add_port( +void pa_alsa_ucm_add_ports_combination( pa_hashmap *hash, pa_alsa_ucm_mapping_context *context, bool is_sink, @@ -2522,7 +2401,7 @@ void pa_alsa_ucm_add_port( pa_core *core) { } -int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port) { +int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) { return -1; } diff --git a/spa/plugins/alsa/acp/alsa-ucm.h b/spa/plugins/alsa/acp/alsa-ucm.h index 9a5909598..696209eba 100644 --- a/spa/plugins/alsa/acp/alsa-ucm.h +++ b/spa/plugins/alsa/acp/alsa-ucm.h @@ -142,13 +142,12 @@ typedef struct pa_alsa_ucm_modifier pa_alsa_ucm_modifier; typedef struct pa_alsa_ucm_device pa_alsa_ucm_device; typedef struct pa_alsa_ucm_config pa_alsa_ucm_config; typedef struct pa_alsa_ucm_mapping_context pa_alsa_ucm_mapping_context; -typedef struct pa_alsa_ucm_profile_context pa_alsa_ucm_profile_context; typedef struct pa_alsa_ucm_port_data pa_alsa_ucm_port_data; typedef struct pa_alsa_ucm_volume pa_alsa_ucm_volume; int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index); pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map); -int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, pa_alsa_profile *new_profile, pa_alsa_profile *old_profile); +int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile); int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name, const char *verb_desc, pa_alsa_ucm_verb **p_verb); @@ -160,14 +159,14 @@ void pa_alsa_ucm_add_ports( pa_card *card, snd_pcm_t *pcm_handle, bool ignore_dB); -void pa_alsa_ucm_add_port( +void pa_alsa_ucm_add_ports_combination( pa_hashmap *hash, pa_alsa_ucm_mapping_context *context, bool is_sink, pa_hashmap *ports, pa_card_profile *cp, pa_core *core); -int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port); +int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink); void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm); void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context); @@ -224,8 +223,11 @@ struct pa_alsa_ucm_modifier { pa_proplist *proplist; - pa_idxset *conflicting_devices; - pa_idxset *supported_devices; + int n_confdev; + int n_suppdev; + + const char **conflicting_devices; + const char **supported_devices; pa_direction_t action_direction; @@ -268,23 +270,21 @@ struct pa_alsa_ucm_mapping_context { pa_alsa_ucm_config *ucm; pa_direction_t direction; - pa_alsa_ucm_device *ucm_device; - pa_alsa_ucm_modifier *ucm_modifier; -}; - -struct pa_alsa_ucm_profile_context { - pa_alsa_ucm_verb *verb; + pa_idxset *ucm_devices; + pa_idxset *ucm_modifiers; }; struct pa_alsa_ucm_port_data { pa_alsa_ucm_config *ucm; pa_device_port *core_port; - pa_alsa_ucm_device *device; + /* A single port will be associated with multiple devices if it represents + * a combination of devices. */ + pa_dynarray *devices; /* pa_alsa_ucm_device */ - /* verb name -> pa_alsa_path for volume control */ + /* profile name -> pa_alsa_path for volume control */ pa_hashmap *paths; - /* Current path, set when activating verb */ + /* Current path, set when activating profile */ pa_alsa_path *path; /* ELD info */ diff --git a/spa/plugins/alsa/acp/alsa-util.c b/spa/plugins/alsa/acp/alsa-util.c index 4630144f8..38ae934eb 100644 --- a/spa/plugins/alsa/acp/alsa-util.c +++ b/spa/plugins/alsa/acp/alsa-util.c @@ -505,8 +505,6 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( snd_pcm_uframes_t tsched_size, bool *use_mmap, bool *use_tsched, - pa_sample_format_t **query_supported_formats, - unsigned int **query_supported_rates, pa_alsa_profile_set *ps, pa_alsa_mapping **mapping) { @@ -545,8 +543,6 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( tsched_size, use_mmap, use_tsched, - query_supported_formats, - query_supported_rates, m); if (pcm_handle) { @@ -574,8 +570,6 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( tsched_size, use_mmap, use_tsched, - query_supported_formats, - query_supported_rates, m); if (pcm_handle) { @@ -600,8 +594,6 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( tsched_size, use_mmap, use_tsched, - query_supported_formats, - query_supported_rates, false); pa_xfree(d); @@ -623,8 +615,6 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping( snd_pcm_uframes_t tsched_size, bool *use_mmap, bool *use_tsched, - pa_sample_format_t **query_supported_formats, - unsigned int **query_supported_rates, pa_alsa_mapping *m) { snd_pcm_t *pcm_handle; @@ -654,8 +644,6 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping( tsched_size, use_mmap, use_tsched, - query_supported_formats, - query_supported_rates, pa_channel_map_valid(&m->channel_map) /* Query the channel count if we don't know what we want */); if (!pcm_handle) @@ -693,8 +681,6 @@ snd_pcm_t *pa_alsa_open_by_device_string( snd_pcm_uframes_t tsched_size, bool *use_mmap, bool *use_tsched, - pa_sample_format_t **query_supported_formats, - unsigned int **query_supported_rates, bool require_exact_channel_number) { int err; @@ -722,12 +708,6 @@ snd_pcm_t *pa_alsa_open_by_device_string( pa_log_info("ALSA device open '%s' %s: %p", d, mode == SND_PCM_STREAM_CAPTURE ? "capture" : "playback", pcm_handle); - if (query_supported_formats) - *query_supported_formats = pa_alsa_get_supported_formats(pcm_handle, ss->format); - - if (query_supported_rates) - *query_supported_rates = pa_alsa_get_supported_rates(pcm_handle, ss->rate); - if ((err = pa_alsa_set_hw_params( pcm_handle, ss, @@ -801,8 +781,6 @@ snd_pcm_t *pa_alsa_open_by_template( snd_pcm_uframes_t tsched_size, bool *use_mmap, bool *use_tsched, - pa_sample_format_t **query_supported_formats, - unsigned int **query_supported_rates, bool require_exact_channel_number) { snd_pcm_t *pcm_handle; @@ -824,8 +802,6 @@ snd_pcm_t *pa_alsa_open_by_template( tsched_size, use_mmap, use_tsched, - query_supported_formats, - query_supported_rates, require_exact_channel_number); pa_xfree(d); @@ -1435,24 +1411,6 @@ char *pa_alsa_get_reserve_name(const char *device) { return pa_sprintf_malloc("Audio%i", i); } -#endif - -static void dump_supported_rates(unsigned int* values) -{ - pa_strbuf *buf; - char *str; - int i; - - buf = pa_strbuf_new(); - - for (i = 0; values[i]; i++) { - pa_strbuf_printf(buf, " %u", values[i]); - } - - str = pa_strbuf_to_string_free(buf); - pa_log_debug("Supported rates:%s", str); - pa_xfree(str); -} unsigned int *pa_alsa_get_supported_rates(snd_pcm_t *pcm, unsigned int fallback_rate) { static unsigned int all_rates[] = { 8000, 11025, 12000, @@ -1460,8 +1418,7 @@ unsigned int *pa_alsa_get_supported_rates(snd_pcm_t *pcm, unsigned int fallback_ 32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000, - 352800, 384000, - 705600, 768000 }; + 384000 }; bool supported[PA_ELEMENTSOF(all_rates)] = { false, }; snd_pcm_hw_params_t *hwparams; unsigned int i, j, n, *rates = NULL; @@ -1503,40 +1460,39 @@ unsigned int *pa_alsa_get_supported_rates(snd_pcm_t *pcm, unsigned int fallback_ rates[1] = 0; } - dump_supported_rates(rates); return rates; } pa_sample_format_t *pa_alsa_get_supported_formats(snd_pcm_t *pcm, pa_sample_format_t fallback_format) { - static const snd_pcm_format_t format_trans_to_pcm[] = { - [PA_SAMPLE_U8] = SND_PCM_FORMAT_U8, - [PA_SAMPLE_ALAW] = SND_PCM_FORMAT_A_LAW, - [PA_SAMPLE_ULAW] = SND_PCM_FORMAT_MU_LAW, - [PA_SAMPLE_S16LE] = SND_PCM_FORMAT_S16_LE, - [PA_SAMPLE_S16BE] = SND_PCM_FORMAT_S16_BE, - [PA_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE, - [PA_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE, - [PA_SAMPLE_S32LE] = SND_PCM_FORMAT_S32_LE, - [PA_SAMPLE_S32BE] = SND_PCM_FORMAT_S32_BE, - [PA_SAMPLE_S24LE] = SND_PCM_FORMAT_S24_3LE, - [PA_SAMPLE_S24BE] = SND_PCM_FORMAT_S24_3BE, - [PA_SAMPLE_S24_32LE] = SND_PCM_FORMAT_S24_LE, - [PA_SAMPLE_S24_32BE] = SND_PCM_FORMAT_S24_BE, + static const snd_pcm_format_t format_trans_to_pa[] = { + [SND_PCM_FORMAT_U8] = PA_SAMPLE_U8, + [SND_PCM_FORMAT_A_LAW] = PA_SAMPLE_ALAW, + [SND_PCM_FORMAT_MU_LAW] = PA_SAMPLE_ULAW, + [SND_PCM_FORMAT_S16_LE] = PA_SAMPLE_S16LE, + [SND_PCM_FORMAT_S16_BE] = PA_SAMPLE_S16BE, + [SND_PCM_FORMAT_FLOAT_LE] = PA_SAMPLE_FLOAT32LE, + [SND_PCM_FORMAT_FLOAT_BE] = PA_SAMPLE_FLOAT32BE, + [SND_PCM_FORMAT_S32_LE] = PA_SAMPLE_S32LE, + [SND_PCM_FORMAT_S32_BE] = PA_SAMPLE_S32BE, + [SND_PCM_FORMAT_S24_3LE] = PA_SAMPLE_S24LE, + [SND_PCM_FORMAT_S24_3BE] = PA_SAMPLE_S24BE, + [SND_PCM_FORMAT_S24_LE] = PA_SAMPLE_S24_32LE, + [SND_PCM_FORMAT_S24_BE] = PA_SAMPLE_S24_32BE, }; - static const pa_sample_format_t all_formats[] = { - PA_SAMPLE_U8, - PA_SAMPLE_ALAW, - PA_SAMPLE_ULAW, - PA_SAMPLE_S16LE, - PA_SAMPLE_S16BE, - PA_SAMPLE_FLOAT32LE, - PA_SAMPLE_FLOAT32BE, - PA_SAMPLE_S32LE, - PA_SAMPLE_S32BE, - PA_SAMPLE_S24LE, - PA_SAMPLE_S24BE, - PA_SAMPLE_S24_32LE, - PA_SAMPLE_S24_32BE, + static const snd_pcm_format_t all_formats[] = { + SND_PCM_FORMAT_U8, + SND_PCM_FORMAT_A_LAW, + SND_PCM_FORMAT_MU_LAW, + SND_PCM_FORMAT_S16_LE, + SND_PCM_FORMAT_S16_BE, + SND_PCM_FORMAT_FLOAT_LE, + SND_PCM_FORMAT_FLOAT_BE, + SND_PCM_FORMAT_S32_LE, + SND_PCM_FORMAT_S32_BE, + SND_PCM_FORMAT_S24_3LE, + SND_PCM_FORMAT_S24_3BE, + SND_PCM_FORMAT_S24_LE, + SND_PCM_FORMAT_S24_BE, }; bool supported[PA_ELEMENTSOF(all_formats)] = { false, @@ -1554,7 +1510,7 @@ pa_sample_format_t *pa_alsa_get_supported_formats(snd_pcm_t *pcm, pa_sample_form } for (i = 0, n = 0; i < PA_ELEMENTSOF(all_formats); i++) { - if (snd_pcm_hw_params_test_format(pcm, hwparams, format_trans_to_pcm[all_formats[i]]) == 0) { + if (snd_pcm_hw_params_test_format(pcm, hwparams, all_formats[i]) == 0) { supported[i] = true; n++; } @@ -1565,7 +1521,7 @@ pa_sample_format_t *pa_alsa_get_supported_formats(snd_pcm_t *pcm, pa_sample_form for (i = 0, j = 0; i < PA_ELEMENTSOF(all_formats); i++) { if (supported[i]) - formats[j++] = all_formats[i]; + formats[j++] = format_trans_to_pa[all_formats[i]]; } formats[j] = PA_SAMPLE_MAX; @@ -1573,7 +1529,7 @@ pa_sample_format_t *pa_alsa_get_supported_formats(snd_pcm_t *pcm, pa_sample_form formats = pa_xnew(pa_sample_format_t, 2); formats[0] = fallback_format; - if ((ret = snd_pcm_hw_params_set_format(pcm, hwparams, format_trans_to_pcm[formats[0]])) < 0) { + if ((ret = snd_pcm_hw_params_set_format(pcm, hwparams, format_trans_to_pa[formats[0]])) < 0) { pa_log_debug("snd_pcm_hw_params_set_format() failed: %s", pa_alsa_strerror(ret)); pa_xfree(formats); return NULL; @@ -1584,6 +1540,7 @@ pa_sample_format_t *pa_alsa_get_supported_formats(snd_pcm_t *pcm, pa_sample_form return formats; } +#endif bool pa_alsa_pcm_is_hw(snd_pcm_t *pcm) { snd_pcm_info_t* info; @@ -1643,16 +1600,14 @@ static snd_mixer_elem_t *pa_alsa_mixer_find(snd_mixer_t *mixer, snd_ctl_elem_iface_t iface, const char *name, unsigned int index, - unsigned int device, - unsigned int subdevice) { + unsigned int device) { snd_mixer_elem_t *elem; for (elem = snd_mixer_first_elem(mixer); elem; elem = snd_mixer_elem_next(elem)) { - snd_hctl_elem_t **_helem, *helem; + snd_hctl_elem_t *helem; if (snd_mixer_elem_get_type(elem) != SND_MIXER_ELEM_PULSEAUDIO) continue; - _helem = snd_mixer_elem_get_private(elem); - helem = *_helem; + helem = snd_mixer_elem_get_private(elem); if (snd_hctl_elem_get_interface(helem) != iface) continue; if (!pa_streq(snd_hctl_elem_get_name(helem), name)) @@ -1661,19 +1616,17 @@ static snd_mixer_elem_t *pa_alsa_mixer_find(snd_mixer_t *mixer, continue; if (snd_hctl_elem_get_device(helem) != device) continue; - if (snd_hctl_elem_get_subdevice(helem) != subdevice) - continue; return elem; } return NULL; } snd_mixer_elem_t *pa_alsa_mixer_find_card(snd_mixer_t *mixer, struct pa_alsa_mixer_id *alsa_id, unsigned int device) { - return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_CARD, alsa_id->name, alsa_id->index, device, 0); + return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_CARD, alsa_id->name, alsa_id->index, device); } snd_mixer_elem_t *pa_alsa_mixer_find_pcm(snd_mixer_t *mixer, const char *name, unsigned int device) { - return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_PCM, name, 0, device, 0); + return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_PCM, name, 0, device); } static int mixer_class_compare(const snd_mixer_elem_t *c1, const snd_mixer_elem_t *c2) @@ -1682,26 +1635,15 @@ static int mixer_class_compare(const snd_mixer_elem_t *c1, const snd_mixer_elem_ return c1 == c2 ? 0 : (c1 > c2 ? 1 : -1); } -static void mixer_melem_free(snd_mixer_elem_t *elem) -{ - snd_hctl_elem_t **_helem; - _helem = snd_mixer_elem_get_private(elem); - pa_xfree(_helem); -} - static int mixer_class_event(snd_mixer_class_t *class, unsigned int mask, snd_hctl_elem_t *helem, snd_mixer_elem_t *melem) { int err; const char *name = snd_hctl_elem_get_name(helem); - snd_hctl_elem_t **_helem; - /* NOTE: The remove event is defined as '~0U`. */ + // NOTE: The remove event defined as '~0U`. if (mask == SND_CTL_EVENT_MASK_REMOVE) { - /* NOTE: Unless we remove the pointer to melem from the linked-list at - * private_data of helem, an assertion will be hit in alsa-lib since - * the list is not empty. */ - _helem = snd_mixer_elem_get_private(melem); - *_helem = NULL; + // NOTE: unless remove pointer to melem from link-list at private_data of helem, hits + // assersion in alsa-lib since the list is not empty. snd_mixer_elem_detach(melem, helem); } else if (mask & SND_CTL_EVENT_MASK_ADD) { snd_ctl_elem_iface_t iface = snd_hctl_elem_get_interface(helem); @@ -1711,50 +1653,26 @@ static int mixer_class_event(snd_mixer_class_t *class, unsigned int mask, const char *name = snd_hctl_elem_get_name(helem); const int index = snd_hctl_elem_get_index(helem); const int device = snd_hctl_elem_get_device(helem); - const int subdevice = snd_hctl_elem_get_subdevice(helem); snd_mixer_elem_t *new_melem; - bool found = true; - new_melem = pa_alsa_mixer_find(mixer, iface, name, index, device, subdevice); + new_melem = pa_alsa_mixer_find(mixer, iface, name, index, device); if (!new_melem) { - _helem = pa_xmalloc(sizeof(snd_hctl_elem_t *)); - *_helem = helem; /* Put the hctl pointer as our private data - it will be useful for callbacks */ - if ((err = snd_mixer_elem_new(&new_melem, SND_MIXER_ELEM_PULSEAUDIO, 0, _helem, mixer_melem_free)) < 0) { + if ((err = snd_mixer_elem_new(&new_melem, SND_MIXER_ELEM_PULSEAUDIO, 0, helem, NULL)) < 0) { pa_log_warn("snd_mixer_elem_new failed: %s", pa_alsa_strerror(err)); return 0; } - found = false; - } else { - _helem = snd_mixer_elem_get_private(new_melem); - if (_helem) { - char *s1, *s2; - snd_ctl_elem_id_t *id1, *id2; - snd_ctl_elem_id_alloca(&id1); - snd_ctl_elem_id_alloca(&id2); - snd_hctl_elem_get_id(helem, id1); - snd_hctl_elem_get_id(*_helem, id2); - s1 = snd_ctl_ascii_elem_id_get(id1); - s2 = snd_ctl_ascii_elem_id_get(id2); - pa_log_warn("mixer_class_event - duplicate mixer controls: %s | %s", s1, s2); - free(s2); - free(s1); - return 0; - } - *_helem = helem; } if ((err = snd_mixer_elem_attach(new_melem, helem)) < 0) { pa_log_warn("snd_mixer_elem_attach failed: %s", pa_alsa_strerror(err)); - snd_mixer_elem_free(melem); + snd_mixer_elem_free(melem); return 0; } - if (!found) { - if ((err = snd_mixer_elem_add(new_melem, class)) < 0) { - pa_log_warn("snd_mixer_elem_add failed: %s", pa_alsa_strerror(err)); - return 0; - } + if ((err = snd_mixer_elem_add(new_melem, class)) < 0) { + pa_log_warn("snd_mixer_elem_add failed: %s", pa_alsa_strerror(err)); + return 0; } } } diff --git a/spa/plugins/alsa/acp/alsa-util.h b/spa/plugins/alsa/acp/alsa-util.h index c97ff3d90..b18b98df9 100644 --- a/spa/plugins/alsa/acp/alsa-util.h +++ b/spa/plugins/alsa/acp/alsa-util.h @@ -64,8 +64,6 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( snd_pcm_uframes_t tsched_size, bool *use_mmap, /* modified at return */ bool *use_tsched, /* modified at return */ - pa_sample_format_t **query_supported_formats, /* modified at return */ - unsigned int **query_supported_rates, /* modified at return */ pa_alsa_profile_set *ps, pa_alsa_mapping **mapping); /* modified at return */ #endif @@ -82,8 +80,6 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping( snd_pcm_uframes_t tsched_size, bool *use_mmap, /* modified at return */ bool *use_tsched, /* modified at return */ - pa_sample_format_t **query_supported_formats, /* modified at return */ - unsigned int **query_supported_rates, /* modified at return */ pa_alsa_mapping *mapping); /* Opens the explicit ALSA device */ @@ -98,8 +94,6 @@ snd_pcm_t *pa_alsa_open_by_device_string( snd_pcm_uframes_t tsched_size, bool *use_mmap, /* modified at return */ bool *use_tsched, /* modified at return */ - pa_sample_format_t **query_supported_formats, /* modified at return */ - unsigned int **query_supported_rates, /* modified at return */ bool require_exact_channel_number); /* Opens the explicit ALSA device with a fallback list */ @@ -115,8 +109,6 @@ snd_pcm_t *pa_alsa_open_by_template( snd_pcm_uframes_t tsched_size, bool *use_mmap, /* modified at return */ bool *use_tsched, /* modified at return */ - pa_sample_format_t **query_supported_formats, /* modified at return */ - unsigned int **query_supported_rates, /* modified at return */ bool require_exact_channel_number); #if 0 diff --git a/spa/plugins/alsa/acp/compat.h b/spa/plugins/alsa/acp/compat.h index 6532be817..d60f9ef90 100644 --- a/spa/plugins/alsa/acp/compat.h +++ b/spa/plugins/alsa/acp/compat.h @@ -47,12 +47,10 @@ typedef void (*pa_free_cb_t)(void *p); #define PA_LIKELY(x) (__builtin_expect(!!(x),1)) #define PA_UNLIKELY(x) (__builtin_expect(!!(x),0)) #define PA_PRINTF_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1))) -#define PA_UNUSED __attribute__ ((unused)) #else #define PA_LIKELY(x) (x) #define PA_UNLIKELY(x) (x) #define PA_PRINTF_FUNC(fmt, arg1) -#define PA_UNUSED #endif #define PA_MIN(a,b) \ @@ -98,7 +96,7 @@ typedef enum pa_available { PA_AVAILABLE_YES = 2, } pa_available_t; -#define PA_RATE_MAX (48000U*16U) +#define PA_RATE_MAX (48000U*8U) typedef enum pa_sample_format { PA_SAMPLE_U8, /**< Unsigned 8 Bit PCM */ diff --git a/spa/plugins/alsa/acp/idxset.h b/spa/plugins/alsa/acp/idxset.h index 2638133da..4a23eee35 100644 --- a/spa/plugins/alsa/acp/idxset.h +++ b/spa/plugins/alsa/acp/idxset.h @@ -130,25 +130,13 @@ static inline unsigned pa_idxset_size(pa_idxset*s) return count; } -static inline pa_idxset_item *pa_idxset_search(pa_idxset *s, uint32_t *idx) +static inline void *pa_idxset_search(pa_idxset *s, uint32_t *idx) { pa_idxset_item *item; for (item = pa_array_get_unchecked(&s->array, *idx, pa_idxset_item); pa_array_check(&s->array, item); item++, (*idx)++) { if (item->ptr != NULL) - return item; - } - *idx = PA_IDXSET_INVALID; - return NULL; -} - -static inline pa_idxset_item *pa_idxset_reverse_search(pa_idxset *s, uint32_t *idx) -{ - pa_idxset_item *item; - for (item = pa_array_get_unchecked(&s->array, *idx, pa_idxset_item); - pa_array_check(&s->array, item); item--, (*idx)--) { - if (item->ptr != NULL) - return item; + return item->ptr; } *idx = PA_IDXSET_INVALID; return NULL; @@ -156,93 +144,29 @@ static inline pa_idxset_item *pa_idxset_reverse_search(pa_idxset *s, uint32_t *i static inline void *pa_idxset_next(pa_idxset *s, uint32_t *idx) { - pa_idxset_item *item; (*idx)++;; - item = pa_idxset_search(s, idx); - return item ? item->ptr : NULL; + return pa_idxset_search(s, idx); } static inline void* pa_idxset_first(pa_idxset *s, uint32_t *idx) { uint32_t i = 0; - pa_idxset_item *item = pa_idxset_search(s, &i); + void *ptr = pa_idxset_search(s, &i); if (idx) *idx = i; - return item ? item->ptr : NULL; -} - -static inline void* pa_idxset_last(pa_idxset *s, uint32_t *idx) -{ - uint32_t i = pa_array_get_len(&s->array, pa_idxset_item) - 1; - pa_idxset_item *item = pa_idxset_reverse_search(s, &i); - if (idx) - *idx = i; - return item ? item->ptr : NULL; -} - -static inline void* pa_idxset_steal_last(pa_idxset *s, uint32_t *idx) -{ - uint32_t i = pa_array_get_len(&s->array, pa_idxset_item) - 1; - void *ptr = NULL; - pa_idxset_item *item = pa_idxset_reverse_search(s, &i); - if (idx) - *idx = i; - if (item) { - ptr = item->ptr; - item->ptr = NULL; - pa_array_remove(&s->array, item); - } return ptr; } static inline void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) { pa_idxset_item *item = pa_idxset_find(s, p); - if (item == NULL) { - if (idx) - *idx = PA_IDXSET_INVALID; + if (item == NULL) return NULL; - } if (idx) *idx = item - (pa_idxset_item*)s->array.data; return item->ptr; } -static inline bool pa_idxset_contains(pa_idxset *s, const void *p) -{ - return pa_idxset_get_by_data(s, p, NULL) == p; -} - -static inline bool pa_idxset_isdisjoint(pa_idxset *s, pa_idxset *t) -{ - pa_idxset_item *item; - pa_array_for_each(item, &s->array) { - if (item->ptr && pa_idxset_contains(t, item->ptr)) - return false; - } - return true; -} - -static inline bool pa_idxset_issubset(pa_idxset *s, pa_idxset *t) -{ - pa_idxset_item *item; - pa_array_for_each(item, &s->array) { - if (item->ptr && !pa_idxset_contains(t, item->ptr)) - return false; - } - return true; -} - -static inline bool pa_idxset_issuperset(pa_idxset *s, pa_idxset *t) -{ - return pa_idxset_issubset(t, s); -} - -static inline bool pa_idxset_equals(pa_idxset *s, pa_idxset *t) -{ - return pa_idxset_issubset(s, t) && pa_idxset_issuperset(s, t); -} - static inline void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx) { pa_idxset_item *item;