[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