[pulseaudio-discuss] [PATCH 1/2] new files for ALSA UCM support
jankovac503 at gmail.com
jankovac503 at gmail.com
Wed Nov 2 16:55:23 PDT 2011
From: Janos Kovacs <jankovac503 at gmail.com>
---
src/modules/alsa/alsa-ucm.c | 1481 +++++++++++++++++++++++++++++++++++++++++++
src/modules/alsa/alsa-ucm.h | 52 ++
2 files changed, 1533 insertions(+), 0 deletions(-)
create mode 100644 src/modules/alsa/alsa-ucm.c
create mode 100644 src/modules/alsa/alsa-ucm.h
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
new file mode 100644
index 0000000..4036227
--- /dev/null
+++ b/src/modules/alsa/alsa-ucm.c
@@ -0,0 +1,1481 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2011 Intel Corporation.
+ Author Janos Kovacs <jankovac503 at gmail.com>
+
+ 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 <string.h>
+#include <stdio.h>
+#include <ctype.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 <pulse/utf8.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 <pulsecore/i18n.h>
+
+#include "alsa-mixer.h"
+#include "alsa-util.h"
+#include "alsa-ucm.h"
+
+
+
+#define UNDEFINED 0
+#define NONE UNDEFINED
+#define OUTPUT 1
+#define INPUT 2
+#define DUPLEX (INPUT | OUTPUT)
+
+#define MUSIC 1
+#define VOICE 2
+
+#define SINGLEDEV 0
+#define DUALDEV 1
+
+
+#define DIRECTION_ALSA(d) direction_map[(d) & 3].alsa
+#define DIRECTION_PULSE(d) direction_map[(d) & 3].id
+#define DIRECTION_DESCRIPTION(d) direction_map[(d) & 3].descr
+
+#define VERB_TO_MEDIA(v) strcasestr(v, "voice") ? VOICE : MUSIC;
+#define VERB_TO_MODE(v) verb_to_mode(v)
+#define VERB_TO_DESCRIPTION(v) verb_to_desc(v)
+
+#define MEDIA_ID(m) media_map[(m) & 3].id
+#define MEDIA_DESCRIPTION(m) media_map[(m) & 3].descr
+
+
+
+enum {
+ CAPTURE_CTL = 0,
+ CAPTURE_PCM,
+ PLAYBACK_CTL,
+ PLAYBACK_PCM,
+ MAX_DEVICE
+};
+
+typedef enum {
+ PROFILE_MATCHING_MODIFIER,
+ PROFILE_ANY_MODIFIER,
+ PORT_MODIFIER
+} modifier_type;
+
+
+struct ucm_list {
+ const char *name;
+ const char *descr;
+};
+
+struct direction {
+ pa_alsa_direction_t alsa;
+ const char *id;
+ const char *descr;
+};
+
+struct media {
+ const char *id;
+ const char *descr;
+};
+
+struct mode {
+ const char *verb;
+ const char *mode;
+ const char *descr;
+};
+
+
+struct support {
+ int dir; /* INPUT | OUTPUT */
+ const char **devs; /* UCM device list */
+ int len; /* length of devs list */
+};
+
+
+static struct media media_map[4] = {
+ [ UNDEFINED ] = { "music" , "music" },
+ [ MUSIC ] = { "music" , "music" },
+ [ VOICE ] = { "voice" , "voice" },
+ [MUSIC | VOICE] = { "music-and-voice", "music and voice" }
+};
+
+
+static struct direction direction_map[4] = {
+ [UNDEFINED] = { PA_ALSA_DIRECTION_ANY , "duplex", "playback and capture" },
+ [ OUTPUT ] = { PA_ALSA_DIRECTION_OUTPUT , "output", "playback" },
+ [ INPUT ] = { PA_ALSA_DIRECTION_INPUT , "input" , "capture" },
+ [ DUPLEX ] = { PA_ALSA_DIRECTION_ANY , "duplex", "playback and capture" }
+};
+
+static struct mode mode_map[] = {
+ { SND_USE_CASE_VERB_HIFI , "hifi" , "HiFi" },
+ { SND_USE_CASE_VERB_HIFI_LOW_POWER , "hifi-low-power" , "HiFi powersaving" },
+ { SND_USE_CASE_VERB_VOICECALL , "gsm" , "gsm or 3g" },
+ { SND_USE_CASE_VERB_IP_VOICECALL , "voip" , "VoIP" },
+ { SND_USE_CASE_VERB_ANALOG_RADIO , "analog-radio" , "analog FM radio" },
+ { SND_USE_CASE_VERB_DIGITAL_RADIO , "digital-radio" , "digital FM radio" },
+ { NULL , NULL , NULL }
+};
+
+
+static void profile_set_populate(snd_use_case_mgr_t *, pa_alsa_profile_set *, const char *);
+static void profile_set_add_profile(snd_use_case_mgr_t *, pa_alsa_profile_set *, const char *,
+ struct ucm_list *, struct ucm_list *);
+static char **merge_device_list(const char **, int);
+static char **get_dualdev_list(snd_use_case_mgr_t *, const char *, struct ucm_list *);
+static void get_path_lists(snd_use_case_mgr_t *, pa_alsa_profile_set *, const char *,
+ char **, char **, char ***, char ***, pa_alsa_direction_t *);
+static int get_volctl(snd_use_case_mgr_t *, const char *, char *, const char *,
+ int, const char **);
+static void add_path(snd_use_case_mgr_t *, pa_alsa_profile_set *, unsigned int,
+ const char *, char *, char **,int *, char **,int *,
+ char **,char **,
+#ifndef INPUT_DEVICE_BASED_JACK_DETECTION
+ const char *,
+#else
+ const char *,const char *,
+#endif
+ int);
+static pa_alsa_path *path_new(const char *, const char *, pa_alsa_direction_t,
+ const char *, const char *, char **);
+static void path_add_volume_control(pa_alsa_path *, const char *);
+static pa_alsa_profile *profile_new(pa_alsa_profile_set *, const char *, struct ucm_list *);
+static void profile_add_mapping(pa_alsa_profile *, const char *, const char *, pa_alsa_direction_t,
+ char **, char **, char **);
+static char *compose_profile_name(const char *, struct ucm_list *, char *, int);
+static char *compose_profile_description(const char *, struct ucm_list *, char *, int);
+static void get_device_changes(char *, char *, pa_bool_t *, pa_bool_t *);
+static void get_modifier_changes(pa_bool_t, char **, char **, char **,int, char **,int);
+static int get_list(snd_use_case_mgr_t *, const char *, struct ucm_list **);
+static struct ucm_list *get_modifiers(snd_use_case_mgr_t *, const char *, modifier_type);
+static void init_channel_map(pa_channel_map *, const char *);
+static const char *verb_to_mode(const char *);
+static const char *verb_to_desc(const char *);
+static char **listdup(struct ucm_list *);
+static char **listmerge(char **, const char **);
+
+#ifdef INPUT_DEVICE_BASED_JACK_DETECTION
+static void get_inputdev(snd_use_case_mgr_t *, const char *, char *,
+ const char **, const char **);
+static int path_add_jack_input_device(pa_alsa_path *, const char *, const char *);
+#endif
+
+pa_alsa_profile_set *pa_alsa_ucm_profile_set_new(int alsa_card_index, struct pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map)
+{
+ char *alsa_card_name;
+ pa_alsa_profile_set *ps;
+ pa_alsa_profile *p;
+ pa_alsa_mapping *m;
+ snd_use_case_mgr_t *uc_mgr;
+ struct ucm_list *verbs;
+ const char *verb;
+ int i,n;
+ void *state;
+
+ if (snd_card_get_name(alsa_card_index, &alsa_card_name) < 0 ||
+ snd_use_case_mgr_open(&uc_mgr, alsa_card_name) < 0)
+ return NULL;
+
+ 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);
+ ps->input_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ ps->output_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ ps->use_ucm = TRUE;
+
+ for (n = get_list(uc_mgr, "_verbs", &verbs), i = 0; i < n; i++) {
+ verb = verbs[i].name;
+
+ if (snd_use_case_set(uc_mgr, "_verb", verb) < 0) {
+ pa_log("can't set verb '%s'", verb);
+ continue;
+ }
+
+ profile_set_populate(uc_mgr, ps, verb);
+ }
+
+ ucm->enabled = TRUE;
+ ucm->uc_mgr = uc_mgr;
+
+
+ PA_HASHMAP_FOREACH(m, ps->mappings, state) {
+ if (pa_alsa_mapping_verify(m, default_channel_map) < 0)
+ goto fail;
+ }
+
+ PA_HASHMAP_FOREACH(p, ps->profiles, state) {
+ if (pa_alsa_profile_verify(p) < 0)
+ goto fail;
+ }
+
+ return ps;
+
+fail:
+ snd_use_case_mgr_close(uc_mgr);
+ pa_alsa_profile_set_free(ps);
+ return NULL;
+}
+
+int pa_alsa_ucm_profile_select(pa_alsa_profile *newp, pa_alsa_profile *oldp, pa_alsa_ucm_config *ucm)
+{
+ snd_use_case_mgr_t *uc_mgr;
+ const char *old_verb;
+ const char *new_verb;
+ const char **modifs;
+ int nmodif;
+ int i;
+ int ret = 0;
+
+ pa_assert(ucm);
+ pa_assert_se(uc_mgr = ucm->uc_mgr);
+ pa_assert(!newp || newp->ucm_verb);
+
+ new_verb = newp ? newp->ucm_verb : "Inactive";
+
+ if (snd_use_case_get(uc_mgr, "_verb", &old_verb) < 0) {
+ if (!oldp)
+ old_verb = pa_xstrdup("");
+ else {
+ pa_log("can't get _verb");
+ return -1;
+ }
+ }
+
+ if (!pa_streq(old_verb, new_verb)) {
+ pa_log_debug("changing UCM verb '%s' => '%s'", old_verb, new_verb);
+ pa_log_debug(" disabling modiofiers:");
+
+ if ((nmodif = snd_use_case_get_list(uc_mgr, "_enamods", &modifs)) < 1)
+ pa_log_debug(" no modiers were enabled");
+ else {
+ for (i = 0; i < nmodif; i++) {
+ pa_log_debug(" disabling '%s'", modifs[i]);
+ snd_use_case_set(uc_mgr, "_dismod", modifs[i]);
+ }
+
+ snd_use_case_free_list(modifs, nmodif);
+ }
+
+ if (snd_use_case_set(uc_mgr, "_verb", new_verb) == 0)
+ pa_log_debug(" verb '%s' was sucessfully set", new_verb);
+ else {
+ pa_log("can't set new verb '%s', when profile changed", new_verb);
+ ret = -1;
+ }
+ }
+
+ pa_xfree((void *)old_verb);
+
+ return ret;
+}
+
+int pa_alsa_ucm_path_select(pa_alsa_path *newp, pa_alsa_path *oldp, pa_alsa_ucm_config *ucm)
+{
+#define MAX_MODIFIER_LIST 16
+
+ snd_use_case_mgr_t *uc_mgr;
+ char *modnam;
+ char *od, **om;
+ char *nd, **nm;
+ pa_bool_t device_changed;
+ pa_bool_t device_swapped;
+ char *disable_modifs[MAX_MODIFIER_LIST];
+ char *enable_modifs[MAX_MODIFIER_LIST];
+ char id[256];
+ int i;
+
+ pa_assert(ucm);
+ pa_assert_se(uc_mgr = ucm->uc_mgr);
+
+
+ if (newp) {
+ pa_log_debug("Activating ucm path %s", newp->name);
+ pa_alsa_path_dump(newp);
+
+ nd = newp->ucm_device;
+ nm = newp->ucm_modifiers;
+ }
+ else {
+ nd = NULL;
+ nm = NULL;
+ }
+
+ if (oldp) {
+ if (!newp) {
+ pa_log_debug("Reseting ucm path %s", oldp->name);
+ pa_alsa_path_dump(oldp);
+ }
+
+ od = oldp->ucm_device;
+ om = oldp->ucm_modifiers;
+ }
+ else {
+ od = NULL;
+ om = NULL;
+ }
+
+
+ get_device_changes(od, nd, &device_changed, &device_swapped);
+ get_modifier_changes(device_changed, om, nm,
+ disable_modifs, MAX_MODIFIER_LIST,
+ enable_modifs, MAX_MODIFIER_LIST);
+
+
+ for (i = 0; (modnam = disable_modifs[i]); i++) {
+ pa_log_debug(" disabling modifier '%s'", modnam);
+ if (snd_use_case_set(uc_mgr, "_dismod", modnam) < 0) {
+ pa_log("failed to disable modifier '%s'", modnam);
+ return -1;
+ }
+ }
+
+ if (device_changed) {
+ if (device_swapped) {
+ pa_log_debug(" swapping devices '%s' => '%s'", od, nd);
+ snprintf(id, sizeof(id), "_swdev/%s", od);
+ if (snd_use_case_set(uc_mgr, id, nd) < 0) {
+ pa_log("failed to swap devices '%s' => '%s'", od, nd);
+ return -1;
+ }
+ }
+ else {
+ if (od) {
+ pa_log_debug(" disabling device '%s'", od);
+ if (snd_use_case_set(uc_mgr, "_disdev", od) < 0) {
+ pa_log("failed to disable device '%s'", od);
+ return -1;
+ }
+ }
+ if (nd) {
+ pa_log_debug(" enabling device '%s'", nd);
+ if (snd_use_case_set(uc_mgr, "_enadev", nd) < 0) {
+ pa_log("failed to enable device '%s'", nd);
+ return -1;
+ }
+ }
+ }
+ }
+
+ for (i = 0; (modnam = enable_modifs[i]); i++) {
+ pa_log_debug(" enabling modifier '%s'", modnam);
+ if (snd_use_case_set(uc_mgr, "_enamod", modnam) < 0) {
+ pa_log("failed to enable modifier '%s'", modnam);
+ return -1;
+ }
+ }
+
+ return 0;
+
+#undef MAX_MODIFIER_LIST
+}
+
+
+static void profile_set_populate(snd_use_case_mgr_t *uc_mgr, pa_alsa_profile_set *ps, const char *verb)
+{
+ struct ucm_list *profmods;
+ struct ucm_list *portmods;
+ struct ucm_list subset[3];
+ int i,j;
+
+ pa_assert(ps);
+ pa_assert(verb);
+
+ profmods = get_modifiers(uc_mgr, verb, PROFILE_ANY_MODIFIER);
+ portmods = get_modifiers(uc_mgr, verb, PORT_MODIFIER);
+
+ memset(subset+2, 0, sizeof(struct ucm_list));
+
+ if (!profmods[0].name)
+ profile_set_add_profile(uc_mgr, ps, verb, profmods, portmods);
+ else {
+ for (i = 0; profmods[i].name; i++) {
+ subset[0] = profmods[i];
+ memset(subset + 1, 0, sizeof(struct ucm_list));
+
+ profile_set_add_profile(uc_mgr, ps, verb, subset, portmods);
+
+ for (j = i + 1; profmods[j].name; j++) {
+ subset[1] = profmods[j];
+ profile_set_add_profile(uc_mgr, ps, verb, subset, portmods);
+ }
+ }
+ }
+
+ pa_xstrfreev((char **)portmods);
+ pa_xstrfreev((char **)profmods);
+}
+
+
+static void profile_set_add_profile(snd_use_case_mgr_t *uc_mgr,
+ pa_alsa_profile_set *ps,
+ const char *verb,
+ struct ucm_list *profmods,
+ struct ucm_list *portmods)
+{
+ pa_alsa_profile *p;
+ const char *devs[MAX_DEVICE];
+ char ***inpp;
+ char ***outpp;
+ char **merged_devs;
+ char **in_paths = NULL;
+ char **out_paths = NULL;
+ char **dualdevs;
+ pa_alsa_direction_t direction;
+ char map_name[64];
+ char map_desc[512];
+ char captpcm_id[80];
+ char captctl_id[80];
+ char playpcm_id[80];
+ char playctl_id[80];
+
+
+ snprintf(captpcm_id, sizeof(captpcm_id), "CapturePCM/%s", verb);
+ snprintf(captctl_id, sizeof(captctl_id), "CaptureCTL/%s", verb);
+
+ snprintf(playpcm_id, sizeof(playpcm_id), "PlaybackPCM/%s", verb);
+ snprintf(playctl_id, sizeof(playctl_id), "PlaybackCTL/%s", verb);
+
+
+ memset(devs, 0, sizeof(devs));
+
+ snd_use_case_get(uc_mgr, captpcm_id, &devs[CAPTURE_PCM ]);
+ snd_use_case_get(uc_mgr, captctl_id, &devs[CAPTURE_CTL ]);
+ snd_use_case_get(uc_mgr, playpcm_id, &devs[PLAYBACK_PCM]);
+ snd_use_case_get(uc_mgr, playctl_id, &devs[PLAYBACK_CTL]);
+
+ inpp = (devs[CAPTURE_PCM] || devs[CAPTURE_CTL] ) ? &in_paths : NULL;
+ outpp = (devs[PLAYBACK_PCM] || devs[PLAYBACK_CTL]) ? &out_paths : NULL;
+
+ /* entries in devs either moved to merged devices of freed */
+ merged_devs = merge_device_list(devs, MAX_DEVICE);
+ dualdevs = get_dualdev_list(uc_mgr, verb, portmods);
+
+ if (!(p = profile_new(ps, verb, profmods)))
+ return;
+
+ snprintf(map_name, sizeof(map_name), "%s", p->name);
+ snprintf(map_desc, sizeof(map_desc), "Mapping for profile %s", p->name);
+
+ pa_log_debug("Find devices for profile '%s'", p->name);
+ get_path_lists(uc_mgr, ps, verb, p->ucm_modifiers, dualdevs, inpp, outpp, &direction);
+
+ profile_add_mapping(p, map_name, map_desc, direction, merged_devs, in_paths, out_paths);
+
+ pa_xstrfreev(dualdevs);
+}
+
+static char **merge_device_list(const char **list, int len)
+{
+ char **merged;
+ int cnt;
+ int i,j;
+ int duplicate;
+
+ pa_assert(list);
+ pa_assert(len > 0);
+
+ merged = pa_xnew0(char *, len+1);
+
+ for (cnt = i = 0; i < len; i++) {
+ if (list[i]) {
+ for (duplicate = FALSE, j = 0; j < cnt; j++) {
+ if (pa_streq(list[i], merged[j])) {
+ duplicate = TRUE;
+ free((void *)list[i]);
+ list[i] = NULL;
+ break;
+ }
+ }
+ if (!duplicate)
+ merged[cnt++] = (char *)list[i];
+ }
+ }
+
+ return merged;
+}
+
+static char **get_dualdev_list(snd_use_case_mgr_t *uc_mgr, const char *verb, struct ucm_list *portmods)
+{
+ struct ucm_list *modifier;
+ char id[256];
+ char **dev_list;
+ char **ret_list;
+ int n;
+
+ pa_assert(uc_mgr);
+
+ if (portmods) {
+ for (modifier = portmods; modifier->name; modifier++) {
+ if (pa_streq(modifier->name, SND_USE_CASE_MOD_PLAY_TONE)) {
+ snprintf(id, sizeof(id), "_supporteddevs/" SND_USE_CASE_MOD_PLAY_TONE "/%s", verb);
+
+ if ((n = snd_use_case_get_list(uc_mgr, id, (const char ***)&dev_list)) > 0) {
+ ret_list = pa_xrealloc(dev_list, sizeof(char *)*(n+1));
+
+ pa_assert(ret_list);
+
+ ret_list[n] = NULL;
+
+ return ret_list;
+ }
+
+ break;
+ }
+ }
+ }
+
+ return pa_xnew0(char *, 1);
+}
+
+
+static void get_path_lists(snd_use_case_mgr_t *uc_mgr,
+ pa_alsa_profile_set *ps,
+ const char *verb,
+ char **modifiers,
+ char **dualdevs,
+ char ***input_path_names_ret,
+ char ***output_path_names_ret,
+ pa_alsa_direction_t *direction_ret)
+{
+#define MAX_MODIFIERS 16
+
+ char id[256];
+ struct ucm_list *devices;
+ int ndev;
+ const char **sd;
+ int nmodif;
+ char devnam[256];
+ char *modnam;
+ struct support sup_mods[MAX_MODIFIERS];
+ struct support *sm;
+ char **in_paths;
+ char **out_paths;
+ char **in_modifs;
+ char **out_modifs;
+ int in_pidx;
+ int out_pidx;
+ int in_midx;
+ int out_midx;
+ unsigned int supported;
+ unsigned int mask = 0;
+ char *c;
+ int i,j,k;
+ int found;
+#ifndef INPUT_DEVICE_BASED_JACK_DETECTION
+ const char *inpctl = NULL;
+#else
+ const char *inpdev;
+ const char *inpcode;
+#endif
+
+ pa_assert(uc_mgr);
+ pa_assert(ps);
+ pa_assert(verb);
+ pa_assert(modifiers);
+ pa_assert(input_path_names_ret || output_path_names_ret);
+ pa_assert(direction_ret);
+
+ for (nmodif = 0; (modnam = modifiers[nmodif]) && nmodif < MAX_MODIFIERS; nmodif++) {
+ sm = sup_mods + nmodif;
+
+ snprintf(id, sizeof(id), "_supporteddevs/%s/%s", modnam, verb);
+
+ if (strcasestr(modnam, "capture"))
+ sm->dir = INPUT;
+ else if (strcasestr(modnam, "play"))
+ sm->dir = OUTPUT;
+ else
+ sm->dir = UNDEFINED;
+
+ if ((sm->len = snd_use_case_get_list(uc_mgr, id, &sd)) > 0 &&
+ (sm->devs = realloc(sd, sizeof(char *)*(sm->len + 1))) )
+ sm->devs[sm->len] = NULL;
+ else {
+ sm->len = 0;
+ sm->devs = pa_xnew0(const char *, 1);
+ }
+ }
+
+ snprintf(id, sizeof(id), "_devices/%s", verb);
+ ndev = get_list(uc_mgr, id, &devices);
+
+ /* it is faster to over allocate for the worst case
+ than calculate the exact number of entries.
+ The worst case is twice the devices + a terminating NULL */
+
+ if (input_path_names_ret) {
+ in_paths = *input_path_names_ret = pa_xnew0(char *, ndev * 2 + 1);
+ in_pidx = 0;
+ }
+
+ if (output_path_names_ret) {
+ out_paths = *output_path_names_ret = pa_xnew0(char *, ndev * 2 + 1);
+ out_pidx = 0;
+ }
+
+ for (i = 0; i < ndev; i++) {
+
+ strncpy(devnam, devices[i].name, sizeof(devnam));
+ devnam[sizeof(devnam)-1] = '\0';
+ if ((c = strchr(devnam, '.')))
+ *c = '\0';
+
+ supported = 0;
+ in_midx = out_midx = 0;
+
+ in_modifs = pa_xnew0(char *, nmodif + 1);
+ out_modifs = pa_xnew0(char *, nmodif + 1);
+
+ pa_log_debug(" looking at UCM configuration of device '%s':", devnam);
+
+ if (!modifiers[0]) {
+ pa_log_debug(" no modifier to check");
+
+ if (input_path_names_ret)
+ supported |= INPUT;
+ if (output_path_names_ret)
+ supported |= OUTPUT;
+ }
+ else {
+ pa_log_debug(" checking supported modifiers:");
+
+ for (j = 0; (modnam = modifiers[j]); j++) {
+ sm = sup_mods + j;
+
+ if (sm->dir != UNDEFINED) {
+ for (found = FALSE, k = 0; k < sm->len; k++) {
+ if (pa_streq(devnam, sm->devs[k])) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found)
+ pa_log_debug(" modifier '%s' is not supported", modnam);
+ else {
+ pa_log_debug(" modifier '%s' supported", modnam);
+ if ((sm->dir & INPUT) && input_path_names_ret) {
+ supported |= INPUT;
+ in_modifs[in_midx++] = pa_xstrdup(modnam);
+ }
+ else if ((sm->dir && OUTPUT) && output_path_names_ret) {
+ supported = OUTPUT;
+ out_modifs[out_midx++] = pa_xstrdup(modnam);
+ }
+ }
+ }
+ } /* for */
+ }
+
+ if (!supported) {
+ pa_log_debug(" not supported");
+ pa_xstrfreev(in_modifs);
+ pa_xstrfreev(out_modifs);
+ }
+ else {
+ pa_log_debug(" supported");
+
+ mask |= supported;
+
+#ifndef INPUT_DEVICE_BASED_JACK_DETECTION
+#if 0 /* TODO: something to get the ALSA controls */
+ get_inputdev(uc_mgr, verb, devnam, &inpctl);
+#endif
+
+ add_path(uc_mgr, ps, supported, verb, devnam,
+ in_paths,&in_pidx, out_paths,&out_pidx,
+ in_modifs,out_modifs, inpctl, SINGLEDEV);
+
+ for (k = 0; dualdevs[i]; i++) {
+ if (pa_streq(devnam, dualdevs[i])) {
+ add_path(uc_mgr, ps, supported, verb, devnam,
+ in_paths,&in_pidx, out_paths,&out_pidx,
+ in_modifs,out_modifs, inpctl, DUALDEV);
+ break;
+ }
+ }
+#else
+ get_inputdev(uc_mgr, verb, devnam, &inpdev, &inpcode);
+
+ add_path(uc_mgr, ps, supported, verb, devnam,
+ in_paths,&in_pidx, out_paths,&out_pidx,
+ in_modifs,out_modifs, inpdev,inpcode, SINGLEDEV);
+
+ for (k = 0; dualdevs[i]; i++) {
+ if (pa_streq(devnam, dualdevs[i])) {
+ add_path(uc_mgr, ps, supported, verb, devnam,
+ in_paths,&in_pidx, out_paths,&out_pidx,
+ in_modifs,out_modifs, inpdev,inpcode, DUALDEV);
+ break;
+ }
+ }
+#endif
+ }
+ }
+
+ *direction_ret = DIRECTION_ALSA(mask);
+
+#undef MAX_MODIFIERS
+}
+
+static int get_volctl(snd_use_case_mgr_t *uc_mgr,
+ const char *id,
+ char *devnam,
+ const char *verb,
+ int dualdev,
+ const char **volctl_ret)
+{
+ char query[256];
+
+ if (dualdev) {
+ snprintf(query, sizeof(query), "%s/%s/%s", id, SND_USE_CASE_MOD_PLAY_TONE, verb);
+
+ if (snd_use_case_get(uc_mgr, query, volctl_ret) == 0)
+ goto found;
+ }
+
+ snprintf(query, sizeof(query), "%s/%s/%s", id, devnam, verb);
+
+ if (snd_use_case_get(uc_mgr, query, volctl_ret) == 0)
+ goto found;
+
+ *volctl_ret = NULL;
+ return - 1;
+
+ found:
+ pa_log_debug(" found capture volume control @ '%s'", *volctl_ret);
+ return 0;
+}
+
+#ifdef INPUT_DEVICE_BASED_JACK_DETECTION
+static void get_inputdev(snd_use_case_mgr_t *uc_mgr,
+ const char *verb,
+ char *devnam,
+ const char **name_ret,
+ const char **code_ret)
+{
+ char id[256];
+ const char *name;
+ const char *code;
+
+ pa_assert(uc_mgr);
+ pa_assert(verb);
+ pa_assert(devnam);
+ pa_assert(name_ret);
+ pa_assert(code_ret);
+
+ snprintf(id, sizeof(id), "InputDeviceName/%s/%s", devnam, verb);
+
+ if (snd_use_case_get(uc_mgr, id, &name) == 0)
+ pa_log_debug(" input device '%s'", name);
+ else
+ name = NULL;
+
+ snprintf(id, sizeof(id), "InputDeviceCode/%s/%s", devnam, verb);
+
+ if (snd_use_case_get(uc_mgr, id, &code) == 0)
+ pa_log_debug(" input device code '%s'", code);
+ else
+ code = NULL;
+
+ if ((!name && code) || (name && !code)) {
+ name = code = NULL;
+ pa_log_debug("incomplete input device specification (verb %s, device %s)", verb, devnam);
+ }
+
+ *name_ret = name;
+ *code_ret = code;
+}
+#endif
+
+static void add_path(snd_use_case_mgr_t *uc_mgr,
+ pa_alsa_profile_set *ps,
+ unsigned int supported,
+ const char *verb,
+ char *devnam,
+ char **in_paths,
+ int *in_pidx_ptr,
+ char **out_paths,
+ int *out_pidx_ptr,
+ char **in_modifs,
+ char **out_modifs,
+#ifndef INPUT_DEVICE_BASED_JACK_DETECTION
+ const char *inpctl,
+#else
+ const char *inpdev,
+ const char *inpcode,
+#endif
+ int dualdev)
+{
+ static const char *dual_modifs[2] = {SND_USE_CASE_MOD_PLAY_TONE, NULL};
+
+ char id[256];
+ char extnam[512];
+ char **modifs;
+ pa_alsa_path *p;
+ int in_pidx;
+ int out_pidx;
+ const char *volctl;
+
+ snprintf(extnam, sizeof(extnam), "%s%s", devnam, dualdev ? "+" SND_USE_CASE_DEV_SPEAKER : "");
+
+ if (!(supported & INPUT) || dualdev) {
+ if (!dualdev)
+ pa_xstrfreev(in_modifs);
+ }
+ else {
+ in_pidx = *in_pidx_ptr;
+ modifs = dualdev ? listmerge(in_modifs, dual_modifs) : in_modifs;
+
+ snprintf(id, sizeof(id), "%s-%s-input", extnam, verb);
+
+ if ((p = path_new(id, extnam, PA_ALSA_DIRECTION_INPUT, verb, devnam, modifs))) {
+ pa_hashmap_put(ps->input_paths, p->name, p);
+ in_paths[in_pidx++] = pa_xstrdup(id);
+
+ if (get_volctl(uc_mgr, "CaptureVolume", devnam, verb, dualdev, &volctl) == 0)
+ path_add_volume_control(p, volctl);
+ }
+
+ *in_pidx_ptr = in_pidx;
+ }
+
+ if (!(supported & OUTPUT)) {
+ if (!dualdev)
+ pa_xstrfreev(out_modifs);
+ }
+ else {
+ out_pidx = *out_pidx_ptr;
+ modifs = dualdev ? listmerge(out_modifs, dual_modifs) : out_modifs;
+
+ snprintf(id, sizeof(id), "%s-%s-output", extnam, verb);
+
+ if ((p = path_new(id, extnam, PA_ALSA_DIRECTION_OUTPUT, verb, devnam, modifs))) {
+ pa_hashmap_put(ps->output_paths, p->name, p);
+ out_paths[out_pidx++] = pa_xstrdup(id);
+
+ if (get_volctl(uc_mgr, "PlaybackVolume", devnam, verb, dualdev, &volctl) == 0)
+ path_add_volume_control(p, volctl);
+ }
+
+ *out_pidx_ptr = out_pidx;
+ }
+
+#ifndef INPUT_DEVICE_BASED_JACK_DETECTION
+#if 0 /* TODO: implement this when it will emerge */
+ if (supported && inpctl)
+ path_add_jack_alsa_control(p, inpctl);
+#endif
+#else
+ if (supported && inpdev && inpcode)
+ path_add_jack_input_device(p, inpdev, inpcode);
+#endif
+}
+
+
+static pa_alsa_path *path_new(const char *name,
+ const char *desc,
+ pa_alsa_direction_t direction,
+ const char *ucm_verb,
+ const char *ucm_device,
+ char **ucm_modifiers)
+{
+ pa_alsa_path *p;
+
+ pa_assert(name);
+ pa_assert(desc);
+ pa_assert(ucm_device);
+ pa_assert(ucm_modifiers);
+
+ pa_log_debug("new alsa path '%s' (%s)", name, desc);
+
+ p = pa_xnew0(pa_alsa_path, 1);
+
+ p->direction = direction;
+ p->name = pa_xstrdup(name);
+ p->description = pa_xstrdup(desc);
+ p->use_ucm = TRUE;
+ p->ucm_verb = pa_xstrdup(ucm_verb);
+ p->ucm_device = pa_xstrdup(ucm_device);
+ p->ucm_modifiers = ucm_modifiers;
+
+ return p;
+}
+
+static void path_add_volume_control(pa_alsa_path *p, const char *alsa_name)
+{
+ pa_alsa_element *e;
+
+ pa_assert(p);
+ pa_assert(alsa_name);
+
+ e = pa_xnew0(pa_alsa_element, 1);
+
+ e->path = p;
+ e->alsa_name = (char *)alsa_name;
+ e->direction = p->direction;
+ e->volume_limit = -1;
+ e->switch_use = PA_ALSA_SWITCH_MUTE;
+ e->volume_use = PA_ALSA_VOLUME_MERGE;
+
+ PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
+ p->last_element = e;
+}
+
+#ifdef INPUT_DEVICE_BASED_JACK_DETECTION
+static int path_add_jack_input_device(pa_alsa_path *p, const char *dev_name, const char *dev_code)
+{
+ int ret;
+
+ pa_assert(p);
+ pa_assert(dev_name);
+ pa_assert(dev_code);
+
+ if (p->jack_inputdev_name || p->jack_inputdev_code)
+ ret = -1;
+ else {
+ ret = 0;
+ p->jack_inputdev_name = pa_xstrdup(dev_name); /* this is probably a leak */
+ p->jack_inputdev_code = pa_xstrdup(dev_code);
+ }
+
+ return ret;
+}
+#endif
+
+static pa_alsa_profile *profile_new(pa_alsa_profile_set *ps, const char *verb, struct ucm_list *modifiers)
+{
+ pa_alsa_profile *p;
+ char name[512];
+ char descr[1024];
+
+ pa_assert(ps);
+ pa_assert(verb);
+
+ if (!compose_profile_name(verb, modifiers, name, sizeof(name)) ||
+ !compose_profile_description(verb, modifiers, descr, sizeof(descr)))
+ return NULL;
+
+ if (pa_hashmap_get(ps->profiles, name))
+ return NULL;
+
+ p = pa_xnew0(pa_alsa_profile, 1);
+ p->profile_set = ps;
+ p->name = pa_xstrdup(name);
+ p->description = pa_xstrdup(descr);
+ p->ucm_verb = pa_xstrdup(verb);
+ p->ucm_modifiers = listdup(modifiers);
+
+ pa_hashmap_put(ps->profiles, p->name, p);
+
+ return p;
+}
+
+
+static void profile_add_mapping(pa_alsa_profile *p,
+ const char *name,
+ const char *desc,
+ pa_alsa_direction_t direction,
+ char **devs,
+ char **in_paths,
+ char **out_paths)
+{
+ pa_alsa_profile_set *ps;
+ pa_alsa_mapping *m;
+ char **inmap_names = NULL;
+ char **outmap_names = NULL;
+ char query[256];
+
+ pa_assert(p);
+ pa_assert(name);
+ pa_assert(desc);
+ pa_assert(devs);
+ pa_assert_se(ps = p->profile_set);
+
+
+ snprintf(query, sizeof(query), "Mapping %s", name);
+
+ if (!(m = pa_alsa_mapping_get(ps, query))) {
+ pa_xstrfreev(devs);
+ pa_xstrfreev(in_paths);
+ pa_xstrfreev(out_paths);
+ return;
+ }
+
+ if (direction == PA_ALSA_DIRECTION_ANY || direction == PA_ALSA_DIRECTION_INPUT)
+ inmap_names = pa_split_spaces_strv(name);
+
+ if (direction == PA_ALSA_DIRECTION_ANY || direction == PA_ALSA_DIRECTION_OUTPUT)
+ outmap_names = pa_split_spaces_strv(name);
+
+
+ if (m->description) {
+ /* mapping already exists: modify it */
+
+ if (direction != m->direction && m->direction != PA_ALSA_DIRECTION_ANY) {
+ m->direction = PA_ALSA_DIRECTION_ANY;
+
+ /* TODO: merge the devs */
+
+ pa_xstrfreev(devs);
+ pa_xstrfreev(in_paths);
+ pa_xstrfreev(out_paths);
+ }
+ }
+ else {
+ /* new mapping: initialize it */
+ m->description = pa_xstrdup(desc);
+ m->priority = 1;
+ m->direction = direction;
+ m->device_strings = devs;
+ m->input_path_names = in_paths;
+ m->output_path_names = out_paths;
+
+ init_channel_map(&m->channel_map, p->ucm_verb);
+ }
+
+ p->input_mapping_names = inmap_names;
+ p->output_mapping_names = outmap_names;
+}
+
+
+static char *compose_profile_name(const char *verb, struct ucm_list *modifiers, char *buf, int len)
+{
+ const char *mode_str;
+ const char *media_str;
+ const char *dir_str;
+ const char *modifier;
+ int media;
+ int dir;
+ int i;
+
+
+ pa_assert(verb);
+ pa_assert(buf);
+ pa_assert(len > 0);
+
+ if (!(mode_str = VERB_TO_MODE(verb)))
+ return NULL;
+
+ if (!modifiers || !modifiers[0].name) {
+ dir = DUPLEX;
+ media = VERB_TO_MEDIA(verb);
+ }
+ else {
+ for (i = 0, media = dir = UNDEFINED; (modifier = modifiers[i].name); i++) {
+
+ if (strcasestr(modifier, "capture"))
+ dir |= INPUT;
+ else if(strcasestr(modifier, "play"))
+ dir |= OUTPUT;
+
+ if (strcasestr(modifier, "voice"))
+ media |= VOICE;
+ else
+ media |= MUSIC;
+ }
+ }
+
+
+ dir_str = DIRECTION_PULSE(dir);
+ media_str = MEDIA_ID(media);
+
+ snprintf(buf, len, "%s-%s-for-%s", mode_str, dir_str, media_str);
+
+ return buf;
+}
+
+static char *compose_profile_description(const char *verb, struct ucm_list *modifiers, char *buf, int len)
+{
+ const char *mode_str;
+ const char *media_str;
+ const char *dir_str;
+ const char *modifier;
+ int media;
+ int dir;
+ int i;
+
+ pa_assert(verb);
+ pa_assert(buf);
+ pa_assert(len > 0);
+
+ if (!(mode_str = VERB_TO_DESCRIPTION(verb)))
+ return NULL;
+
+ if (!modifiers || !modifiers[0].name) {
+ dir = DUPLEX;
+ media = VERB_TO_MEDIA(verb);
+ }
+ else {
+ for (i = 0, media = dir = UNDEFINED; (modifier = modifiers[i].name); i++) {
+
+ if (strcasestr(modifier, "capture"))
+ dir |= INPUT;
+ else if(strcasestr(modifier, "play"))
+ dir |= OUTPUT;
+
+ if (strcasestr(modifier, "voice"))
+ media |= VOICE;
+ else
+ media |= MUSIC;
+ }
+ }
+
+
+ dir_str = DIRECTION_DESCRIPTION(dir);
+ media_str = MEDIA_DESCRIPTION(media);
+
+ snprintf(buf, len, "%s %s in %s mode", media_str, dir_str, mode_str);
+
+ return buf;
+}
+
+
+static void get_device_changes(char *old_dev, char *new_dev,
+ pa_bool_t *changed_ret, pa_bool_t *swapped_ret)
+{
+ pa_bool_t changed;
+ pa_bool_t swapped;
+
+ if (old_dev) {
+ changed = !(new_dev && pa_streq(old_dev, new_dev));
+ swapped = changed ? !!new_dev : FALSE;
+ }
+ else {
+ changed = !!new_dev;
+ swapped = FALSE;
+ }
+
+ *changed_ret = changed;
+ *swapped_ret = swapped;
+}
+
+
+/*
+ * We assume that the enable and disable sequences are inverse operations, i.e
+ * a disable/enable or an enable/disable operation can be safely ignored without
+ * any side effects.
+ *
+ * Furthermore we assume that neither old_modifs nor new_modifs have duplicate
+ * entries.
+ *
+ * For finding duplicates we use linear search since the number of available
+ * modifiers is 5 and the linear search beleived to be faster. However, it can
+ * be changed easily to using PA_HASHMAPs instead.
+ *
+ */
+
+static void get_modifier_changes(pa_bool_t dev_changed,
+ char **old_modifs,
+ char **new_modifs,
+ char **disable_mods,
+ int disable_len,
+ char **enable_mods,
+ int enable_len)
+{
+#define MODIFIER_LIST_DIM 16
+
+ struct modsts {
+ char *name;
+ int status;
+ };
+
+ struct modsts m[MODIFIER_LIST_DIM];
+ int handled;
+ int i,j,k,l;
+
+ (void)dev_changed;
+
+ pa_assert(disable_mods);
+ pa_assert(enable_mods);
+ pa_assert(disable_len > 0);
+ pa_assert(enable_len > 0);
+
+ *disable_mods = *enable_mods = NULL;
+
+ i = 0;
+
+ if (old_modifs) {
+ for (; old_modifs[i]; i++) {
+ if (i >= MODIFIER_LIST_DIM)
+ goto overflow;
+
+ m[i].name = old_modifs[i];
+ m[i].status = -1;
+ }
+ }
+
+ if (new_modifs) {
+ for (j = 0; new_modifs[j]; j++) {
+ for (handled = FALSE, k = 0; k < i; k++) {
+ if (pa_streq(new_modifs[j], m[k].name)) {
+ m[k].status = 0;
+ handled = TRUE;
+ break;
+ }
+ }
+ if (!handled) {
+ if (i >= MODIFIER_LIST_DIM)
+ goto overflow;
+ m[i].name = new_modifs[j];
+ m[i].status = +1;
+ i++;
+ }
+ }
+ }
+
+ for (j = k = l = 0; j < i; j++) {
+ if (m[j].status < 0) {
+ if (k < disable_len - 1)
+ disable_mods[k++] = m[j].name;
+ else {
+ pa_log("%s: disable_mods overflowed (dimension is %d)",
+ __FUNCTION__, disable_len);
+ }
+ }
+ else if (m[j].status > 0) {
+ if (l < enable_len - 1)
+ enable_mods[l++] = m[j].name;
+ else {
+ pa_log("%s: enable_mods overflowed (dimension is %d)",
+ __FUNCTION__, enable_len);
+ }
+ }
+ }
+
+ disable_mods[k] = NULL;
+ enable_mods[l] = NULL;
+
+ return;
+
+ overflow:
+ pa_log("%s: internal array overflow (dimension is %d)",
+ __FUNCTION__, MODIFIER_LIST_DIM);
+
+
+#undef MODIFIER_LIST_DIM
+}
+
+
+static int get_list(snd_use_case_mgr_t *uc_mgr, const char *id, struct ucm_list **list_ret)
+{
+ int n;
+
+ if ((n = snd_use_case_get_list(uc_mgr, id, (const char ***)list_ret)) < 0)
+ *list_ret = pa_xnew0(struct ucm_list, 1);
+ else
+ n >>= 1;
+
+ return n;
+}
+
+
+static struct ucm_list *get_modifiers(snd_use_case_mgr_t *uc_mgr, const char *verb, modifier_type type)
+{
+ static struct ucm_list empty_list_entry;
+ static int medias[] = { MUSIC, VOICE, UNDEFINED };
+
+ int media;
+ struct ucm_list *modifiers;
+ struct ucm_list *modifier;
+ struct ucm_list *list;
+ char id[256];
+ int i,j,k,n;
+
+ pa_assert(uc_mgr);
+ pa_assert(verb);
+
+ snprintf(id, sizeof(id), "_modifiers/%s", verb);
+
+ n = get_list(uc_mgr, id, &modifiers);
+ list = pa_xnew0(struct ucm_list, (n > 0 ? n + 1 : 1));
+
+ for (i = j = 0; i < n; i++) {
+ modifier = modifiers + i;
+
+ switch (type) {
+
+ case PROFILE_MATCHING_MODIFIER:
+ media = VERB_TO_MEDIA(verb);
+ if (strcasestr(modifier->name, MEDIA_ID(media))) {
+ list[j++] = *modifier;
+ *modifier = empty_list_entry;
+ }
+ break;
+
+ case PROFILE_ANY_MODIFIER:
+ for (k = 0; (media = medias[k]) != UNDEFINED; k++) {
+ if (strcasestr(modifier->name, MEDIA_ID(media))) {
+ list[j++] = *modifier;
+ *modifier = empty_list_entry;
+ break;
+ }
+ }
+ break;
+
+ case PORT_MODIFIER:
+ if (strstr(modifier->name, SND_USE_CASE_MOD_PLAY_TONE)) {
+ list[j++] = *modifier;
+ *modifier = empty_list_entry;
+ }
+ break;
+
+ default:
+ break;
+ } /* switch type */
+ } /* for */
+
+ snd_use_case_free_list((const char **)modifiers, n);
+
+ memset(list + j, 0, sizeof(struct ucm_list));
+
+ return list;
+}
+
+
+static void init_channel_map(pa_channel_map *cm, const char *verb)
+{
+ pa_assert(cm);
+ pa_assert(verb);
+
+ if (!pa_channel_map_valid(cm)) {
+ if (strcasestr(verb, "voice"))
+ pa_channel_map_init_mono(cm);
+ else
+ pa_channel_map_init_stereo(cm);
+ }
+}
+
+static const char *verb_to_mode(const char *verb)
+{
+ struct mode *m;
+
+ pa_assert(verb);
+
+ for (m = mode_map; m->verb; m++) {
+ if (pa_streq(verb, m->verb))
+ return m->mode;
+ }
+
+ return NULL;
+}
+
+static const char *verb_to_desc(const char *verb)
+{
+ struct mode *m;
+
+ pa_assert(verb);
+
+ for (m = mode_map; m->verb; m++) {
+ if (pa_streq(verb, m->verb))
+ return m->descr;
+ }
+
+ return NULL;
+}
+
+static char **listdup(struct ucm_list *list)
+{
+ char **copy;
+ char *str, *c;
+ int len;
+ int i;
+
+ if (!list)
+ copy = NULL;
+ else {
+ for (i = 0; list[i].name; i++)
+ ;
+
+ copy = pa_xnew0(char *, i+1);
+
+ for (i = 0; list[i].name; i++) {
+ len = (c = strchr(list[i].name, '.')) ? c - list[i].name : (int)strlen(list[i].name);
+ copy[i] = str = pa_xnew0(char, len+1);
+ memcpy(str, list[i].name, len);
+ }
+ }
+
+ return copy;
+}
+
+static char **listmerge(char **list1, const char **list2)
+{
+ char **merged;
+ int i,j,k,l;
+ int duplicate;
+
+ for (i = 0; list1[i]; i++)
+ ;
+
+ for (j = 0; list2[j]; j++)
+ ;
+
+ merged = pa_xnew0(char *, i + j + 1);
+ k = 0;
+
+ for (i = 0; list1[i]; i++) {
+ for (l = 0; l < k; l++) {
+ if (pa_streq(list1[i], merged[l])) {
+ duplicate = TRUE;
+ break;
+ }
+ }
+
+ if (!duplicate)
+ merged[k++] = pa_xstrdup(list1[i]);
+ }
+
+ for (duplicate = FALSE, j = 0; list2[j]; j++) {
+ for (l = 0; l < k; l++) {
+ if (pa_streq(list2[j], merged[l])) {
+ duplicate = TRUE;
+ break;
+ }
+ }
+
+ if (!duplicate)
+ merged[k++] = pa_xstrdup(list2[j]);
+ }
+
+ return merged;
+}
diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
new file mode 100644
index 0000000..a4a928e
--- /dev/null
+++ b/src/modules/alsa/alsa-ucm.h
@@ -0,1 +1,52 @@
+#ifndef foopulseucmhfoo
+#define foopulseucmhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2011 Intel Corporation.
+ Author Janos Kovacs <jankovac503 at gmail.com>
+
+ 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_ucm_config pa_alsa_ucm_config;
+
+struct profile_data {
+ pa_alsa_profile *profile;
+};
+
+struct pa_alsa_ucm_config {
+ pa_bool_t enabled;
+ snd_use_case_mgr_t *uc_mgr;
+};
+
+/* Argh... this stuff just temporarily parking here */
+struct pa_card;
+pa_alsa_ucm_config *pa_alsa_card_get_ucm_config(struct pa_card *);
+
+
+pa_alsa_profile_set *pa_alsa_ucm_profile_set_new(int alsa_card_index,
+ struct pa_alsa_ucm_config *ucm,
+ pa_channel_map *default_channel_map);
+
+int pa_alsa_ucm_profile_select(pa_alsa_profile *, pa_alsa_profile *, pa_alsa_ucm_config *);
+int pa_alsa_ucm_path_select(pa_alsa_path *, pa_alsa_path *, pa_alsa_ucm_config *);
+
+#endif
--
1.7.5.2
More information about the pulseaudio-discuss
mailing list