[pulseaudio-discuss] [RFC PATCH 1/4] Integrate UCM basic functions into alsa card module

Feng Wei feng.wei at linaro.org
Mon Jun 11 03:28:24 PDT 2012


2012/6/8 Arun Raghavan <arun.raghavan at collabora.co.uk>:
> Hello,
> Comments inline. I'm leaving the ALSA-specific bits to David/Tanu since
> they are more well-versed with that code than I am. I've been using this
> code on the Galaxy Nexus, and things seem to work fine, which is
> nice. :)
>
> Thanks,
> Arun
>
> On Fri, 2012-05-18 at 18:06 +0800, feng.wei at linaro.org wrote:
>> From: Feng Wei <feng.wei at linaro.org>
>>
>> UCM basic functions will provide another way to handle the alsa mixer and controls.
>> That means alsa card module will make use of alsa ucm configurations provided by
>> various audio systems instead of mixer and paths configurations provided by PA.
>>
>> PA profiles come from UCM verb, PA sinks/sources and ports come from UCM devices.
>>
>> A new "use_ucm" module arg is added to enable the UCM branches, in case the proper
>> UCM configurations are found. Or we will still fall through to the original way.
>>
>> Signed-off-by: Feng Wei <feng.wei at linaro.org>
>> ---
>>  src/Makefile.am                     |    1 +
>>  src/modules/alsa/alsa-mixer.c       |   18 +-
>>  src/modules/alsa/alsa-mixer.h       |    5 +
>>  src/modules/alsa/alsa-sink.c        |   45 +-
>>  src/modules/alsa/alsa-source.c      |   46 +-
>>  src/modules/alsa/alsa-ucm.c         | 1083 +++++++++++++++++++++++++++++++++++
>>  src/modules/alsa/alsa-ucm.h         |  111 ++++
>>  src/modules/alsa/module-alsa-card.c |  129 ++++-
>>  src/pulse/proplist.h                |   45 ++
>>  9 files changed, 1459 insertions(+), 24 deletions(-)
>>  create mode 100644 src/modules/alsa/alsa-ucm.c
>>  create mode 100644 src/modules/alsa/alsa-ucm.h
>>
>> diff --git a/src/Makefile.am b/src/Makefile.am
>> index 127956a..879080f 100644
>> --- a/src/Makefile.am
>> +++ b/src/Makefile.am
>> @@ -1611,6 +1611,7 @@ module_coreaudio_device_la_LIBADD = $(MODULE_LIBADD)
>>
>>  libalsa_util_la_SOURCES = \
>>               modules/alsa/alsa-util.c modules/alsa/alsa-util.h \
>> +             modules/alsa/alsa-ucm.c modules/alsa/alsa-ucm.h \
>>               modules/alsa/alsa-mixer.c modules/alsa/alsa-mixer.h \
>>               modules/alsa/alsa-sink.c modules/alsa/alsa-sink.h \
>>               modules/alsa/alsa-source.c modules/alsa/alsa-source.h \
>> diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
>> index 8b54f75..1151b8d 100644
>> --- a/src/modules/alsa/alsa-mixer.c
>> +++ b/src/modules/alsa/alsa-mixer.c
>> @@ -3294,6 +3294,8 @@ static void mapping_free(pa_alsa_mapping *m) {
>>      pa_assert(!m->input_pcm);
>>      pa_assert(!m->output_pcm);
>>
>> +    pa_xfree(m->ucm_context.ucm_devices);
>> +
>>      pa_xfree(m);
>>  }
>>
>> @@ -3366,7 +3368,7 @@ void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
>>      pa_xfree(ps);
>>  }
>>
>> -static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
>> +pa_alsa_mapping *pa_alsa_mapping_get(pa_alsa_profile_set *ps, const char *name) {
>>      pa_alsa_mapping *m;
>>
>>      if (!pa_startswith(name, "Mapping "))
>> @@ -3441,7 +3443,7 @@ static int mapping_parse_device_strings(
>>
>>      pa_assert(ps);
>>
>> -    if (!(m = mapping_get(ps, section))) {
>> +    if (!(m = pa_alsa_mapping_get(ps, section))) {
>>          pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
>>          return -1;
>>      }
>> @@ -3469,7 +3471,7 @@ static int mapping_parse_channel_map(
>>
>>      pa_assert(ps);
>>
>> -    if (!(m = mapping_get(ps, section))) {
>> +    if (!(m = pa_alsa_mapping_get(ps, section))) {
>>          pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
>>          return -1;
>>      }
>> @@ -3496,7 +3498,7 @@ static int mapping_parse_paths(
>>
>>      pa_assert(ps);
>>
>> -    if (!(m = mapping_get(ps, section))) {
>> +    if (!(m = pa_alsa_mapping_get(ps, section))) {
>>          pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
>>          return -1;
>>      }
>> @@ -3526,7 +3528,7 @@ static int mapping_parse_element(
>>
>>      pa_assert(ps);
>>
>> -    if (!(m = mapping_get(ps, section))) {
>> +    if (!(m = pa_alsa_mapping_get(ps, section))) {
>>          pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
>>          return -1;
>>      }
>> @@ -3556,7 +3558,7 @@ static int mapping_parse_direction(
>>
>>      pa_assert(ps);
>>
>> -    if (!(m = mapping_get(ps, section))) {
>> +    if (!(m = pa_alsa_mapping_get(ps, section))) {
>>          pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
>>          return -1;
>>      }
>> @@ -3590,7 +3592,7 @@ static int mapping_parse_description(
>>
>>      pa_assert(ps);
>>
>> -    if ((m = mapping_get(ps, section))) {
>> +    if ((m = pa_alsa_mapping_get(ps, section))) {
>>          pa_xfree(m->description);
>>          m->description = pa_xstrdup(rvalue);
>>      } else if ((p = profile_get(ps, section))) {
>> @@ -3625,7 +3627,7 @@ static int mapping_parse_priority(
>>          return -1;
>>      }
>>
>> -    if ((m = mapping_get(ps, section)))
>> +    if ((m = pa_alsa_mapping_get(ps, section)))
>>          m->priority = prio;
>>      else if ((p = profile_get(ps, section)))
>>          p->priority = prio;
>> diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
>> index fdcff76..1e424bd 100644
>> --- a/src/modules/alsa/alsa-mixer.h
>> +++ b/src/modules/alsa/alsa-mixer.h
>> @@ -48,6 +48,7 @@ typedef struct pa_alsa_profile_set pa_alsa_profile_set;
>>  typedef struct pa_alsa_port_data pa_alsa_port_data;
>>
>>  #include "alsa-util.h"
>> +#include "alsa-ucm.h"
>>
>>  typedef enum pa_alsa_switch_use {
>>      PA_ALSA_SWITCH_IGNORE,
>> @@ -264,6 +265,9 @@ struct pa_alsa_mapping {
>>
>>      pa_sink *sink;
>>      pa_source *source;
>> +
>> +    /* ucm device context*/
>> +    pa_alsa_ucm_mapping_context ucm_context;
>>  };
>>
>>  struct pa_alsa_profile {
>> @@ -313,6 +317,7 @@ struct pa_alsa_profile_set {
>>  void pa_alsa_mapping_dump(pa_alsa_mapping *m);
>>  void pa_alsa_profile_dump(pa_alsa_profile *p);
>>  void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix);
>> +pa_alsa_mapping *pa_alsa_mapping_get(pa_alsa_profile_set *ps, const char *name);
>>
>>  pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus);
>>  void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, const pa_sample_spec *ss, unsigned default_n_fragments, unsigned default_fragment_size_msec);
>> diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
>> index c3d18e3..4896672 100644
>> --- a/src/modules/alsa/alsa-sink.c
>> +++ b/src/modules/alsa/alsa-sink.c
>> @@ -152,6 +152,9 @@ struct userdata {
>>      pa_hook_slot *reserve_slot;
>>      pa_reserve_monitor_wrapper *monitor;
>>      pa_hook_slot *monitor_slot;
>> +
>> +    /* ucm context */
>> +    pa_alsa_ucm_mapping_context *ucm_context;
>>  };
>>
>>  static void userdata_free(struct userdata *u);
>> @@ -1449,6 +1452,16 @@ static void mixer_volume_init(struct userdata *u) {
>>      }
>>  }
>>
>> +static int sink_set_port_ucm_cb(pa_sink *s, pa_device_port *p) {
>> +    struct userdata *u = s->userdata;
>> +
>> +    pa_assert(u);
>> +    pa_assert(p);
>> +    pa_assert(u->ucm_context);
>> +
>> +    return pa_ucm_set_port(u->ucm_context, p, 1);
>> +}
>> +
>>  static int sink_set_port_cb(pa_sink *s, pa_device_port *p) {
>>      struct userdata *u = s->userdata;
>>      pa_alsa_port_data *data;
>> @@ -1886,6 +1899,16 @@ fail:
>>      }
>>  }
>>
>> +static int setup_mixer_ucm(struct userdata *u, pa_bool_t ignore_dB) {
>> +    pa_assert(u);
>> +    pa_assert(u->sink);
>> +    pa_assert(u->ucm_context);
>> +
>> +    if (u->sink->active_port)
>> +        return pa_ucm_set_port(u->ucm_context, u->sink->active_port, 1);
>> +
>> +    return 0;
>> +}
>>
>>  static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
>>      pa_bool_t need_mixer_callback = FALSE;
>> @@ -2078,6 +2101,10 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
>>              TRUE);
>>      u->smoother_interval = SMOOTHER_MIN_INTERVAL;
>>
>> +    /* use ucm */
>> +    if (mapping && mapping->ucm_context.ucm)
>> +        u->ucm_context = &mapping->ucm_context;
>> +
>>      dev_id = pa_modargs_get_value(
>>              ma, "device_id",
>>              pa_modargs_get_value(ma, "device", DEFAULT_DEVICE));
>> @@ -2178,7 +2205,8 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
>>      /* ALSA might tweak the sample spec, so recalculate the frame size */
>>      frame_size = pa_frame_size(&ss);
>>
>> -    find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), ignore_dB);
>> +    if (!u->ucm_context)
>> +        find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), ignore_dB);
>>
>>      pa_sink_new_data_init(&data);
>>      data.driver = driver;
>> @@ -2224,7 +2252,9 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
>>          goto fail;
>>      }
>>
>> -    if (u->mixer_path_set)
>> +    if (u->ucm_context)
>> +        pa_ucm_add_ports(&data.ports, data.proplist, u->ucm_context, 1, card);
>> +    else if (u->mixer_path_set)
>>          pa_alsa_add_ports(&data.ports, u->mixer_path_set, card);
>>
>>      u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE | PA_SINK_LATENCY | (u->use_tsched ? PA_SINK_DYNAMIC_LATENCY : 0) |
>> @@ -2252,7 +2282,10 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
>>      if (u->use_tsched)
>>          u->sink->update_requested_latency = sink_update_requested_latency_cb;
>>      u->sink->set_state = sink_set_state_cb;
>> -    u->sink->set_port = sink_set_port_cb;
>> +    if (u->ucm_context)
>> +        u->sink->set_port = sink_set_port_ucm_cb;
>> +    else
>> +        u->sink->set_port = sink_set_port_cb;
>>      if (u->sink->alternate_sample_rate)
>>          u->sink->update_rate = sink_update_rate_cb;
>>      u->sink->userdata = u;
>> @@ -2291,7 +2324,11 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
>>      if (update_sw_params(u) < 0)
>>          goto fail;
>>
>> -    if (setup_mixer(u, ignore_dB) < 0)
>> +    if (u->ucm_context) {
>> +        if (setup_mixer_ucm(u, ignore_dB) < 0)
>> +            goto fail;
>> +    }
>> +    else if (setup_mixer(u, ignore_dB) < 0)
>>          goto fail;
>>
>>      pa_alsa_dump(PA_LOG_DEBUG, u->pcm_handle);
>> diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
>> index 97092bb..7630678 100644
>> --- a/src/modules/alsa/alsa-source.c
>> +++ b/src/modules/alsa/alsa-source.c
>> @@ -136,6 +136,9 @@ struct userdata {
>>      pa_hook_slot *reserve_slot;
>>      pa_reserve_monitor_wrapper *monitor;
>>      pa_hook_slot *monitor_slot;
>> +
>> +    /* ucm context */
>> +    pa_alsa_ucm_mapping_context *ucm_context;
>>  };
>>
>>  static void userdata_free(struct userdata *u);
>> @@ -1352,6 +1355,16 @@ static void mixer_volume_init(struct userdata *u) {
>>      }
>>  }
>>
>> +static int source_set_port_ucm_cb(pa_source *s, pa_device_port *p) {
>> +    struct userdata *u = s->userdata;
>> +
>> +    pa_assert(u);
>> +    pa_assert(p);
>> +    pa_assert(u->ucm_context);
>> +
>> +    return pa_ucm_set_port(u->ucm_context, p, 0);
>> +}
>> +
>>  static int source_set_port_cb(pa_source *s, pa_device_port *p) {
>>      struct userdata *u = s->userdata;
>>      pa_alsa_port_data *data;
>> @@ -1624,6 +1637,17 @@ fail:
>>      }
>>  }
>>
>> +static int setup_mixer_ucm(struct userdata *u, pa_bool_t ignore_dB) {
>> +    pa_assert(u);
>> +    pa_assert(u->source);
>> +    pa_assert(u->ucm_context);
>> +
>> +    if (u->source->active_port)
>> +        return pa_ucm_set_port(u->ucm_context, u->source->active_port, 0);
>> +
>> +    return 0;
>> +}
>> +
>>  static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
>>      pa_bool_t need_mixer_callback = FALSE;
>>
>> @@ -1808,6 +1832,10 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
>>              TRUE);
>>      u->smoother_interval = SMOOTHER_MIN_INTERVAL;
>>
>> +    /* use ucm */
>> +    if (mapping && mapping->ucm_context.ucm)
>> +        u->ucm_context = &mapping->ucm_context;
>> +
>>      dev_id = pa_modargs_get_value(
>>              ma, "device_id",
>>              pa_modargs_get_value(ma, "device", DEFAULT_DEVICE));
>> @@ -1904,7 +1932,8 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
>>      /* ALSA might tweak the sample spec, so recalculate the frame size */
>>      frame_size = pa_frame_size(&ss);
>>
>> -    find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), ignore_dB);
>> +    if (!u->ucm_context)
>> +        find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), ignore_dB);
>>
>>      pa_source_new_data_init(&data);
>>      data.driver = driver;
>> @@ -1950,7 +1979,9 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
>>          goto fail;
>>      }
>>
>> -    if (u->mixer_path_set)
>> +    if (u->ucm_context)
>> +        pa_ucm_add_ports(&data.ports, data.proplist, u->ucm_context, 0, card);
>> +    else if (u->mixer_path_set)
>>          pa_alsa_add_ports(&data.ports, u->mixer_path_set, card);
>>
>>      u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY|(u->use_tsched ? PA_SOURCE_DYNAMIC_LATENCY : 0));
>> @@ -1977,7 +2008,10 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
>>      if (u->use_tsched)
>>          u->source->update_requested_latency = source_update_requested_latency_cb;
>>      u->source->set_state = source_set_state_cb;
>> -    u->source->set_port = source_set_port_cb;
>> +    if (u->ucm_context)
>> +        u->source->set_port = source_set_port_ucm_cb;
>> +    else
>> +        u->source->set_port = source_set_port_cb;
>>      if (u->source->alternate_sample_rate)
>>          u->source->update_rate = source_update_rate_cb;
>>      u->source->userdata = u;
>> @@ -2009,7 +2043,11 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
>>      if (update_sw_params(u) < 0)
>>          goto fail;
>>
>> -    if (setup_mixer(u, ignore_dB) < 0)
>> +    if (u->ucm_context) {
>> +        if (setup_mixer_ucm(u, ignore_dB) < 0)
>> +            goto fail;
>> +    }
>> +    else if (setup_mixer(u, ignore_dB) < 0)
>>          goto fail;
>>
>>      pa_alsa_dump(PA_LOG_DEBUG, u->pcm_handle);
>> diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
>> new file mode 100644
>> index 0000000..452a1c4
>> --- /dev/null
>> +++ b/src/modules/alsa/alsa-ucm.c
>> @@ -0,0 +1,1083 @@
>> +/***
>> + This file is part of PulseAudio.
>> +
>> + Copyright 2011 Wolfson Microelectronics PLC
>> + Author Margarita Olaya <magi at slimlogic.co.uk>
>> + Copyright 2012 Feng Wei <feng.wei at linaro.org>, Linaro
>> +
>> + PulseAudio is free software; you can redistribute it and/or modify
>> + it under the terms of the GNU Lesser General Public License as published
>> + by the Free Software Foundation; either version 2.1 of the License,
>> + or (at your option) any later version.
>> +
>> + PulseAudio is distributed in the hope that it will be useful, but
>> + WITHOUT ANY WARRANTY; without even the implied warranty of
>> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>> + General Public License for more details.
>> +
>> + You should have received a copy of the GNU Lesser General Public License
>> + along with PulseAudio; if not, write to the Free Software
>> + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
>> + USA.
>> +
>> +***/
>> +
>> +#ifdef HAVE_CONFIG_H
>> +#include <config.h>
>> +#endif
>> +
>> +#include <sys/types.h>
>> +#include <limits.h>
>> +#include <asoundlib.h>
>> +
>> +#ifdef HAVE_VALGRIND_MEMCHECK_H
>> +#include <valgrind/memcheck.h>
>> +#endif
>> +
>> +#include <pulse/sample.h>
>> +#include <pulse/xmalloc.h>
>> +#include <pulse/timeval.h>
>> +#include <pulse/util.h>
>> +
>> +#include <pulsecore/log.h>
>> +#include <pulsecore/macro.h>
>> +#include <pulsecore/core-util.h>
>> +#include <pulsecore/atomic.h>
>> +#include <pulsecore/core-error.h>
>> +#include <pulsecore/once.h>
>> +#include <pulsecore/thread.h>
>> +#include <pulsecore/conf-parser.h>
>> +#include <pulsecore/strbuf.h>
>> +
>> +#include "alsa-mixer.h"
>> +#include "alsa-util.h"
>> +#include "alsa-ucm.h"
>> +
>> +#define PA_UCM_PLAYBACK_PRIORITY_UNSET(device)      ((device)->playback_channels && !(device)->playback_priority)
>> +#define PA_UCM_CAPTURE_PRIORITY_UNSET(device)       ((device)->capture_channels && !(device)->capture_priority)
>> +#define PA_UCM_DEVICE_PRIORITY_SET(device, priority) \
>> +    do { \
>> +        if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device)) (device)->playback_priority = (priority);   \
>> +        if (PA_UCM_CAPTURE_PRIORITY_UNSET(device))  (device)->capture_priority = (priority);    \
>> +    } while(0)
>> +
>> +struct ucm_items {
>> +    const char *id;
>> +    const char *property;
>> +};
>> +
>> +struct ucm_info {
>> +    const char *id;
>> +    unsigned priority;
>> +};
>> +
>> +static struct ucm_items item[] = {
>> +    {"PlaybackPCM", PA_PROP_UCM_SINK},
>> +    {"CapturePCM", PA_PROP_UCM_SOURCE},
>> +    {"PlaybackVolume", PA_PROP_UCM_PLAYBACK_VOLUME},
>> +    {"PlaybackSwitch", PA_PROP_UCM_PLAYBACK_SWITCH},
>> +    {"PlaybackPriority", PA_PROP_UCM_PLAYBACK_PRIORITY},
>> +    {"PlaybackChannels", PA_PROP_UCM_PLAYBACK_CHANNELS},
>> +    {"CaptureVolume", PA_PROP_UCM_CAPTURE_VOLUME},
>> +    {"CaptureSwitch", PA_PROP_UCM_CAPTURE_SWITCH},
>> +    {"CapturePriority", PA_PROP_UCM_CAPTURE_PRIORITY},
>> +    {"CaptureChannels", PA_PROP_UCM_CAPTURE_CHANNELS},
>> +    {"TQ", PA_PROP_UCM_QOS},
>> +    {NULL, NULL},
>> +};
>> +
>> +/* UCM verb info - this should eventually be part of policy manangement */
>> +static struct ucm_info verb_info[] = {
>> +    {SND_USE_CASE_VERB_INACTIVE, 0},
>> +    {SND_USE_CASE_VERB_HIFI, 8000},
>> +    {SND_USE_CASE_VERB_HIFI_LOW_POWER, 7000},
>> +    {SND_USE_CASE_VERB_VOICE, 6000},
>> +    {SND_USE_CASE_VERB_VOICE_LOW_POWER, 5000},
>> +    {SND_USE_CASE_VERB_VOICECALL, 4000},
>> +    {SND_USE_CASE_VERB_IP_VOICECALL, 4000},
>> +    {SND_USE_CASE_VERB_ANALOG_RADIO, 3000},
>> +    {SND_USE_CASE_VERB_DIGITAL_RADIO, 3000},
>> +    {NULL, 0}
>> +};
>> +
>> +/* UCM device info - should be overwritten by ucm property */
>> +static struct ucm_info dev_info[] = {
>> +    {SND_USE_CASE_DEV_SPEAKER, 100},
>> +    {SND_USE_CASE_DEV_LINE, 100},
>> +    {SND_USE_CASE_DEV_HEADPHONES, 100},
>> +    {SND_USE_CASE_DEV_HEADSET, 300},
>> +    {SND_USE_CASE_DEV_HANDSET, 200},
>> +    {SND_USE_CASE_DEV_BLUETOOTH, 400},
>> +    {SND_USE_CASE_DEV_EARPIECE, 100},
>> +    {SND_USE_CASE_DEV_SPDIF, 100},
>> +    {SND_USE_CASE_DEV_HDMI, 100},
>> +    {SND_USE_CASE_DEV_NONE, 100},
>> +    {NULL, 0}
>> +};
>
> Should Headphones be higher priority than speaker by default? That way a
> sensible default will be picked if both ports are available.
I don't have much sense of the device priorities, and I'm glad to
update all those values if someone could give suggestion.
>
>> +
>> +/* UCM profile properties - The verb data is store so it can be used to fill
>> + * the new profiles properties */
>> +static int ucm_get_property(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr, const char *verb_name) {
>> +    const char *value;
>> +    char *id;
>> +    int i = 0;
>> +
>> +    do {
>> +        int err;
>> +
>> +        id = pa_sprintf_malloc("=%s//%s", item[i].id, verb_name);
>> +        err = snd_use_case_get(uc_mgr, id, &value);
>> +        pa_xfree(id);
>> +        if (err < 0 ) {
>> +            pa_log_info("No %s for verb %s", item[i].id, verb_name);
>> +            continue;
>> +        }
>> +
>> +        pa_log_info("Got %s for verb %s: %s", item[i].id, verb_name, value);
>> +        pa_proplist_sets(verb->proplist, item[i].property, value);
>> +        free((void*)value);
>> +    } while (item[++i].id);
>> +
>> +    return 0;
>> +};
>> +
>> +static char **dup_strv(const char **src, int n) {
>> +    char **dest = pa_xnew0(char *, n+1);
>> +    int i;
>> +
>> +    for (i=0; i<n; i++)
>> +        dest[i] = pa_xstrdup(src[i]);
>> +
>> +    return dest;
>> +}
>> +
>> +static int ucm_device_in(char **device_names, int num, pa_alsa_ucm_device *dev) {
>> +    int i;
>> +    const char *dev_name = pa_proplist_gets(dev->proplist, PA_PROP_UCM_NAME);
>> +
>> +    for (i=0; i<num; i++)
>> +        if (!strcmp(dev_name, device_names[i]))
>
> Use pa_streq() here.
I will fix all those kind of stuffs.
>
>> +            return 1;
>> +
>> +    return 0;
>> +}
>> +
>> +/* Create a property list for this ucm device */
>> +static int ucm_get_device_property(pa_alsa_ucm_device *device,
>> +        snd_use_case_mgr_t *uc_mgr, pa_alsa_ucm_verb *verb, const char *device_name) {
>> +    const char *value;
>> +    const char **devices;
>> +    char *id;
>> +    int i = 0;
>> +    int err;
>> +    uint32_t ui;
>> +
>> +    do {
>> +        id = pa_sprintf_malloc("=%s/%s", item[i].id, device_name);
>> +        err = snd_use_case_get(uc_mgr, id, &value);
>> +        pa_xfree(id);
>> +        if (err < 0) {
>> +            pa_log_info("No %s for device %s", item[i].id, device_name);
>> +            continue;
>> +        }
>> +
>> +        pa_log_info("Got %s for device %s: %s", item[i].id, device_name, value);
>> +        pa_proplist_sets(device->proplist, item[i].property, value);
>> +        free((void*)value);
>> +    } while (item[++i].id);
>> +
>> +    /* get direction and channels */
>> +    value = pa_proplist_gets(device->proplist, PA_PROP_UCM_PLAYBACK_CHANNELS);
>> +    if (value) { /* output */
>> +        /* get channels */
>> +        if (pa_atou(value, &ui) == 0 && ui < PA_CHANNELS_MAX)
>> +            device->playback_channels = ui;
>> +        else
>> +            pa_log("UCM playback channels %s for device %s out of range", value, device_name);
>> +
>> +        /* get pcm */
>> +        value = pa_proplist_gets(device->proplist, PA_PROP_UCM_SINK);
>> +        if (!value) { /* take pcm from verb playback default */
>> +            value = pa_proplist_gets(verb->proplist, PA_PROP_UCM_SINK);
>> +            if (value) {
>> +                pa_log_info("UCM playback device %s fetch pcm from verb default %s", device_name, value);
>> +                pa_proplist_sets(device->proplist, PA_PROP_UCM_SINK, value);
>> +            }
>> +            else
>> +                pa_log("UCM playback device %s fetch pcm failed", device_name);
>> +        }
>> +    }
>> +    value = pa_proplist_gets(device->proplist, PA_PROP_UCM_CAPTURE_CHANNELS);
>> +    if (value) { /* input */
>> +        /* get channels */
>> +        if (pa_atou(value, &ui) == 0 && ui < PA_CHANNELS_MAX)
>> +            device->capture_channels = ui;
>> +        else
>> +            pa_log("UCM capture channels %s for device %s out of range", value, device_name);
>> +
>> +        /* get pcm */
>> +        value = pa_proplist_gets(device->proplist, PA_PROP_UCM_SOURCE);
>> +        if (!value) { /* take pcm from verb capture default */
>> +            value = pa_proplist_gets(verb->proplist, PA_PROP_UCM_SOURCE);
>> +            if (value) {
>> +                pa_log_info("UCM capture device %s fetch pcm from verb default %s", device_name, value);
>> +                pa_proplist_sets(device->proplist, PA_PROP_UCM_SOURCE, value);
>> +            }
>> +            else
>> +                pa_log("UCM capture device %s fetch pcm failed", device_name);
>> +        }
>> +    }
>> +    pa_assert(device->playback_channels || device->capture_channels);
>> +
>> +    /* get priority of device */
>> +    if (device->playback_channels) { /* sink device */
>> +        value = pa_proplist_gets(device->proplist, PA_PROP_UCM_PLAYBACK_PRIORITY);
>> +        if (value) {
>> +            /* get priority from ucm config */
>> +            if (pa_atou(value, &ui) == 0)
>> +                device->playback_priority = ui;
>> +            else
>> +                pa_log("UCM playback priority %s for device %s error", value, device_name);
>> +        }
>> +    }
>> +    if (device->capture_channels) { /* source device */
>> +        value = pa_proplist_gets(device->proplist, PA_PROP_UCM_CAPTURE_PRIORITY);
>> +        if (value) {
>> +            /* get priority from ucm config */
>> +            if (pa_atou(value, &ui) == 0)
>> +                device->capture_priority = ui;
>> +            else
>> +                pa_log("UCM capture priority %s for device %s error", value, device_name);
>> +        }
>> +    }
>> +    if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device) || PA_UCM_CAPTURE_PRIORITY_UNSET(device)) {
>> +        /* get priority from static table */
>> +        i = 0;
>> +        do {
>> +            if (strcasecmp(dev_info[i].id, device_name) == 0) {
>> +                PA_UCM_DEVICE_PRIORITY_SET(device, dev_info[i].priority);
>> +                break;
>> +            }
>> +        } while (dev_info[++i].id);
>> +    }
>> +    if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device))
>> +        /* fall through to default priority */
>> +        device->playback_priority = 100;
>> +    if (PA_UCM_CAPTURE_PRIORITY_UNSET(device))
>> +        /* fall through to default priority */
>> +        device->capture_priority = 100;
>> +
>> +    id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", device_name);
>> +    device->n_confdev = snd_use_case_get_list(uc_mgr, id, &devices);
>> +    pa_xfree(id);
>> +    if (device->n_confdev <= 0)
>> +        pa_log_info("No %s for device %s", "_conflictingdevs", device_name);
>> +    else {
>> +        device->conflicting_devices = dup_strv(devices, device->n_confdev);
>> +        snd_use_case_free_list(devices, device->n_confdev);
>> +    }
>> +
>> +    id = pa_sprintf_malloc("%s/%s", "_supporteddevs", device_name);
>> +    device->n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices);
>> +    pa_xfree(id);
>> +    if (device->n_suppdev <= 0)
>> +        pa_log_info("No %s for device %s", "_supporteddevs", device_name);
>> +    else {
>> +        device->supported_devices = dup_strv(devices, device->n_suppdev);
>> +        snd_use_case_free_list(devices, device->n_suppdev);
>> +    }
>> +
>> +    return 0;
>> +};
>> +
>> +/* 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, const char *modifier_name) {
>> +    const char *value;
>> +    char *id;
>> +    int i = 0;
>> +
>> +    do {
>> +        int err;
>> +
>> +        id = pa_sprintf_malloc("=%s/%s", item[i].id, modifier_name);
>> +        err = snd_use_case_get(uc_mgr, id, &value);
>> +        pa_xfree(id);
>> +        if (err < 0 ) {
>> +            pa_log_info("No %s for modifier %s", item[i].id, modifier_name);
>> +            continue;
>> +        }
>> +
>> +        pa_log_info("Got %s for modifier %s: %s", item[i].id, modifier_name, value);
>> +        pa_proplist_sets(modifier->proplist, item[i].property, value);
>> +        free((void*)value);
>> +    } while (item[++i].id);
>> +
>> +    id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", modifier_name);
>> +    modifier->n_confdev = snd_use_case_get_list(uc_mgr, id, &modifier->conflicting_devices);
>> +    pa_xfree(id);
>> +    if (modifier->n_confdev < 0)
>> +        pa_log_info("No %s for modifier %s", "_conflictingdevs", modifier_name);
>> +
>> +    id = pa_sprintf_malloc("%s/%s", "_supporteddevs", modifier_name);
>> +    modifier->n_suppdev = snd_use_case_get_list(uc_mgr, id, &modifier->supported_devices);
>> +    pa_xfree(id);
>> +    if (modifier->n_suppdev < 0)
>> +        pa_log_info("No %s for modifier %s", "_supporteddevs", modifier_name);
>> +
>> +    return 0;
>> +};
>> +
>> +/* Create a list of devices for this verb */
>> +static int ucm_get_devices(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) {
>> +    const char **dev_list;
>> +    int num_dev, i;
>> +
>> +    num_dev = snd_use_case_get_list(uc_mgr, "_devices", &dev_list);
>> +    if (num_dev <= 0)
>> +        return num_dev;
>> +
>> +    for (i = 0; i < num_dev; i += 2) {
>> +        pa_alsa_ucm_device *d;
>> +        d = pa_xnew0(pa_alsa_ucm_device, 1);
>> +        d->proplist = pa_proplist_new();
>> +        pa_proplist_sets(d->proplist, PA_PROP_UCM_NAME, pa_strnull(dev_list[i]));
>> +        pa_proplist_sets(d->proplist, PA_PROP_UCM_DESCRIPTION, pa_strna(dev_list[i+1]));
>> +        PA_LLIST_PREPEND(pa_alsa_ucm_device, verb->devices, d);
>> +    }
>> +    snd_use_case_free_list(dev_list, num_dev);
>> +
>> +    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;
>> +
>> +    num_mod = snd_use_case_get_list(uc_mgr, "_modifiers", &mod_list);
>> +    if (num_mod <= 0)
>> +        return num_mod;
>> +
>> +    for (i = 0; i < num_mod; i += 2) {
>> +        pa_alsa_ucm_modifier *m;
>> +
>> +        m = pa_xnew0(pa_alsa_ucm_modifier, 1);
>> +        m->proplist = pa_proplist_new();
>> +        pa_proplist_sets(m->proplist, PA_PROP_UCM_NAME, pa_strnull(mod_list[i]));
>> +        pa_proplist_sets(m->proplist, PA_PROP_UCM_DESCRIPTION, pa_strna(mod_list[i+1]));
>> +        PA_LLIST_PREPEND(pa_alsa_ucm_modifier, verb->modifiers, m);
>> +    }
>> +    snd_use_case_free_list(mod_list, num_mod);
>> +
>> +    return 0;
>> +};
>> +
>> +static pa_bool_t role_match(const char *cur, const char *role) {
>> +    char *r;
>> +    const char *state=NULL;
>> +
>> +    if (!cur || !role)
>> +        return FALSE;
>> +
>> +    while ((r = pa_split_spaces(cur, &state))) {
>> +        if (!strcasecmp(role, r)) {
>> +            pa_xfree(r);
>> +            return TRUE;
>> +        }
>> +        pa_xfree(r);
>> +    }
>> +
>> +    return FALSE;
>> +}
>
> You can drop this function entirely and use pa_str_in_list_spaces().
Exactly. The code is somewhat older than pa_str_in_list_spaces.
>
>> +
>> +static void add_role_to_device(pa_alsa_ucm_device *dev, const char *dev_name,
>> +        const char *role_name, const char *role) {
>
> This and subsequent functions seem to be indented at 80 columns. Other
> places seem to follow PA coding style of 128 columns.
I had checked my coding style, but still left a lot of mismatched
lines there. Thank your careful review.
>
>> +    const char *cur = pa_proplist_gets(dev->proplist, role_name);
>> +
>> +    if (!cur)
>> +        pa_proplist_sets(dev->proplist, role_name, role);
>> +    else if (!role_match(cur, role)) { /* not exists */
>> +        char *value = pa_sprintf_malloc("%s %s", cur, role);
>> +
>> +        pa_proplist_sets(dev->proplist, role_name, value);
>> +        pa_xfree(value);
>> +    }
>> +    pa_log_info("Add role %s to device %s(%s), result %s", role,
>> +            dev_name, role_name, pa_proplist_gets(dev->proplist, role_name));
>> +}
>> +
>> +static void add_media_role(const char *name, pa_alsa_ucm_device *list,
>> +        const char *role_name, const char *role, int is_sink) {
>> +    pa_alsa_ucm_device *d;
>> +
>> +    PA_LLIST_FOREACH(d, list) {
>> +        const char *dev_name = pa_proplist_gets(d->proplist, PA_PROP_UCM_NAME);
>> +
>> +        if (!strcmp(dev_name, name)) {
>
> Use pa_streq(). I'll likely miss some call sites of strcmp(), so do
> replace all of them.
Yup.
>
>> +            const char *sink = pa_proplist_gets(d->proplist, PA_PROP_UCM_SINK);
>> +            const char *source = pa_proplist_gets(d->proplist, PA_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 void ucm_set_media_roles(pa_alsa_ucm_modifier *modifier,
>> +        pa_alsa_ucm_device *list, const char *mod_name) {
>> +    int i;
>> +    int is_sink=0;
>
> Missing spaces around '='. There are quite a few of these, please fix
> all of them.
Okay.
>
>> +    const char *sub = NULL;
>> +    const char *role_name;
>> +
>> +    if (pa_startswith(mod_name, "Play")) {
>> +        is_sink = 1;
>> +        sub = mod_name + 4;
>> +    }
>> +    else if (pa_startswith(mod_name, "Capture"))
>> +        sub = mod_name + 7;
>> +
>> +    if (!sub || !*sub) {
>> +        pa_log_warn("Can't match media roles for modifer %s", mod_name);
>> +        return;
>> +    }
>> +
>> +    modifier->action_direct = is_sink ?
>> +        PA_ALSA_UCM_DIRECT_SINK : PA_ALSA_UCM_DIRECT_SOURCE;
>> +    modifier->media_role = pa_xstrdup(sub);
>> +
>> +    role_name = is_sink ? PA_PROP_UCM_PLAYBACK_ROLES : PA_PROP_UCM_CAPTURE_ROLES;
>> +
>> +    for (i=0; i<modifier->n_suppdev; i++)
>> +        add_media_role(modifier->supported_devices[i], list, role_name, sub, is_sink);
>> +}
>> +
>> +static void append_me_to_device(pa_alsa_ucm_device *devices,
>> +        const char *dev_name, pa_alsa_ucm_device *me, const char *my_name, int is_conflicting) {
>> +    pa_alsa_ucm_device *d;
>> +    char ***pdevices;
>> +    int *pnum;
>> +
>> +    PA_LLIST_FOREACH(d, devices) {
>> +        const char *name = pa_proplist_gets(d->proplist, PA_PROP_UCM_NAME);
>> +
>> +        if (!strcmp(name, dev_name)) {
>> +            pdevices = is_conflicting ? &d->conflicting_devices : &d->supported_devices;
>> +            pnum = is_conflicting ? &d->n_confdev : &d->n_suppdev;
>> +            if (!ucm_device_in(*pdevices, *pnum, me)) {
>> +                /* append my name */
>> +                *pdevices = pa_xrealloc(*pdevices, sizeof(char *) * (*pnum+2));
>> +                (*pdevices)[*pnum] = pa_xstrdup(my_name);
>> +                (*pdevices)[*pnum+1] = NULL;
>> +                (*pnum)++;
>> +                pa_log_info("== Device %s complemented to %s's %s list",
>> +                        my_name, name, is_conflicting ? "conflicting" : "supported");
>> +            }
>
> Maybe I'm missing something obvious, but why not convert the conflicting
> and supported devices lists from a string list to a PA list and avoid
> all this repeated dynamic allocation?
These structures came from ucm interface in alsa-lib. It's true I
didn't optimize the data structure. I will fix the issues, including
the similar comments below.
>
>> +            break;
>> +        }
>> +    }
>> +}
>> +
>> +static void append_lost_relationship(pa_alsa_ucm_device *devices,
>> +        pa_alsa_ucm_device *dev, const char *dev_name) {
>> +    int i;
>> +
>> +    for (i=0; i<dev->n_confdev; i++)
>> +        append_me_to_device(devices, dev->conflicting_devices[i], dev, dev_name, 1);
>> +    for (i=0; i<dev->n_suppdev; i++)
>> +        append_me_to_device(devices, dev->supported_devices[i], dev, dev_name, 0);
>> +}
>> +
>> +int pa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name,
>> +        const char *verb_desc, pa_alsa_ucm_verb **p_verb) {
>> +    pa_alsa_ucm_device *d;
>> +    pa_alsa_ucm_modifier *mod;
>> +    pa_alsa_ucm_verb *verb;
>> +    int err=0;
>> +
>> +    *p_verb = NULL;
>> +    pa_log_info("pa_ucm_get_verb: Set ucm verb to %s", verb_name);
>> +    err = snd_use_case_set(uc_mgr, "_verb", verb_name);
>> +    if (err < 0)
>> +        return err;
>> +
>> +    verb = pa_xnew0(pa_alsa_ucm_verb, 1);
>> +    verb->proplist = pa_proplist_new();
>> +    pa_proplist_sets(verb->proplist, PA_PROP_UCM_NAME, pa_strnull(verb_name));
>> +    pa_proplist_sets(verb->proplist, PA_PROP_UCM_DESCRIPTION, pa_strna(verb_desc));
>> +    err = ucm_get_devices(verb, uc_mgr);
>> +    if (err < 0)
>> +        pa_log("No UCM devices for verb %s", verb_name);
>> +
>> +    err = ucm_get_modifiers(verb, uc_mgr);
>> +    if (err < 0)
>> +        pa_log("No UCM modifiers for verb %s", verb_name);
>> +
>> +    /* Verb properties */
>> +    ucm_get_property(verb, uc_mgr, verb_name);
>> +
>> +    PA_LLIST_FOREACH(d, verb->devices) {
>> +        const char *dev_name = pa_proplist_gets(d->proplist, PA_PROP_UCM_NAME);
>> +
>> +        /* Devices properties */
>> +        ucm_get_device_property(d, uc_mgr, verb, dev_name);
>> +    }
>> +    /* make conflicting or supported device mutual */
>> +    PA_LLIST_FOREACH(d, verb->devices) {
>> +        const char *dev_name = pa_proplist_gets(d->proplist, PA_PROP_UCM_NAME);
>> +
>> +        append_lost_relationship(verb->devices, d, dev_name);
>> +    }
>> +
>> +    PA_LLIST_FOREACH(mod, verb->modifiers) {
>> +        const char *mod_name = pa_proplist_gets(mod->proplist, PA_PROP_UCM_NAME);
>> +
>> +        /* Modifier properties */
>> +        ucm_get_modifier_property(mod, uc_mgr, mod_name);
>> +
>> +        /* Set PA_PROP_DEVICE_INTENDED_ROLES property to devices */
>> +        pa_log_info("Set media roles for verb %s, modifier %s", verb_name, mod_name);
>> +        ucm_set_media_roles(mod, verb->devices, mod_name);
>> +    }
>> +
>> +    *p_verb = verb;
>> +    return 0;
>> +}
>> +
>> +static void ucm_add_port_combination(pa_hashmap *hash, pa_alsa_ucm_mapping_context *context,
>> +        int is_sink, int *dev_indices, int num, pa_hashmap *ports, pa_card_profile *cp, pa_core *core) {
>> +    pa_device_port *port;
>> +    int i;
>> +    unsigned priority;
>> +    char *name, *desc;
>> +    const char *dev_name;
>> +    const char *direction;
>> +    pa_alsa_ucm_device *dev;
>> +
>> +    dev = context->ucm_devices[dev_indices[0]];
>> +    dev_name = pa_proplist_gets(dev->proplist, PA_PROP_UCM_NAME);
>> +    name = pa_xstrdup(dev_name);
>> +    desc = num == 1 ? pa_xstrdup(pa_proplist_gets(dev->proplist, PA_PROP_UCM_DESCRIPTION))
>> +            : pa_sprintf_malloc("Combination port for %s", dev_name);
>> +    priority = is_sink ? dev->playback_priority : dev->capture_priority;
>> +    for (i=1; i<num; i++) {
>> +        char *tmp;
>> +        dev = context->ucm_devices[dev_indices[i]];
>> +        dev_name = pa_proplist_gets(dev->proplist, PA_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;
>> +        /* FIXME: Is it true? */
>> +        priority += (is_sink ? dev->playback_priority : dev->capture_priority);
>> +    }
>> +
>> +    port = pa_hashmap_get(ports, name);
>> +    if (!port) {
>> +        port = pa_device_port_new(core, pa_strna(name), desc, sizeof(pa_alsa_port_data_ucm));
>> +        pa_assert(port);
>> +        pa_hashmap_put(ports, port->name, port);
>> +        pa_log_debug("Add port %s: %s", port->name, port->description);
>> +        port->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
>> +    }
>> +    port->priority = priority;
>> +    if (is_sink)
>> +        port->is_output = TRUE;
>> +    else
>> +        port->is_input = TRUE;
>> +
>> +    pa_xfree(name);
>> +    pa_xfree(desc);
>> +
>> +    direction = is_sink ? "output" : "input";
>> +    pa_log_debug("Port %s direction %s, priority %d", port->name, direction, priority);
>> +
>> +    if (cp) {
>> +        pa_log_debug("Adding port %s to profile %s", port->name, cp->name);
>> +        pa_hashmap_put(port->profiles, cp->name, cp);
>> +    }
>> +    if (hash) {
>> +        pa_hashmap_put(hash, port->name, port);
>> +        pa_device_port_ref(port);
>> +    }
>> +}
>> +
>> +static int ucm_device_contain(pa_alsa_ucm_mapping_context *context,
>> +        int *dev_indices, int dev_num, const char *device_name) {
>> +    int i;
>> +    const char *dev_name;
>> +    pa_alsa_ucm_device *dev;
>> +
>> +    for (i=0; i<dev_num; i++) {
>> +        dev = context->ucm_devices[dev_indices[i]];
>> +        dev_name = pa_proplist_gets(dev->proplist, PA_PROP_UCM_NAME);
>> +        if (!strcmp(dev_name, device_name))
>> +            return 1;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int ucm_port_contain(const char *port_name, const char *dev_name) {
>> +    int ret=0;
>> +    char *r;
>> +    const char *state=NULL;
>> +
>> +    if (!port_name || !dev_name)
>> +        return FALSE;
>> +
>> +    while ((r = pa_split(port_name, "+", &state))) {
>> +        if (!strcmp(r, dev_name)) {
>> +            pa_xfree(r);
>> +            ret = 1;
>> +            break;
>> +        }
>> +        pa_xfree(r);
>> +    }
>> +    return ret;
>> +}
>
> Better to use strstr() here. Avoids some unnecessary allocation +
> deallocation.
Is it possible that we have sub string matching in error?
>
>> +
>> +static int ucm_check_conformance(pa_alsa_ucm_mapping_context *context,
>> +        int *dev_indices, int dev_num, int map_index) {
>> +    int i;
>> +    pa_alsa_ucm_device *dev = context->ucm_devices[map_index];
>> +
>> +    pa_log_debug("Check device %s conformance with %d other devices",
>> +            pa_proplist_gets(dev->proplist, PA_PROP_UCM_NAME), dev_num);
>> +    if (dev_num == 0) {
>> +        pa_log_debug("First device in combination, number 1");
>> +        return 1;
>> +    }
>> +
>> +    if (dev->n_confdev > 0) { /* the device defines conflicting devices */
>> +        for (i=0; i<dev->n_confdev; i++)
>> +            if (ucm_device_contain(context, dev_indices, dev_num, dev->conflicting_devices[i])) {
>> +                pa_log_debug("Conflicting device found");
>> +                return 0;
>> +            }
>> +    }
>> +    else if (dev->n_suppdev >= dev_num) { /* the device defines supported devices */
>> +        for (i=0; i<dev_num; i++)
>> +            if (!ucm_device_in(dev->supported_devices, dev->n_suppdev, context->ucm_devices[dev_indices[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;
>> +}
>> +
>> +void pa_ucm_add_ports_combination(pa_hashmap *hash,
>> +        pa_alsa_ucm_mapping_context *context, int is_sink, int *dev_indices, int dev_num,
>> +        int map_index, pa_hashmap *ports, pa_card_profile *cp, pa_core *core) {
>> +
>> +    if (map_index >= context->ucm_devices_num)
>> +        return;
>> +
>> +    /* check if device at map_index can combine with existing devices combination */
>> +    if (ucm_check_conformance(context, dev_indices, dev_num, map_index)) {
>> +        /* add device at map_index to devices combination */
>> +        dev_indices[dev_num] = map_index;
>> +        /* add current devices combination as a new port */
>> +        ucm_add_port_combination(hash, context, is_sink, dev_indices, dev_num+1, ports, cp, core);
>> +        /* try more elements combination */
>> +        pa_ucm_add_ports_combination(hash, context, is_sink, dev_indices, dev_num+1, map_index+1, ports, cp, core);
>> +    }
>> +    /* try other device with current elements number */
>> +    pa_ucm_add_ports_combination(hash, context, is_sink, dev_indices, dev_num, map_index+1, ports, cp, core);
>> +}
>> +
>> +static char* merge_roles(const char *cur, const char *add) {
>> +    char *r, *ret = NULL;
>> +    const char *state=NULL;
>> +
>> +    if (add == NULL)
>> +        return pa_xstrdup(cur);
>> +    else if (cur == NULL)
>> +        return pa_xstrdup(add);
>> +
>> +    while ((r = pa_split_spaces(add, &state))) {
>> +        char *value;
>> +
>> +        if (!ret)
>> +            value = pa_xstrdup(r);
>
> Simpler to just allocate ret as pa_xstrdup(cur) than do this.
Exactly, I produced a bug here.
>
>> +        else if (!role_match(cur, r))
>> +            value = pa_sprintf_malloc("%s %s", ret, r);
>> +        else {
>> +            pa_xfree(r);
>> +            continue;
>> +        }
>> +        pa_xfree(ret);
>> +        ret = value;
>> +        pa_xfree(r);
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +void pa_ucm_add_ports(pa_hashmap **p, pa_proplist *proplist,
>> +        pa_alsa_ucm_mapping_context *context, int is_sink, pa_card *card) {
>> +    int *dev_indices = pa_xnew(int, context->ucm_devices_num);
>> +    int i;
>> +    char *merged_roles;
>> +    const char *role_name = is_sink ? PA_PROP_UCM_PLAYBACK_ROLES : PA_PROP_UCM_CAPTURE_ROLES;
>> +
>> +    pa_assert(p);
>> +    pa_assert(!*p);
>> +    pa_assert(context->ucm_devices_num > 0);
>> +
>> +    /* add ports first */
>> +    *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
>> +    pa_ucm_add_ports_combination(*p, context, is_sink, dev_indices, 0, 0, card->ports, NULL, card->core);
>> +    pa_xfree(dev_indices);
>> +
>> +    /* then set property PA_PROP_DEVICE_INTENDED_ROLES */
>> +    merged_roles = pa_xstrdup(pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES));
>> +    for (i=0; i<context->ucm_devices_num; i++) {
>> +        const char *roles = pa_proplist_gets(context->ucm_devices[i]->proplist, role_name);
>> +        char *tmp;
>> +
>> +        tmp = merge_roles(merged_roles, roles);
>> +        pa_xfree(merged_roles);
>> +        merged_roles = tmp;
>> +    }
>> +
>> +    if (merged_roles)
>> +        pa_proplist_sets(proplist, PA_PROP_DEVICE_INTENDED_ROLES, merged_roles);
>> +
>> +    pa_log_info("Alsa device %s roles: %s", pa_proplist_gets(
>> +                proplist, PA_PROP_DEVICE_STRING), pa_strnull(merged_roles));
>> +    pa_xfree(merged_roles);
>> +}
>> +
>> +/* Change UCM verb and device to match selected card profile */
>> +int pa_ucm_set_profile(pa_alsa_ucm_config *ucm, const char *new_profile,
>> +        const char *old_profile) {
>> +    int ret = 0;
>> +    const char *profile;
>> +    pa_alsa_ucm_verb *verb;
>> +
>> +    if (new_profile == old_profile)
>> +        return ret;
>> +    else if (new_profile == NULL || old_profile == NULL)
>> +        profile = new_profile ? new_profile : SND_USE_CASE_VERB_INACTIVE;
>> +    else if (strcmp(new_profile, old_profile) != 0)
>> +        profile = new_profile;
>> +    else
>> +        return ret;
>> +
>> +    /* change verb */
>> +    pa_log_info("Set ucm verb to %s", profile);
>> +    if ((snd_use_case_set(ucm->ucm_mgr, "_verb", profile)) < 0) {
>> +        pa_log("failed to set verb %s", profile);
>> +        ret = -1;
>> +    }
>> +
>> +    /* 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_PROP_UCM_NAME);
>> +        if (!strcmp(verb_name, profile)) {
>> +            ucm->active_verb = verb;
>> +            break;
>> +        }
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +int pa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, int is_sink) {
>> +    int i, ret=0;
>> +    pa_alsa_ucm_config *ucm;
>> +    char *enable_devs=NULL;
>> +    char *r;
>> +    const char *state=NULL;
>> +
>> +    pa_assert(context && context->ucm);
>> +
>> +    ucm = context->ucm;
>> +    pa_assert(ucm->ucm_mgr);
>> +
>> +    /* first disable then enable */
>> +    for (i=0; i<context->ucm_devices_num; i++) {
>> +        const char *dev_name = pa_proplist_gets(context->ucm_devices[i]->proplist, PA_PROP_UCM_NAME);
>> +
>> +        if (ucm_port_contain(port->name, dev_name)) {
>> +            char *tmp = enable_devs ? pa_sprintf_malloc("%s,%s", enable_devs, dev_name) : pa_xstrdup(dev_name);
>
> That's a bunch of unnecessary memory allocation and deallocation. Use
> either an linked list or an idxset here.
>
>> +
>> +            pa_xfree(enable_devs);
>> +            enable_devs = tmp;
>> +        }
>> +        else {
>> +            pa_log_info("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;
>> +            }
>> +        }
>> +    }
>> +    if (enable_devs) {
>> +        while ((r = pa_split(enable_devs, ",", &state))) {
>> +            pa_log_info("Enable ucm device %s", r);
>> +            if (snd_use_case_set(ucm->ucm_mgr, "_enadev", r) < 0) {
>> +                pa_log("failed to enable ucm device %s", r);
>> +                pa_xfree(r);
>> +                ret = -1;
>> +                break;
>> +            }
>> +            pa_xfree(r);
>> +        }
>> +        pa_xfree(enable_devs);
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static void ucm_add_mapping(pa_alsa_profile *p, pa_alsa_mapping *m) {
>> +
>> +    switch (m->direction) {
>> +        case PA_ALSA_DIRECTION_ANY:
>> +            pa_idxset_put(p->output_mappings, m, NULL);
>> +            pa_idxset_put(p->input_mappings, m, NULL);
>> +            break;
>> +        case PA_ALSA_DIRECTION_OUTPUT:
>> +            pa_idxset_put(p->output_mappings, m, NULL);
>> +            break;
>> +        case PA_ALSA_DIRECTION_INPUT:
>> +            pa_idxset_put(p->input_mappings, m, NULL);
>> +            break;
>> +    }
>> +}
>> +
>> +static void alsa_mapping_add_ucm_device(pa_alsa_mapping *m, pa_alsa_ucm_device *device) {
>> +    char *cur_desc;
>> +    const char *new_desc;
>> +
>> +    /* we expand 8 entries each time */
>> +    if ((m->ucm_context.ucm_devices_num & 7) == 0)
>> +        m->ucm_context.ucm_devices = pa_xrealloc(m->ucm_context.ucm_devices,
>> +                sizeof(pa_alsa_ucm_device *) * (m->ucm_context.ucm_devices_num + 8));
>> +    m->ucm_context.ucm_devices[m->ucm_context.ucm_devices_num++] = device;
>
> Use an idxset here.
>
>> +
>> +    new_desc = pa_proplist_gets(device->proplist, PA_PROP_UCM_DESCRIPTION);
>> +    cur_desc = m->description;
>> +    if (cur_desc)
>> +        m->description = pa_sprintf_malloc("%s + %s", cur_desc, new_desc);
>> +    else
>> +        m->description = pa_xstrdup(new_desc);
>> +    pa_xfree(cur_desc);
>> +
>> +    /* walk around null case */
>> +    m->description = m->description ? m->description : pa_xstrdup("");
>> +
>> +    /* save mapping to ucm device */
>> +    if (m->direction == PA_ALSA_DIRECTION_OUTPUT)
>> +        device->playback_mapping = m;
>> +    else
>> +        device->capture_mapping = m;
>> +}
>> +
>> +static int ucm_create_mapping_direction(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, const char *device_str, int is_sink) {
>> +    pa_alsa_mapping *m;
>> +    char *mapping_name;
>> +    unsigned priority, channels;
>> +
>> +    mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, device_str, is_sink ? "sink" : "source");
>> +
>> +    m = pa_alsa_mapping_get(ps, mapping_name);
>> +    if (!m) {
>> +        pa_log("no mapping for %s", mapping_name);
>> +        pa_xfree(mapping_name);
>> +        return -1;
>> +    }
>> +    pa_log_info("ucm mapping: %s dev %s", mapping_name, device_name);
>> +    pa_xfree(mapping_name);
>> +
>> +    priority = is_sink ? device->playback_priority : device->capture_priority;
>> +    channels = is_sink ? device->playback_channels : device->capture_channels;
>> +    if (m->ucm_context.ucm_devices_num == 0) {   /* new mapping */
>> +        m->supported = TRUE;
>> +        m->ucm_context.ucm = ucm;
>> +
>> +        m->device_strings = pa_xnew0(char*, 2);
>> +        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);
>> +        pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
>> +    }
>> +
>> +    /* mapping priority is the highest one of ucm devices */
>> +    if (priority > m->priority)
>> +        m->priority = priority;
>> +
>> +    /* mapping channels is the lowest one of ucm devices */
>> +    if (channels < m->channel_map.channels)
>> +        pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
>> +    alsa_mapping_add_ucm_device(m, device);
>> +
>> +    return 0;
>> +}
>> +
>> +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, const char *sink, const char *source) {
>> +    int ret=0;
>> +
>> +    if (!sink && !source) {
>> +        pa_log("no sink and source at %s: %s", verb_name, device_name);
>> +        return -1;
>> +    }
>> +
>> +    if (sink)
>> +        ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, sink, 1);
>> +    if (ret == 0 && source)
>> +        ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, source, 0);
>> +
>> +    return ret;
>> +}
>> +
>> +static int ucm_create_profile(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps,
>> +        pa_alsa_ucm_verb *verb, const char *verb_name, const char *verb_desc) {
>> +    pa_alsa_profile *p;
>> +    pa_alsa_ucm_device *dev;
>> +    int i=0;
>> +
>> +    pa_assert(ps);
>> +
>> +    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(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);
>> +
>> +    p->supported = TRUE;
>> +    pa_hashmap_put(ps->profiles, p->name, p);
>> +
>> +    /* TODO: get profile priority from ucm info or policy management */
>> +    do {
>> +        /* We allow UCM verb name to be separated by "_",
>> +         * while predefined alsa ucm name is splitted by " "
>> +         */
>> +        char *verb_cmp = pa_xstrdup(verb_name);
>> +        char *c = verb_cmp;
>> +        while (*c) {
>> +            if (*c == '_') *c = ' ';
>> +            c++;
>> +        }
>> +        if (strcasecmp(verb_info[i].id, verb_cmp) == 0) {
>> +            p->priority = verb_info[i].priority;
>> +            pa_xfree(verb_cmp);
>> +            break;
>> +        }
>> +        pa_xfree(verb_cmp);
>> +    } while (verb_info[++i].id);
>> +
>> +    if (verb_info[++i].id == NULL)
>> +        p->priority = 1000;
>> +
>> +    PA_LLIST_FOREACH(dev, verb->devices) {
>> +        const char *dev_name, *sink, *source;
>> +
>> +        dev_name = pa_proplist_gets(dev->proplist, PA_PROP_UCM_NAME);
>> +
>> +        sink = pa_proplist_gets(dev->proplist, PA_PROP_UCM_SINK);
>> +        source = pa_proplist_gets(dev->proplist, PA_PROP_UCM_SOURCE);
>> +
>> +        ucm_create_mapping(ucm, ps, p, dev, verb_name, dev_name, sink, source);
>> +    }
>> +    pa_alsa_profile_dump(p);
>> +
>> +    return 0;
>> +}
>> +
>> +pa_alsa_profile_set* pa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map) {
>> +    pa_alsa_ucm_verb *verb;
>> +    pa_alsa_profile_set *ps;
>> +
>> +    ps = pa_xnew0(pa_alsa_profile_set, 1);
>> +    ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
>> +    ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
>> +    ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
>> +
>> +    /* create a profile for each verb */
>> +    PA_LLIST_FOREACH(verb, ucm->verbs) {
>> +        const char *verb_name;
>> +        const char *verb_desc;
>> +
>> +        verb_name = pa_proplist_gets(verb->proplist, PA_PROP_UCM_NAME);
>> +        verb_desc = pa_proplist_gets(verb->proplist, PA_PROP_UCM_DESCRIPTION);
>> +        if (verb_name == NULL) {
>> +            pa_log("verb with no name");
>> +            continue;
>> +        }
>> +
>> +         ucm_create_profile(ucm, ps, verb, verb_name, verb_desc);
>> +    }
>> +    ps->probed = TRUE;
>> +
>> +    return ps;
>> +}
>> +
>> +static void free_verb(pa_alsa_ucm_verb *verb) {
>> +    pa_alsa_ucm_device *di, *dn;
>> +    pa_alsa_ucm_modifier *mi, *mn;
>> +
>> +    PA_LLIST_FOREACH_SAFE(di, dn, verb->devices) {
>> +        PA_LLIST_REMOVE(pa_alsa_ucm_device, verb->devices, di);
>> +        pa_proplist_free(di->proplist);
>> +        if (di->n_suppdev > 0)
>> +            pa_xstrfreev(di->supported_devices);
>> +        if (di->n_confdev > 0)
>> +            pa_xstrfreev(di->conflicting_devices);
>> +        pa_xfree(di);
>> +    }
>> +
>> +    PA_LLIST_FOREACH_SAFE(mi, mn, verb->modifiers) {
>> +        PA_LLIST_REMOVE(pa_alsa_ucm_modifier, verb->modifiers, mi);
>> +        pa_proplist_free(mi->proplist);
>> +        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);
>> +    }
>> +    pa_proplist_free(verb->proplist);
>> +    pa_xfree(verb);
>> +}
>> +
>> +void pa_ucm_free(pa_alsa_ucm_config *ucm) {
>> +    pa_alsa_ucm_verb *vi, *vn;
>> +
>> +    PA_LLIST_FOREACH_SAFE(vi, vn, ucm->verbs) {
>> +        PA_LLIST_REMOVE(pa_alsa_ucm_verb, ucm->verbs, vi);
>> +        free_verb(vi);
>> +    }
>> +    if (ucm->ucm_mgr) {
>> +        snd_use_case_mgr_close(ucm->ucm_mgr);
>> +        ucm->ucm_mgr = NULL;
>> +    }
>> +}
>> diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
>> new file mode 100644
>> index 0000000..18b6fe0
>> --- /dev/null
>> +++ b/src/modules/alsa/alsa-ucm.h
>> @@ -0,0 +1,111 @@
>> +#ifndef fooalsaucmhfoo
>> +#define fooalsaucmhfoo
>> +
>> +/***
>> +  This file is part of PulseAudio.
>> +
>> +  Copyright 2011 Wolfson Microelectronics PLC
>> +  Author Margarita Olaya <magi at slimlogic.co.uk>
>> +  Copyright 2012 Feng Wei <feng.wei at linaro.org>, Linaro
>> +
>> +  PulseAudio is free software; you can redistribute it and/or modify
>> +  it under the terms of the GNU Lesser General Public License as published
>> +  by the Free Software Foundation; either version 2.1 of the License,
>> +  or (at your option) any later version.
>> +
>> +  PulseAudio is distributed in the hope that it will be useful, but
>> +  WITHOUT ANY WARRANTY; without even the implied warranty of
>> +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>> +  General Public License for more details.
>> +
>> +  You should have received a copy of the GNU Lesser General Public License
>> +  along with PulseAudio; if not, write to the Free Software
>> +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
>> +  USA.
>> +***/
>> +
>> +#include <asoundlib.h>
>> +#include <use-case.h>
>> +
>> +typedef struct pa_alsa_mapping pa_alsa_mapping;
>
> This makes the file not compile for me, since this is already defined in
> alsa-util.h.
I didn't have same error, but I will include header file instead of
redefine the type.
>
>> +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;
>> +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_port_data_ucm pa_alsa_port_data_ucm;
>> +
>> +pa_alsa_profile_set* pa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map);
>> +int pa_ucm_set_profile(pa_alsa_ucm_config *ucm, const char *new_profile, const char *old_profile);
>> +
>> +int pa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name, const char *verb_desc, pa_alsa_ucm_verb **p_verb);
>> +
>> +void pa_ucm_add_ports(pa_hashmap **hash, pa_proplist *proplist,
>> +        pa_alsa_ucm_mapping_context *context, int is_sink, pa_card *card);
>> +void pa_ucm_add_ports_combination(pa_hashmap *hash, pa_alsa_ucm_mapping_context *context,
>> +        int is_sink, int *dev_indices, int dev_num, int map_index, pa_hashmap *ports,
>> +        pa_card_profile *cp, pa_core *core);
>> +int pa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, int is_sink);
>> +
>> +void pa_ucm_free(pa_alsa_ucm_config *ucm);
>> +
>> +/* UCM modifier action direction */
>> +enum {
>> +    PA_ALSA_UCM_DIRECT_NONE = 0,
>> +    PA_ALSA_UCM_DIRECT_SINK,
>> +    PA_ALSA_UCM_DIRECT_SOURCE
>> +};
>> +
>> +/* UCM - Use Case Manager is available on some audio cards */
>> +
>> +struct pa_alsa_ucm_device {
>> +    PA_LLIST_FIELDS(pa_alsa_ucm_device);
>> +    pa_proplist *proplist;
>> +    unsigned playback_priority;
>> +    unsigned capture_priority;
>> +    unsigned playback_channels;
>> +    unsigned capture_channels;
>> +    pa_alsa_mapping *playback_mapping;
>> +    pa_alsa_mapping *capture_mapping;
>> +    int n_confdev;
>> +    int n_suppdev;
>> +    char **conflicting_devices;
>> +    char **supported_devices;
>> +};
>> +
>> +struct pa_alsa_ucm_modifier {
>> +    PA_LLIST_FIELDS(pa_alsa_ucm_modifier);
>> +    pa_proplist *proplist;
>> +    int n_confdev;
>> +    int n_suppdev;
>> +    const char **conflicting_devices;
>> +    const char **supported_devices;
>> +    int action_direct;
>> +    char *media_role;
>> +};
>> +
>> +struct pa_alsa_ucm_verb {
>> +    PA_LLIST_FIELDS(pa_alsa_ucm_verb);
>> +    pa_proplist *proplist;
>> +    PA_LLIST_HEAD(pa_alsa_ucm_device, devices);
>> +    PA_LLIST_HEAD(pa_alsa_ucm_modifier, modifiers);
>> +};
>> +
>> +struct pa_alsa_ucm_config {
>> +    snd_use_case_mgr_t *ucm_mgr;
>> +    pa_alsa_ucm_verb *active_verb;
>> +
>> +    PA_LLIST_HEAD(pa_alsa_ucm_verb, verbs);
>> +};
>> +
>> +struct pa_alsa_ucm_mapping_context {
>> +    pa_alsa_ucm_config *ucm;
>> +    int ucm_devices_num;
>> +    pa_alsa_ucm_device **ucm_devices;
>> +};
>> +
>> +struct pa_alsa_port_data_ucm {
>> +    int dummy;
>> +};
>> +
>> +#endif
>> diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
>> index b06394d..7d09938 100644
>> --- a/src/modules/alsa/module-alsa-card.c
>> +++ b/src/modules/alsa/module-alsa-card.c
>> @@ -37,6 +37,7 @@
>>  #endif
>>
>>  #include "alsa-util.h"
>> +#include "alsa-ucm.h"
>>  #include "alsa-sink.h"
>>  #include "alsa-source.h"
>>  #include "module-alsa-card-symdef.h"
>> @@ -69,6 +70,7 @@ PA_MODULE_USAGE(
>>          "deferred_volume=<Synchronize software and hardware volume changes to avoid momentary jumps?> "
>>          "profile_set=<profile set configuration file> "
>>          "paths_dir=<directory containing the path configuration files> "
>> +        "use_ucm=<load use case manager> "
>>  );
>>
>>  static const char* const valid_modargs[] = {
>> @@ -95,6 +97,7 @@ static const char* const valid_modargs[] = {
>>      "deferred_volume",
>>      "profile_set",
>>      "paths_dir",
>> +    "use_ucm",
>>      NULL
>>  };
>>
>> @@ -117,6 +120,10 @@ struct userdata {
>>      pa_modargs *modargs;
>>
>>      pa_alsa_profile_set *profile_set;
>> +
>> +    /* ucm stuffs */
>> +    pa_bool_t use_ucm;
>> +    pa_alsa_ucm_config ucm;
>>  };
>>
>>  struct profile_data {
>> @@ -126,6 +133,7 @@ struct profile_data {
>>  static void add_profiles(struct userdata *u, pa_hashmap *h, pa_hashmap *ports) {
>>      pa_alsa_profile *ap;
>>      void *state;
>> +    int *dev_indices;
>>
>>      pa_assert(u);
>>      pa_assert(h);
>> @@ -143,7 +151,13 @@ static void add_profiles(struct userdata *u, pa_hashmap *h, pa_hashmap *ports) {
>>              cp->n_sinks = pa_idxset_size(ap->output_mappings);
>>
>>              PA_IDXSET_FOREACH(m, ap->output_mappings, idx) {
>> -                pa_alsa_path_set_add_ports(m->output_path_set, cp, ports, NULL, u->core);
>> +                if (u->use_ucm) {
>> +                    dev_indices = pa_xnew(int, m->ucm_context.ucm_devices_num);
>> +                    pa_ucm_add_ports_combination(NULL, &m->ucm_context, 1, dev_indices, 0, 0, ports, cp, u->core);
>> +                    pa_xfree(dev_indices);
>> +                }
>> +                else
>> +                    pa_alsa_path_set_add_ports(m->output_path_set, cp, ports, NULL, u->core);
>>                  if (m->channel_map.channels > cp->max_sink_channels)
>>                      cp->max_sink_channels = m->channel_map.channels;
>>              }
>> @@ -153,7 +167,13 @@ static void add_profiles(struct userdata *u, pa_hashmap *h, pa_hashmap *ports) {
>>              cp->n_sources = pa_idxset_size(ap->input_mappings);
>>
>>              PA_IDXSET_FOREACH(m, ap->input_mappings, idx) {
>> -                pa_alsa_path_set_add_ports(m->input_path_set, cp, ports, NULL, u->core);
>> +                if (u->use_ucm) {
>> +                    dev_indices = pa_xnew(int, m->ucm_context.ucm_devices_num);
>> +                    pa_ucm_add_ports_combination(NULL, &m->ucm_context, 0, dev_indices, 0, 0, ports, cp, u->core);
>> +                    pa_xfree(dev_indices);
>> +                }
>> +                else
>> +                    pa_alsa_path_set_add_ports(m->input_path_set, cp, ports, NULL, u->core);
>>                  if (m->channel_map.channels > cp->max_source_channels)
>>                      cp->max_source_channels = m->channel_map.channels;
>>              }
>> @@ -222,6 +242,13 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
>>              am->source = NULL;
>>          }
>>
>> +    /* if UCM is available for this card then update the verb */
>> +    if (u->use_ucm) {
>> +        if (pa_ucm_set_profile(&u->ucm, nd->profile ? nd->profile->name : NULL,
>> +                    od->profile ? od->profile->name : NULL) < 0)
>> +            return -1;
>> +    }
>> +
>>      if (nd->profile && nd->profile->output_mappings)
>>          PA_IDXSET_FOREACH(am, nd->profile->output_mappings, idx) {
>>
>> @@ -259,11 +286,20 @@ static void init_profile(struct userdata *u) {
>>      uint32_t idx;
>>      pa_alsa_mapping *am;
>>      struct profile_data *d;
>> +    struct pa_alsa_ucm_config *ucm = &u->ucm;
>>
>>      pa_assert(u);
>>
>>      d = PA_CARD_PROFILE_DATA(u->card->active_profile);
>>
>> +    if (d->profile && u->use_ucm) {
>> +        /* Set initial verb */
>> +        if (pa_ucm_set_profile(ucm, d->profile->name, NULL) < 0) {
>> +            pa_log("failed to set ucm profile %s", d->profile->name);
>> +            return;
>> +        }
>> +    }
>> +
>>      if (d->profile && d->profile->output_mappings)
>>          PA_IDXSET_FOREACH(am, d->profile->output_mappings, idx)
>>              am->sink = pa_alsa_sink_new(u->module, u->modargs, __FILE__, u->card, am);
>> @@ -408,6 +444,74 @@ static void set_card_name(pa_card_new_data *data, pa_modargs *ma, const char *de
>>      pa_xfree(t);
>>  }
>>
>> +static int card_query_ucm_profiles(struct userdata *u, int card_index)
>> +{
>> +    char *card_name;
>> +    const char **verb_list;
>> +    int num_verbs, i, err=0;
>> +
>> +    /* is UCM available for this card ? */
>> +    if(snd_card_get_name(card_index, &card_name) < 0)
>> +    {
>
> Rewrite as if (...) { -- same for other code in this function.
>
>> +        pa_log("Card can't get card_name from card_index %d", card_index);
>> +        err = -1;
>> +        goto name_fail;
>> +    }
>> +    err = snd_use_case_mgr_open(&u->ucm.ucm_mgr, card_name);
>> +    if (err < 0) {
>> +        pa_log("UCM not available for card %s", card_name);
>> +        err = -1;
>> +        goto ucm_mgr_fail;
>> +    }
>> +
>> +    pa_log_info("UCM available for card %s", card_name);
>> +
>> +    /* get a list of all UCM verbs (profiles) for this card */
>> +    num_verbs = snd_use_case_verb_list(u->ucm.ucm_mgr, &verb_list);
>> +    if (num_verbs <= 0) {
>> +        pa_log("UCM verb list not found for %s", card_name);
>> +        err = -1;
>> +        goto ucm_verb_fail;
>> +    }
>> +
>> +    /* get the properties of each UCM verb */
>> +    for (i = 0; i < num_verbs; i += 2) {
>> +        struct pa_alsa_ucm_verb *verb;
>> +
>> +        /* Get devices and modifiers for each verb */
>> +        err = pa_ucm_get_verb(u->ucm.ucm_mgr, verb_list[i], verb_list[i+1], &verb);
>> +        if (err < 0) {
>> +            pa_log("Failed to get the verb %s", verb_list[i]);
>> +            continue;
>> +        }
>> +        PA_LLIST_PREPEND(pa_alsa_ucm_verb, u->ucm.verbs, verb);
>> +    }
>> +
>> +    if(u->ucm.verbs)
>> +    {
>> +        /* create the profile set for the UCM card */
>> +        u->profile_set = pa_ucm_add_profile_set(&u->ucm, &u->core->default_channel_map);
>> +        pa_alsa_profile_set_dump(u->profile_set);
>> +        err = 0;
>> +    }
>> +    else
>> +    {
>> +        pa_log("No UCM verb is valid for %s", card_name);
>> +        err = -1;
>> +    }
>> +    snd_use_case_free_list(verb_list, num_verbs);
>> +ucm_verb_fail:
>> +    if(err < 0)
>> +    {
>> +        snd_use_case_mgr_close(u->ucm.ucm_mgr);
>> +        u->ucm.ucm_mgr = NULL;
>> +    }
>> +ucm_mgr_fail:
>> +    free(card_name);
>> +name_fail:
>> +    return err;
>> +}
>> +
>>  int pa__init(pa_module *m) {
>>      pa_card_new_data data;
>>      pa_modargs *ma;
>> @@ -456,18 +560,25 @@ int pa__init(pa_module *m) {
>>          }
>>      }
>>
>> +    pa_modargs_get_value_boolean(ma, "use_ucm", &u->use_ucm);
>> +    if (u->use_ucm && !card_query_ucm_profiles(u, u->alsa_card_index)) {
>> +        pa_log_info("Found UCM profiles");
>> +    }
>> +    else {
>> +        u->use_ucm = FALSE;
>>  #ifdef HAVE_UDEV
>> -    fn = pa_udev_get_property(u->alsa_card_index, "PULSE_PROFILE_SET");
>> +        fn = pa_udev_get_property(u->alsa_card_index, "PULSE_PROFILE_SET");
>>  #endif
>>
>> -    if (pa_modargs_get_value(ma, "profile_set", NULL)) {
>> +        if (pa_modargs_get_value(ma, "profile_set", NULL)) {
>> +            pa_xfree(fn);
>> +            fn = pa_xstrdup(pa_modargs_get_value(ma, "profile_set", NULL));
>> +        }
>> +
>> +        u->profile_set = pa_alsa_profile_set_new(fn, &u->core->default_channel_map);
>>          pa_xfree(fn);
>> -        fn = pa_xstrdup(pa_modargs_get_value(ma, "profile_set", NULL));
>>      }
>>
>> -    u->profile_set = pa_alsa_profile_set_new(fn, &u->core->default_channel_map);
>> -    pa_xfree(fn);
>> -
>>      u->profile_set->ignore_dB = ignore_dB;
>>
>>      if (!u->profile_set)
>> @@ -605,6 +716,8 @@ void pa__done(pa_module*m) {
>>              pa_alsa_source_free(s);
>>      }
>>
>> +    pa_ucm_free(&u->ucm);
>> +
>>      if (u->card)
>>          pa_card_free(u->card);
>>
>> diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
>> index 359212a..c81e6bc 100644
>> --- a/src/pulse/proplist.h
>> +++ b/src/pulse/proplist.h
>> @@ -257,6 +257,51 @@ PA_C_DECL_BEGIN
>>  /** For modules: a version string for the module. e.g. "0.9.15" */
>>  #define PA_PROP_MODULE_VERSION                 "module.version"
>>
>> +/** For devices: List of verbs, devices or modifiers availables */
>
> availables -> available
>
>> +#define PA_PROP_UCM_NAME                       "ucm.name"
>> +
>> +/** For devices: List of supported devices per verb*/
>> +#define PA_PROP_UCM_DESCRIPTION                "ucm.description"
>> +
>> +/** For devices: Playback device name e.g PlaybackPCM */
>> +#define PA_PROP_UCM_SINK                       "ucm.sink"
>> +
>> +/** For devices: Capture device name e.g CapturePCM*/
>> +#define PA_PROP_UCM_SOURCE                     "ucm.source"
>> +
>> +/** For devices: Playback roles */
>> +#define PA_PROP_UCM_PLAYBACK_ROLES             "ucm.playback.roles"
>> +
>> +/** For devices: Playback control volume ID string. e.g PlaybackVolume */
>> +#define PA_PROP_UCM_PLAYBACK_VOLUME            "ucm.playback.volume"
>> +
>> +/** For devices: Playback switch e.g PlaybackSwitch */
>> +#define PA_PROP_UCM_PLAYBACK_SWITCH            "ucm.playback.switch"
>> +
>> +/** For devices: Playback priority */
>> +#define PA_PROP_UCM_PLAYBACK_PRIORITY          "ucm.playback.priority"
>> +
>> +/** For devices: Playback channels */
>> +#define PA_PROP_UCM_PLAYBACK_CHANNELS          "ucm.playback.channels"
>> +
>> +/** For devices: Capture roles */
>> +#define PA_PROP_UCM_CAPTURE_ROLES              "ucm.capture.roles"
>> +
>> +/** For devices: Capture controls volume ID string. e.g CaptureVolume */
>> +#define PA_PROP_UCM_CAPTURE_VOLUME             "ucm.capture.volume"
>> +
>> +/** For devices: Capture switch e.g CaptureSwitch */
>> +#define PA_PROP_UCM_CAPTURE_SWITCH             "ucm.capture.switch"
>> +
>> +/** For devices: Capture priority */
>> +#define PA_PROP_UCM_CAPTURE_PRIORITY           "ucm.capture.priority"
>> +
>> +/** For devices: Capture channels */
>> +#define PA_PROP_UCM_CAPTURE_CHANNELS           "ucm.capture.channels"
>> +
>> +/** For devices: Quality of Service */
>> +#define PA_PROP_UCM_QOS                        "ucm.qos"
>> +
>>  /** For PCM formats: the sample format used as returned by pa_sample_format_to_string() \since 1.0 */
>>  #define PA_PROP_FORMAT_SAMPLE_FORMAT           "format.sample_format"
>>
>
>
> _______________________________________________
> pulseaudio-discuss mailing list
> pulseaudio-discuss at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss



-- 
Wei.Feng (irc wei_feng)
Linaro Multimedia Team
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog


More information about the pulseaudio-discuss mailing list