"Fossies" - the Fresh Open Source Software Archive

Member "pulseaudio-14.2/src/modules/alsa/alsa-mixer.c" (16 Jan 2021, 175558 Bytes) of package /linux/misc/pulseaudio-14.2.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "alsa-mixer.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.0_vs_14.2.

    1 /***
    2   This file is part of PulseAudio.
    3 
    4   Copyright 2004-2009 Lennart Poettering
    5   Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
    6 
    7   PulseAudio is free software; you can redistribute it and/or modify
    8   it under the terms of the GNU Lesser General Public License as published
    9   by the Free Software Foundation; either version 2.1 of the License,
   10   or (at your option) any later version.
   11 
   12   PulseAudio is distributed in the hope that it will be useful, but
   13   WITHOUT ANY WARRANTY; without even the implied warranty of
   14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   15   General Public License for more details.
   16 
   17   You should have received a copy of the GNU Lesser General Public License
   18   along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
   19 ***/
   20 
   21 #ifdef HAVE_CONFIG_H
   22 #include <config.h>
   23 #endif
   24 
   25 #include <sys/types.h>
   26 #include <alsa/asoundlib.h>
   27 #include <math.h>
   28 
   29 #ifdef HAVE_VALGRIND_MEMCHECK_H
   30 #include <valgrind/memcheck.h>
   31 #endif
   32 
   33 #include <pulse/mainloop-api.h>
   34 #include <pulse/sample.h>
   35 #include <pulse/timeval.h>
   36 #include <pulse/util.h>
   37 #include <pulse/volume.h>
   38 #include <pulse/xmalloc.h>
   39 #include <pulse/utf8.h>
   40 
   41 #include <pulsecore/i18n.h>
   42 #include <pulsecore/log.h>
   43 #include <pulsecore/macro.h>
   44 #include <pulsecore/core-util.h>
   45 #include <pulsecore/conf-parser.h>
   46 #include <pulsecore/strbuf.h>
   47 
   48 #include "alsa-mixer.h"
   49 #include "alsa-util.h"
   50 
   51 #ifdef HAVE_VALGRIND_MEMCHECK_H
   52 /* These macros are workarounds for a bug in valgrind, which is not handling the
   53  * ALSA TLV syscalls correctly. See
   54  * http://valgrind.10908.n7.nabble.com/Missing-ioctl-for-SNDRV-CTL-IOCTL-TLV-READ-td42711.html */
   55 
   56 static inline int vgfix_get_capture_dB(snd_mixer_elem_t *a, snd_mixer_selem_channel_id_t b, long *c) {
   57     int r = snd_mixer_selem_get_capture_dB(a, b, c);
   58     VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
   59     return r;
   60 }
   61 
   62 static inline int vgfix_get_playback_dB(snd_mixer_elem_t *a, snd_mixer_selem_channel_id_t b, long *c) {
   63     int r = snd_mixer_selem_get_playback_dB(a, b, c);
   64     VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
   65     return r;
   66 }
   67 
   68 static inline int vgfix_ask_capture_vol_dB(snd_mixer_elem_t *a, long b, long *c) {
   69     int r = snd_mixer_selem_ask_capture_vol_dB(a, b, c);
   70     VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
   71     return r;
   72 }
   73 
   74 static inline int vgfix_ask_playback_vol_dB(snd_mixer_elem_t *a, long b, long *c) {
   75     int r = snd_mixer_selem_ask_playback_vol_dB(a, b, c);
   76     VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
   77     return r;
   78 }
   79 
   80 static inline int vgfix_get_capture_dB_range(snd_mixer_elem_t *a, long *b, long *c) {
   81     int r = snd_mixer_selem_get_capture_dB_range(a, b, c);
   82     VALGRIND_MAKE_MEM_DEFINED(b, sizeof(*b));
   83     VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
   84     return r;
   85 }
   86 
   87 static inline int vgfix_get_playback_dB_range(snd_mixer_elem_t *a, long *b, long *c) {
   88     int r = snd_mixer_selem_get_playback_dB_range(a, b, c);
   89     VALGRIND_MAKE_MEM_DEFINED(b, sizeof(*b));
   90     VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
   91     return r;
   92 }
   93 
   94 #define snd_mixer_selem_get_capture_dB(a, b, c) vgfix_get_capture_dB(a, b, c)
   95 #define snd_mixer_selem_get_playback_dB(a, b, c) vgfix_get_playback_dB(a, b, c)
   96 #define snd_mixer_selem_ask_capture_vol_dB(a, b, c) vgfix_ask_capture_vol_dB(a, b, c)
   97 #define snd_mixer_selem_ask_playback_vol_dB(a, b, c) vgfix_ask_playback_vol_dB(a, b, c)
   98 #define snd_mixer_selem_get_capture_dB_range(a, b, c) vgfix_get_capture_dB_range(a, b, c)
   99 #define snd_mixer_selem_get_playback_dB_range(a, b, c) vgfix_get_playback_dB_range(a, b, c)
  100 
  101 #endif
  102 
  103 static int setting_select(pa_alsa_setting *s, snd_mixer_t *m);
  104 
  105 struct description_map {
  106     const char *key;
  107     const char *description;
  108 };
  109 
  110 struct description2_map {
  111     const char *key;
  112     const char *description;
  113     pa_device_port_type_t type;
  114 };
  115 
  116 char *pa_alsa_mixer_id_to_string(char *dst, size_t dst_len, pa_alsa_mixer_id *id) {
  117     if (id->index > 0) {
  118         snprintf(dst, dst_len, "'%s',%d", id->name, id->index);
  119     } else {
  120         snprintf(dst, dst_len, "'%s'", id->name);
  121     }
  122     return dst;
  123 }
  124 
  125 static int alsa_id_decode(const char *src, char *name, int *index) {
  126     char *idx, c;
  127     int i;
  128 
  129     *index = 0;
  130     c = src[0];
  131     /* Strip quotes in entries such as 'Speaker',1 or "Speaker",1 */
  132     if (c == '\'' || c == '"') {
  133         strcpy(name, src + 1);
  134         for (i = 0; name[i] != '\0' && name[i] != c; i++);
  135         idx = NULL;
  136         if (name[i]) {
  137                 name[i] = '\0';
  138                 idx = strchr(name + i + 1, ',');
  139         }
  140     } else {
  141         strcpy(name, src);
  142         idx = strchr(name, ',');
  143     }
  144     if (idx == NULL)
  145         return 0;
  146     *idx = '\0';
  147     idx++;
  148     if (*idx < '0' || *idx > '9') {
  149         pa_log("Element %s: index value is invalid", src);
  150         return 1;
  151     }
  152     *index = atoi(idx);
  153     return 0;
  154 }
  155 
  156 pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *mixer_device_name, const char *name, int index) {
  157     pa_alsa_jack *jack;
  158 
  159     pa_assert(name);
  160 
  161     jack = pa_xnew0(pa_alsa_jack, 1);
  162     jack->path = path;
  163     jack->mixer_device_name = pa_xstrdup(mixer_device_name);
  164     jack->name = pa_xstrdup(name);
  165     jack->alsa_id.name = pa_sprintf_malloc("%s Jack", name);
  166     jack->alsa_id.index = index;
  167     jack->state_unplugged = PA_AVAILABLE_NO;
  168     jack->state_plugged = PA_AVAILABLE_YES;
  169     jack->ucm_devices = pa_dynarray_new(NULL);
  170     jack->ucm_hw_mute_devices = pa_dynarray_new(NULL);
  171 
  172     return jack;
  173 }
  174 
  175 void pa_alsa_jack_free(pa_alsa_jack *jack) {
  176     pa_assert(jack);
  177 
  178     pa_dynarray_free(jack->ucm_hw_mute_devices);
  179     pa_dynarray_free(jack->ucm_devices);
  180 
  181     pa_xfree(jack->alsa_id.name);
  182     pa_xfree(jack->name);
  183     pa_xfree(jack->mixer_device_name);
  184     pa_xfree(jack);
  185 }
  186 
  187 void pa_alsa_jack_set_has_control(pa_alsa_jack *jack, bool has_control) {
  188     pa_alsa_ucm_device *device;
  189     unsigned idx;
  190 
  191     pa_assert(jack);
  192 
  193     if (has_control == jack->has_control)
  194         return;
  195 
  196     jack->has_control = has_control;
  197 
  198     PA_DYNARRAY_FOREACH(device, jack->ucm_hw_mute_devices, idx)
  199         pa_alsa_ucm_device_update_available(device);
  200 
  201     PA_DYNARRAY_FOREACH(device, jack->ucm_devices, idx)
  202         pa_alsa_ucm_device_update_available(device);
  203 }
  204 
  205 void pa_alsa_jack_set_plugged_in(pa_alsa_jack *jack, bool plugged_in) {
  206     pa_alsa_ucm_device *device;
  207     unsigned idx;
  208 
  209     pa_assert(jack);
  210 
  211     if (plugged_in == jack->plugged_in)
  212         return;
  213 
  214     jack->plugged_in = plugged_in;
  215 
  216     /* XXX: If this is a headphone jack that mutes speakers when plugged in,
  217      * and the headphones get unplugged, then the headphone device must be set
  218      * to unavailable and the speaker device must be set to unknown. So far so
  219      * good. But there's an ugly detail: we must first set the availability of
  220      * the speakers and then the headphones. We shouldn't need to care about
  221      * the order, but we have to, because module-switch-on-port-available gets
  222      * separate events for the two devices, and the intermediate state between
  223      * the two events is such that the second event doesn't trigger the desired
  224      * port switch, if the event order is "wrong".
  225      *
  226      * These are the transitions when the event order is "right":
  227      *
  228      *     speakers:   1) unavailable -> 2) unknown   -> 3) unknown
  229      *     headphones: 1) available   -> 2) available -> 3) unavailable
  230      *
  231      * In the 2 -> 3 transition, headphones become unavailable, and
  232      * module-switch-on-port-available sees that speakers can be used, so the
  233      * port gets changed as it should.
  234      *
  235      * These are the transitions when the event order is "wrong":
  236      *
  237      *     speakers:   1) unavailable -> 2) unavailable -> 3) unknown
  238      *     headphones: 1) available   -> 2) unavailable -> 3) unavailable
  239      *
  240      * In the 1 -> 2 transition, headphones become unavailable, and there are
  241      * no available ports to use, so no port change happens. In the 2 -> 3
  242      * transition, speaker availability becomes unknown, but that's not
  243      * a strong enough signal for module-switch-on-port-available, so it still
  244      * doesn't do the port switch.
  245      *
  246      * We should somehow merge the two events so that
  247      * module-switch-on-port-available would handle both transitions in one go.
  248      * If module-switch-on-port-available used a defer event to delay
  249      * the port availability processing, that would probably do the trick. */
  250 
  251     PA_DYNARRAY_FOREACH(device, jack->ucm_hw_mute_devices, idx)
  252         pa_alsa_ucm_device_update_available(device);
  253 
  254     PA_DYNARRAY_FOREACH(device, jack->ucm_devices, idx)
  255         pa_alsa_ucm_device_update_available(device);
  256 }
  257 
  258 void pa_alsa_jack_add_ucm_device(pa_alsa_jack *jack, pa_alsa_ucm_device *device) {
  259     pa_alsa_ucm_device *idevice;
  260     unsigned idx, prio, iprio;
  261 
  262     pa_assert(jack);
  263     pa_assert(device);
  264 
  265     /* store the ucm device with the sequence of priority from low to high. this
  266      * could guarantee when the jack state is changed, the device with highest
  267      * priority will send to the module-switch-on-port-available last */
  268     prio = device->playback_priority ? device->playback_priority : device->capture_priority;
  269 
  270     PA_DYNARRAY_FOREACH(idevice, jack->ucm_devices, idx) {
  271         iprio = idevice->playback_priority ? idevice->playback_priority : idevice->capture_priority;
  272         if (iprio > prio)
  273             break;
  274     }
  275     pa_dynarray_insert_by_index(jack->ucm_devices, device, idx);
  276 }
  277 
  278 void pa_alsa_jack_add_ucm_hw_mute_device(pa_alsa_jack *jack, pa_alsa_ucm_device *device) {
  279     pa_assert(jack);
  280     pa_assert(device);
  281 
  282     pa_dynarray_append(jack->ucm_hw_mute_devices, device);
  283 }
  284 
  285 static const char *lookup_description(const char *key, const struct description_map dm[], unsigned n) {
  286     unsigned i;
  287 
  288     if (!key)
  289         return NULL;
  290 
  291     for (i = 0; i < n; i++)
  292         if (pa_streq(dm[i].key, key))
  293             return _(dm[i].description);
  294 
  295     return NULL;
  296 }
  297 
  298 static const struct description2_map *lookup_description2(const char *key, const struct description2_map dm[], unsigned n) {
  299     unsigned i;
  300 
  301     if (!key)
  302         return NULL;
  303 
  304     for (i = 0; i < n; i++)
  305         if (pa_streq(dm[i].key, key))
  306             return &dm[i];
  307 
  308     return NULL;
  309 }
  310 
  311 struct pa_alsa_fdlist {
  312     unsigned num_fds;
  313     struct pollfd *fds;
  314     /* This is a temporary buffer used to avoid lots of mallocs */
  315     struct pollfd *work_fds;
  316 
  317     snd_mixer_t *mixer;
  318     snd_hctl_t *hctl;
  319 
  320     pa_mainloop_api *m;
  321     pa_defer_event *defer;
  322     pa_io_event **ios;
  323 
  324     bool polled;
  325 
  326     void (*cb)(void *userdata);
  327     void *userdata;
  328 };
  329 
  330 static void io_cb(pa_mainloop_api *a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
  331 
  332     struct pa_alsa_fdlist *fdl = userdata;
  333     int err;
  334     unsigned i;
  335     unsigned short revents;
  336 
  337     pa_assert(a);
  338     pa_assert(fdl);
  339     pa_assert(fdl->mixer || fdl->hctl);
  340     pa_assert(fdl->fds);
  341     pa_assert(fdl->work_fds);
  342 
  343     if (fdl->polled)
  344         return;
  345 
  346     fdl->polled = true;
  347 
  348     memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds);
  349 
  350     for (i = 0; i < fdl->num_fds; i++) {
  351         if (e == fdl->ios[i]) {
  352             if (events & PA_IO_EVENT_INPUT)
  353                 fdl->work_fds[i].revents |= POLLIN;
  354             if (events & PA_IO_EVENT_OUTPUT)
  355                 fdl->work_fds[i].revents |= POLLOUT;
  356             if (events & PA_IO_EVENT_ERROR)
  357                 fdl->work_fds[i].revents |= POLLERR;
  358             if (events & PA_IO_EVENT_HANGUP)
  359                 fdl->work_fds[i].revents |= POLLHUP;
  360             break;
  361         }
  362     }
  363 
  364     pa_assert(i != fdl->num_fds);
  365 
  366     if (fdl->hctl)
  367         err = snd_hctl_poll_descriptors_revents(fdl->hctl, fdl->work_fds, fdl->num_fds, &revents);
  368     else
  369         err = snd_mixer_poll_descriptors_revents(fdl->mixer, fdl->work_fds, fdl->num_fds, &revents);
  370 
  371     if (err < 0) {
  372         pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
  373         return;
  374     }
  375 
  376     a->defer_enable(fdl->defer, 1);
  377 
  378     if (revents) {
  379         if (fdl->hctl)
  380             snd_hctl_handle_events(fdl->hctl);
  381         else
  382             snd_mixer_handle_events(fdl->mixer);
  383     }
  384 }
  385 
  386 static void defer_cb(pa_mainloop_api *a, pa_defer_event *e, void *userdata) {
  387     struct pa_alsa_fdlist *fdl = userdata;
  388     unsigned num_fds, i;
  389     int err, n;
  390     struct pollfd *temp;
  391 
  392     pa_assert(a);
  393     pa_assert(fdl);
  394     pa_assert(fdl->mixer || fdl->hctl);
  395 
  396     a->defer_enable(fdl->defer, 0);
  397 
  398     if (fdl->hctl)
  399         n = snd_hctl_poll_descriptors_count(fdl->hctl);
  400     else
  401         n = snd_mixer_poll_descriptors_count(fdl->mixer);
  402 
  403     if (n < 0) {
  404         pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
  405         return;
  406     }
  407     else if (n == 0) {
  408         pa_log_warn("Mixer has no poll descriptors. Please control mixer from PulseAudio only.");
  409         return;
  410     }
  411     num_fds = (unsigned) n;
  412 
  413     if (num_fds != fdl->num_fds) {
  414         if (fdl->fds)
  415             pa_xfree(fdl->fds);
  416         if (fdl->work_fds)
  417             pa_xfree(fdl->work_fds);
  418         fdl->fds = pa_xnew0(struct pollfd, num_fds);
  419         fdl->work_fds = pa_xnew(struct pollfd, num_fds);
  420     }
  421 
  422     memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds);
  423 
  424     if (fdl->hctl)
  425         err = snd_hctl_poll_descriptors(fdl->hctl, fdl->work_fds, num_fds);
  426     else
  427         err = snd_mixer_poll_descriptors(fdl->mixer, fdl->work_fds, num_fds);
  428 
  429     if (err < 0) {
  430         pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
  431         return;
  432     }
  433 
  434     fdl->polled = false;
  435 
  436     if (memcmp(fdl->fds, fdl->work_fds, sizeof(struct pollfd) * num_fds) == 0)
  437         return;
  438 
  439     if (fdl->ios) {
  440         for (i = 0; i < fdl->num_fds; i++)
  441             a->io_free(fdl->ios[i]);
  442 
  443         if (num_fds != fdl->num_fds) {
  444             pa_xfree(fdl->ios);
  445             fdl->ios = NULL;
  446         }
  447     }
  448 
  449     if (!fdl->ios)
  450         fdl->ios = pa_xnew(pa_io_event*, num_fds);
  451 
  452     /* Swap pointers */
  453     temp = fdl->work_fds;
  454     fdl->work_fds = fdl->fds;
  455     fdl->fds = temp;
  456 
  457     fdl->num_fds = num_fds;
  458 
  459     for (i = 0;i < num_fds;i++)
  460         fdl->ios[i] = a->io_new(a, fdl->fds[i].fd,
  461             ((fdl->fds[i].events & POLLIN) ? PA_IO_EVENT_INPUT : 0) |
  462             ((fdl->fds[i].events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0),
  463             io_cb, fdl);
  464 }
  465 
  466 struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) {
  467     struct pa_alsa_fdlist *fdl;
  468 
  469     fdl = pa_xnew0(struct pa_alsa_fdlist, 1);
  470 
  471     return fdl;
  472 }
  473 
  474 void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {
  475     pa_assert(fdl);
  476 
  477     if (fdl->defer) {
  478         pa_assert(fdl->m);
  479         fdl->m->defer_free(fdl->defer);
  480     }
  481 
  482     if (fdl->ios) {
  483         unsigned i;
  484         pa_assert(fdl->m);
  485         for (i = 0; i < fdl->num_fds; i++)
  486             fdl->m->io_free(fdl->ios[i]);
  487         pa_xfree(fdl->ios);
  488     }
  489 
  490     if (fdl->fds)
  491         pa_xfree(fdl->fds);
  492     if (fdl->work_fds)
  493         pa_xfree(fdl->work_fds);
  494 
  495     pa_xfree(fdl);
  496 }
  497 
  498 /* We can listen to either a snd_hctl_t or a snd_mixer_t, but not both */
  499 int pa_alsa_fdlist_set_handle(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, snd_hctl_t *hctl_handle, pa_mainloop_api *m) {
  500     pa_assert(fdl);
  501     pa_assert(hctl_handle || mixer_handle);
  502     pa_assert(!(hctl_handle && mixer_handle));
  503     pa_assert(m);
  504     pa_assert(!fdl->m);
  505 
  506     fdl->hctl = hctl_handle;
  507     fdl->mixer = mixer_handle;
  508     fdl->m = m;
  509     fdl->defer = m->defer_new(m, defer_cb, fdl);
  510 
  511     return 0;
  512 }
  513 
  514 struct pa_alsa_mixer_pdata {
  515     pa_rtpoll *rtpoll;
  516     pa_rtpoll_item *poll_item;
  517     snd_mixer_t *mixer;
  518 };
  519 
  520 struct pa_alsa_mixer_pdata *pa_alsa_mixer_pdata_new(void) {
  521     struct pa_alsa_mixer_pdata *pd;
  522 
  523     pd = pa_xnew0(struct pa_alsa_mixer_pdata, 1);
  524 
  525     return pd;
  526 }
  527 
  528 void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata *pd) {
  529     pa_assert(pd);
  530 
  531     if (pd->poll_item) {
  532         pa_rtpoll_item_free(pd->poll_item);
  533     }
  534 
  535     pa_xfree(pd);
  536 }
  537 
  538 static int rtpoll_work_cb(pa_rtpoll_item *i) {
  539     struct pa_alsa_mixer_pdata *pd;
  540     struct pollfd *p;
  541     unsigned n_fds;
  542     unsigned short revents = 0;
  543     int err, ret = 0;
  544 
  545     pd = pa_rtpoll_item_get_work_userdata(i);
  546     pa_assert_fp(pd);
  547     pa_assert_fp(i == pd->poll_item);
  548 
  549     p = pa_rtpoll_item_get_pollfd(i, &n_fds);
  550 
  551     if ((err = snd_mixer_poll_descriptors_revents(pd->mixer, p, n_fds, &revents)) < 0) {
  552         pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
  553         ret = -1;
  554         goto fail;
  555     }
  556 
  557     if (revents) {
  558         if (revents & (POLLNVAL | POLLERR)) {
  559             pa_log_debug("Device disconnected, stopping poll on mixer");
  560             goto fail;
  561         } else if (revents & POLLERR) {
  562             /* This shouldn't happen. */
  563             pa_log_error("Got a POLLERR (revents = %04x), stopping poll on mixer", revents);
  564             goto fail;
  565         }
  566 
  567         err = snd_mixer_handle_events(pd->mixer);
  568 
  569         if (PA_LIKELY(err >= 0)) {
  570             pa_rtpoll_item_free(i);
  571             pa_alsa_set_mixer_rtpoll(pd, pd->mixer, pd->rtpoll);
  572         } else {
  573             pa_log_error("Error handling mixer event: %s", pa_alsa_strerror(err));
  574             ret = -1;
  575             goto fail;
  576         }
  577     }
  578 
  579     return ret;
  580 
  581 fail:
  582     pa_rtpoll_item_free(i);
  583 
  584     pd->poll_item = NULL;
  585     pd->rtpoll = NULL;
  586     pd->mixer = NULL;
  587 
  588     return ret;
  589 }
  590 
  591 int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata *pd, snd_mixer_t *mixer, pa_rtpoll *rtp) {
  592     pa_rtpoll_item *i;
  593     struct pollfd *p;
  594     int err, n;
  595 
  596     pa_assert(pd);
  597     pa_assert(mixer);
  598     pa_assert(rtp);
  599 
  600     if ((n = snd_mixer_poll_descriptors_count(mixer)) < 0) {
  601         pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
  602         return -1;
  603     }
  604     else if (n == 0) {
  605         pa_log_warn("Mixer has no poll descriptors. Please control mixer from PulseAudio only.");
  606         return 0;
  607     }
  608 
  609     i = pa_rtpoll_item_new(rtp, PA_RTPOLL_LATE, (unsigned) n);
  610 
  611     p = pa_rtpoll_item_get_pollfd(i, NULL);
  612 
  613     memset(p, 0, sizeof(struct pollfd) * n);
  614 
  615     if ((err = snd_mixer_poll_descriptors(mixer, p, (unsigned) n)) < 0) {
  616         pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
  617         pa_rtpoll_item_free(i);
  618         return -1;
  619     }
  620 
  621     pd->rtpoll = rtp;
  622     pd->poll_item = i;
  623     pd->mixer = mixer;
  624 
  625     pa_rtpoll_item_set_work_callback(i, rtpoll_work_cb, pd);
  626 
  627     return 0;
  628 }
  629 
  630 static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_MAX] = {
  631     [PA_CHANNEL_POSITION_MONO] = SND_MIXER_SCHN_MONO, /* The ALSA name is just an alias! */
  632 
  633     [PA_CHANNEL_POSITION_FRONT_CENTER] = SND_MIXER_SCHN_FRONT_CENTER,
  634     [PA_CHANNEL_POSITION_FRONT_LEFT] = SND_MIXER_SCHN_FRONT_LEFT,
  635     [PA_CHANNEL_POSITION_FRONT_RIGHT] = SND_MIXER_SCHN_FRONT_RIGHT,
  636 
  637     [PA_CHANNEL_POSITION_REAR_CENTER] = SND_MIXER_SCHN_REAR_CENTER,
  638     [PA_CHANNEL_POSITION_REAR_LEFT] = SND_MIXER_SCHN_REAR_LEFT,
  639     [PA_CHANNEL_POSITION_REAR_RIGHT] = SND_MIXER_SCHN_REAR_RIGHT,
  640 
  641     [PA_CHANNEL_POSITION_LFE] = SND_MIXER_SCHN_WOOFER,
  642 
  643     [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
  644     [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
  645 
  646     [PA_CHANNEL_POSITION_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT,
  647     [PA_CHANNEL_POSITION_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT,
  648 
  649     [PA_CHANNEL_POSITION_AUX0] = SND_MIXER_SCHN_UNKNOWN,
  650     [PA_CHANNEL_POSITION_AUX1] = SND_MIXER_SCHN_UNKNOWN,
  651     [PA_CHANNEL_POSITION_AUX2] = SND_MIXER_SCHN_UNKNOWN,
  652     [PA_CHANNEL_POSITION_AUX3] = SND_MIXER_SCHN_UNKNOWN,
  653     [PA_CHANNEL_POSITION_AUX4] = SND_MIXER_SCHN_UNKNOWN,
  654     [PA_CHANNEL_POSITION_AUX5] = SND_MIXER_SCHN_UNKNOWN,
  655     [PA_CHANNEL_POSITION_AUX6] = SND_MIXER_SCHN_UNKNOWN,
  656     [PA_CHANNEL_POSITION_AUX7] = SND_MIXER_SCHN_UNKNOWN,
  657     [PA_CHANNEL_POSITION_AUX8] = SND_MIXER_SCHN_UNKNOWN,
  658     [PA_CHANNEL_POSITION_AUX9] =  SND_MIXER_SCHN_UNKNOWN,
  659     [PA_CHANNEL_POSITION_AUX10] = SND_MIXER_SCHN_UNKNOWN,
  660     [PA_CHANNEL_POSITION_AUX11] = SND_MIXER_SCHN_UNKNOWN,
  661     [PA_CHANNEL_POSITION_AUX12] = SND_MIXER_SCHN_UNKNOWN,
  662     [PA_CHANNEL_POSITION_AUX13] = SND_MIXER_SCHN_UNKNOWN,
  663     [PA_CHANNEL_POSITION_AUX14] = SND_MIXER_SCHN_UNKNOWN,
  664     [PA_CHANNEL_POSITION_AUX15] = SND_MIXER_SCHN_UNKNOWN,
  665     [PA_CHANNEL_POSITION_AUX16] = SND_MIXER_SCHN_UNKNOWN,
  666     [PA_CHANNEL_POSITION_AUX17] = SND_MIXER_SCHN_UNKNOWN,
  667     [PA_CHANNEL_POSITION_AUX18] = SND_MIXER_SCHN_UNKNOWN,
  668     [PA_CHANNEL_POSITION_AUX19] = SND_MIXER_SCHN_UNKNOWN,
  669     [PA_CHANNEL_POSITION_AUX20] = SND_MIXER_SCHN_UNKNOWN,
  670     [PA_CHANNEL_POSITION_AUX21] = SND_MIXER_SCHN_UNKNOWN,
  671     [PA_CHANNEL_POSITION_AUX22] = SND_MIXER_SCHN_UNKNOWN,
  672     [PA_CHANNEL_POSITION_AUX23] = SND_MIXER_SCHN_UNKNOWN,
  673     [PA_CHANNEL_POSITION_AUX24] = SND_MIXER_SCHN_UNKNOWN,
  674     [PA_CHANNEL_POSITION_AUX25] = SND_MIXER_SCHN_UNKNOWN,
  675     [PA_CHANNEL_POSITION_AUX26] = SND_MIXER_SCHN_UNKNOWN,
  676     [PA_CHANNEL_POSITION_AUX27] = SND_MIXER_SCHN_UNKNOWN,
  677     [PA_CHANNEL_POSITION_AUX28] = SND_MIXER_SCHN_UNKNOWN,
  678     [PA_CHANNEL_POSITION_AUX29] = SND_MIXER_SCHN_UNKNOWN,
  679     [PA_CHANNEL_POSITION_AUX30] = SND_MIXER_SCHN_UNKNOWN,
  680     [PA_CHANNEL_POSITION_AUX31] = SND_MIXER_SCHN_UNKNOWN,
  681 
  682     [PA_CHANNEL_POSITION_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN,
  683 
  684     [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SND_MIXER_SCHN_UNKNOWN,
  685     [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SND_MIXER_SCHN_UNKNOWN,
  686     [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SND_MIXER_SCHN_UNKNOWN,
  687 
  688     [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SND_MIXER_SCHN_UNKNOWN,
  689     [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SND_MIXER_SCHN_UNKNOWN,
  690     [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SND_MIXER_SCHN_UNKNOWN
  691 };
  692 
  693 static snd_mixer_selem_channel_id_t alsa_channel_positions[POSITION_MASK_CHANNELS] = {
  694     SND_MIXER_SCHN_FRONT_LEFT,
  695     SND_MIXER_SCHN_FRONT_RIGHT,
  696     SND_MIXER_SCHN_REAR_LEFT,
  697     SND_MIXER_SCHN_REAR_RIGHT,
  698     SND_MIXER_SCHN_FRONT_CENTER,
  699     SND_MIXER_SCHN_WOOFER,
  700     SND_MIXER_SCHN_SIDE_LEFT,
  701     SND_MIXER_SCHN_SIDE_RIGHT,
  702 #if POSITION_MASK_CHANNELS > 8
  703 #error "Extend alsa_channel_positions[] array (9+)"
  704 #endif
  705 };
  706 
  707 static void setting_free(pa_alsa_setting *s) {
  708     pa_assert(s);
  709 
  710     if (s->options)
  711         pa_idxset_free(s->options, NULL);
  712 
  713     pa_xfree(s->name);
  714     pa_xfree(s->description);
  715     pa_xfree(s);
  716 }
  717 
  718 static void option_free(pa_alsa_option *o) {
  719     pa_assert(o);
  720 
  721     pa_xfree(o->alsa_name);
  722     pa_xfree(o->name);
  723     pa_xfree(o->description);
  724     pa_xfree(o);
  725 }
  726 
  727 static void decibel_fix_free(pa_alsa_decibel_fix *db_fix) {
  728     pa_assert(db_fix);
  729 
  730     pa_xfree(db_fix->name);
  731     pa_xfree(db_fix->db_values);
  732 
  733     pa_xfree(db_fix->key);
  734     pa_xfree(db_fix);
  735 }
  736 
  737 static void element_free(pa_alsa_element *e) {
  738     pa_alsa_option *o;
  739     pa_assert(e);
  740 
  741     while ((o = e->options)) {
  742         PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
  743         option_free(o);
  744     }
  745 
  746     if (e->db_fix)
  747         decibel_fix_free(e->db_fix);
  748 
  749     pa_xfree(e->alsa_id.name);
  750     pa_xfree(e);
  751 }
  752 
  753 void pa_alsa_path_free(pa_alsa_path *p) {
  754     pa_alsa_jack *j;
  755     pa_alsa_element *e;
  756     pa_alsa_setting *s;
  757 
  758     pa_assert(p);
  759 
  760     while ((j = p->jacks)) {
  761         PA_LLIST_REMOVE(pa_alsa_jack, p->jacks, j);
  762         pa_alsa_jack_free(j);
  763     }
  764 
  765     while ((e = p->elements)) {
  766         PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
  767         element_free(e);
  768     }
  769 
  770     while ((s = p->settings)) {
  771         PA_LLIST_REMOVE(pa_alsa_setting, p->settings, s);
  772         setting_free(s);
  773     }
  774 
  775     pa_proplist_free(p->proplist);
  776     pa_xfree(p->availability_group);
  777     pa_xfree(p->name);
  778     pa_xfree(p->description);
  779     pa_xfree(p->description_key);
  780     pa_xfree(p);
  781 }
  782 
  783 void pa_alsa_path_set_free(pa_alsa_path_set *ps) {
  784     pa_assert(ps);
  785 
  786     if (ps->paths)
  787         pa_hashmap_free(ps->paths);
  788 
  789     pa_xfree(ps);
  790 }
  791 
  792 int pa_alsa_path_set_is_empty(pa_alsa_path_set *ps) {
  793     if (ps && !pa_hashmap_isempty(ps->paths))
  794         return 0;
  795     return 1;
  796 }
  797 
  798 static long to_alsa_dB(pa_volume_t v) {
  799     return lround(pa_sw_volume_to_dB(v) * 100.0);
  800 }
  801 
  802 static pa_volume_t from_alsa_dB(long v) {
  803     return pa_sw_volume_from_dB((double) v / 100.0);
  804 }
  805 
  806 static long to_alsa_volume(pa_volume_t v, long min, long max) {
  807     long w;
  808 
  809     w = (long) round(((double) v * (double) (max - min)) / PA_VOLUME_NORM) + min;
  810     return PA_CLAMP_UNLIKELY(w, min, max);
  811 }
  812 
  813 static pa_volume_t from_alsa_volume(long v, long min, long max) {
  814     return (pa_volume_t) round(((double) (v - min) * PA_VOLUME_NORM) / (double) (max - min));
  815 }
  816 
  817 #define SELEM_INIT(sid, aid)                                     \
  818     do {                                                     \
  819         snd_mixer_selem_id_alloca(&(sid));                   \
  820         snd_mixer_selem_id_set_name((sid), (aid)->name);     \
  821         snd_mixer_selem_id_set_index((sid), (aid)->index);   \
  822     } while(false)
  823 
  824 static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
  825     snd_mixer_selem_id_t *sid;
  826     snd_mixer_elem_t *me;
  827     snd_mixer_selem_channel_id_t c;
  828     pa_channel_position_mask_t mask = 0;
  829     char buf[64];
  830     unsigned k;
  831 
  832     pa_assert(m);
  833     pa_assert(e);
  834     pa_assert(cm);
  835     pa_assert(v);
  836 
  837     SELEM_INIT(sid, &e->alsa_id);
  838     if (!(me = snd_mixer_find_selem(m, sid))) {
  839         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
  840         pa_log_warn("Element %s seems to have disappeared.", buf);
  841         return -1;
  842     }
  843 
  844     pa_cvolume_mute(v, cm->channels);
  845 
  846     /* We take the highest volume of all channels that match */
  847 
  848     for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
  849         int r;
  850         pa_volume_t f;
  851 
  852         if (e->has_dB) {
  853             long value = 0;
  854 
  855             if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
  856                 if (snd_mixer_selem_has_playback_channel(me, c)) {
  857                     if (e->db_fix) {
  858                         if ((r = snd_mixer_selem_get_playback_volume(me, c, &value)) >= 0) {
  859                             /* If the channel volume is outside the limits set
  860                              * by the dB fix, we clamp the hw volume to be
  861                              * within the limits. */
  862                             if (value < e->db_fix->min_step) {
  863                                 value = e->db_fix->min_step;
  864                                 snd_mixer_selem_set_playback_volume(me, c, value);
  865                                 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
  866                                 pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
  867                                              "Volume reset to %0.2f dB.", buf, c,
  868                                              e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
  869                             } else if (value > e->db_fix->max_step) {
  870                                 value = e->db_fix->max_step;
  871                                 snd_mixer_selem_set_playback_volume(me, c, value);
  872                                 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
  873                                 pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
  874                                              "Volume reset to %0.2f dB.", buf, c,
  875                                              e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
  876                             }
  877 
  878                             /* Volume step -> dB value conversion. */
  879                             value = e->db_fix->db_values[value - e->db_fix->min_step];
  880                         }
  881                     } else
  882                         r = snd_mixer_selem_get_playback_dB(me, c, &value);
  883                 } else
  884                     r = -1;
  885             } else {
  886                 if (snd_mixer_selem_has_capture_channel(me, c)) {
  887                     if (e->db_fix) {
  888                         if ((r = snd_mixer_selem_get_capture_volume(me, c, &value)) >= 0) {
  889                             /* If the channel volume is outside the limits set
  890                              * by the dB fix, we clamp the hw volume to be
  891                              * within the limits. */
  892                             if (value < e->db_fix->min_step) {
  893                                 value = e->db_fix->min_step;
  894                                 snd_mixer_selem_set_capture_volume(me, c, value);
  895                                 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
  896                                 pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
  897                                              "Volume reset to %0.2f dB.", buf, c,
  898                                              e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
  899                             } else if (value > e->db_fix->max_step) {
  900                                 value = e->db_fix->max_step;
  901                                 snd_mixer_selem_set_capture_volume(me, c, value);
  902                                 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
  903                                 pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
  904                                              "Volume reset to %0.2f dB.", buf, c,
  905                                              e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
  906                             }
  907 
  908                             /* Volume step -> dB value conversion. */
  909                             value = e->db_fix->db_values[value - e->db_fix->min_step];
  910                         }
  911                     } else
  912                         r = snd_mixer_selem_get_capture_dB(me, c, &value);
  913                 } else
  914                     r = -1;
  915             }
  916 
  917             if (r < 0)
  918                 continue;
  919 
  920 #ifdef HAVE_VALGRIND_MEMCHECK_H
  921                 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
  922 #endif
  923 
  924             f = from_alsa_dB(value);
  925 
  926         } else {
  927             long value = 0;
  928 
  929             if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
  930                 if (snd_mixer_selem_has_playback_channel(me, c))
  931                     r = snd_mixer_selem_get_playback_volume(me, c, &value);
  932                 else
  933                     r = -1;
  934             } else {
  935                 if (snd_mixer_selem_has_capture_channel(me, c))
  936                     r = snd_mixer_selem_get_capture_volume(me, c, &value);
  937                 else
  938                     r = -1;
  939             }
  940 
  941             if (r < 0)
  942                 continue;
  943 
  944             f = from_alsa_volume(value, e->min_volume, e->max_volume);
  945         }
  946 
  947         for (k = 0; k < cm->channels; k++)
  948             if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
  949                 if (v->values[k] < f)
  950                     v->values[k] = f;
  951 
  952         mask |= e->masks[c][e->n_channels-1];
  953     }
  954 
  955     for (k = 0; k < cm->channels; k++)
  956         if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
  957             v->values[k] = PA_VOLUME_NORM;
  958 
  959     return 0;
  960 }
  961 
  962 int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
  963     pa_alsa_element *e;
  964 
  965     pa_assert(m);
  966     pa_assert(p);
  967     pa_assert(cm);
  968     pa_assert(v);
  969 
  970     if (!p->has_volume)
  971         return -1;
  972 
  973     pa_cvolume_reset(v, cm->channels);
  974 
  975     PA_LLIST_FOREACH(e, p->elements) {
  976         pa_cvolume ev;
  977 
  978         if (e->volume_use != PA_ALSA_VOLUME_MERGE)
  979             continue;
  980 
  981         pa_assert(!p->has_dB || e->has_dB);
  982 
  983         if (element_get_volume(e, m, cm, &ev) < 0)
  984             return -1;
  985 
  986         /* If we have no dB information all we can do is take the first element and leave */
  987         if (!p->has_dB) {
  988             *v = ev;
  989             return 0;
  990         }
  991 
  992         pa_sw_cvolume_multiply(v, v, &ev);
  993     }
  994 
  995     return 0;
  996 }
  997 
  998 static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, bool *b) {
  999     snd_mixer_selem_id_t *sid;
 1000     snd_mixer_elem_t *me;
 1001     snd_mixer_selem_channel_id_t c;
 1002     char buf[64];
 1003 
 1004     pa_assert(m);
 1005     pa_assert(e);
 1006     pa_assert(b);
 1007 
 1008     SELEM_INIT(sid, &e->alsa_id);
 1009     if (!(me = snd_mixer_find_selem(m, sid))) {
 1010         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 1011         pa_log_warn("Element %s seems to have disappeared.", buf);
 1012         return -1;
 1013     }
 1014 
 1015     /* We return muted if at least one channel is muted */
 1016 
 1017     for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
 1018         int r;
 1019         int value = 0;
 1020 
 1021         if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
 1022             if (snd_mixer_selem_has_playback_channel(me, c))
 1023                 r = snd_mixer_selem_get_playback_switch(me, c, &value);
 1024             else
 1025                 r = -1;
 1026         } else {
 1027             if (snd_mixer_selem_has_capture_channel(me, c))
 1028                 r = snd_mixer_selem_get_capture_switch(me, c, &value);
 1029             else
 1030                 r = -1;
 1031         }
 1032 
 1033         if (r < 0)
 1034             continue;
 1035 
 1036         if (!value) {
 1037             *b = false;
 1038             return 0;
 1039         }
 1040     }
 1041 
 1042     *b = true;
 1043     return 0;
 1044 }
 1045 
 1046 int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, bool *muted) {
 1047     pa_alsa_element *e;
 1048 
 1049     pa_assert(m);
 1050     pa_assert(p);
 1051     pa_assert(muted);
 1052 
 1053     if (!p->has_mute)
 1054         return -1;
 1055 
 1056     PA_LLIST_FOREACH(e, p->elements) {
 1057         bool b;
 1058 
 1059         if (e->switch_use != PA_ALSA_SWITCH_MUTE)
 1060             continue;
 1061 
 1062         if (element_get_switch(e, m, &b) < 0)
 1063             return -1;
 1064 
 1065         if (!b) {
 1066             *muted = true;
 1067             return 0;
 1068         }
 1069     }
 1070 
 1071     *muted = false;
 1072     return 0;
 1073 }
 1074 
 1075 /* Finds the closest item in db_fix->db_values and returns the corresponding
 1076  * step. *db_value is replaced with the value from the db_values table.
 1077  * Rounding is done based on the rounding parameter: -1 means rounding down and
 1078  * +1 means rounding up. */
 1079 static long decibel_fix_get_step(pa_alsa_decibel_fix *db_fix, long *db_value, int rounding) {
 1080     unsigned i = 0;
 1081     unsigned max_i = 0;
 1082 
 1083     pa_assert(db_fix);
 1084     pa_assert(db_value);
 1085     pa_assert(rounding != 0);
 1086 
 1087     max_i = db_fix->max_step - db_fix->min_step;
 1088 
 1089     if (rounding > 0) {
 1090         for (i = 0; i < max_i; i++) {
 1091             if (db_fix->db_values[i] >= *db_value)
 1092                 break;
 1093         }
 1094     } else {
 1095         for (i = 0; i < max_i; i++) {
 1096             if (db_fix->db_values[i + 1] > *db_value)
 1097                 break;
 1098         }
 1099     }
 1100 
 1101     *db_value = db_fix->db_values[i];
 1102 
 1103     return i + db_fix->min_step;
 1104 }
 1105 
 1106 /* Alsa lib documentation says for snd_mixer_selem_set_playback_dB() direction argument,
 1107  * that "-1 = accurate or first below, 0 = accurate, 1 = accurate or first above".
 1108  * But even with accurate nearest dB volume step is not selected, so that is why we need
 1109  * this function. Returns 0 and nearest selectable volume in *value_dB on success or
 1110  * negative error code if fails. */
 1111 static int element_get_nearest_alsa_dB(snd_mixer_elem_t *me, snd_mixer_selem_channel_id_t c, pa_alsa_direction_t d, long *value_dB) {
 1112 
 1113     long alsa_val;
 1114     long value_high;
 1115     long value_low;
 1116     int r = -1;
 1117 
 1118     pa_assert(me);
 1119     pa_assert(value_dB);
 1120 
 1121     if (d == PA_ALSA_DIRECTION_OUTPUT) {
 1122         if ((r = snd_mixer_selem_ask_playback_dB_vol(me, *value_dB, +1, &alsa_val)) >= 0)
 1123             r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value_high);
 1124 
 1125         if (r < 0)
 1126             return r;
 1127 
 1128         if (value_high == *value_dB)
 1129             return r;
 1130 
 1131         if ((r = snd_mixer_selem_ask_playback_dB_vol(me, *value_dB, -1, &alsa_val)) >= 0)
 1132             r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value_low);
 1133     } else {
 1134         if ((r = snd_mixer_selem_ask_capture_dB_vol(me, *value_dB, +1, &alsa_val)) >= 0)
 1135             r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value_high);
 1136 
 1137         if (r < 0)
 1138             return r;
 1139 
 1140         if (value_high == *value_dB)
 1141             return r;
 1142 
 1143         if ((r = snd_mixer_selem_ask_capture_dB_vol(me, *value_dB, -1, &alsa_val)) >= 0)
 1144             r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value_low);
 1145     }
 1146 
 1147     if (r < 0)
 1148         return r;
 1149 
 1150     if (labs(value_high - *value_dB) < labs(value_low - *value_dB))
 1151         *value_dB = value_high;
 1152     else
 1153         *value_dB = value_low;
 1154 
 1155     return r;
 1156 }
 1157 
 1158 static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, bool deferred_volume, bool write_to_hw) {
 1159 
 1160     snd_mixer_selem_id_t *sid;
 1161     pa_cvolume rv;
 1162     snd_mixer_elem_t *me;
 1163     snd_mixer_selem_channel_id_t c;
 1164     pa_channel_position_mask_t mask = 0;
 1165     char buf[64];
 1166     unsigned k;
 1167 
 1168     pa_assert(m);
 1169     pa_assert(e);
 1170     pa_assert(cm);
 1171     pa_assert(v);
 1172     pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
 1173 
 1174     SELEM_INIT(sid, &e->alsa_id);
 1175     if (!(me = snd_mixer_find_selem(m, sid))) {
 1176         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 1177         pa_log_warn("Element %s seems to have disappeared.", buf);
 1178         return -1;
 1179     }
 1180 
 1181     pa_cvolume_mute(&rv, cm->channels);
 1182 
 1183     for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
 1184         int r;
 1185         pa_volume_t f = PA_VOLUME_MUTED;
 1186         bool found = false;
 1187 
 1188         for (k = 0; k < cm->channels; k++)
 1189             if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) {
 1190                 found = true;
 1191                 if (v->values[k] > f)
 1192                     f = v->values[k];
 1193             }
 1194 
 1195         if (!found) {
 1196             /* Hmm, so this channel does not exist in the volume
 1197              * struct, so let's bind it to the overall max of the
 1198              * volume. */
 1199             f = pa_cvolume_max(v);
 1200         }
 1201 
 1202         if (e->has_dB) {
 1203             long value = to_alsa_dB(f);
 1204             int rounding;
 1205 
 1206             if (e->volume_limit >= 0 && value > (e->max_dB * 100))
 1207                 value = e->max_dB * 100;
 1208 
 1209             if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
 1210                 /* If we call set_playback_volume() without checking first
 1211                  * if the channel is available, ALSA behaves very
 1212                  * strangely and doesn't fail the call */
 1213                 if (snd_mixer_selem_has_playback_channel(me, c)) {
 1214                     rounding = +1;
 1215                     if (e->db_fix) {
 1216                         if (write_to_hw)
 1217                             r = snd_mixer_selem_set_playback_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding));
 1218                         else {
 1219                             decibel_fix_get_step(e->db_fix, &value, rounding);
 1220                             r = 0;
 1221                         }
 1222 
 1223                     } else {
 1224                         if (write_to_hw) {
 1225                             if (deferred_volume) {
 1226                                 if ((r = element_get_nearest_alsa_dB(me, c, PA_ALSA_DIRECTION_OUTPUT, &value)) >= 0)
 1227                                     r = snd_mixer_selem_set_playback_dB(me, c, value, 0);
 1228                             } else {
 1229                                 if ((r = snd_mixer_selem_set_playback_dB(me, c, value, rounding)) >= 0)
 1230                                     r = snd_mixer_selem_get_playback_dB(me, c, &value);
 1231                            }
 1232                         } else {
 1233                             long alsa_val;
 1234                             if ((r = snd_mixer_selem_ask_playback_dB_vol(me, value, rounding, &alsa_val)) >= 0)
 1235                                 r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value);
 1236                         }
 1237                     }
 1238                 } else
 1239                     r = -1;
 1240             } else {
 1241                 if (snd_mixer_selem_has_capture_channel(me, c)) {
 1242                     rounding = -1;
 1243                     if (e->db_fix) {
 1244                         if (write_to_hw)
 1245                             r = snd_mixer_selem_set_capture_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding));
 1246                         else {
 1247                             decibel_fix_get_step(e->db_fix, &value, rounding);
 1248                             r = 0;
 1249                         }
 1250 
 1251                     } else {
 1252                         if (write_to_hw) {
 1253                             if (deferred_volume) {
 1254                                 if ((r = element_get_nearest_alsa_dB(me, c, PA_ALSA_DIRECTION_INPUT, &value)) >= 0)
 1255                                     r = snd_mixer_selem_set_capture_dB(me, c, value, 0);
 1256                             } else {
 1257                                 if ((r = snd_mixer_selem_set_capture_dB(me, c, value, rounding)) >= 0)
 1258                                     r = snd_mixer_selem_get_capture_dB(me, c, &value);
 1259                             }
 1260                         } else {
 1261                             long alsa_val;
 1262                             if ((r = snd_mixer_selem_ask_capture_dB_vol(me, value, rounding, &alsa_val)) >= 0)
 1263                                 r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value);
 1264                         }
 1265                     }
 1266                 } else
 1267                     r = -1;
 1268             }
 1269 
 1270             if (r < 0)
 1271                 continue;
 1272 
 1273             f = from_alsa_dB(value);
 1274 
 1275         } else {
 1276             long value;
 1277 
 1278             value = to_alsa_volume(f, e->min_volume, e->max_volume);
 1279 
 1280             if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
 1281                 if (snd_mixer_selem_has_playback_channel(me, c)) {
 1282                     if ((r = snd_mixer_selem_set_playback_volume(me, c, value)) >= 0)
 1283                         r = snd_mixer_selem_get_playback_volume(me, c, &value);
 1284                 } else
 1285                     r = -1;
 1286             } else {
 1287                 if (snd_mixer_selem_has_capture_channel(me, c)) {
 1288                     if ((r = snd_mixer_selem_set_capture_volume(me, c, value)) >= 0)
 1289                         r = snd_mixer_selem_get_capture_volume(me, c, &value);
 1290                 } else
 1291                     r = -1;
 1292             }
 1293 
 1294             if (r < 0)
 1295                 continue;
 1296 
 1297             f = from_alsa_volume(value, e->min_volume, e->max_volume);
 1298         }
 1299 
 1300         for (k = 0; k < cm->channels; k++)
 1301             if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
 1302                 if (rv.values[k] < f)
 1303                     rv.values[k] = f;
 1304 
 1305         mask |= e->masks[c][e->n_channels-1];
 1306     }
 1307 
 1308     for (k = 0; k < cm->channels; k++)
 1309         if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
 1310             rv.values[k] = PA_VOLUME_NORM;
 1311 
 1312     *v = rv;
 1313     return 0;
 1314 }
 1315 
 1316 int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, bool deferred_volume, bool write_to_hw) {
 1317 
 1318     pa_alsa_element *e;
 1319     pa_cvolume rv;
 1320 
 1321     pa_assert(m);
 1322     pa_assert(p);
 1323     pa_assert(cm);
 1324     pa_assert(v);
 1325     pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
 1326 
 1327     if (!p->has_volume)
 1328         return -1;
 1329 
 1330     rv = *v; /* Remaining adjustment */
 1331     pa_cvolume_reset(v, cm->channels); /* Adjustment done */
 1332 
 1333     PA_LLIST_FOREACH(e, p->elements) {
 1334         pa_cvolume ev;
 1335 
 1336         if (e->volume_use != PA_ALSA_VOLUME_MERGE)
 1337             continue;
 1338 
 1339         pa_assert(!p->has_dB || e->has_dB);
 1340 
 1341         ev = rv;
 1342         if (element_set_volume(e, m, cm, &ev, deferred_volume, write_to_hw) < 0)
 1343             return -1;
 1344 
 1345         if (!p->has_dB) {
 1346             *v = ev;
 1347             return 0;
 1348         }
 1349 
 1350         pa_sw_cvolume_multiply(v, v, &ev);
 1351         pa_sw_cvolume_divide(&rv, &rv, &ev);
 1352     }
 1353 
 1354     return 0;
 1355 }
 1356 
 1357 static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, bool b) {
 1358     snd_mixer_elem_t *me;
 1359     snd_mixer_selem_id_t *sid;
 1360     char buf[64];
 1361     int r;
 1362 
 1363     pa_assert(m);
 1364     pa_assert(e);
 1365 
 1366     SELEM_INIT(sid, &e->alsa_id);
 1367     if (!(me = snd_mixer_find_selem(m, sid))) {
 1368         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 1369         pa_log_warn("Element %s seems to have disappeared.", buf);
 1370         return -1;
 1371     }
 1372 
 1373     if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
 1374         r = snd_mixer_selem_set_playback_switch_all(me, b);
 1375     else
 1376         r = snd_mixer_selem_set_capture_switch_all(me, b);
 1377 
 1378     if (r < 0) {
 1379         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 1380         pa_log_warn("Failed to set switch of %s: %s", buf, pa_alsa_strerror(errno));
 1381     }
 1382 
 1383     return r;
 1384 }
 1385 
 1386 int pa_alsa_path_set_mute(pa_alsa_path *p, snd_mixer_t *m, bool muted) {
 1387     pa_alsa_element *e;
 1388 
 1389     pa_assert(m);
 1390     pa_assert(p);
 1391 
 1392     if (!p->has_mute)
 1393         return -1;
 1394 
 1395     PA_LLIST_FOREACH(e, p->elements) {
 1396 
 1397         if (e->switch_use != PA_ALSA_SWITCH_MUTE)
 1398             continue;
 1399 
 1400         if (element_set_switch(e, m, !muted) < 0)
 1401             return -1;
 1402     }
 1403 
 1404     return 0;
 1405 }
 1406 
 1407 /* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
 1408  * function sets all channels of the volume element to e->min_volume, 0 dB or
 1409  * e->constant_volume. */
 1410 static int element_set_constant_volume(pa_alsa_element *e, snd_mixer_t *m) {
 1411     snd_mixer_elem_t *me = NULL;
 1412     snd_mixer_selem_id_t *sid = NULL;
 1413     int r = 0;
 1414     long volume = -1;
 1415     bool volume_set = false;
 1416     char buf[64];
 1417 
 1418     pa_assert(m);
 1419     pa_assert(e);
 1420 
 1421     SELEM_INIT(sid, &e->alsa_id);
 1422     if (!(me = snd_mixer_find_selem(m, sid))) {
 1423         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 1424         pa_log_warn("Element %s seems to have disappeared.", buf);
 1425         return -1;
 1426     }
 1427 
 1428     switch (e->volume_use) {
 1429         case PA_ALSA_VOLUME_OFF:
 1430             volume = e->min_volume;
 1431             volume_set = true;
 1432             break;
 1433 
 1434         case PA_ALSA_VOLUME_ZERO:
 1435             if (e->db_fix) {
 1436                 long dB = 0;
 1437 
 1438                 volume = decibel_fix_get_step(e->db_fix, &dB, (e->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1));
 1439                 volume_set = true;
 1440             }
 1441             break;
 1442 
 1443         case PA_ALSA_VOLUME_CONSTANT:
 1444             volume = e->constant_volume;
 1445             volume_set = true;
 1446             break;
 1447 
 1448         default:
 1449             pa_assert_not_reached();
 1450     }
 1451 
 1452     if (volume_set) {
 1453         if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
 1454             r = snd_mixer_selem_set_playback_volume_all(me, volume);
 1455         else
 1456             r = snd_mixer_selem_set_capture_volume_all(me, volume);
 1457     } else {
 1458         pa_assert(e->volume_use == PA_ALSA_VOLUME_ZERO);
 1459         pa_assert(!e->db_fix);
 1460 
 1461         if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
 1462             r = snd_mixer_selem_set_playback_dB_all(me, 0, +1);
 1463         else
 1464             r = snd_mixer_selem_set_capture_dB_all(me, 0, -1);
 1465     }
 1466 
 1467     if (r < 0) {
 1468         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 1469         pa_log_warn("Failed to set volume of %s: %s", buf, pa_alsa_strerror(errno));
 1470     }
 1471 
 1472     return r;
 1473 }
 1474 
 1475 int pa_alsa_path_select(pa_alsa_path *p, pa_alsa_setting *s, snd_mixer_t *m, bool device_is_muted) {
 1476     pa_alsa_element *e;
 1477     int r = 0;
 1478 
 1479     pa_assert(m);
 1480     pa_assert(p);
 1481 
 1482     pa_log_debug("Activating path %s", p->name);
 1483     pa_alsa_path_dump(p);
 1484 
 1485     /* First turn on hw mute if available, to avoid noise
 1486      * when setting the mixer controls. */
 1487     if (p->mute_during_activation) {
 1488         PA_LLIST_FOREACH(e, p->elements) {
 1489             if (e->switch_use == PA_ALSA_SWITCH_MUTE)
 1490                 /* If the muting fails here, that's not a critical problem for
 1491                  * selecting a path, so we ignore the return value.
 1492                  * element_set_switch() will print a warning anyway, so this
 1493                  * won't be a silent failure either. */
 1494                 (void) element_set_switch(e, m, false);
 1495         }
 1496     }
 1497 
 1498     PA_LLIST_FOREACH(e, p->elements) {
 1499 
 1500         switch (e->switch_use) {
 1501             case PA_ALSA_SWITCH_OFF:
 1502                 r = element_set_switch(e, m, false);
 1503                 break;
 1504 
 1505             case PA_ALSA_SWITCH_ON:
 1506                 r = element_set_switch(e, m, true);
 1507                 break;
 1508 
 1509             case PA_ALSA_SWITCH_MUTE:
 1510             case PA_ALSA_SWITCH_IGNORE:
 1511             case PA_ALSA_SWITCH_SELECT:
 1512                 r = 0;
 1513                 break;
 1514         }
 1515 
 1516         if (r < 0)
 1517             return -1;
 1518 
 1519         switch (e->volume_use) {
 1520             case PA_ALSA_VOLUME_OFF:
 1521             case PA_ALSA_VOLUME_ZERO:
 1522             case PA_ALSA_VOLUME_CONSTANT:
 1523                 r = element_set_constant_volume(e, m);
 1524                 break;
 1525 
 1526             case PA_ALSA_VOLUME_MERGE:
 1527             case PA_ALSA_VOLUME_IGNORE:
 1528                 r = 0;
 1529                 break;
 1530         }
 1531 
 1532         if (r < 0)
 1533             return -1;
 1534     }
 1535 
 1536     if (s)
 1537         setting_select(s, m);
 1538 
 1539     /* Finally restore hw mute to the device mute status. */
 1540     if (p->mute_during_activation) {
 1541         PA_LLIST_FOREACH(e, p->elements) {
 1542             if (e->switch_use == PA_ALSA_SWITCH_MUTE) {
 1543                 if (element_set_switch(e, m, !device_is_muted) < 0)
 1544                     return -1;
 1545             }
 1546         }
 1547     }
 1548 
 1549     return 0;
 1550 }
 1551 
 1552 static int check_required(pa_alsa_element *e, snd_mixer_elem_t *me) {
 1553     bool has_switch;
 1554     bool has_enumeration;
 1555     bool has_volume;
 1556 
 1557     pa_assert(e);
 1558     pa_assert(me);
 1559 
 1560     if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
 1561         has_switch =
 1562             snd_mixer_selem_has_playback_switch(me) ||
 1563             (e->direction_try_other && snd_mixer_selem_has_capture_switch(me));
 1564     } else {
 1565         has_switch =
 1566             snd_mixer_selem_has_capture_switch(me) ||
 1567             (e->direction_try_other && snd_mixer_selem_has_playback_switch(me));
 1568     }
 1569 
 1570     if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
 1571         has_volume =
 1572             snd_mixer_selem_has_playback_volume(me) ||
 1573             (e->direction_try_other && snd_mixer_selem_has_capture_volume(me));
 1574     } else {
 1575         has_volume =
 1576             snd_mixer_selem_has_capture_volume(me) ||
 1577             (e->direction_try_other && snd_mixer_selem_has_playback_volume(me));
 1578     }
 1579 
 1580     has_enumeration = snd_mixer_selem_is_enumerated(me);
 1581 
 1582     if ((e->required == PA_ALSA_REQUIRED_SWITCH && !has_switch) ||
 1583         (e->required == PA_ALSA_REQUIRED_VOLUME && !has_volume) ||
 1584         (e->required == PA_ALSA_REQUIRED_ENUMERATION && !has_enumeration))
 1585         return -1;
 1586 
 1587     if (e->required == PA_ALSA_REQUIRED_ANY && !(has_switch || has_volume || has_enumeration))
 1588         return -1;
 1589 
 1590     if ((e->required_absent == PA_ALSA_REQUIRED_SWITCH && has_switch) ||
 1591         (e->required_absent == PA_ALSA_REQUIRED_VOLUME && has_volume) ||
 1592         (e->required_absent == PA_ALSA_REQUIRED_ENUMERATION && has_enumeration))
 1593         return -1;
 1594 
 1595     if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration))
 1596         return -1;
 1597 
 1598     if (e->required_any != PA_ALSA_REQUIRED_IGNORE) {
 1599         switch (e->required_any) {
 1600             case PA_ALSA_REQUIRED_VOLUME:
 1601                 e->path->req_any_present |= (e->volume_use != PA_ALSA_VOLUME_IGNORE);
 1602                 break;
 1603             case PA_ALSA_REQUIRED_SWITCH:
 1604                 e->path->req_any_present |= (e->switch_use != PA_ALSA_SWITCH_IGNORE);
 1605                 break;
 1606             case PA_ALSA_REQUIRED_ENUMERATION:
 1607                 e->path->req_any_present |= (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
 1608                 break;
 1609             case PA_ALSA_REQUIRED_ANY:
 1610                 e->path->req_any_present |=
 1611                     (e->volume_use != PA_ALSA_VOLUME_IGNORE) ||
 1612                     (e->switch_use != PA_ALSA_SWITCH_IGNORE) ||
 1613                     (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
 1614                 break;
 1615             default:
 1616                 pa_assert_not_reached();
 1617         }
 1618     }
 1619 
 1620     if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
 1621         pa_alsa_option *o;
 1622         PA_LLIST_FOREACH(o, e->options) {
 1623             e->path->req_any_present |= (o->required_any != PA_ALSA_REQUIRED_IGNORE) &&
 1624                 (o->alsa_idx >= 0);
 1625             if (o->required != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx < 0)
 1626                 return -1;
 1627             if (o->required_absent != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx >= 0)
 1628                 return -1;
 1629         }
 1630     }
 1631 
 1632     return 0;
 1633 }
 1634 
 1635 static int element_ask_vol_dB(snd_mixer_elem_t *me, pa_alsa_direction_t dir, long value, long *dBvalue) {
 1636     if (dir == PA_ALSA_DIRECTION_OUTPUT)
 1637         return snd_mixer_selem_ask_playback_vol_dB(me, value, dBvalue);
 1638     else
 1639         return snd_mixer_selem_ask_capture_vol_dB(me, value, dBvalue);
 1640 }
 1641 
 1642 static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
 1643 
 1644     long min_dB = 0, max_dB = 0;
 1645     int r;
 1646     bool is_mono;
 1647     pa_channel_position_t p;
 1648     char buf[64];
 1649 
 1650     if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
 1651         if (!snd_mixer_selem_has_playback_volume(me)) {
 1652             if (e->direction_try_other && snd_mixer_selem_has_capture_volume(me))
 1653                 e->direction = PA_ALSA_DIRECTION_INPUT;
 1654             else
 1655                 return false;
 1656         }
 1657     } else {
 1658         if (!snd_mixer_selem_has_capture_volume(me)) {
 1659             if (e->direction_try_other && snd_mixer_selem_has_playback_volume(me))
 1660                 e->direction = PA_ALSA_DIRECTION_OUTPUT;
 1661             else
 1662                 return false;
 1663         }
 1664     }
 1665 
 1666     e->direction_try_other = false;
 1667 
 1668     if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
 1669         r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume);
 1670     else
 1671         r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
 1672 
 1673     if (r < 0) {
 1674         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 1675         pa_log_warn("Failed to get volume range of %s: %s", buf, pa_alsa_strerror(r));
 1676         return false;
 1677     }
 1678 
 1679     if (e->min_volume >= e->max_volume) {
 1680         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 1681         pa_log_warn("Your kernel driver is broken for element %s: it reports a volume range from %li to %li which makes no sense.",
 1682                     buf, e->min_volume, e->max_volume);
 1683         return false;
 1684     }
 1685     if (e->volume_use == PA_ALSA_VOLUME_CONSTANT && (e->min_volume > e->constant_volume || e->max_volume < e->constant_volume)) {
 1686         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 1687         pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
 1688                     e->constant_volume, buf, e->min_volume, e->max_volume);
 1689         return false;
 1690     }
 1691 
 1692 
 1693     if (e->db_fix && ((e->min_volume > e->db_fix->min_step) || (e->max_volume < e->db_fix->max_step))) {
 1694         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 1695         pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
 1696                     "real hardware range (%li-%li). Disabling the decibel fix.", buf,
 1697                     e->db_fix->min_step, e->db_fix->max_step, e->min_volume, e->max_volume);
 1698 
 1699         decibel_fix_free(e->db_fix);
 1700         e->db_fix = NULL;
 1701     }
 1702 
 1703     if (e->db_fix) {
 1704         e->has_dB = true;
 1705         e->min_volume = e->db_fix->min_step;
 1706         e->max_volume = e->db_fix->max_step;
 1707         min_dB = e->db_fix->db_values[0];
 1708         max_dB = e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step];
 1709     } else if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
 1710         e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0;
 1711     else
 1712         e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0;
 1713 
 1714     /* Check that the kernel driver returns consistent limits with
 1715      * both _get_*_dB_range() and _ask_*_vol_dB(). */
 1716     if (e->has_dB && !e->db_fix) {
 1717         long min_dB_checked = 0;
 1718         long max_dB_checked = 0;
 1719 
 1720         if (element_ask_vol_dB(me, e->direction, e->min_volume, &min_dB_checked) < 0) {
 1721             pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 1722             pa_log_warn("Failed to query the dB value for %s at volume level %li", buf, e->min_volume);
 1723             return false;
 1724         }
 1725 
 1726         if (element_ask_vol_dB(me, e->direction, e->max_volume, &max_dB_checked) < 0) {
 1727             pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 1728             pa_log_warn("Failed to query the dB value for %s at volume level %li", buf, e->max_volume);
 1729             return false;
 1730         }
 1731 
 1732         if (min_dB != min_dB_checked || max_dB != max_dB_checked) {
 1733             pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 1734             pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
 1735                         "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
 1736                         "%0.2f dB at level %li.", buf, min_dB / 100.0, max_dB / 100.0,
 1737                         min_dB_checked / 100.0, e->min_volume, max_dB_checked / 100.0, e->max_volume);
 1738             return false;
 1739         }
 1740     }
 1741 
 1742     if (e->has_dB) {
 1743         e->min_dB = ((double) min_dB) / 100.0;
 1744         e->max_dB = ((double) max_dB) / 100.0;
 1745 
 1746         if (min_dB >= max_dB) {
 1747             pa_assert(!e->db_fix);
 1748             pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.",
 1749                         e->min_dB, e->max_dB);
 1750             e->has_dB = false;
 1751         }
 1752     }
 1753 
 1754     if (e->volume_limit >= 0) {
 1755         if (e->volume_limit <= e->min_volume || e->volume_limit > e->max_volume) {
 1756             pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 1757             pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
 1758                         "%li-%li. The volume limit is ignored.",
 1759                         buf, e->path->name, e->volume_limit, e->min_volume + 1, e->max_volume);
 1760         } else {
 1761             e->max_volume = e->volume_limit;
 1762 
 1763             if (e->has_dB) {
 1764                 if (e->db_fix) {
 1765                     e->db_fix->max_step = e->max_volume;
 1766                     e->max_dB = ((double) e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step]) / 100.0;
 1767                 } else if (element_ask_vol_dB(me, e->direction, e->max_volume, &max_dB) < 0) {
 1768                     pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 1769                     pa_log_warn("Failed to get dB value of %s: %s", buf, pa_alsa_strerror(r));
 1770                     e->has_dB = false;
 1771                 } else
 1772                     e->max_dB = ((double) max_dB) / 100.0;
 1773             }
 1774         }
 1775     }
 1776 
 1777     if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
 1778         is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
 1779     else
 1780         is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
 1781 
 1782     if (is_mono) {
 1783         e->n_channels = 1;
 1784 
 1785         if ((e->override_map & (1 << (e->n_channels-1))) && e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] == 0) {
 1786             pa_log_warn("Override map for mono element %s is invalid, ignoring override map", e->path->name);
 1787             e->override_map &= ~(1 << (e->n_channels-1));
 1788         }
 1789         if (!(e->override_map & (1 << (e->n_channels-1)))) {
 1790             for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
 1791                 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
 1792                     continue;
 1793                 e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
 1794             }
 1795             e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
 1796         }
 1797         e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
 1798         return true;
 1799     }
 1800 
 1801     e->n_channels = 0;
 1802     for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
 1803         if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
 1804             continue;
 1805 
 1806         if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
 1807             e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
 1808         else
 1809             e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
 1810     }
 1811 
 1812     if (e->n_channels <= 0) {
 1813         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 1814         pa_log_warn("Volume element %s with no channels?", buf);
 1815         return false;
 1816     } else if (e->n_channels > POSITION_MASK_CHANNELS) {
 1817         /* FIXME: In some places code like this is used:
 1818          *
 1819          *     e->masks[alsa_channel_ids[p]][e->n_channels-1]
 1820          *
 1821          * The definition of e->masks is
 1822          *
 1823          *     pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][POSITION_MASK_CHANNELS];
 1824          *
 1825          * Since the array size is fixed at POSITION_MASK_CHANNELS, we obviously
 1826          * don't support elements with more than POSITION_MASK_CHANNELS
 1827          * channels... */
 1828         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 1829         pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", buf, e->n_channels);
 1830         return false;
 1831     }
 1832 
 1833 retry:
 1834     if (!(e->override_map & (1 << (e->n_channels-1)))) {
 1835         for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
 1836             bool has_channel;
 1837 
 1838             if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
 1839                 continue;
 1840 
 1841             if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
 1842                 has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
 1843             else
 1844                 has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
 1845 
 1846             e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
 1847         }
 1848     }
 1849 
 1850     e->merged_mask = 0;
 1851     for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
 1852         if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
 1853             continue;
 1854 
 1855         e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
 1856     }
 1857 
 1858     if (e->merged_mask == 0) {
 1859         if (!(e->override_map & (1 << (e->n_channels-1)))) {
 1860             pa_log_warn("Channel map for element %s is invalid", e->path->name);
 1861             return false;
 1862         }
 1863         pa_log_warn("Override map for element %s has empty result, ignoring override map", e->path->name);
 1864         e->override_map &= ~(1 << (e->n_channels-1));
 1865         goto retry;
 1866     }
 1867 
 1868     return true;
 1869 }
 1870 
 1871 static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
 1872     snd_mixer_selem_id_t *sid;
 1873     snd_mixer_elem_t *me;
 1874 
 1875     pa_assert(m);
 1876     pa_assert(e);
 1877     pa_assert(e->path);
 1878 
 1879     SELEM_INIT(sid, &e->alsa_id);
 1880 
 1881     if (!(me = snd_mixer_find_selem(m, sid))) {
 1882 
 1883         if (e->required != PA_ALSA_REQUIRED_IGNORE)
 1884             return -1;
 1885 
 1886         e->switch_use = PA_ALSA_SWITCH_IGNORE;
 1887         e->volume_use = PA_ALSA_VOLUME_IGNORE;
 1888         e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
 1889 
 1890         return 0;
 1891     }
 1892 
 1893     if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
 1894         if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
 1895 
 1896             if (!snd_mixer_selem_has_playback_switch(me)) {
 1897                 if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
 1898                     e->direction = PA_ALSA_DIRECTION_INPUT;
 1899                 else
 1900                     e->switch_use = PA_ALSA_SWITCH_IGNORE;
 1901             }
 1902 
 1903         } else {
 1904 
 1905             if (!snd_mixer_selem_has_capture_switch(me)) {
 1906                 if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
 1907                     e->direction = PA_ALSA_DIRECTION_OUTPUT;
 1908                 else
 1909                     e->switch_use = PA_ALSA_SWITCH_IGNORE;
 1910             }
 1911         }
 1912 
 1913         if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
 1914             e->direction_try_other = false;
 1915     }
 1916 
 1917     if (!element_probe_volume(e, me))
 1918         e->volume_use = PA_ALSA_VOLUME_IGNORE;
 1919 
 1920     if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
 1921         pa_alsa_option *o;
 1922 
 1923         PA_LLIST_FOREACH(o, e->options)
 1924             o->alsa_idx = pa_streq(o->alsa_name, "on") ? 1 : 0;
 1925     } else if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
 1926         int n;
 1927         pa_alsa_option *o;
 1928 
 1929         if ((n = snd_mixer_selem_get_enum_items(me)) < 0) {
 1930             pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n));
 1931             return -1;
 1932         }
 1933 
 1934         PA_LLIST_FOREACH(o, e->options) {
 1935             int i;
 1936 
 1937             for (i = 0; i < n; i++) {
 1938                 char buf[128];
 1939 
 1940                 if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
 1941                     continue;
 1942 
 1943                 if (!pa_streq(buf, o->alsa_name))
 1944                     continue;
 1945 
 1946                 o->alsa_idx = i;
 1947             }
 1948         }
 1949     }
 1950 
 1951     if (check_required(e, me) < 0)
 1952         return -1;
 1953 
 1954     return 0;
 1955 }
 1956 
 1957 static int jack_probe(pa_alsa_jack *j, pa_alsa_mapping *mapping, snd_mixer_t *m) {
 1958     bool has_control;
 1959 
 1960     pa_assert(j);
 1961     pa_assert(j->path);
 1962 
 1963     if (j->append_pcm_to_name) {
 1964         char *new_name;
 1965 
 1966         if (!mapping) {
 1967             /* This could also be an assertion, because this should never
 1968              * happen. At the time of writing, mapping can only be NULL when
 1969              * module-alsa-sink/source synthesizes a path, and those
 1970              * synthesized paths never have any jacks, so jack_probe() should
 1971              * never be called with a NULL mapping. */
 1972             pa_log("Jack %s: append_pcm_to_name is set, but mapping is NULL. Can't use this jack.", j->name);
 1973             return -1;
 1974         }
 1975 
 1976         new_name = pa_sprintf_malloc("%s,pcm=%i Jack", j->name, mapping->hw_device_index);
 1977         pa_xfree(j->alsa_id.name);
 1978         j->alsa_id.name = new_name;
 1979         j->append_pcm_to_name = false;
 1980     }
 1981 
 1982     has_control = pa_alsa_mixer_find_card(m, &j->alsa_id, 0) != NULL;
 1983     pa_alsa_jack_set_has_control(j, has_control);
 1984 
 1985     if (j->has_control) {
 1986         if (j->required_absent != PA_ALSA_REQUIRED_IGNORE)
 1987             return -1;
 1988         if (j->required_any != PA_ALSA_REQUIRED_IGNORE)
 1989             j->path->req_any_present = true;
 1990     } else {
 1991         if (j->required != PA_ALSA_REQUIRED_IGNORE)
 1992             return -1;
 1993     }
 1994 
 1995     return 0;
 1996 }
 1997 
 1998 pa_alsa_element * pa_alsa_element_get(pa_alsa_path *p, const char *section, bool prefixed) {
 1999     pa_alsa_element *e;
 2000     char *name;
 2001     int index;
 2002 
 2003     pa_assert(p);
 2004     pa_assert(section);
 2005 
 2006     if (prefixed) {
 2007         if (!pa_startswith(section, "Element "))
 2008             return NULL;
 2009 
 2010         section += 8;
 2011     }
 2012 
 2013     /* This is not an element section, but an enum section? */
 2014     if (strchr(section, ':'))
 2015         return NULL;
 2016 
 2017     name = alloca(strlen(section) + 1);
 2018     if (alsa_id_decode(section, name, &index))
 2019         return NULL;
 2020 
 2021     if (p->last_element && pa_streq(p->last_element->alsa_id.name, name) &&
 2022         p->last_element->alsa_id.index == index)
 2023         return p->last_element;
 2024 
 2025     PA_LLIST_FOREACH(e, p->elements)
 2026         if (pa_streq(e->alsa_id.name, name) && e->alsa_id.index == index)
 2027             goto finish;
 2028 
 2029     e = pa_xnew0(pa_alsa_element, 1);
 2030     e->path = p;
 2031     e->alsa_id.name = pa_xstrdup(name);
 2032     e->alsa_id.index = index;
 2033     e->direction = p->direction;
 2034     e->volume_limit = -1;
 2035 
 2036     PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
 2037 
 2038 finish:
 2039     p->last_element = e;
 2040     return e;
 2041 }
 2042 
 2043 static pa_alsa_jack* jack_get(pa_alsa_path *p, const char *section) {
 2044     pa_alsa_jack *j;
 2045     char *name;
 2046     int index;
 2047 
 2048     if (!pa_startswith(section, "Jack "))
 2049         return NULL;
 2050     section += 5;
 2051 
 2052     name = alloca(strlen(section) + 1);
 2053     if (alsa_id_decode(section, name, &index))
 2054         return NULL;
 2055 
 2056     if (p->last_jack && pa_streq(p->last_jack->name, name) &&
 2057         p->last_jack->alsa_id.index == index)
 2058         return p->last_jack;
 2059 
 2060     PA_LLIST_FOREACH(j, p->jacks)
 2061         if (pa_streq(j->name, name) && j->alsa_id.index == index)
 2062             goto finish;
 2063 
 2064     j = pa_alsa_jack_new(p, NULL, name, index);
 2065     PA_LLIST_INSERT_AFTER(pa_alsa_jack, p->jacks, p->last_jack, j);
 2066 
 2067 finish:
 2068     p->last_jack = j;
 2069     return j;
 2070 }
 2071 
 2072 static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
 2073     char *en, *name;
 2074     const char *on;
 2075     pa_alsa_option *o;
 2076     pa_alsa_element *e;
 2077     size_t len;
 2078     int index;
 2079 
 2080     if (!pa_startswith(section, "Option "))
 2081         return NULL;
 2082 
 2083     section += 7;
 2084 
 2085     /* This is not an enum section, but an element section? */
 2086     if (!(on = strchr(section, ':')))
 2087         return NULL;
 2088 
 2089     len = on - section;
 2090     en = alloca(len + 1);
 2091     strncpy(en, section, len);
 2092     en[len] = '\0';
 2093 
 2094     name = alloca(strlen(en) + 1);
 2095     if (alsa_id_decode(en, name, &index))
 2096         return NULL;
 2097 
 2098     on++;
 2099 
 2100     if (p->last_option &&
 2101         pa_streq(p->last_option->element->alsa_id.name, name) &&
 2102         p->last_option->element->alsa_id.index == index &&
 2103         pa_streq(p->last_option->alsa_name, on)) {
 2104         return p->last_option;
 2105     }
 2106 
 2107     pa_assert_se(e = pa_alsa_element_get(p, en, false));
 2108 
 2109     PA_LLIST_FOREACH(o, e->options)
 2110         if (pa_streq(o->alsa_name, on))
 2111             goto finish;
 2112 
 2113     o = pa_xnew0(pa_alsa_option, 1);
 2114     o->element = e;
 2115     o->alsa_name = pa_xstrdup(on);
 2116     o->alsa_idx = -1;
 2117 
 2118     if (p->last_option && p->last_option->element == e)
 2119         PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
 2120     else
 2121         PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
 2122 
 2123 finish:
 2124     p->last_option = o;
 2125     return o;
 2126 }
 2127 
 2128 static int element_parse_switch(pa_config_parser_state *state) {
 2129     pa_alsa_path *p;
 2130     pa_alsa_element *e;
 2131 
 2132     pa_assert(state);
 2133 
 2134     p = state->userdata;
 2135 
 2136     if (!(e = pa_alsa_element_get(p, state->section, true))) {
 2137         pa_log("[%s:%u] Switch makes no sense in '%s'", state->filename, state->lineno, state->section);
 2138         return -1;
 2139     }
 2140 
 2141     if (pa_streq(state->rvalue, "ignore"))
 2142         e->switch_use = PA_ALSA_SWITCH_IGNORE;
 2143     else if (pa_streq(state->rvalue, "mute"))
 2144         e->switch_use = PA_ALSA_SWITCH_MUTE;
 2145     else if (pa_streq(state->rvalue, "off"))
 2146         e->switch_use = PA_ALSA_SWITCH_OFF;
 2147     else if (pa_streq(state->rvalue, "on"))
 2148         e->switch_use = PA_ALSA_SWITCH_ON;
 2149     else if (pa_streq(state->rvalue, "select"))
 2150         e->switch_use = PA_ALSA_SWITCH_SELECT;
 2151     else {
 2152         pa_log("[%s:%u] Switch invalid of '%s'", state->filename, state->lineno, state->section);
 2153         return -1;
 2154     }
 2155 
 2156     return 0;
 2157 }
 2158 
 2159 static int element_parse_volume(pa_config_parser_state *state) {
 2160     pa_alsa_path *p;
 2161     pa_alsa_element *e;
 2162 
 2163     pa_assert(state);
 2164 
 2165     p = state->userdata;
 2166 
 2167     if (!(e = pa_alsa_element_get(p, state->section, true))) {
 2168         pa_log("[%s:%u] Volume makes no sense in '%s'", state->filename, state->lineno, state->section);
 2169         return -1;
 2170     }
 2171 
 2172     if (pa_streq(state->rvalue, "ignore"))
 2173         e->volume_use = PA_ALSA_VOLUME_IGNORE;
 2174     else if (pa_streq(state->rvalue, "merge"))
 2175         e->volume_use = PA_ALSA_VOLUME_MERGE;
 2176     else if (pa_streq(state->rvalue, "off"))
 2177         e->volume_use = PA_ALSA_VOLUME_OFF;
 2178     else if (pa_streq(state->rvalue, "zero"))
 2179         e->volume_use = PA_ALSA_VOLUME_ZERO;
 2180     else {
 2181         uint32_t constant;
 2182 
 2183         if (pa_atou(state->rvalue, &constant) >= 0) {
 2184             e->volume_use = PA_ALSA_VOLUME_CONSTANT;
 2185             e->constant_volume = constant;
 2186         } else {
 2187             pa_log("[%s:%u] Volume invalid of '%s'", state->filename, state->lineno, state->section);
 2188             return -1;
 2189         }
 2190     }
 2191 
 2192     return 0;
 2193 }
 2194 
 2195 static int element_parse_enumeration(pa_config_parser_state *state) {
 2196     pa_alsa_path *p;
 2197     pa_alsa_element *e;
 2198 
 2199     pa_assert(state);
 2200 
 2201     p = state->userdata;
 2202 
 2203     if (!(e = pa_alsa_element_get(p, state->section, true))) {
 2204         pa_log("[%s:%u] Enumeration makes no sense in '%s'", state->filename, state->lineno, state->section);
 2205         return -1;
 2206     }
 2207 
 2208     if (pa_streq(state->rvalue, "ignore"))
 2209         e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
 2210     else if (pa_streq(state->rvalue, "select"))
 2211         e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
 2212     else {
 2213         pa_log("[%s:%u] Enumeration invalid of '%s'", state->filename, state->lineno, state->section);
 2214         return -1;
 2215     }
 2216 
 2217     return 0;
 2218 }
 2219 
 2220 static int parse_type(pa_config_parser_state *state) {
 2221     struct device_port_types {
 2222         const char *name;
 2223         pa_device_port_type_t type;
 2224     } device_port_types[] = {
 2225         { "unknown",      PA_DEVICE_PORT_TYPE_UNKNOWN },
 2226         { "aux",          PA_DEVICE_PORT_TYPE_AUX },
 2227         { "speaker",      PA_DEVICE_PORT_TYPE_SPEAKER },
 2228         { "headphones",   PA_DEVICE_PORT_TYPE_HEADPHONES },
 2229         { "line",         PA_DEVICE_PORT_TYPE_LINE },
 2230         { "mic",          PA_DEVICE_PORT_TYPE_MIC },
 2231         { "headset",      PA_DEVICE_PORT_TYPE_HEADSET },
 2232         { "handset",      PA_DEVICE_PORT_TYPE_HANDSET },
 2233         { "earpiece",     PA_DEVICE_PORT_TYPE_EARPIECE },
 2234         { "spdif",        PA_DEVICE_PORT_TYPE_SPDIF },
 2235         { "hdmi",         PA_DEVICE_PORT_TYPE_HDMI },
 2236         { "tv",           PA_DEVICE_PORT_TYPE_TV },
 2237         { "radio",        PA_DEVICE_PORT_TYPE_RADIO },
 2238         { "video",        PA_DEVICE_PORT_TYPE_VIDEO },
 2239         { "usb",          PA_DEVICE_PORT_TYPE_USB },
 2240         { "bluetooth",    PA_DEVICE_PORT_TYPE_BLUETOOTH },
 2241         { "portable",     PA_DEVICE_PORT_TYPE_PORTABLE },
 2242         { "handsfree",    PA_DEVICE_PORT_TYPE_HANDSFREE },
 2243         { "car",          PA_DEVICE_PORT_TYPE_CAR },
 2244         { "hifi",         PA_DEVICE_PORT_TYPE_HIFI },
 2245         { "phone",        PA_DEVICE_PORT_TYPE_PHONE },
 2246         { "network",      PA_DEVICE_PORT_TYPE_NETWORK },
 2247         { "analog",       PA_DEVICE_PORT_TYPE_ANALOG },
 2248     };
 2249     pa_alsa_path *path;
 2250     unsigned int idx;
 2251 
 2252     path = state->userdata;
 2253 
 2254     for (idx = 0; idx < PA_ELEMENTSOF(device_port_types); idx++)
 2255         if (pa_streq(state->rvalue, device_port_types[idx].name)) {
 2256             path->device_port_type = device_port_types[idx].type;
 2257             return 0;
 2258         }
 2259 
 2260     pa_log("[%s:%u] Invalid value for option 'type': %s", state->filename, state->lineno, state->rvalue);
 2261     return -1;
 2262 }
 2263 
 2264 static int parse_eld_device(pa_config_parser_state *state) {
 2265     pa_alsa_path *path;
 2266     uint32_t eld_device;
 2267 
 2268     path = state->userdata;
 2269 
 2270     if (pa_atou(state->rvalue, &eld_device) >= 0) {
 2271         path->autodetect_eld_device = false;
 2272         path->eld_device = eld_device;
 2273         return 0;
 2274     }
 2275 
 2276     if (pa_streq(state->rvalue, "auto")) {
 2277         path->autodetect_eld_device = true;
 2278         path->eld_device = -1;
 2279         return 0;
 2280     }
 2281 
 2282     pa_log("[%s:%u] Invalid value for option 'eld-device': %s", state->filename, state->lineno, state->rvalue);
 2283     return -1;
 2284 }
 2285 
 2286 static int option_parse_priority(pa_config_parser_state *state) {
 2287     pa_alsa_path *p;
 2288     pa_alsa_option *o;
 2289     uint32_t prio;
 2290 
 2291     pa_assert(state);
 2292 
 2293     p = state->userdata;
 2294 
 2295     if (!(o = option_get(p, state->section))) {
 2296         pa_log("[%s:%u] Priority makes no sense in '%s'", state->filename, state->lineno, state->section);
 2297         return -1;
 2298     }
 2299 
 2300     if (pa_atou(state->rvalue, &prio) < 0) {
 2301         pa_log("[%s:%u] Priority invalid of '%s'", state->filename, state->lineno, state->section);
 2302         return -1;
 2303     }
 2304 
 2305     o->priority = prio;
 2306     return 0;
 2307 }
 2308 
 2309 static int option_parse_name(pa_config_parser_state *state) {
 2310     pa_alsa_path *p;
 2311     pa_alsa_option *o;
 2312 
 2313     pa_assert(state);
 2314 
 2315     p = state->userdata;
 2316 
 2317     if (!(o = option_get(p, state->section))) {
 2318         pa_log("[%s:%u] Name makes no sense in '%s'", state->filename, state->lineno, state->section);
 2319         return -1;
 2320     }
 2321 
 2322     pa_xfree(o->name);
 2323     o->name = pa_xstrdup(state->rvalue);
 2324 
 2325     return 0;
 2326 }
 2327 
 2328 static int element_parse_required(pa_config_parser_state *state) {
 2329     pa_alsa_path *p;
 2330     pa_alsa_element *e;
 2331     pa_alsa_option *o;
 2332     pa_alsa_jack *j;
 2333     pa_alsa_required_t req;
 2334 
 2335     pa_assert(state);
 2336 
 2337     p = state->userdata;
 2338 
 2339     e = pa_alsa_element_get(p, state->section, true);
 2340     o = option_get(p, state->section);
 2341     j = jack_get(p, state->section);
 2342     if (!e && !o && !j) {
 2343         pa_log("[%s:%u] Required makes no sense in '%s'", state->filename, state->lineno, state->section);
 2344         return -1;
 2345     }
 2346 
 2347     if (pa_streq(state->rvalue, "ignore"))
 2348         req = PA_ALSA_REQUIRED_IGNORE;
 2349     else if (pa_streq(state->rvalue, "switch") && e)
 2350         req = PA_ALSA_REQUIRED_SWITCH;
 2351     else if (pa_streq(state->rvalue, "volume") && e)
 2352         req = PA_ALSA_REQUIRED_VOLUME;
 2353     else if (pa_streq(state->rvalue, "enumeration"))
 2354         req = PA_ALSA_REQUIRED_ENUMERATION;
 2355     else if (pa_streq(state->rvalue, "any"))
 2356         req = PA_ALSA_REQUIRED_ANY;
 2357     else {
 2358         pa_log("[%s:%u] Required invalid of '%s'", state->filename, state->lineno, state->section);
 2359         return -1;
 2360     }
 2361 
 2362     if (pa_streq(state->lvalue, "required-absent")) {
 2363         if (e)
 2364             e->required_absent = req;
 2365         if (o)
 2366             o->required_absent = req;
 2367         if (j)
 2368             j->required_absent = req;
 2369     }
 2370     else if (pa_streq(state->lvalue, "required-any")) {
 2371         if (e) {
 2372             e->required_any = req;
 2373             e->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
 2374         }
 2375         if (o) {
 2376             o->required_any = req;
 2377             o->element->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
 2378         }
 2379         if (j) {
 2380             j->required_any = req;
 2381             j->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
 2382         }
 2383 
 2384     }
 2385     else {
 2386         if (e)
 2387             e->required = req;
 2388         if (o)
 2389             o->required = req;
 2390         if (j)
 2391             j->required = req;
 2392     }
 2393 
 2394     return 0;
 2395 }
 2396 
 2397 static int element_parse_direction(pa_config_parser_state *state) {
 2398     pa_alsa_path *p;
 2399     pa_alsa_element *e;
 2400 
 2401     pa_assert(state);
 2402 
 2403     p = state->userdata;
 2404 
 2405     if (!(e = pa_alsa_element_get(p, state->section, true))) {
 2406         pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section);
 2407         return -1;
 2408     }
 2409 
 2410     if (pa_streq(state->rvalue, "playback"))
 2411         e->direction = PA_ALSA_DIRECTION_OUTPUT;
 2412     else if (pa_streq(state->rvalue, "capture"))
 2413         e->direction = PA_ALSA_DIRECTION_INPUT;
 2414     else {
 2415         pa_log("[%s:%u] Direction invalid of '%s'", state->filename, state->lineno, state->section);
 2416         return -1;
 2417     }
 2418 
 2419     return 0;
 2420 }
 2421 
 2422 static int element_parse_direction_try_other(pa_config_parser_state *state) {
 2423     pa_alsa_path *p;
 2424     pa_alsa_element *e;
 2425     int yes;
 2426 
 2427     pa_assert(state);
 2428 
 2429     p = state->userdata;
 2430 
 2431     if (!(e = pa_alsa_element_get(p, state->section, true))) {
 2432         pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section);
 2433         return -1;
 2434     }
 2435 
 2436     if ((yes = pa_parse_boolean(state->rvalue)) < 0) {
 2437         pa_log("[%s:%u] Direction invalid of '%s'", state->filename, state->lineno, state->section);
 2438         return -1;
 2439     }
 2440 
 2441     e->direction_try_other = !!yes;
 2442     return 0;
 2443 }
 2444 
 2445 static int element_parse_volume_limit(pa_config_parser_state *state) {
 2446     pa_alsa_path *p;
 2447     pa_alsa_element *e;
 2448     long volume_limit;
 2449 
 2450     pa_assert(state);
 2451 
 2452     p = state->userdata;
 2453 
 2454     if (!(e = pa_alsa_element_get(p, state->section, true))) {
 2455         pa_log("[%s:%u] volume-limit makes no sense in '%s'", state->filename, state->lineno, state->section);
 2456         return -1;
 2457     }
 2458 
 2459     if (pa_atol(state->rvalue, &volume_limit) < 0 || volume_limit < 0) {
 2460         pa_log("[%s:%u] Invalid value for volume-limit", state->filename, state->lineno);
 2461         return -1;
 2462     }
 2463 
 2464     e->volume_limit = volume_limit;
 2465     return 0;
 2466 }
 2467 
 2468 static unsigned int parse_channel_position(const char *m)
 2469 {
 2470     pa_channel_position_t p;
 2471 
 2472     if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
 2473         return SND_MIXER_SCHN_UNKNOWN;
 2474 
 2475     return alsa_channel_ids[p];
 2476 }
 2477 
 2478 static pa_channel_position_mask_t parse_mask(const char *m) {
 2479     pa_channel_position_mask_t v;
 2480 
 2481     if (pa_streq(m, "all-left"))
 2482         v = PA_CHANNEL_POSITION_MASK_LEFT;
 2483     else if (pa_streq(m, "all-right"))
 2484         v = PA_CHANNEL_POSITION_MASK_RIGHT;
 2485     else if (pa_streq(m, "all-center"))
 2486         v = PA_CHANNEL_POSITION_MASK_CENTER;
 2487     else if (pa_streq(m, "all-front"))
 2488         v = PA_CHANNEL_POSITION_MASK_FRONT;
 2489     else if (pa_streq(m, "all-rear"))
 2490         v = PA_CHANNEL_POSITION_MASK_REAR;
 2491     else if (pa_streq(m, "all-side"))
 2492         v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER;
 2493     else if (pa_streq(m, "all-top"))
 2494         v = PA_CHANNEL_POSITION_MASK_TOP;
 2495     else if (pa_streq(m, "all-no-lfe"))
 2496         v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE);
 2497     else if (pa_streq(m, "all"))
 2498         v = PA_CHANNEL_POSITION_MASK_ALL;
 2499     else {
 2500         pa_channel_position_t p;
 2501 
 2502         if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
 2503             return 0;
 2504 
 2505         v = PA_CHANNEL_POSITION_MASK(p);
 2506     }
 2507 
 2508     return v;
 2509 }
 2510 
 2511 static int element_parse_override_map(pa_config_parser_state *state) {
 2512     pa_alsa_path *p;
 2513     pa_alsa_element *e;
 2514     const char *split_state = NULL;
 2515     char *s;
 2516     unsigned i = 0;
 2517     int channel_count = 0;
 2518     char *n;
 2519 
 2520     pa_assert(state);
 2521 
 2522     p = state->userdata;
 2523 
 2524     if (!(e = pa_alsa_element_get(p, state->section, true))) {
 2525         pa_log("[%s:%u] Override map makes no sense in '%s'", state->filename, state->lineno, state->section);
 2526         return -1;
 2527     }
 2528 
 2529     s = strstr(state->lvalue, ".");
 2530     if (s) {
 2531         pa_atoi(s + 1, &channel_count);
 2532         if (channel_count < 1 || channel_count > POSITION_MASK_CHANNELS) {
 2533             pa_log("[%s:%u] Override map index '%s' invalid in '%s'", state->filename, state->lineno, state->lvalue, state->section);
 2534             return 0;
 2535         }
 2536     } else {
 2537         pa_log("[%s:%u] Invalid override map syntax '%s' in '%s'", state->filename, state->lineno, state->lvalue, state->section);
 2538         return -1;
 2539     }
 2540 
 2541     while ((n = pa_split(state->rvalue, ",", &split_state))) {
 2542         pa_channel_position_mask_t m;
 2543         snd_mixer_selem_channel_id_t channel_position;
 2544 
 2545         if (i >= (unsigned)channel_count) {
 2546             pa_log("[%s:%u] Invalid override map size (>%d) in '%s'", state->filename, state->lineno, channel_count, state->section);
 2547             return -1;
 2548         }
 2549         channel_position = alsa_channel_positions[i];
 2550 
 2551         if (!*n)
 2552             m = 0;
 2553         else {
 2554             s = strstr(n, ":");
 2555             if (s) {
 2556                 *s = '\0';
 2557                 s++;
 2558                 channel_position = parse_channel_position(n);
 2559                 if (channel_position == SND_MIXER_SCHN_UNKNOWN) {
 2560                     pa_log("[%s:%u] Override map position '%s' invalid in '%s'", state->filename, state->lineno, n, state->section);
 2561                     pa_xfree(n);
 2562                     return -1;
 2563                 }
 2564             }
 2565             if ((m = parse_mask(s ? s : n)) == 0) {
 2566                 pa_log("[%s:%u] Override map '%s' invalid in '%s'", state->filename, state->lineno, s ? s : n, state->section);
 2567                 pa_xfree(n);
 2568                 return -1;
 2569             }
 2570         }
 2571 
 2572         if (e->masks[channel_position][channel_count-1]) {
 2573             pa_log("[%s:%u] Override map '%s' duplicate position '%s' in '%s'", state->filename, state->lineno, s ? s : n, snd_mixer_selem_channel_name(channel_position), state->section);
 2574             pa_xfree(n);
 2575             return -1;
 2576         }
 2577         e->override_map |= (1 << (channel_count - 1));
 2578         e->masks[channel_position][channel_count-1] = m;
 2579         pa_xfree(n);
 2580         i++;
 2581     }
 2582 
 2583     return 0;
 2584 }
 2585 
 2586 static int jack_parse_state(pa_config_parser_state *state) {
 2587     pa_alsa_path *p;
 2588     pa_alsa_jack *j;
 2589     pa_available_t pa;
 2590 
 2591     pa_assert(state);
 2592 
 2593     p = state->userdata;
 2594 
 2595     if (!(j = jack_get(p, state->section))) {
 2596         pa_log("[%s:%u] state makes no sense in '%s'", state->filename, state->lineno, state->section);
 2597         return -1;
 2598     }
 2599 
 2600     if (pa_streq(state->rvalue, "yes"))
 2601         pa = PA_AVAILABLE_YES;
 2602     else if (pa_streq(state->rvalue, "no"))
 2603         pa = PA_AVAILABLE_NO;
 2604     else if (pa_streq(state->rvalue, "unknown"))
 2605         pa = PA_AVAILABLE_UNKNOWN;
 2606     else {
 2607         pa_log("[%s:%u] state must be 'yes', 'no' or 'unknown' in '%s'", state->filename, state->lineno, state->section);
 2608         return -1;
 2609     }
 2610 
 2611     if (pa_streq(state->lvalue, "state.unplugged"))
 2612         j->state_unplugged = pa;
 2613     else {
 2614         j->state_plugged = pa;
 2615         pa_assert(pa_streq(state->lvalue, "state.plugged"));
 2616     }
 2617 
 2618     return 0;
 2619 }
 2620 
 2621 static int jack_parse_append_pcm_to_name(pa_config_parser_state *state) {
 2622     pa_alsa_path *path;
 2623     pa_alsa_jack *jack;
 2624     int b;
 2625 
 2626     pa_assert(state);
 2627 
 2628     path = state->userdata;
 2629     if (!(jack = jack_get(path, state->section))) {
 2630         pa_log("[%s:%u] Option 'append_pcm_to_name' not expected in section '%s'",
 2631                state->filename, state->lineno, state->section);
 2632         return -1;
 2633     }
 2634 
 2635     b = pa_parse_boolean(state->rvalue);
 2636     if (b < 0) {
 2637         pa_log("[%s:%u] Invalid value for 'append_pcm_to_name': %s", state->filename, state->lineno, state->rvalue);
 2638         return -1;
 2639     }
 2640 
 2641     jack->append_pcm_to_name = b;
 2642     return 0;
 2643 }
 2644 
 2645 static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
 2646     snd_mixer_selem_id_t *sid;
 2647     snd_mixer_elem_t *me;
 2648     char buf[64];
 2649     int r;
 2650 
 2651     pa_assert(e);
 2652     pa_assert(m);
 2653 
 2654     SELEM_INIT(sid, &e->alsa_id);
 2655     if (!(me = snd_mixer_find_selem(m, sid))) {
 2656         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 2657         pa_log_warn("Element %s seems to have disappeared.", buf);
 2658         return -1;
 2659     }
 2660 
 2661     if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
 2662 
 2663         if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
 2664             r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
 2665         else
 2666             r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
 2667 
 2668         if (r < 0) {
 2669             pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 2670             pa_log_warn("Failed to set switch of %s: %s", buf, pa_alsa_strerror(errno));
 2671         }
 2672 
 2673     } else {
 2674         pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
 2675 
 2676         if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0) {
 2677             pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 2678             pa_log_warn("Failed to set enumeration of %s: %s", buf, pa_alsa_strerror(errno));
 2679         }
 2680     }
 2681 
 2682     return r;
 2683 }
 2684 
 2685 static int setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
 2686     pa_alsa_option *o;
 2687     uint32_t idx;
 2688 
 2689     pa_assert(s);
 2690     pa_assert(m);
 2691 
 2692     PA_IDXSET_FOREACH(o, s->options, idx)
 2693         element_set_option(o->element, m, o->alsa_idx);
 2694 
 2695     return 0;
 2696 }
 2697 
 2698 static int option_verify(pa_alsa_option *o) {
 2699     static const struct description_map well_known_descriptions[] = {
 2700         { "input",                     N_("Input") },
 2701         { "input-docking",             N_("Docking Station Input") },
 2702         { "input-docking-microphone",  N_("Docking Station Microphone") },
 2703         { "input-docking-linein",      N_("Docking Station Line In") },
 2704         { "input-linein",              N_("Line In") },
 2705         { "input-microphone",          N_("Microphone") },
 2706         { "input-microphone-front",    N_("Front Microphone") },
 2707         { "input-microphone-rear",     N_("Rear Microphone") },
 2708         { "input-microphone-external", N_("External Microphone") },
 2709         { "input-microphone-internal", N_("Internal Microphone") },
 2710         { "input-radio",               N_("Radio") },
 2711         { "input-video",               N_("Video") },
 2712         { "input-agc-on",              N_("Automatic Gain Control") },
 2713         { "input-agc-off",             N_("No Automatic Gain Control") },
 2714         { "input-boost-on",            N_("Boost") },
 2715         { "input-boost-off",           N_("No Boost") },
 2716         { "output-amplifier-on",       N_("Amplifier") },
 2717         { "output-amplifier-off",      N_("No Amplifier") },
 2718         { "output-bass-boost-on",      N_("Bass Boost") },
 2719         { "output-bass-boost-off",     N_("No Bass Boost") },
 2720         { "output-speaker",            N_("Speaker") },
 2721         { "output-headphones",         N_("Headphones") }
 2722     };
 2723     char buf[64];
 2724 
 2725     pa_assert(o);
 2726 
 2727     if (!o->name) {
 2728         pa_log("No name set for option %s", o->alsa_name);
 2729         return -1;
 2730     }
 2731 
 2732     if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
 2733         o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
 2734         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &o->element->alsa_id);
 2735         pa_log("Element %s of option %s not set for select.", buf, o->name);
 2736         return -1;
 2737     }
 2738 
 2739     if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
 2740         !pa_streq(o->alsa_name, "on") &&
 2741         !pa_streq(o->alsa_name, "off")) {
 2742         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &o->element->alsa_id);
 2743         pa_log("Switch %s options need be named off or on ", buf);
 2744         return -1;
 2745     }
 2746 
 2747     if (!o->description)
 2748         o->description = pa_xstrdup(lookup_description(o->name,
 2749                                                        well_known_descriptions,
 2750                                                        PA_ELEMENTSOF(well_known_descriptions)));
 2751     if (!o->description)
 2752         o->description = pa_xstrdup(o->name);
 2753 
 2754     return 0;
 2755 }
 2756 
 2757 static int element_verify(pa_alsa_element *e) {
 2758     pa_alsa_option *o;
 2759     char buf[64];
 2760 
 2761     pa_assert(e);
 2762 
 2763 //    pa_log_debug("Element %s, path %s: r=%d, r-any=%d, r-abs=%d", e->alsa_name, e->path->name, e->required, e->required_any, e->required_absent);
 2764     if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
 2765         (e->required_any != PA_ALSA_REQUIRED_IGNORE && e->required_any == e->required_absent) ||
 2766         (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required_any != PA_ALSA_REQUIRED_IGNORE) ||
 2767         (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
 2768         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 2769         pa_log("Element %s cannot be required and absent at the same time.", buf);
 2770         return -1;
 2771     }
 2772 
 2773     if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
 2774         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 2775         pa_log("Element %s cannot set select for both switch and enumeration.", buf);
 2776         return -1;
 2777     }
 2778 
 2779     PA_LLIST_FOREACH(o, e->options)
 2780         if (option_verify(o) < 0)
 2781             return -1;
 2782 
 2783     return 0;
 2784 }
 2785 
 2786 static int path_verify(pa_alsa_path *p) {
 2787     static const struct description2_map well_known_descriptions[] = {
 2788         { "analog-input",                     N_("Analog Input"),                 PA_DEVICE_PORT_TYPE_ANALOG },
 2789         { "analog-input-microphone",          N_("Microphone"),                   PA_DEVICE_PORT_TYPE_MIC },
 2790         { "analog-input-microphone-front",    N_("Front Microphone"),             PA_DEVICE_PORT_TYPE_MIC },
 2791         { "analog-input-microphone-rear",     N_("Rear Microphone"),              PA_DEVICE_PORT_TYPE_MIC },
 2792         { "analog-input-microphone-dock",     N_("Dock Microphone"),              PA_DEVICE_PORT_TYPE_MIC },
 2793         { "analog-input-microphone-internal", N_("Internal Microphone"),          PA_DEVICE_PORT_TYPE_MIC },
 2794         { "analog-input-microphone-headset",  N_("Headset Microphone"),           PA_DEVICE_PORT_TYPE_HEADSET },
 2795         { "analog-input-linein",              N_("Line In"),                      PA_DEVICE_PORT_TYPE_LINE },
 2796         { "analog-input-radio",               N_("Radio"),                        PA_DEVICE_PORT_TYPE_RADIO },
 2797         { "analog-input-video",               N_("Video"),                        PA_DEVICE_PORT_TYPE_VIDEO },
 2798         { "analog-output",                    N_("Analog Output"),                PA_DEVICE_PORT_TYPE_ANALOG },
 2799         { "analog-output-headphones",         N_("Headphones"),                   PA_DEVICE_PORT_TYPE_HEADPHONES },
 2800         { "analog-output-headphones-2",       N_("Headphones 2"),                 PA_DEVICE_PORT_TYPE_HEADPHONES },
 2801         { "analog-output-headphones-mono",    N_("Headphones Mono Output"),       PA_DEVICE_PORT_TYPE_HEADPHONES },
 2802         { "analog-output-lineout",            N_("Line Out"),                     PA_DEVICE_PORT_TYPE_LINE },
 2803         { "analog-output-mono",               N_("Analog Mono Output"),           PA_DEVICE_PORT_TYPE_ANALOG },
 2804         { "analog-output-speaker",            N_("Speakers"),                     PA_DEVICE_PORT_TYPE_SPEAKER },
 2805         { "hdmi-output",                      N_("HDMI / DisplayPort"),           PA_DEVICE_PORT_TYPE_HDMI },
 2806         { "iec958-stereo-output",             N_("Digital Output (S/PDIF)"),      PA_DEVICE_PORT_TYPE_SPDIF },
 2807         { "iec958-stereo-input",              N_("Digital Input (S/PDIF)"),       PA_DEVICE_PORT_TYPE_SPDIF },
 2808         { "multichannel-input",               N_("Multichannel Input"),           PA_DEVICE_PORT_TYPE_LINE },
 2809         { "multichannel-output",              N_("Multichannel Output"),          PA_DEVICE_PORT_TYPE_LINE },
 2810         { "steelseries-arctis-output-game-common", N_("Game Output"),             PA_DEVICE_PORT_TYPE_HEADSET },
 2811         { "steelseries-arctis-output-chat-common", N_("Chat Output"),             PA_DEVICE_PORT_TYPE_HEADSET },
 2812     };
 2813 
 2814     pa_alsa_element *e;
 2815     const char *key = p->description_key ? p->description_key : p->name;
 2816     const struct description2_map *map = lookup_description2(key,
 2817                                                              well_known_descriptions,
 2818                                                              PA_ELEMENTSOF(well_known_descriptions));
 2819 
 2820     pa_assert(p);
 2821 
 2822     PA_LLIST_FOREACH(e, p->elements)
 2823         if (element_verify(e) < 0)
 2824             return -1;
 2825 
 2826     if (map) {
 2827         if (p->device_port_type == PA_DEVICE_PORT_TYPE_UNKNOWN)
 2828             p->device_port_type = map->type;
 2829         if (!p->description)
 2830             p->description = pa_xstrdup(map->description);
 2831     }
 2832 
 2833     if (!p->description) {
 2834         if (p->description_key)
 2835             pa_log_warn("Path %s: Unrecognized description key: %s", p->name, p->description_key);
 2836 
 2837         p->description = pa_xstrdup(p->name);
 2838     }
 2839 
 2840     return 0;
 2841 }
 2842 
 2843 static const char *get_default_paths_dir(void) {
 2844 #ifdef HAVE_RUNNING_FROM_BUILD_TREE
 2845     if (pa_run_from_build_tree())
 2846         return PA_SRCDIR "/modules/alsa/mixer/paths/";
 2847     else
 2848 #endif
 2849         return PA_ALSA_PATHS_DIR;
 2850 }
 2851 
 2852 pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa_direction_t direction) {
 2853     pa_alsa_path *p;
 2854     char *fn;
 2855     int r;
 2856     const char *n;
 2857     bool mute_during_activation = false;
 2858 
 2859     pa_config_item items[] = {
 2860         /* [General] */
 2861         { "priority",            pa_config_parse_unsigned,          NULL, "General" },
 2862         { "description-key",     pa_config_parse_string,            NULL, "General" },
 2863         { "description",         pa_config_parse_string,            NULL, "General" },
 2864         { "mute-during-activation", pa_config_parse_bool,           NULL, "General" },
 2865         { "type",                parse_type,                        NULL, "General" },
 2866         { "eld-device",          parse_eld_device,                  NULL, "General" },
 2867 
 2868         /* [Option ...] */
 2869         { "priority",            option_parse_priority,             NULL, NULL },
 2870         { "name",                option_parse_name,                 NULL, NULL },
 2871 
 2872         /* [Jack ...] */
 2873         { "state.plugged",       jack_parse_state,                  NULL, NULL },
 2874         { "state.unplugged",     jack_parse_state,                  NULL, NULL },
 2875         { "append-pcm-to-name",  jack_parse_append_pcm_to_name,     NULL, NULL },
 2876 
 2877         /* [Element ...] */
 2878         { "switch",              element_parse_switch,              NULL, NULL },
 2879         { "volume",              element_parse_volume,              NULL, NULL },
 2880         { "enumeration",         element_parse_enumeration,         NULL, NULL },
 2881         { "override-map.1",      element_parse_override_map,        NULL, NULL },
 2882         { "override-map.2",      element_parse_override_map,        NULL, NULL },
 2883         { "override-map.3",      element_parse_override_map,        NULL, NULL },
 2884         { "override-map.4",      element_parse_override_map,        NULL, NULL },
 2885         { "override-map.5",      element_parse_override_map,        NULL, NULL },
 2886         { "override-map.6",      element_parse_override_map,        NULL, NULL },
 2887         { "override-map.7",      element_parse_override_map,        NULL, NULL },
 2888         { "override-map.8",      element_parse_override_map,        NULL, NULL },
 2889 #if POSITION_MASK_CHANNELS > 8
 2890 #error "Add override-map.9+ definitions"
 2891 #endif
 2892         /* ... later on we might add override-map.3 and so on here ... */
 2893         { "required",            element_parse_required,            NULL, NULL },
 2894         { "required-any",        element_parse_required,            NULL, NULL },
 2895         { "required-absent",     element_parse_required,            NULL, NULL },
 2896         { "direction",           element_parse_direction,           NULL, NULL },
 2897         { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
 2898         { "volume-limit",        element_parse_volume_limit,        NULL, NULL },
 2899         { NULL, NULL, NULL, NULL }
 2900     };
 2901 
 2902     pa_assert(fname);
 2903 
 2904     p = pa_xnew0(pa_alsa_path, 1);
 2905     n = pa_path_get_filename(fname);
 2906     p->name = pa_xstrndup(n, strcspn(n, "."));
 2907     p->proplist = pa_proplist_new();
 2908     p->direction = direction;
 2909     p->eld_device = -1;
 2910 
 2911     items[0].data = &p->priority;
 2912     items[1].data = &p->description_key;
 2913     items[2].data = &p->description;
 2914     items[3].data = &mute_during_activation;
 2915 
 2916     if (!paths_dir)
 2917         paths_dir = get_default_paths_dir();
 2918 
 2919     fn = pa_maybe_prefix_path(fname, paths_dir);
 2920 
 2921     r = pa_config_parse(fn, NULL, items, p->proplist, false, p);
 2922     pa_xfree(fn);
 2923 
 2924     if (r < 0)
 2925         goto fail;
 2926 
 2927     p->mute_during_activation = mute_during_activation;
 2928 
 2929     if (path_verify(p) < 0)
 2930         goto fail;
 2931 
 2932     return p;
 2933 
 2934 fail:
 2935     pa_alsa_path_free(p);
 2936     return NULL;
 2937 }
 2938 
 2939 pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction) {
 2940     pa_alsa_path *p;
 2941     pa_alsa_element *e;
 2942     char *name;
 2943     int index;
 2944 
 2945     pa_assert(element);
 2946 
 2947     name = alloca(strlen(element) + 1);
 2948     if (alsa_id_decode(element, name, &index))
 2949         return NULL;
 2950 
 2951     p = pa_xnew0(pa_alsa_path, 1);
 2952     p->name = pa_xstrdup(element);
 2953     p->direction = direction;
 2954     p->proplist = pa_proplist_new();
 2955 
 2956     e = pa_xnew0(pa_alsa_element, 1);
 2957     e->path = p;
 2958     e->alsa_id.name = pa_xstrdup(name);
 2959     e->alsa_id.index = index;
 2960     e->direction = direction;
 2961     e->volume_limit = -1;
 2962 
 2963     e->switch_use = PA_ALSA_SWITCH_MUTE;
 2964     e->volume_use = PA_ALSA_VOLUME_MERGE;
 2965 
 2966     PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
 2967     p->last_element = e;
 2968     return p;
 2969 }
 2970 
 2971 static bool element_drop_unsupported(pa_alsa_element *e) {
 2972     pa_alsa_option *o, *n;
 2973 
 2974     pa_assert(e);
 2975 
 2976     for (o = e->options; o; o = n) {
 2977         n = o->next;
 2978 
 2979         if (o->alsa_idx < 0) {
 2980             PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
 2981             option_free(o);
 2982         }
 2983     }
 2984 
 2985     return
 2986         e->switch_use != PA_ALSA_SWITCH_IGNORE ||
 2987         e->volume_use != PA_ALSA_VOLUME_IGNORE ||
 2988         e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
 2989 }
 2990 
 2991 static void path_drop_unsupported(pa_alsa_path *p) {
 2992     pa_alsa_element *e, *n;
 2993 
 2994     pa_assert(p);
 2995 
 2996     for (e = p->elements; e; e = n) {
 2997         n = e->next;
 2998 
 2999         if (!element_drop_unsupported(e)) {
 3000             PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
 3001             element_free(e);
 3002         }
 3003     }
 3004 }
 3005 
 3006 static void path_make_options_unique(pa_alsa_path *p) {
 3007     pa_alsa_element *e;
 3008     pa_alsa_option *o, *u;
 3009 
 3010     PA_LLIST_FOREACH(e, p->elements) {
 3011         PA_LLIST_FOREACH(o, e->options) {
 3012             unsigned i;
 3013             char *m;
 3014 
 3015             for (u = o->next; u; u = u->next)
 3016                 if (pa_streq(u->name, o->name))
 3017                     break;
 3018 
 3019             if (!u)
 3020                 continue;
 3021 
 3022             m = pa_xstrdup(o->name);
 3023 
 3024             /* OK, this name is not unique, hence let's rename */
 3025             for (i = 1, u = o; u; u = u->next) {
 3026                 char *nn, *nd;
 3027 
 3028                 if (!pa_streq(u->name, m))
 3029                     continue;
 3030 
 3031                 nn = pa_sprintf_malloc("%s-%u", m, i);
 3032                 pa_xfree(u->name);
 3033                 u->name = nn;
 3034 
 3035                 nd = pa_sprintf_malloc("%s %u", u->description, i);
 3036                 pa_xfree(u->description);
 3037                 u->description = nd;
 3038 
 3039                 i++;
 3040             }
 3041 
 3042             pa_xfree(m);
 3043         }
 3044     }
 3045 }
 3046 
 3047 static bool element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
 3048     pa_alsa_option *o;
 3049 
 3050     for (; e; e = e->next)
 3051         if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
 3052             e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
 3053             break;
 3054 
 3055     if (!e)
 3056         return false;
 3057 
 3058     for (o = e->options; o; o = o->next) {
 3059         pa_alsa_setting *s;
 3060 
 3061         if (template) {
 3062             s = pa_xnewdup(pa_alsa_setting, template, 1);
 3063             s->options = pa_idxset_copy(template->options, NULL);
 3064             s->name = pa_sprintf_malloc("%s+%s", template->name, o->name);
 3065             s->description =
 3066                 (template->description[0] && o->description[0])
 3067                 ? pa_sprintf_malloc("%s / %s", template->description, o->description)
 3068                 : (template->description[0]
 3069                    ? pa_xstrdup(template->description)
 3070                    : pa_xstrdup(o->description));
 3071 
 3072             s->priority = PA_MAX(template->priority, o->priority);
 3073         } else {
 3074             s = pa_xnew0(pa_alsa_setting, 1);
 3075             s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 3076             s->name = pa_xstrdup(o->name);
 3077             s->description = pa_xstrdup(o->description);
 3078             s->priority = o->priority;
 3079         }
 3080 
 3081         pa_idxset_put(s->options, o, NULL);
 3082 
 3083         if (element_create_settings(e->next, s))
 3084             /* This is not a leaf, so let's get rid of it */
 3085             setting_free(s);
 3086         else {
 3087             /* This is a leaf, so let's add it */
 3088             PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
 3089 
 3090             e->path->last_setting = s;
 3091         }
 3092     }
 3093 
 3094     return true;
 3095 }
 3096 
 3097 static void path_create_settings(pa_alsa_path *p) {
 3098     pa_assert(p);
 3099 
 3100     element_create_settings(p->elements, NULL);
 3101 }
 3102 
 3103 int pa_alsa_path_probe(pa_alsa_path *p, pa_alsa_mapping *mapping, snd_mixer_t *m, bool ignore_dB) {
 3104     pa_alsa_element *e;
 3105     pa_alsa_jack *j;
 3106     double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
 3107     pa_channel_position_t t;
 3108     pa_channel_position_mask_t path_volume_channels = 0;
 3109     bool min_dB_set, max_dB_set;
 3110     char buf[64];
 3111 
 3112     pa_assert(p);
 3113     pa_assert(m);
 3114 
 3115     if (p->probed)
 3116         return p->supported ? 0 : -1;
 3117     p->probed = true;
 3118 
 3119     pa_zero(min_dB);
 3120     pa_zero(max_dB);
 3121 
 3122     pa_log_debug("Probing path '%s'", p->name);
 3123 
 3124     PA_LLIST_FOREACH(j, p->jacks) {
 3125         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &j->alsa_id);
 3126         if (jack_probe(j, mapping, m) < 0) {
 3127             p->supported = false;
 3128             pa_log_debug("Probe of jack %s failed.", buf);
 3129             return -1;
 3130         }
 3131         pa_log_debug("Probe of jack %s succeeded (%s)", buf, j->has_control ? "found!" : "not found");
 3132     }
 3133 
 3134     PA_LLIST_FOREACH(e, p->elements) {
 3135         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 3136         if (element_probe(e, m) < 0) {
 3137             p->supported = false;
 3138             pa_log_debug("Probe of element %s failed.", buf);
 3139             return -1;
 3140         }
 3141         pa_log_debug("Probe of element %s succeeded (volume=%d, switch=%d, enumeration=%d, has_dB=%d).", buf, e->volume_use, e->switch_use, e->enumeration_use, e->has_dB);
 3142 
 3143         if (ignore_dB)
 3144             e->has_dB = false;
 3145 
 3146         if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
 3147 
 3148             if (!p->has_volume) {
 3149                 p->min_volume = e->min_volume;
 3150                 p->max_volume = e->max_volume;
 3151             }
 3152 
 3153             if (e->has_dB) {
 3154                 if (!p->has_volume) {
 3155                     for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
 3156                         if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
 3157                             min_dB[t] = e->min_dB;
 3158                             max_dB[t] = e->max_dB;
 3159                             path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
 3160                         }
 3161 
 3162                     p->has_dB = true;
 3163                 } else {
 3164 
 3165                     if (p->has_dB) {
 3166                         for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
 3167                             if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
 3168                                 min_dB[t] += e->min_dB;
 3169                                 max_dB[t] += e->max_dB;
 3170                                 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
 3171                             }
 3172                     } else {
 3173                         /* Hmm, there's another element before us
 3174                          * which cannot do dB volumes, so we we need
 3175                          * to 'neutralize' this slider */
 3176                         e->volume_use = PA_ALSA_VOLUME_ZERO;
 3177                         pa_log_info("Zeroing volume of %s on path '%s'", buf, p->name);
 3178                     }
 3179                 }
 3180             } else if (p->has_volume) {
 3181                 /* We can't use this volume, so let's ignore it */
 3182                 e->volume_use = PA_ALSA_VOLUME_IGNORE;
 3183                 pa_log_info("Ignoring volume of %s on path '%s' (missing dB info)", buf, p->name);
 3184             }
 3185             p->has_volume = true;
 3186         }
 3187 
 3188         if (e->switch_use == PA_ALSA_SWITCH_MUTE)
 3189             p->has_mute = true;
 3190     }
 3191 
 3192     if (p->has_req_any && !p->req_any_present) {
 3193         p->supported = false;
 3194         pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name);
 3195         return -1;
 3196     }
 3197 
 3198     path_drop_unsupported(p);
 3199     path_make_options_unique(p);
 3200     path_create_settings(p);
 3201 
 3202     p->supported = true;
 3203 
 3204     p->min_dB = INFINITY;
 3205     min_dB_set = false;
 3206     p->max_dB = -INFINITY;
 3207     max_dB_set = false;
 3208 
 3209     for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
 3210         if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) {
 3211             if (p->min_dB > min_dB[t]) {
 3212                 p->min_dB = min_dB[t];
 3213                 min_dB_set = true;
 3214             }
 3215 
 3216             if (p->max_dB < max_dB[t]) {
 3217                 p->max_dB = max_dB[t];
 3218                 max_dB_set = true;
 3219             }
 3220         }
 3221     }
 3222 
 3223     /* this is probably a wrong prediction, but it should be safe */
 3224     if (!min_dB_set)
 3225         p->min_dB = -INFINITY;
 3226     if (!max_dB_set)
 3227         p->max_dB = 0;
 3228 
 3229     return 0;
 3230 }
 3231 
 3232 void pa_alsa_setting_dump(pa_alsa_setting *s) {
 3233     pa_assert(s);
 3234 
 3235     pa_log_debug("Setting %s (%s) priority=%u",
 3236                  s->name,
 3237                  pa_strnull(s->description),
 3238                  s->priority);
 3239 }
 3240 
 3241 void pa_alsa_jack_dump(pa_alsa_jack *j) {
 3242     pa_assert(j);
 3243 
 3244     pa_log_debug("Jack %s, alsa_name='%s', index='%d', detection %s", j->name, j->alsa_id.name, j->alsa_id.index, j->has_control ? "possible" : "unavailable");
 3245 }
 3246 
 3247 void pa_alsa_option_dump(pa_alsa_option *o) {
 3248     pa_assert(o);
 3249 
 3250     pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
 3251                  o->alsa_name,
 3252                  pa_strnull(o->name),
 3253                  pa_strnull(o->description),
 3254                  o->alsa_idx,
 3255                  o->priority);
 3256 }
 3257 
 3258 void pa_alsa_element_dump(pa_alsa_element *e) {
 3259     char buf[64];
 3260 
 3261     pa_alsa_option *o;
 3262     pa_assert(e);
 3263 
 3264     pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 3265     pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, volume_limit=%li, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%02x",
 3266                  buf,
 3267                  e->direction,
 3268                  e->switch_use,
 3269                  e->volume_use,
 3270                  e->volume_limit,
 3271                  e->enumeration_use,
 3272                  e->required,
 3273                  e->required_any,
 3274                  e->required_absent,
 3275                  (long long unsigned) e->merged_mask,
 3276                  e->n_channels,
 3277                  e->override_map);
 3278 
 3279     PA_LLIST_FOREACH(o, e->options)
 3280         pa_alsa_option_dump(o);
 3281 }
 3282 
 3283 void pa_alsa_path_dump(pa_alsa_path *p) {
 3284     pa_alsa_element *e;
 3285     pa_alsa_jack *j;
 3286     pa_alsa_setting *s;
 3287     pa_assert(p);
 3288 
 3289     pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
 3290                  "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
 3291                  p->name,
 3292                  pa_strnull(p->description),
 3293                  p->direction,
 3294                  p->priority,
 3295                  pa_yes_no(p->probed),
 3296                  pa_yes_no(p->supported),
 3297                  pa_yes_no(p->has_mute),
 3298                  pa_yes_no(p->has_volume),
 3299                  pa_yes_no(p->has_dB),
 3300                  p->min_volume, p->max_volume,
 3301                  p->min_dB, p->max_dB);
 3302 
 3303     PA_LLIST_FOREACH(e, p->elements)
 3304         pa_alsa_element_dump(e);
 3305 
 3306     PA_LLIST_FOREACH(j, p->jacks)
 3307         pa_alsa_jack_dump(j);
 3308 
 3309     PA_LLIST_FOREACH(s, p->settings)
 3310         pa_alsa_setting_dump(s);
 3311 }
 3312 
 3313 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
 3314     snd_mixer_selem_id_t *sid;
 3315     snd_mixer_elem_t *me;
 3316     char buf[64];
 3317 
 3318     pa_assert(e);
 3319     pa_assert(m);
 3320     pa_assert(cb);
 3321 
 3322     SELEM_INIT(sid, &e->alsa_id);
 3323     if (!(me = snd_mixer_find_selem(m, sid))) {
 3324         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
 3325         pa_log_warn("Element %s seems to have disappeared.", buf);
 3326         return;
 3327     }
 3328 
 3329     snd_mixer_elem_set_callback(me, cb);
 3330     snd_mixer_elem_set_callback_private(me, userdata);
 3331 }
 3332 
 3333 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
 3334     pa_alsa_element *e;
 3335 
 3336     pa_assert(p);
 3337     pa_assert(m);
 3338     pa_assert(cb);
 3339 
 3340     PA_LLIST_FOREACH(e, p->elements)
 3341         element_set_callback(e, m, cb, userdata);
 3342 }
 3343 
 3344 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
 3345     pa_alsa_path *p;
 3346     void *state;
 3347 
 3348     pa_assert(ps);
 3349     pa_assert(m);
 3350     pa_assert(cb);
 3351 
 3352     PA_HASHMAP_FOREACH(p, ps->paths, state)
 3353         pa_alsa_path_set_callback(p, m, cb, userdata);
 3354 }
 3355 
 3356 static pa_alsa_path *profile_set_get_path(pa_alsa_profile_set *ps, const char *path_name) {
 3357     pa_alsa_path *path;
 3358 
 3359     pa_assert(ps);
 3360     pa_assert(path_name);
 3361 
 3362     if ((path = pa_hashmap_get(ps->output_paths, path_name)))
 3363         return path;
 3364 
 3365     return pa_hashmap_get(ps->input_paths, path_name);
 3366 }
 3367 
 3368 static void profile_set_add_path(pa_alsa_profile_set *ps, pa_alsa_path *path) {
 3369     pa_assert(ps);
 3370     pa_assert(path);
 3371 
 3372     switch (path->direction) {
 3373         case PA_ALSA_DIRECTION_OUTPUT:
 3374             pa_assert_se(pa_hashmap_put(ps->output_paths, path->name, path) >= 0);
 3375             break;
 3376 
 3377         case PA_ALSA_DIRECTION_INPUT:
 3378             pa_assert_se(pa_hashmap_put(ps->input_paths, path->name, path) >= 0);
 3379             break;
 3380 
 3381         default:
 3382             pa_assert_not_reached();
 3383     }
 3384 }
 3385 
 3386 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction, const char *paths_dir) {
 3387     pa_alsa_path_set *ps;
 3388     char **pn = NULL, **en = NULL, **ie;
 3389     pa_alsa_decibel_fix *db_fix;
 3390     void *state, *state2;
 3391     char name[64];
 3392     int index;
 3393 
 3394     pa_assert(m);
 3395     pa_assert(m->profile_set);
 3396     pa_assert(m->profile_set->decibel_fixes);
 3397     pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
 3398 
 3399     if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
 3400         return NULL;
 3401 
 3402     ps = pa_xnew0(pa_alsa_path_set, 1);
 3403     ps->direction = direction;
 3404     ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 3405 
 3406     if (direction == PA_ALSA_DIRECTION_OUTPUT)
 3407         pn = m->output_path_names;
 3408     else
 3409         pn = m->input_path_names;
 3410 
 3411     if (pn) {
 3412         char **in;
 3413 
 3414         for (in = pn; *in; in++) {
 3415             pa_alsa_path *p = NULL;
 3416             bool duplicate = false;
 3417             char **kn;
 3418 
 3419             for (kn = pn; kn < in; kn++)
 3420                 if (pa_streq(*kn, *in)) {
 3421                     duplicate = true;
 3422                     break;
 3423                 }
 3424 
 3425             if (duplicate)
 3426                 continue;
 3427 
 3428             p = profile_set_get_path(m->profile_set, *in);
 3429 
 3430             if (p && p->direction != direction) {
 3431                 pa_log("Configuration error: Path %s is used both as an input and as an output path.", p->name);
 3432                 goto fail;
 3433             }
 3434 
 3435             if (!p) {
 3436                 char *fn = pa_sprintf_malloc("%s.conf", *in);
 3437                 p = pa_alsa_path_new(paths_dir, fn, direction);
 3438                 pa_xfree(fn);
 3439                 if (p)
 3440                     profile_set_add_path(m->profile_set, p);
 3441             }
 3442 
 3443             if (p)
 3444                 pa_hashmap_put(ps->paths, p, p);
 3445 
 3446         }
 3447 
 3448         goto finish;
 3449     }
 3450 
 3451     if (direction == PA_ALSA_DIRECTION_OUTPUT)
 3452         en = m->output_element;
 3453     else
 3454         en = m->input_element;
 3455 
 3456     if (!en)
 3457         goto fail;
 3458 
 3459     for (ie = en; *ie; ie++) {
 3460         char **je;
 3461         pa_alsa_path *p;
 3462 
 3463         p = pa_alsa_path_synthesize(*ie, direction);
 3464 
 3465         /* Mark all other passed elements for require-absent */
 3466         for (je = en; *je; je++) {
 3467             pa_alsa_element *e;
 3468 
 3469             if (je == ie)
 3470                 continue;
 3471 
 3472             if (strlen(*je) + 1 >= sizeof(name)) {
 3473                 pa_log("Element identifier %s is too long!", *je);
 3474                 continue;
 3475             }
 3476 
 3477             if (alsa_id_decode(*je, name, &index))
 3478                 continue;
 3479 
 3480             e = pa_xnew0(pa_alsa_element, 1);
 3481             e->path = p;
 3482             e->alsa_id.name = pa_xstrdup(name);
 3483             e->alsa_id.index = index;
 3484             e->direction = direction;
 3485             e->required_absent = PA_ALSA_REQUIRED_ANY;
 3486             e->volume_limit = -1;
 3487 
 3488             PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
 3489             p->last_element = e;
 3490         }
 3491 
 3492         pa_hashmap_put(ps->paths, *ie, p);
 3493     }
 3494 
 3495 finish:
 3496     /* Assign decibel fixes to elements. */
 3497     PA_HASHMAP_FOREACH(db_fix, m->profile_set->decibel_fixes, state) {
 3498         pa_alsa_path *p;
 3499 
 3500         PA_HASHMAP_FOREACH(p, ps->paths, state2) {
 3501             pa_alsa_element *e;
 3502 
 3503             PA_LLIST_FOREACH(e, p->elements) {
 3504                 if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_id.name) &&
 3505                     db_fix->index == e->alsa_id.index) {
 3506                     /* The profile set that contains the dB fix may be freed
 3507                      * before the element, so we have to copy the dB fix
 3508                      * object. */
 3509                     e->db_fix = pa_xnewdup(pa_alsa_decibel_fix, db_fix, 1);
 3510                     e->db_fix->profile_set = NULL;
 3511                     e->db_fix->name = pa_xstrdup(db_fix->name);
 3512                     e->db_fix->db_values = pa_xmemdup(db_fix->db_values, (db_fix->max_step - db_fix->min_step + 1) * sizeof(long));
 3513                 }
 3514             }
 3515         }
 3516     }
 3517 
 3518     return ps;
 3519 
 3520 fail:
 3521     if (ps)
 3522         pa_alsa_path_set_free(ps);
 3523 
 3524     return NULL;
 3525 }
 3526 
 3527 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
 3528     pa_alsa_path *p;
 3529     void *state;
 3530     pa_assert(ps);
 3531 
 3532     pa_log_debug("Path Set %p, direction=%i",
 3533                  (void*) ps,
 3534                  ps->direction);
 3535 
 3536     PA_HASHMAP_FOREACH(p, ps->paths, state)
 3537         pa_alsa_path_dump(p);
 3538 }
 3539 
 3540 static bool options_have_option(pa_alsa_option *options, const char *alsa_name) {
 3541     pa_alsa_option *o;
 3542 
 3543     pa_assert(options);
 3544     pa_assert(alsa_name);
 3545 
 3546     PA_LLIST_FOREACH(o, options) {
 3547         if (pa_streq(o->alsa_name, alsa_name))
 3548             return true;
 3549     }
 3550     return false;
 3551 }
 3552 
 3553 static bool enumeration_is_subset(pa_alsa_option *a_options, pa_alsa_option *b_options) {
 3554     pa_alsa_option *oa, *ob;
 3555 
 3556     if (!a_options) return true;
 3557     if (!b_options) return false;
 3558 
 3559     /* If there is an option A offers that B does not, then A is not a subset of B. */
 3560     PA_LLIST_FOREACH(oa, a_options) {
 3561         bool found = false;
 3562         PA_LLIST_FOREACH(ob, b_options) {
 3563             if (pa_streq(oa->alsa_name, ob->alsa_name)) {
 3564                 found = true;
 3565                 break;
 3566             }
 3567         }
 3568         if (!found)
 3569             return false;
 3570     }
 3571     return true;
 3572 }
 3573 
 3574 /**
 3575  *  Compares two elements to see if a is a subset of b
 3576  */
 3577 static bool element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_t *m) {
 3578     char buf[64];
 3579 
 3580     pa_assert(a);
 3581     pa_assert(b);
 3582     pa_assert(m);
 3583 
 3584     /* General rules:
 3585      * Every state is a subset of itself (with caveats for volume_limits and options)
 3586      * IGNORE is a subset of every other state */
 3587 
 3588     /* Check the volume_use */
 3589     if (a->volume_use != PA_ALSA_VOLUME_IGNORE) {
 3590 
 3591         /* "Constant" is subset of "Constant" only when their constant values are equal */
 3592         if (a->volume_use == PA_ALSA_VOLUME_CONSTANT && b->volume_use == PA_ALSA_VOLUME_CONSTANT && a->constant_volume != b->constant_volume)
 3593             return false;
 3594 
 3595         /* Different volume uses when b is not "Merge" means we are definitely not a subset */
 3596         if (a->volume_use != b->volume_use && b->volume_use != PA_ALSA_VOLUME_MERGE)
 3597             return false;
 3598 
 3599         /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
 3600          * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
 3601          * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
 3602         if (b->volume_use == PA_ALSA_VOLUME_MERGE && b->volume_limit >= 0) {
 3603             long a_limit;
 3604 
 3605             if (a->volume_use == PA_ALSA_VOLUME_CONSTANT)
 3606                 a_limit = a->constant_volume;
 3607             else if (a->volume_use == PA_ALSA_VOLUME_ZERO) {
 3608                 long dB = 0;
 3609 
 3610                 if (a->db_fix) {
 3611                     int rounding = (a->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1);
 3612                     a_limit = decibel_fix_get_step(a->db_fix, &dB, rounding);
 3613                 } else {
 3614                     snd_mixer_selem_id_t *sid;
 3615                     snd_mixer_elem_t *me;
 3616 
 3617                     SELEM_INIT(sid, &a->alsa_id);
 3618                     if (!(me = snd_mixer_find_selem(m, sid))) {
 3619                         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &a->alsa_id);
 3620                         pa_log_warn("Element %s seems to have disappeared.", buf);
 3621                         return false;
 3622                     }
 3623 
 3624                     if (a->direction == PA_ALSA_DIRECTION_OUTPUT) {
 3625                         if (snd_mixer_selem_ask_playback_dB_vol(me, dB, +1, &a_limit) < 0)
 3626                             return false;
 3627                     } else {
 3628                         if (snd_mixer_selem_ask_capture_dB_vol(me, dB, -1, &a_limit) < 0)
 3629                             return false;
 3630                     }
 3631                 }
 3632             } else if (a->volume_use == PA_ALSA_VOLUME_OFF)
 3633                 a_limit = a->min_volume;
 3634             else if (a->volume_use == PA_ALSA_VOLUME_MERGE)
 3635                 a_limit = a->volume_limit;
 3636             else
 3637                 pa_assert_not_reached();
 3638 
 3639             if (a_limit > b->volume_limit)
 3640                 return false;
 3641         }
 3642 
 3643         if (a->volume_use == PA_ALSA_VOLUME_MERGE) {
 3644             int s;
 3645             /* If override-maps are different, they're not subsets */
 3646             if (a->n_channels != b->n_channels)
 3647                 return false;
 3648             for (s = 0; s <= SND_MIXER_SCHN_LAST; s++)
 3649                 if (a->masks[s][a->n_channels-1] != b->masks[s][b->n_channels-1]) {
 3650                     pa_alsa_mixer_id_to_string(buf, sizeof(buf), &a->alsa_id);
 3651                     pa_log_debug("Element %s is not a subset - mask a: 0x%" PRIx64 ", mask b: 0x%" PRIx64 ", at channel %d",
 3652                                  buf, a->masks[s][a->n_channels-1], b->masks[s][b->n_channels-1], s);
 3653                     return false;
 3654                }
 3655         }
 3656     }
 3657 
 3658     if (a->switch_use != PA_ALSA_SWITCH_IGNORE) {
 3659         /* "On" is a subset of "Mute".
 3660          * "Off" is a subset of "Mute".
 3661          * "On" is a subset of "Select", if there is an "Option:On" in B.
 3662          * "Off" is a subset of "Select", if there is an "Option:Off" in B.
 3663          * "Select" is a subset of "Select", if they have the same options (is this always true?). */
 3664 
 3665         if (a->switch_use != b->switch_use) {
 3666 
 3667             if (a->switch_use == PA_ALSA_SWITCH_SELECT || a->switch_use == PA_ALSA_SWITCH_MUTE
 3668                 || b->switch_use == PA_ALSA_SWITCH_OFF || b->switch_use == PA_ALSA_SWITCH_ON)
 3669                 return false;
 3670 
 3671             if (b->switch_use == PA_ALSA_SWITCH_SELECT) {
 3672                 if (a->switch_use == PA_ALSA_SWITCH_ON) {
 3673                     if (!options_have_option(b->options, "on"))
 3674                         return false;
 3675                 } else if (a->switch_use == PA_ALSA_SWITCH_OFF) {
 3676                     if (!options_have_option(b->options, "off"))
 3677                         return false;
 3678                 }
 3679             }
 3680         } else if (a->switch_use == PA_ALSA_SWITCH_SELECT) {
 3681             if (!enumeration_is_subset(a->options, b->options))
 3682                 return false;
 3683         }
 3684     }
 3685 
 3686     if (a->enumeration_use != PA_ALSA_ENUMERATION_IGNORE) {
 3687         if (b->enumeration_use == PA_ALSA_ENUMERATION_IGNORE)
 3688             return false;
 3689         if (!enumeration_is_subset(a->options, b->options))
 3690             return false;
 3691     }
 3692 
 3693     return true;
 3694 }
 3695 
 3696 static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
 3697     pa_alsa_path *p;
 3698     void *state;
 3699 
 3700     pa_assert(ps);
 3701     pa_assert(m);
 3702 
 3703     /* If we only have one path, then don't bother */
 3704     if (pa_hashmap_size(ps->paths) < 2)
 3705         return;
 3706 
 3707     PA_HASHMAP_FOREACH(p, ps->paths, state) {
 3708         pa_alsa_path *p2;
 3709         void *state2;
 3710 
 3711         PA_HASHMAP_FOREACH(p2, ps->paths, state2) {
 3712             pa_alsa_element *ea, *eb;
 3713             pa_alsa_jack *ja, *jb;
 3714             bool is_subset = true;
 3715 
 3716             if (p == p2)
 3717                 continue;
 3718 
 3719             /* If a has a jack that b does not have, a is not a subset */
 3720             PA_LLIST_FOREACH(ja, p->jacks) {
 3721                 bool exists = false;
 3722 
 3723                 if (!ja->has_control)
 3724                     continue;
 3725 
 3726                 PA_LLIST_FOREACH(jb, p2->jacks) {
 3727                     if (jb->has_control && pa_streq(ja->alsa_id.name, jb->alsa_id.name) &&
 3728                        (ja->alsa_id.index == jb->alsa_id.index) &&
 3729                        (ja->state_plugged == jb->state_plugged) &&
 3730                        (ja->state_unplugged == jb->state_unplugged)) {
 3731                         exists = true;
 3732                         break;
 3733                     }
 3734                 }
 3735 
 3736                 if (!exists) {
 3737                     is_subset = false;
 3738                     break;
 3739                 }
 3740             }
 3741 
 3742             /* Compare the elements of each set... */
 3743             PA_LLIST_FOREACH(ea, p->elements) {
 3744                 bool found_matching_element = false;
 3745 
 3746                 if (!is_subset)
 3747                     break;
 3748 
 3749                 PA_LLIST_FOREACH(eb, p2->elements) {
 3750                     if (pa_streq(ea->alsa_id.name, eb->alsa_id.name) &&
 3751                         ea->alsa_id.index == eb->alsa_id.index) {
 3752                         found_matching_element = true;
 3753                         is_subset = element_is_subset(ea, eb, m);
 3754                         break;
 3755                     }
 3756                 }
 3757 
 3758                 if (!found_matching_element)
 3759                     is_subset = false;
 3760             }
 3761 
 3762             if (is_subset) {
 3763                 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p->name, p2->name);
 3764                 pa_hashmap_remove(ps->paths, p);
 3765                 break;
 3766             }
 3767         }
 3768     }
 3769 }
 3770 
 3771 static pa_alsa_path* path_set_find_path_by_description(pa_alsa_path_set *ps, const char* description, pa_alsa_path *ignore) {
 3772     pa_alsa_path* p;
 3773     void *state;
 3774 
 3775     PA_HASHMAP_FOREACH(p, ps->paths, state)
 3776         if (p != ignore && pa_streq(p->description, description))
 3777             return p;
 3778 
 3779     return NULL;
 3780 }
 3781 
 3782 static void path_set_make_path_descriptions_unique(pa_alsa_path_set *ps) {
 3783     pa_alsa_path *p, *q;
 3784     void *state, *state2;
 3785 
 3786     PA_HASHMAP_FOREACH(p, ps->paths, state) {
 3787         unsigned i;
 3788         char *old_description;
 3789 
 3790         q = path_set_find_path_by_description(ps, p->description, p);
 3791 
 3792         if (!q)
 3793             continue;
 3794 
 3795         old_description = pa_xstrdup(p->description);
 3796 
 3797         /* OK, this description is not unique, hence let's rename */
 3798         i = 1;
 3799         PA_HASHMAP_FOREACH(q, ps->paths, state2) {
 3800             char *new_description;
 3801 
 3802             if (!pa_streq(q->description, old_description))
 3803                 continue;
 3804 
 3805             new_description = pa_sprintf_malloc("%s %u", q->description, i);
 3806             pa_xfree(q->description);
 3807             q->description = new_description;
 3808 
 3809             i++;
 3810         }
 3811 
 3812         pa_xfree(old_description);
 3813     }
 3814 }
 3815 
 3816 static void mapping_free(pa_alsa_mapping *m) {
 3817     pa_assert(m);
 3818 
 3819     pa_xfree(m->name);
 3820     pa_xfree(m->description);
 3821     pa_xfree(m->description_key);
 3822 
 3823     pa_proplist_free(m->proplist);
 3824 
 3825     pa_xstrfreev(m->device_strings);
 3826     pa_xstrfreev(m->input_path_names);
 3827     pa_xstrfreev(m->output_path_names);
 3828     pa_xstrfreev(m->input_element);
 3829     pa_xstrfreev(m->output_element);
 3830     if (m->input_path_set)
 3831         pa_alsa_path_set_free(m->input_path_set);
 3832     if (m->output_path_set)
 3833         pa_alsa_path_set_free(m->output_path_set);
 3834 
 3835     pa_assert(!m->input_pcm);
 3836     pa_assert(!m->output_pcm);
 3837 
 3838     pa_alsa_ucm_mapping_context_free(&m->ucm_context);
 3839 
 3840     pa_xfree(m);
 3841 }
 3842 
 3843 static void profile_free(pa_alsa_profile *p) {
 3844     pa_assert(p);
 3845 
 3846     pa_xfree(p->name);
 3847     pa_xfree(p->description);
 3848     pa_xfree(p->description_key);
 3849     pa_xfree(p->input_name);
 3850     pa_xfree(p->output_name);
 3851 
 3852     pa_xstrfreev(p->input_mapping_names);
 3853     pa_xstrfreev(p->output_mapping_names);
 3854 
 3855     if (p->input_mappings)
 3856         pa_idxset_free(p->input_mappings, NULL);
 3857 
 3858     if (p->output_mappings)
 3859         pa_idxset_free(p->output_mappings, NULL);
 3860 
 3861     pa_xfree(p);
 3862 }
 3863 
 3864 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
 3865     pa_assert(ps);
 3866 
 3867     if (ps->input_paths)
 3868         pa_hashmap_free(ps->input_paths);
 3869 
 3870     if (ps->output_paths)
 3871         pa_hashmap_free(ps->output_paths);
 3872 
 3873     if (ps->profiles)
 3874         pa_hashmap_free(ps->profiles);
 3875 
 3876     if (ps->mappings)
 3877         pa_hashmap_free(ps->mappings);
 3878 
 3879     if (ps->decibel_fixes)
 3880         pa_hashmap_free(ps->decibel_fixes);
 3881 
 3882     pa_xfree(ps);
 3883 }
 3884 
 3885 pa_alsa_mapping *pa_alsa_mapping_get(pa_alsa_profile_set *ps, const char *name) {
 3886     pa_alsa_mapping *m;
 3887 
 3888     if (!pa_startswith(name, "Mapping "))
 3889         return NULL;
 3890 
 3891     name += 8;
 3892 
 3893     if ((m = pa_hashmap_get(ps->mappings, name)))
 3894         return m;
 3895 
 3896     m = pa_xnew0(pa_alsa_mapping, 1);
 3897     m->profile_set = ps;
 3898     m->exact_channels = true;
 3899     m->name = pa_xstrdup(name);
 3900     pa_sample_spec_init(&m->sample_spec);
 3901     pa_channel_map_init(&m->channel_map);
 3902     m->proplist = pa_proplist_new();
 3903     m->hw_device_index = -1;
 3904 
 3905     pa_hashmap_put(ps->mappings, m->name, m);
 3906 
 3907     return m;
 3908 }
 3909 
 3910 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
 3911     pa_alsa_profile *p;
 3912 
 3913     if (!pa_startswith(name, "Profile "))
 3914         return NULL;
 3915 
 3916     name += 8;
 3917 
 3918     if ((p = pa_hashmap_get(ps->profiles, name)))
 3919         return p;
 3920 
 3921     p = pa_xnew0(pa_alsa_profile, 1);
 3922     p->profile_set = ps;
 3923     p->name = pa_xstrdup(name);
 3924 
 3925     pa_hashmap_put(ps->profiles, p->name, p);
 3926 
 3927     return p;
 3928 }
 3929 
 3930 static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *alsa_id) {
 3931     pa_alsa_decibel_fix *db_fix;
 3932     char *name;
 3933     int index;
 3934 
 3935     if (!pa_startswith(alsa_id, "DecibelFix "))
 3936         return NULL;
 3937 
 3938     alsa_id += 11;
 3939 
 3940     if ((db_fix = pa_hashmap_get(ps->decibel_fixes, alsa_id)))
 3941         return db_fix;
 3942 
 3943     name = alloca(strlen(alsa_id) + 1);
 3944     if (alsa_id_decode(alsa_id, name, &index))
 3945         return NULL;
 3946 
 3947     db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
 3948     db_fix->profile_set = ps;
 3949     db_fix->name = pa_xstrdup(name);
 3950     db_fix->index = index;
 3951     db_fix->key = pa_xstrdup(alsa_id);
 3952 
 3953     pa_hashmap_put(ps->decibel_fixes, db_fix->key, db_fix);
 3954 
 3955     return db_fix;
 3956 }
 3957 
 3958 static int mapping_parse_device_strings(pa_config_parser_state *state) {
 3959     pa_alsa_profile_set *ps;
 3960     pa_alsa_mapping *m;
 3961 
 3962     pa_assert(state);
 3963 
 3964     ps = state->userdata;
 3965 
 3966     if (!(m = pa_alsa_mapping_get(ps, state->section))) {
 3967         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
 3968         return -1;
 3969     }
 3970 
 3971     pa_xstrfreev(m->device_strings);
 3972     if (!(m->device_strings = pa_split_spaces_strv(state->rvalue))) {
 3973         pa_log("[%s:%u] Device string list empty of '%s'", state->filename, state->lineno, state->section);
 3974         return -1;
 3975     }
 3976 
 3977     return 0;
 3978 }
 3979 
 3980 static int mapping_parse_channel_map(pa_config_parser_state *state) {
 3981     pa_alsa_profile_set *ps;
 3982     pa_alsa_mapping *m;
 3983 
 3984     pa_assert(state);
 3985 
 3986     ps = state->userdata;
 3987 
 3988     if (!(m = pa_alsa_mapping_get(ps, state->section))) {
 3989         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
 3990         return -1;
 3991     }
 3992 
 3993     if (!(pa_channel_map_parse(&m->channel_map, state->rvalue))) {
 3994         pa_log("[%s:%u] Channel map invalid of '%s'", state->filename, state->lineno, state->section);
 3995         return -1;
 3996     }
 3997 
 3998     return 0;
 3999 }
 4000 
 4001 static int mapping_parse_paths(pa_config_parser_state *state) {
 4002     pa_alsa_profile_set *ps;
 4003     pa_alsa_mapping *m;
 4004 
 4005     pa_assert(state);
 4006 
 4007     ps = state->userdata;
 4008 
 4009     if (!(m = pa_alsa_mapping_get(ps, state->section))) {
 4010         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
 4011         return -1;
 4012     }
 4013 
 4014     if (pa_streq(state->lvalue, "paths-input")) {
 4015         pa_xstrfreev(m->input_path_names);
 4016         m->input_path_names = pa_split_spaces_strv(state->rvalue);
 4017     } else {
 4018         pa_xstrfreev(m->output_path_names);
 4019         m->output_path_names = pa_split_spaces_strv(state->rvalue);
 4020     }
 4021 
 4022     return 0;
 4023 }
 4024 
 4025 static int mapping_parse_exact_channels(pa_config_parser_state *state) {
 4026     pa_alsa_profile_set *ps;
 4027     pa_alsa_mapping *m;
 4028     int b;
 4029 
 4030     pa_assert(state);
 4031 
 4032     ps = state->userdata;
 4033 
 4034     if (!(m = pa_alsa_mapping_get(ps, state->section))) {
 4035         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
 4036         return -1;
 4037     }
 4038 
 4039     if ((b = pa_parse_boolean(state->rvalue)) < 0) {
 4040         pa_log("[%s:%u] %s has invalid value '%s'", state->filename, state->lineno, state->lvalue, state->section);
 4041         return -1;
 4042     }
 4043 
 4044     m->exact_channels = b;
 4045 
 4046     return 0;
 4047 }
 4048 
 4049 static int mapping_parse_element(pa_config_parser_state *state) {
 4050     pa_alsa_profile_set *ps;
 4051     pa_alsa_mapping *m;
 4052 
 4053     pa_assert(state);
 4054 
 4055     ps = state->userdata;
 4056 
 4057     if (!(m = pa_alsa_mapping_get(ps, state->section))) {
 4058         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
 4059         return -1;
 4060     }
 4061 
 4062     if (pa_streq(state->lvalue, "element-input")) {
 4063         pa_xstrfreev(m->input_element);
 4064         m->input_element = pa_split_spaces_strv(state->rvalue);
 4065     } else {
 4066         pa_xstrfreev(m->output_element);
 4067         m->output_element = pa_split_spaces_strv(state->rvalue);
 4068     }
 4069 
 4070     return 0;
 4071 }
 4072 
 4073 static int mapping_parse_direction(pa_config_parser_state *state) {
 4074     pa_alsa_profile_set *ps;
 4075     pa_alsa_mapping *m;
 4076 
 4077     pa_assert(state);
 4078 
 4079     ps = state->userdata;
 4080 
 4081     if (!(m = pa_alsa_mapping_get(ps, state->section))) {
 4082         pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
 4083         return -1;
 4084     }
 4085 
 4086     if (pa_streq(state->rvalue, "input"))
 4087         m->direction = PA_ALSA_DIRECTION_INPUT;
 4088     else if (pa_streq(state->rvalue, "output"))
 4089         m->direction = PA_ALSA_DIRECTION_OUTPUT;
 4090     else if (pa_streq(state->rvalue, "any"))
 4091         m->direction = PA_ALSA_DIRECTION_ANY;
 4092     else {
 4093         pa_log("[%s:%u] Direction %s invalid.", state->filename, state->lineno, state->rvalue);
 4094         return -1;
 4095     }
 4096 
 4097     return 0;
 4098 }
 4099 
 4100 static int mapping_parse_description(pa_config_parser_state *state) {
 4101     pa_alsa_profile_set *ps;
 4102     pa_alsa_profile *p;
 4103     pa_alsa_mapping *m;
 4104 
 4105     pa_assert(state);
 4106 
 4107     ps = state->userdata;
 4108 
 4109     if ((m = pa_alsa_mapping_get(ps, state->section))) {
 4110         pa_xfree(m->description);
 4111         m->description = pa_xstrdup(state->rvalue);
 4112     } else if ((p = profile_get(ps, state->section))) {
 4113         pa_xfree(p->description);
 4114         p->description = pa_xstrdup(state->rvalue);
 4115     } else {
 4116         pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
 4117         return -1;
 4118     }
 4119 
 4120     return 0;
 4121 }
 4122 
 4123 static int mapping_parse_description_key(pa_config_parser_state *state) {
 4124     pa_alsa_profile_set *ps;
 4125     pa_alsa_profile *p;
 4126     pa_alsa_mapping *m;
 4127 
 4128     pa_assert(state);
 4129 
 4130     ps = state->userdata;
 4131 
 4132     if ((m = pa_alsa_mapping_get(ps, state->section))) {
 4133         pa_xfree(m->description_key);
 4134         m->description_key = pa_xstrdup(state->rvalue);
 4135     } else if ((p = profile_get(ps, state->section))) {
 4136         pa_xfree(p->description_key);
 4137         p->description_key = pa_xstrdup(state->rvalue);
 4138     } else {
 4139         pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
 4140         return -1;
 4141     }
 4142 
 4143     return 0;
 4144 }
 4145 
 4146 
 4147 static int mapping_parse_priority(pa_config_parser_state *state) {
 4148     pa_alsa_profile_set *ps;
 4149     pa_alsa_profile *p;
 4150     pa_alsa_mapping *m;
 4151     uint32_t prio;
 4152 
 4153     pa_assert(state);
 4154 
 4155     ps = state->userdata;
 4156 
 4157     if (pa_atou(state->rvalue, &prio) < 0) {
 4158         pa_log("[%s:%u] Priority invalid of '%s'", state->filename, state->lineno, state->section);
 4159         return -1;
 4160     }
 4161 
 4162     if ((m = pa_alsa_mapping_get(ps, state->section)))
 4163         m->priority = prio;
 4164     else if ((p = profile_get(ps, state->section)))
 4165         p->priority = prio;
 4166     else {
 4167         pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
 4168         return -1;
 4169     }
 4170 
 4171     return 0;
 4172 }
 4173 
 4174 static int mapping_parse_fallback(pa_config_parser_state *state) {
 4175     pa_alsa_profile_set *ps;
 4176     pa_alsa_profile *p;
 4177     pa_alsa_mapping *m;
 4178     int k;
 4179 
 4180     pa_assert(state);
 4181 
 4182     ps = state->userdata;
 4183 
 4184     if ((k = pa_parse_boolean(state->rvalue)) < 0) {
 4185         pa_log("[%s:%u] Fallback invalid of '%s'", state->filename, state->lineno, state->section);
 4186         return -1;
 4187     }
 4188 
 4189     if ((m = pa_alsa_mapping_get(ps, state->section)))
 4190         m->fallback = k;
 4191     else if ((p = profile_get(ps, state->section)))
 4192         p->fallback_input = p->fallback_output = k;
 4193     else {
 4194         pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
 4195         return -1;
 4196     }
 4197 
 4198     return 0;
 4199 }
 4200 
 4201 static int mapping_parse_intended_roles(pa_config_parser_state *state) {
 4202     pa_alsa_profile_set *ps;
 4203     pa_alsa_mapping *m;
 4204 
 4205     pa_assert(state);
 4206 
 4207     ps = state->userdata;
 4208 
 4209     if (!(m = pa_alsa_mapping_get(ps, state->section))) {
 4210         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
 4211         return -1;
 4212     }
 4213 
 4214     pa_proplist_sets(m->proplist, PA_PROP_DEVICE_INTENDED_ROLES, state->rvalue);
 4215 
 4216     return 0;
 4217 }
 4218 
 4219 
 4220 static int profile_parse_mappings(pa_config_parser_state *state) {
 4221     pa_alsa_profile_set *ps;
 4222     pa_alsa_profile *p;
 4223 
 4224     pa_assert(state);
 4225 
 4226     ps = state->userdata;
 4227 
 4228     if (!(p = profile_get(ps, state->section))) {
 4229         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
 4230         return -1;
 4231     }
 4232 
 4233     if (pa_streq(state->lvalue, "input-mappings")) {
 4234         pa_xstrfreev(p->input_mapping_names);
 4235         p->input_mapping_names = pa_split_spaces_strv(state->rvalue);
 4236     } else {
 4237         pa_xstrfreev(p->output_mapping_names);
 4238         p->output_mapping_names = pa_split_spaces_strv(state->rvalue);
 4239     }
 4240 
 4241     return 0;
 4242 }
 4243 
 4244 static int profile_parse_skip_probe(pa_config_parser_state *state) {
 4245     pa_alsa_profile_set *ps;
 4246     pa_alsa_profile *p;
 4247     int b;
 4248 
 4249     pa_assert(state);
 4250 
 4251     ps = state->userdata;
 4252 
 4253     if (!(p = profile_get(ps, state->section))) {
 4254         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
 4255         return -1;
 4256     }
 4257 
 4258     if ((b = pa_parse_boolean(state->rvalue)) < 0) {
 4259         pa_log("[%s:%u] Skip probe invalid of '%s'", state->filename, state->lineno, state->section);
 4260         return -1;
 4261     }
 4262 
 4263     p->supported = b;
 4264 
 4265     return 0;
 4266 }
 4267 
 4268 static int decibel_fix_parse_db_values(pa_config_parser_state *state) {
 4269     pa_alsa_profile_set *ps;
 4270     pa_alsa_decibel_fix *db_fix;
 4271     char **items;
 4272     char *item;
 4273     long *db_values;
 4274     unsigned n = 8; /* Current size of the db_values table. */
 4275     unsigned min_step = 0;
 4276     unsigned max_step = 0;
 4277     unsigned i = 0; /* Index to the items table. */
 4278     unsigned prev_step = 0;
 4279     double prev_db = 0;
 4280 
 4281     pa_assert(state);
 4282 
 4283     ps = state->userdata;
 4284 
 4285     if (!(db_fix = decibel_fix_get(ps, state->section))) {
 4286         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
 4287         return -1;
 4288     }
 4289 
 4290     if (!(items = pa_split_spaces_strv(state->rvalue))) {
 4291         pa_log("[%s:%u] Value missing", state->filename, state->lineno);
 4292         return -1;
 4293     }
 4294 
 4295     db_values = pa_xnew(long, n);
 4296 
 4297     while ((item = items[i++])) {
 4298         char *s = item; /* Step value string. */
 4299         char *d = item; /* dB value string. */
 4300         uint32_t step;
 4301         double db;
 4302 
 4303         /* Move d forward until it points to a colon or to the end of the item. */
 4304         for (; *d && *d != ':'; ++d);
 4305 
 4306         if (d == s) {
 4307             /* item started with colon. */
 4308             pa_log("[%s:%u] No step value found in %s", state->filename, state->lineno, item);
 4309             goto fail;
 4310         }
 4311 
 4312         if (!*d || !*(d + 1)) {
 4313             /* No colon found, or it was the last character in item. */
 4314             pa_log("[%s:%u] No dB value found in %s", state->filename, state->lineno, item);
 4315             goto fail;
 4316         }
 4317 
 4318         /* pa_atou() needs a null-terminating string. Let's replace the colon
 4319          * with a zero byte. */
 4320         *d++ = '\0';
 4321 
 4322         if (pa_atou(s, &step) < 0) {
 4323             pa_log("[%s:%u] Invalid step value: %s", state->filename, state->lineno, s);
 4324             goto fail;
 4325         }
 4326 
 4327         if (pa_atod(d, &db) < 0) {
 4328             pa_log("[%s:%u] Invalid dB value: %s", state->filename, state->lineno, d);
 4329             goto fail;
 4330         }
 4331 
 4332         if (step <= prev_step && i != 1) {
 4333             pa_log("[%s:%u] Step value %u not greater than the previous value %u", state->filename, state->lineno, step, prev_step);
 4334             goto fail;
 4335         }
 4336 
 4337         if (db < prev_db && i != 1) {
 4338             pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", state->filename, state->lineno, db, prev_db);
 4339             goto fail;
 4340         }
 4341 
 4342         if (i == 1) {
 4343             min_step = step;
 4344             db_values[0] = (long) (db * 100.0);
 4345             prev_step = step;
 4346             prev_db = db;
 4347         } else {
 4348             /* Interpolate linearly. */
 4349             double db_increment = (db - prev_db) / (step - prev_step);
 4350 
 4351             for (; prev_step < step; ++prev_step, prev_db += db_increment) {
 4352 
 4353                 /* Reallocate the db_values table if it's about to overflow. */
 4354                 if (prev_step + 1 - min_step == n) {
 4355                     n *= 2;
 4356                     db_values = pa_xrenew(long, db_values, n);
 4357                 }
 4358 
 4359                 db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
 4360             }
 4361         }
 4362 
 4363         max_step = step;
 4364     }
 4365 
 4366     db_fix->min_step = min_step;
 4367     db_fix->max_step = max_step;
 4368     pa_xfree(db_fix->db_values);
 4369     db_fix->db_values = db_values;
 4370 
 4371     pa_xstrfreev(items);
 4372 
 4373     return 0;
 4374 
 4375 fail:
 4376     pa_xstrfreev(items);
 4377     pa_xfree(db_values);
 4378 
 4379     return -1;
 4380 }
 4381 
 4382 /* the logic is simple: if we see the jack in multiple paths */
 4383 /* assign all those paths to one availability_group */
 4384 static void profile_set_set_availability_groups(pa_alsa_profile_set *ps) {
 4385     pa_dynarray *paths;
 4386     pa_alsa_path *p;
 4387     void *state;
 4388     unsigned idx1;
 4389     uint32_t num = 1;
 4390 
 4391     /* Merge ps->input_paths and ps->output_paths into one dynarray. */
 4392     paths = pa_dynarray_new(NULL);
 4393     PA_HASHMAP_FOREACH(p, ps->input_paths, state)
 4394         pa_dynarray_append(paths, p);
 4395     PA_HASHMAP_FOREACH(p, ps->output_paths, state)
 4396         pa_dynarray_append(paths, p);
 4397 
 4398     PA_DYNARRAY_FOREACH(p, paths, idx1) {
 4399         pa_alsa_jack *j;
 4400         const char *found = NULL;
 4401         bool has_control = false;
 4402 
 4403         PA_LLIST_FOREACH(j, p->jacks) {
 4404             pa_alsa_path *p2;
 4405             unsigned idx2;
 4406 
 4407             if (!j->has_control || j->state_plugged == PA_AVAILABLE_NO)
 4408                 continue;
 4409             has_control = true;
 4410             PA_DYNARRAY_FOREACH(p2, paths, idx2) {
 4411                 pa_alsa_jack *j2;
 4412 
 4413                 if (p2 == p)
 4414                     break;
 4415                 PA_LLIST_FOREACH(j2, p2->jacks) {
 4416                     if (!j2->has_control || j2->state_plugged == PA_AVAILABLE_NO)
 4417                         continue;
 4418                     if (pa_streq(j->alsa_id.name, j2->alsa_id.name) &&
 4419                         j->alsa_id.index == j2->alsa_id.index) {
 4420                         j->state_plugged = PA_AVAILABLE_UNKNOWN;
 4421                         j2->state_plugged = PA_AVAILABLE_UNKNOWN;
 4422                         found = p2->availability_group;
 4423                         break;
 4424                     }
 4425                 }
 4426             }
 4427             if (found)
 4428                 break;
 4429         }
 4430         if (!has_control)
 4431             continue;
 4432         if (!found) {
 4433             p->availability_group = pa_sprintf_malloc("Legacy %d", num);
 4434         } else {
 4435             p->availability_group = pa_xstrdup(found);
 4436         }
 4437         if (!found)
 4438             num++;
 4439     }
 4440 
 4441     pa_dynarray_free(paths);
 4442 }
 4443 
 4444 static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
 4445                                 pa_alsa_direction_t direction, pa_hashmap *used_paths,
 4446                                 pa_hashmap *mixers) {
 4447 
 4448     pa_alsa_path *p;
 4449     void *state;
 4450     snd_pcm_t *pcm_handle;
 4451     pa_alsa_path_set *ps;
 4452     snd_mixer_t *mixer_handle;
 4453 
 4454     if (direction == PA_ALSA_DIRECTION_OUTPUT) {
 4455         if (m->output_path_set)
 4456             return; /* Already probed */
 4457         m->output_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
 4458         pcm_handle = m->output_pcm;
 4459     } else {
 4460         if (m->input_path_set)
 4461             return; /* Already probed */
 4462         m->input_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
 4463         pcm_handle = m->input_pcm;
 4464     }
 4465 
 4466     if (!ps)
 4467         return; /* No paths */
 4468 
 4469     pa_assert(pcm_handle);
 4470 
 4471     mixer_handle = pa_alsa_open_mixer_for_pcm(mixers, pcm_handle, true);
 4472     if (!mixer_handle) {
 4473         /* Cannot open mixer, remove all entries */
 4474         pa_hashmap_remove_all(ps->paths);
 4475         return;
 4476     }
 4477 
 4478     PA_HASHMAP_FOREACH(p, ps->paths, state) {
 4479         if (p->autodetect_eld_device)
 4480             p->eld_device = m->hw_device_index;
 4481 
 4482         if (pa_alsa_path_probe(p, m, mixer_handle, m->profile_set->ignore_dB) < 0)
 4483             pa_hashmap_remove(ps->paths, p);
 4484     }
 4485 
 4486     path_set_condense(ps, mixer_handle);
 4487     path_set_make_path_descriptions_unique(ps);
 4488 
 4489     PA_HASHMAP_FOREACH(p, ps->paths, state)
 4490         pa_hashmap_put(used_paths, p, p);
 4491 
 4492     pa_log_debug("Available mixer paths (after tidying):");
 4493     pa_alsa_path_set_dump(ps);
 4494 }
 4495 
 4496 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
 4497 
 4498     static const struct description_map well_known_descriptions[] = {
 4499         { "analog-mono",            N_("Analog Mono") },
 4500         { "analog-stereo",          N_("Analog Stereo") },
 4501         { "mono-fallback",          N_("Mono") },
 4502         { "stereo-fallback",        N_("Stereo") },
 4503         /* Note: Not translated to "Analog Stereo Input", because the source
 4504          * name gets "Input" appended to it automatically, so adding "Input"
 4505          * here would lead to the source name to become "Analog Stereo Input
 4506          * Input". The same logic applies to analog-stereo-output,
 4507          * multichannel-input and multichannel-output. */
 4508         { "analog-stereo-input",    N_("Analog Stereo") },
 4509         { "analog-stereo-output",   N_("Analog Stereo") },
 4510         { "multichannel-input",     N_("Multichannel") },
 4511         { "multichannel-output",    N_("Multichannel") },
 4512         { "analog-surround-21",     N_("Analog Surround 2.1") },
 4513         { "analog-surround-30",     N_("Analog Surround 3.0") },
 4514         { "analog-surround-31",     N_("Analog Surround 3.1") },
 4515         { "analog-surround-40",     N_("Analog Surround 4.0") },
 4516         { "analog-surround-41",     N_("Analog Surround 4.1") },
 4517         { "analog-surround-50",     N_("Analog Surround 5.0") },
 4518         { "analog-surround-51",     N_("Analog Surround 5.1") },
 4519         { "analog-surround-61",     N_("Analog Surround 6.0") },
 4520         { "analog-surround-61",     N_("Analog Surround 6.1") },
 4521         { "analog-surround-70",     N_("Analog Surround 7.0") },
 4522         { "analog-surround-71",     N_("Analog Surround 7.1") },
 4523         { "iec958-stereo",          N_("Digital Stereo (IEC958)") },
 4524         { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
 4525         { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
 4526         { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
 4527         { "hdmi-stereo",            N_("Digital Stereo (HDMI)") },
 4528         { "hdmi-surround-51",       N_("Digital Surround 5.1 (HDMI)") },
 4529         { "gaming-headset-chat",    N_("Chat") },
 4530         { "gaming-headset-game",    N_("Game") },
 4531     };
 4532     const char *description_key = m->description_key ? m->description_key : m->name;
 4533 
 4534     pa_assert(m);
 4535 
 4536     if (!pa_channel_map_valid(&m->channel_map)) {
 4537         pa_log("Mapping %s is missing channel map.", m->name);
 4538         return -1;
 4539     }
 4540 
 4541     if (!m->device_strings) {
 4542         pa_log("Mapping %s is missing device strings.", m->name);
 4543         return -1;
 4544     }
 4545 
 4546     if ((m->input_path_names && m->input_element) ||
 4547         (m->output_path_names && m->output_element)) {
 4548         pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
 4549         return -1;
 4550     }
 4551 
 4552     if (!m->description)
 4553         m->description = pa_xstrdup(lookup_description(description_key,
 4554                                                        well_known_descriptions,
 4555                                                        PA_ELEMENTSOF(well_known_descriptions)));
 4556 
 4557     if (!m->description)
 4558         m->description = pa_xstrdup(m->name);
 4559 
 4560     if (bonus) {
 4561         if (pa_channel_map_equal(&m->channel_map, bonus))
 4562             m->priority += 50;
 4563         else if (m->channel_map.channels == bonus->channels)
 4564             m->priority += 30;
 4565     }
 4566 
 4567     return 0;
 4568 }
 4569 
 4570 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
 4571     char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
 4572 
 4573     pa_assert(m);
 4574 
 4575     pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
 4576                  m->name,
 4577                  pa_strnull(m->description),
 4578                  m->priority,
 4579                  pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
 4580                  pa_yes_no(m->supported),
 4581                  m->direction);
 4582 }
 4583 
 4584 static void profile_set_add_auto_pair(
 4585         pa_alsa_profile_set *ps,
 4586         pa_alsa_mapping *m, /* output */
 4587         pa_alsa_mapping *n  /* input */) {
 4588 
 4589     char *name;
 4590     pa_alsa_profile *p;
 4591 
 4592     pa_assert(ps);
 4593     pa_assert(m || n);
 4594 
 4595     if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
 4596         return;
 4597 
 4598     if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
 4599         return;
 4600 
 4601     if (m && n)
 4602         name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
 4603     else if (m)
 4604         name = pa_sprintf_malloc("output:%s", m->name);
 4605     else
 4606         name = pa_sprintf_malloc("input:%s", n->name);
 4607 
 4608     if (pa_hashmap_get(ps->profiles, name)) {
 4609         pa_xfree(name);
 4610         return;
 4611     }
 4612 
 4613     p = pa_xnew0(pa_alsa_profile, 1);
 4614     p->profile_set = ps;
 4615     p->name = name;
 4616 
 4617     if (m) {
 4618         p->output_name = pa_xstrdup(m->name);
 4619         p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 4620         pa_idxset_put(p->output_mappings, m, NULL);
 4621         p->priority += m->priority * 100;
 4622         p->fallback_output = m->fallback;
 4623     }
 4624 
 4625     if (n) {
 4626         p->input_name = pa_xstrdup(n->name);
 4627         p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 4628         pa_idxset_put(p->input_mappings, n, NULL);
 4629         p->priority += n->priority;
 4630         p->fallback_input = n->fallback;
 4631     }
 4632 
 4633     pa_hashmap_put(ps->profiles, p->name, p);
 4634 }
 4635 
 4636 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
 4637     pa_alsa_mapping *m, *n;
 4638     void *m_state, *n_state;
 4639 
 4640     pa_assert(ps);
 4641 
 4642     /* The order is important here:
 4643        1) try single inputs and outputs before trying their
 4644           combination, because if the half-duplex test failed, we don't have
 4645           to try full duplex.
 4646        2) try the output right before the input combinations with
 4647           that output, because then the output_pcm is not closed between tests.
 4648     */
 4649     PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
 4650         profile_set_add_auto_pair(ps, NULL, n);
 4651 
 4652     PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
 4653         profile_set_add_auto_pair(ps, m, NULL);
 4654 
 4655         PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
 4656             profile_set_add_auto_pair(ps, m, n);
 4657     }
 4658 
 4659 }
 4660 
 4661 static int profile_verify(pa_alsa_profile *p) {
 4662 
 4663     static const struct description_map well_known_descriptions[] = {
 4664         { "output:analog-mono+input:analog-mono",     N_("Analog Mono Duplex") },
 4665         { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
 4666         { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
 4667         { "output:multichannel-output+input:multichannel-input", N_("Multichannel Duplex") },
 4668         { "output:unknown-stereo+input:unknown-stereo", N_("Stereo Duplex") },
 4669         { "off",                                      N_("Off") }
 4670     };
 4671     const char *description_key = p->description_key ? p->description_key : p->name;
 4672 
 4673     pa_assert(p);
 4674 
 4675     /* Replace the output mapping names by the actual mappings */
 4676     if (p->output_mapping_names) {
 4677         char **name;
 4678 
 4679         pa_assert(!p->output_mappings);
 4680         p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 4681 
 4682         for (name = p->output_mapping_names; *name; name++) {
 4683             pa_alsa_mapping *m;
 4684             char **in;
 4685             bool duplicate = false;
 4686 
 4687             for (in = name + 1; *in; in++)
 4688                 if (pa_streq(*name, *in)) {
 4689                     duplicate = true;
 4690                     break;
 4691                 }
 4692 
 4693             if (duplicate)
 4694                 continue;
 4695 
 4696             if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
 4697                 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
 4698                 return -1;
 4699             }
 4700 
 4701             pa_idxset_put(p->output_mappings, m, NULL);
 4702 
 4703             if (p->supported)
 4704                 m->supported++;
 4705         }
 4706 
 4707         pa_xstrfreev(p->output_mapping_names);
 4708         p->output_mapping_names = NULL;
 4709     }
 4710 
 4711     /* Replace the input mapping names by the actual mappings */
 4712     if (p->input_mapping_names) {
 4713         char **name;
 4714 
 4715         pa_assert(!p->input_mappings);
 4716         p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 4717 
 4718         for (name = p->input_mapping_names; *name; name++) {
 4719             pa_alsa_mapping *m;
 4720             char **in;
 4721             bool duplicate = false;
 4722 
 4723             for (in = name + 1; *in; in++)
 4724                 if (pa_streq(*name, *in)) {
 4725                     duplicate = true;
 4726                     break;
 4727                 }
 4728 
 4729             if (duplicate)
 4730                 continue;
 4731 
 4732             if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
 4733                 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
 4734                 return -1;
 4735             }
 4736 
 4737             pa_idxset_put(p->input_mappings, m, NULL);
 4738 
 4739             if (p->supported)
 4740                 m->supported++;
 4741         }
 4742 
 4743         pa_xstrfreev(p->input_mapping_names);
 4744         p->input_mapping_names = NULL;
 4745     }
 4746 
 4747     if (!p->input_mappings && !p->output_mappings) {
 4748         pa_log("Profile '%s' lacks mappings.", p->name);
 4749         return -1;
 4750     }
 4751 
 4752     if (!p->description)
 4753         p->description = pa_xstrdup(lookup_description(description_key,
 4754                                                        well_known_descriptions,
 4755                                                        PA_ELEMENTSOF(well_known_descriptions)));
 4756 
 4757     if (!p->description) {
 4758         pa_strbuf *sb;
 4759         uint32_t idx;
 4760         pa_alsa_mapping *m;
 4761 
 4762         sb = pa_strbuf_new();
 4763 
 4764         if (p->output_mappings)
 4765             PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
 4766                 if (!pa_strbuf_isempty(sb))
 4767                     pa_strbuf_puts(sb, " + ");
 4768 
 4769                 pa_strbuf_printf(sb, _("%s Output"), m->description);
 4770             }
 4771 
 4772         if (p->input_mappings)
 4773             PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
 4774                 if (!pa_strbuf_isempty(sb))
 4775                     pa_strbuf_puts(sb, " + ");
 4776 
 4777                 pa_strbuf_printf(sb, _("%s Input"), m->description);
 4778             }
 4779 
 4780         p->description = pa_strbuf_to_string_free(sb);
 4781     }
 4782 
 4783     return 0;
 4784 }
 4785 
 4786 void pa_alsa_profile_dump(pa_alsa_profile *p) {
 4787     uint32_t idx;
 4788     pa_alsa_mapping *m;
 4789     pa_assert(p);
 4790 
 4791     pa_log_debug("Profile %s (%s), input=%s, output=%s priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
 4792                  p->name,
 4793                  pa_strnull(p->description),
 4794                  pa_strnull(p->input_name),
 4795                  pa_strnull(p->output_name),
 4796                  p->priority,
 4797                  pa_yes_no(p->supported),
 4798                  p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
 4799                  p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
 4800 
 4801     if (p->input_mappings)
 4802         PA_IDXSET_FOREACH(m, p->input_mappings, idx)
 4803             pa_log_debug("Input %s", m->name);
 4804 
 4805     if (p->output_mappings)
 4806         PA_IDXSET_FOREACH(m, p->output_mappings, idx)
 4807             pa_log_debug("Output %s", m->name);
 4808 }
 4809 
 4810 static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
 4811     pa_assert(db_fix);
 4812 
 4813     /* Check that the dB mapping has been configured. Since "db-values" is
 4814      * currently the only option in the DecibelFix section, and decibel fix
 4815      * objects don't get created if a DecibelFix section is empty, this is
 4816      * actually a redundant check. Having this may prevent future bugs,
 4817      * however. */
 4818     if (!db_fix->db_values) {
 4819         pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
 4820         return -1;
 4821     }
 4822 
 4823     return 0;
 4824 }
 4825 
 4826 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
 4827     char *db_values = NULL;
 4828 
 4829     pa_assert(db_fix);
 4830 
 4831     if (db_fix->db_values) {
 4832         pa_strbuf *buf;
 4833         unsigned long i, nsteps;
 4834 
 4835         pa_assert(db_fix->min_step <= db_fix->max_step);
 4836         nsteps = db_fix->max_step - db_fix->min_step + 1;
 4837 
 4838         buf = pa_strbuf_new();
 4839         for (i = 0; i < nsteps; ++i)
 4840             pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
 4841 
 4842         db_values = pa_strbuf_to_string_free(buf);
 4843     }
 4844 
 4845     pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
 4846                  db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
 4847 
 4848     pa_xfree(db_values);
 4849 }
 4850 
 4851 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
 4852     pa_alsa_profile_set *ps;
 4853     pa_alsa_profile *p;
 4854     pa_alsa_mapping *m;
 4855     pa_alsa_decibel_fix *db_fix;
 4856     char *fn;
 4857     int r;
 4858     void *state;
 4859 
 4860     static pa_config_item items[] = {
 4861         /* [General] */
 4862         { "auto-profiles",          pa_config_parse_bool,         NULL, "General" },
 4863 
 4864         /* [Mapping ...] */
 4865         { "device-strings",         mapping_parse_device_strings, NULL, NULL },
 4866         { "channel-map",            mapping_parse_channel_map,    NULL, NULL },
 4867         { "paths-input",            mapping_parse_paths,          NULL, NULL },
 4868         { "paths-output",           mapping_parse_paths,          NULL, NULL },
 4869         { "element-input",          mapping_parse_element,        NULL, NULL },
 4870         { "element-output",         mapping_parse_element,        NULL, NULL },
 4871         { "direction",              mapping_parse_direction,      NULL, NULL },
 4872         { "exact-channels",         mapping_parse_exact_channels, NULL, NULL },
 4873         { "intended-roles",         mapping_parse_intended_roles, NULL, NULL },
 4874 
 4875         /* Shared by [Mapping ...] and [Profile ...] */
 4876         { "description",            mapping_parse_description,    NULL, NULL },
 4877         { "description-key",        mapping_parse_description_key,NULL, NULL },
 4878         { "priority",               mapping_parse_priority,       NULL, NULL },
 4879         { "fallback",               mapping_parse_fallback,       NULL, NULL },
 4880 
 4881         /* [Profile ...] */
 4882         { "input-mappings",         profile_parse_mappings,       NULL, NULL },
 4883         { "output-mappings",        profile_parse_mappings,       NULL, NULL },
 4884         { "skip-probe",             profile_parse_skip_probe,     NULL, NULL },
 4885 
 4886         /* [DecibelFix ...] */
 4887         { "db-values",              decibel_fix_parse_db_values,  NULL, NULL },
 4888         { NULL, NULL, NULL, NULL }
 4889     };
 4890 
 4891     ps = pa_xnew0(pa_alsa_profile_set, 1);
 4892     ps->mappings = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) mapping_free);
 4893     ps->profiles = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) profile_free);
 4894     ps->decibel_fixes = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) decibel_fix_free);
 4895     ps->input_paths = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_alsa_path_free);
 4896     ps->output_paths = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_alsa_path_free);
 4897 
 4898     items[0].data = &ps->auto_profiles;
 4899 
 4900     if (!fname)
 4901         fname = "default.conf";
 4902 
 4903     fn = pa_maybe_prefix_path(fname,
 4904 #ifdef HAVE_RUNNING_FROM_BUILD_TREE
 4905                               pa_run_from_build_tree() ? PA_SRCDIR "/modules/alsa/mixer/profile-sets/" :
 4906 #endif
 4907                               PA_ALSA_PROFILE_SETS_DIR);
 4908 
 4909     r = pa_config_parse(fn, NULL, items, NULL, false, ps);
 4910     pa_xfree(fn);
 4911 
 4912     if (r < 0)
 4913         goto fail;
 4914 
 4915     PA_HASHMAP_FOREACH(m, ps->mappings, state)
 4916         if (mapping_verify(m, bonus) < 0)
 4917             goto fail;
 4918 
 4919     if (ps->auto_profiles)
 4920         profile_set_add_auto(ps);
 4921 
 4922     PA_HASHMAP_FOREACH(p, ps->profiles, state)
 4923         if (profile_verify(p) < 0)
 4924             goto fail;
 4925 
 4926     PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
 4927         if (decibel_fix_verify(db_fix) < 0)