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
56static 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
62static 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
68static 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
74static 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
80static 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
87static 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
103static int setting_select(pa_alsa_setting *s, snd_mixer_t *m);
104
105struct description_map {
106    const char *key;
107    const char *description;
108};
109
110struct description2_map {
111    const char *key;
112    const char *description;
113    pa_device_port_type_t type;
114};
115
116char *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
125static 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
156pa_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
175void 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
187void 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
205void 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
258void 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
278void 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
285static 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
298static 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
311struct 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
330static 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
386static 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
466struct 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
474void 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 */
499int 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
514struct pa_alsa_mixer_pdata {
515    pa_rtpoll *rtpoll;
516    pa_rtpoll_item *poll_item;
517    snd_mixer_t *mixer;
518};
519
520struct 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
528void 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
538static 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
581fail:
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
591int 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
630static 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
693static 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
707static 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
718static 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
727static 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
737static 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
753void 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
783void 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
792int 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
798static long to_alsa_dB(pa_volume_t v) {
799    return lround(pa_sw_volume_to_dB(v) * 100.0);
800}
801
802static pa_volume_t from_alsa_dB(long v) {
803    return pa_sw_volume_from_dB((double) v / 100.0);
804}
805
806static 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
813static 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
824static 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
962int 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
998static 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
1046int 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. */
1079static 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. */
1111static 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
1158static 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
1316int 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
1357static 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
1386int 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. */
1410static 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
1475int 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
1552static 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
1635static 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
1642static 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    /* Assume decibel data to be incorrect if max_dB is negative. */
1715    if (e->has_dB && max_dB < 0 && !e->db_fix) {
1716        pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
1717        pa_log_warn("The decibel volume range for element %s (%li dB - %li dB) has negative maximum. "
1718                    "Disabling the decibel range.", buf, min_dB, max_dB);
1719        e->has_dB = false;
1720    }
1721
1722    /* Check that the kernel driver returns consistent limits with
1723     * both _get_*_dB_range() and _ask_*_vol_dB(). */
1724    if (e->has_dB && !e->db_fix) {
1725        long min_dB_checked = 0;
1726        long max_dB_checked = 0;
1727
1728        if (element_ask_vol_dB(me, e->direction, e->min_volume, &min_dB_checked) < 0) {
1729            pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
1730            pa_log_warn("Failed to query the dB value for %s at volume level %li", buf, e->min_volume);
1731            return false;
1732        }
1733
1734        if (element_ask_vol_dB(me, e->direction, e->max_volume, &max_dB_checked) < 0) {
1735            pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
1736            pa_log_warn("Failed to query the dB value for %s at volume level %li", buf, e->max_volume);
1737            return false;
1738        }
1739
1740        if (min_dB != min_dB_checked || max_dB != max_dB_checked) {
1741            pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
1742            pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
1743                        "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
1744                        "%0.2f dB at level %li.", buf, min_dB / 100.0, max_dB / 100.0,
1745                        min_dB_checked / 100.0, e->min_volume, max_dB_checked / 100.0, e->max_volume);
1746            return false;
1747        }
1748    }
1749
1750    if (e->has_dB) {
1751        e->min_dB = ((double) min_dB) / 100.0;
1752        e->max_dB = ((double) max_dB) / 100.0;
1753
1754        if (min_dB >= max_dB) {
1755            pa_assert(!e->db_fix);
1756            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.",
1757                        e->min_dB, e->max_dB);
1758            e->has_dB = false;
1759        }
1760    }
1761
1762    if (e->volume_limit >= 0) {
1763        if (e->volume_limit <= e->min_volume || e->volume_limit > e->max_volume) {
1764            pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
1765            pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1766                        "%li-%li. The volume limit is ignored.",
1767                        buf, e->path->name, e->volume_limit, e->min_volume + 1, e->max_volume);
1768        } else {
1769            e->max_volume = e->volume_limit;
1770
1771            if (e->has_dB) {
1772                if (e->db_fix) {
1773                    e->db_fix->max_step = e->max_volume;
1774                    e->max_dB = ((double) e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step]) / 100.0;
1775                } else if (element_ask_vol_dB(me, e->direction, e->max_volume, &max_dB) < 0) {
1776                    pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
1777                    pa_log_warn("Failed to get dB value of %s: %s", buf, pa_alsa_strerror(r));
1778                    e->has_dB = false;
1779                } else
1780                    e->max_dB = ((double) max_dB) / 100.0;
1781            }
1782        }
1783    }
1784
1785    if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1786        is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
1787    else
1788        is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
1789
1790    if (is_mono) {
1791        e->n_channels = 1;
1792
1793        if ((e->override_map & (1 << (e->n_channels-1))) && e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] == 0) {
1794            pa_log_warn("Override map for mono element %s is invalid, ignoring override map", e->path->name);
1795            e->override_map &= ~(1 << (e->n_channels-1));
1796        }
1797        if (!(e->override_map & (1 << (e->n_channels-1)))) {
1798            for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1799                if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1800                    continue;
1801                e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
1802            }
1803            e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
1804        }
1805        e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
1806        return true;
1807    }
1808
1809    e->n_channels = 0;
1810    for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1811        if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1812            continue;
1813
1814        if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1815            e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1816        else
1817            e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1818    }
1819
1820    if (e->n_channels <= 0) {
1821        pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
1822        pa_log_warn("Volume element %s with no channels?", buf);
1823        return false;
1824    } else if (e->n_channels > POSITION_MASK_CHANNELS) {
1825        /* FIXME: In some places code like this is used:
1826         *
1827         *     e->masks[alsa_channel_ids[p]][e->n_channels-1]
1828         *
1829         * The definition of e->masks is
1830         *
1831         *     pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][POSITION_MASK_CHANNELS];
1832         *
1833         * Since the array size is fixed at POSITION_MASK_CHANNELS, we obviously
1834         * don't support elements with more than POSITION_MASK_CHANNELS
1835         * channels... */
1836        pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
1837        pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", buf, e->n_channels);
1838        return false;
1839    }
1840
1841retry:
1842    if (!(e->override_map & (1 << (e->n_channels-1)))) {
1843        for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1844            bool has_channel;
1845
1846            if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1847                continue;
1848
1849            if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1850                has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1851            else
1852                has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1853
1854            e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
1855        }
1856    }
1857
1858    e->merged_mask = 0;
1859    for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1860        if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1861            continue;
1862
1863        e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
1864    }
1865
1866    if (e->merged_mask == 0) {
1867        if (!(e->override_map & (1 << (e->n_channels-1)))) {
1868            pa_log_warn("Channel map for element %s is invalid", e->path->name);
1869            return false;
1870        }
1871        pa_log_warn("Override map for element %s has empty result, ignoring override map", e->path->name);
1872        e->override_map &= ~(1 << (e->n_channels-1));
1873        goto retry;
1874    }
1875
1876    return true;
1877}
1878
1879static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
1880    snd_mixer_selem_id_t *sid;
1881    snd_mixer_elem_t *me;
1882
1883    pa_assert(m);
1884    pa_assert(e);
1885    pa_assert(e->path);
1886
1887    SELEM_INIT(sid, &e->alsa_id);
1888
1889    if (!(me = snd_mixer_find_selem(m, sid))) {
1890
1891        if (e->required != PA_ALSA_REQUIRED_IGNORE)
1892            return -1;
1893
1894        e->switch_use = PA_ALSA_SWITCH_IGNORE;
1895        e->volume_use = PA_ALSA_VOLUME_IGNORE;
1896        e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1897
1898        return 0;
1899    }
1900
1901    if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
1902        if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1903
1904            if (!snd_mixer_selem_has_playback_switch(me)) {
1905                if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
1906                    e->direction = PA_ALSA_DIRECTION_INPUT;
1907                else
1908                    e->switch_use = PA_ALSA_SWITCH_IGNORE;
1909            }
1910
1911        } else {
1912
1913            if (!snd_mixer_selem_has_capture_switch(me)) {
1914                if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
1915                    e->direction = PA_ALSA_DIRECTION_OUTPUT;
1916                else
1917                    e->switch_use = PA_ALSA_SWITCH_IGNORE;
1918            }
1919        }
1920
1921        if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
1922            e->direction_try_other = false;
1923    }
1924
1925    if (!element_probe_volume(e, me))
1926        e->volume_use = PA_ALSA_VOLUME_IGNORE;
1927
1928    if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1929        pa_alsa_option *o;
1930
1931        PA_LLIST_FOREACH(o, e->options)
1932            o->alsa_idx = pa_streq(o->alsa_name, "on") ? 1 : 0;
1933    } else if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1934        int n;
1935        pa_alsa_option *o;
1936
1937        if ((n = snd_mixer_selem_get_enum_items(me)) < 0) {
1938            pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n));
1939            return -1;
1940        }
1941
1942        PA_LLIST_FOREACH(o, e->options) {
1943            int i;
1944
1945            for (i = 0; i < n; i++) {
1946                char buf[128];
1947
1948                if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
1949                    continue;
1950
1951                if (!pa_streq(buf, o->alsa_name))
1952                    continue;
1953
1954                o->alsa_idx = i;
1955            }
1956        }
1957    }
1958
1959    if (check_required(e, me) < 0)
1960        return -1;
1961
1962    return 0;
1963}
1964
1965static int jack_probe(pa_alsa_jack *j, pa_alsa_mapping *mapping, snd_mixer_t *m) {
1966    bool has_control;
1967
1968    pa_assert(j);
1969    pa_assert(j->path);
1970
1971    if (j->append_pcm_to_name) {
1972        char *new_name;
1973
1974        if (!mapping) {
1975            /* This could also be an assertion, because this should never
1976             * happen. At the time of writing, mapping can only be NULL when
1977             * module-alsa-sink/source synthesizes a path, and those
1978             * synthesized paths never have any jacks, so jack_probe() should
1979             * never be called with a NULL mapping. */
1980            pa_log("Jack %s: append_pcm_to_name is set, but mapping is NULL. Can't use this jack.", j->name);
1981            return -1;
1982        }
1983
1984        new_name = pa_sprintf_malloc("%s,pcm=%i Jack", j->name, mapping->hw_device_index);
1985        pa_xfree(j->alsa_id.name);
1986        j->alsa_id.name = new_name;
1987        j->append_pcm_to_name = false;
1988    }
1989
1990    has_control = pa_alsa_mixer_find_card(m, &j->alsa_id, 0) != NULL;
1991    pa_alsa_jack_set_has_control(j, has_control);
1992
1993    if (j->has_control) {
1994        if (j->required_absent != PA_ALSA_REQUIRED_IGNORE)
1995            return -1;
1996        if (j->required_any != PA_ALSA_REQUIRED_IGNORE)
1997            j->path->req_any_present = true;
1998    } else {
1999        if (j->required != PA_ALSA_REQUIRED_IGNORE)
2000            return -1;
2001    }
2002
2003    return 0;
2004}
2005
2006pa_alsa_element * pa_alsa_element_get(pa_alsa_path *p, const char *section, bool prefixed) {
2007    pa_alsa_element *e;
2008    char *name;
2009    int index;
2010
2011    pa_assert(p);
2012    pa_assert(section);
2013
2014    if (prefixed) {
2015        if (!pa_startswith(section, "Element "))
2016            return NULL;
2017
2018        section += 8;
2019    }
2020
2021    /* This is not an element section, but an enum section? */
2022    if (strchr(section, ':'))
2023        return NULL;
2024
2025    name = alloca(strlen(section) + 1);
2026    if (alsa_id_decode(section, name, &index))
2027        return NULL;
2028
2029    if (p->last_element && pa_streq(p->last_element->alsa_id.name, name) &&
2030        p->last_element->alsa_id.index == index)
2031        return p->last_element;
2032
2033    PA_LLIST_FOREACH(e, p->elements)
2034        if (pa_streq(e->alsa_id.name, name) && e->alsa_id.index == index)
2035            goto finish;
2036
2037    e = pa_xnew0(pa_alsa_element, 1);
2038    e->path = p;
2039    e->alsa_id.name = pa_xstrdup(name);
2040    e->alsa_id.index = index;
2041    e->direction = p->direction;
2042    e->volume_limit = -1;
2043
2044    PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2045
2046finish:
2047    p->last_element = e;
2048    return e;
2049}
2050
2051static pa_alsa_jack* jack_get(pa_alsa_path *p, const char *section) {
2052    pa_alsa_jack *j;
2053    char *name;
2054    int index;
2055
2056    if (!pa_startswith(section, "Jack "))
2057        return NULL;
2058    section += 5;
2059
2060    name = alloca(strlen(section) + 1);
2061    if (alsa_id_decode(section, name, &index))
2062        return NULL;
2063
2064    if (p->last_jack && pa_streq(p->last_jack->name, name) &&
2065        p->last_jack->alsa_id.index == index)
2066        return p->last_jack;
2067
2068    PA_LLIST_FOREACH(j, p->jacks)
2069        if (pa_streq(j->name, name) && j->alsa_id.index == index)
2070            goto finish;
2071
2072    j = pa_alsa_jack_new(p, NULL, name, index);
2073    PA_LLIST_INSERT_AFTER(pa_alsa_jack, p->jacks, p->last_jack, j);
2074
2075finish:
2076    p->last_jack = j;
2077    return j;
2078}
2079
2080static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
2081    char *en, *name;
2082    const char *on;
2083    pa_alsa_option *o;
2084    pa_alsa_element *e;
2085    size_t len;
2086    int index;
2087
2088    if (!pa_startswith(section, "Option "))
2089        return NULL;
2090
2091    section += 7;
2092
2093    /* This is not an enum section, but an element section? */
2094    if (!(on = strchr(section, ':')))
2095        return NULL;
2096
2097    len = on - section;
2098    en = alloca(len + 1);
2099    strncpy(en, section, len);
2100    en[len] = '\0';
2101
2102    name = alloca(strlen(en) + 1);
2103    if (alsa_id_decode(en, name, &index))
2104        return NULL;
2105
2106    on++;
2107
2108    if (p->last_option &&
2109        pa_streq(p->last_option->element->alsa_id.name, name) &&
2110        p->last_option->element->alsa_id.index == index &&
2111        pa_streq(p->last_option->alsa_name, on)) {
2112        return p->last_option;
2113    }
2114
2115    pa_assert_se(e = pa_alsa_element_get(p, en, false));
2116
2117    PA_LLIST_FOREACH(o, e->options)
2118        if (pa_streq(o->alsa_name, on))
2119            goto finish;
2120
2121    o = pa_xnew0(pa_alsa_option, 1);
2122    o->element = e;
2123    o->alsa_name = pa_xstrdup(on);
2124    o->alsa_idx = -1;
2125
2126    if (p->last_option && p->last_option->element == e)
2127        PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
2128    else
2129        PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
2130
2131finish:
2132    p->last_option = o;
2133    return o;
2134}
2135
2136static int element_parse_switch(pa_config_parser_state *state) {
2137    pa_alsa_path *p;
2138    pa_alsa_element *e;
2139
2140    pa_assert(state);
2141
2142    p = state->userdata;
2143
2144    if (!(e = pa_alsa_element_get(p, state->section, true))) {
2145        pa_log("[%s:%u] Switch makes no sense in '%s'", state->filename, state->lineno, state->section);
2146        return -1;
2147    }
2148
2149    if (pa_streq(state->rvalue, "ignore"))
2150        e->switch_use = PA_ALSA_SWITCH_IGNORE;
2151    else if (pa_streq(state->rvalue, "mute"))
2152        e->switch_use = PA_ALSA_SWITCH_MUTE;
2153    else if (pa_streq(state->rvalue, "off"))
2154        e->switch_use = PA_ALSA_SWITCH_OFF;
2155    else if (pa_streq(state->rvalue, "on"))
2156        e->switch_use = PA_ALSA_SWITCH_ON;
2157    else if (pa_streq(state->rvalue, "select"))
2158        e->switch_use = PA_ALSA_SWITCH_SELECT;
2159    else {
2160        pa_log("[%s:%u] Switch invalid of '%s'", state->filename, state->lineno, state->section);
2161        return -1;
2162    }
2163
2164    return 0;
2165}
2166
2167static int element_parse_volume(pa_config_parser_state *state) {
2168    pa_alsa_path *p;
2169    pa_alsa_element *e;
2170
2171    pa_assert(state);
2172
2173    p = state->userdata;
2174
2175    if (!(e = pa_alsa_element_get(p, state->section, true))) {
2176        pa_log("[%s:%u] Volume makes no sense in '%s'", state->filename, state->lineno, state->section);
2177        return -1;
2178    }
2179
2180    if (pa_streq(state->rvalue, "ignore"))
2181        e->volume_use = PA_ALSA_VOLUME_IGNORE;
2182    else if (pa_streq(state->rvalue, "merge"))
2183        e->volume_use = PA_ALSA_VOLUME_MERGE;
2184    else if (pa_streq(state->rvalue, "off"))
2185        e->volume_use = PA_ALSA_VOLUME_OFF;
2186    else if (pa_streq(state->rvalue, "zero"))
2187        e->volume_use = PA_ALSA_VOLUME_ZERO;
2188    else {
2189        uint32_t constant;
2190
2191        if (pa_atou(state->rvalue, &constant) >= 0) {
2192            e->volume_use = PA_ALSA_VOLUME_CONSTANT;
2193            e->constant_volume = constant;
2194        } else {
2195            pa_log("[%s:%u] Volume invalid of '%s'", state->filename, state->lineno, state->section);
2196            return -1;
2197        }
2198    }
2199
2200    return 0;
2201}
2202
2203static int element_parse_enumeration(pa_config_parser_state *state) {
2204    pa_alsa_path *p;
2205    pa_alsa_element *e;
2206
2207    pa_assert(state);
2208
2209    p = state->userdata;
2210
2211    if (!(e = pa_alsa_element_get(p, state->section, true))) {
2212        pa_log("[%s:%u] Enumeration makes no sense in '%s'", state->filename, state->lineno, state->section);
2213        return -1;
2214    }
2215
2216    if (pa_streq(state->rvalue, "ignore"))
2217        e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
2218    else if (pa_streq(state->rvalue, "select"))
2219        e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
2220    else {
2221        pa_log("[%s:%u] Enumeration invalid of '%s'", state->filename, state->lineno, state->section);
2222        return -1;
2223    }
2224
2225    return 0;
2226}
2227
2228static int parse_type(pa_config_parser_state *state) {
2229    struct device_port_types {
2230        const char *name;
2231        pa_device_port_type_t type;
2232    } device_port_types[] = {
2233        { "unknown",      PA_DEVICE_PORT_TYPE_UNKNOWN },
2234        { "aux",          PA_DEVICE_PORT_TYPE_AUX },
2235        { "speaker",      PA_DEVICE_PORT_TYPE_SPEAKER },
2236        { "headphones",   PA_DEVICE_PORT_TYPE_HEADPHONES },
2237        { "line",         PA_DEVICE_PORT_TYPE_LINE },
2238        { "mic",          PA_DEVICE_PORT_TYPE_MIC },
2239        { "headset",      PA_DEVICE_PORT_TYPE_HEADSET },
2240        { "handset",      PA_DEVICE_PORT_TYPE_HANDSET },
2241        { "earpiece",     PA_DEVICE_PORT_TYPE_EARPIECE },
2242        { "spdif",        PA_DEVICE_PORT_TYPE_SPDIF },
2243        { "hdmi",         PA_DEVICE_PORT_TYPE_HDMI },
2244        { "tv",           PA_DEVICE_PORT_TYPE_TV },
2245        { "radio",        PA_DEVICE_PORT_TYPE_RADIO },
2246        { "video",        PA_DEVICE_PORT_TYPE_VIDEO },
2247        { "usb",          PA_DEVICE_PORT_TYPE_USB },
2248        { "bluetooth",    PA_DEVICE_PORT_TYPE_BLUETOOTH },
2249        { "portable",     PA_DEVICE_PORT_TYPE_PORTABLE },
2250        { "handsfree",    PA_DEVICE_PORT_TYPE_HANDSFREE },
2251        { "car",          PA_DEVICE_PORT_TYPE_CAR },
2252        { "hifi",         PA_DEVICE_PORT_TYPE_HIFI },
2253        { "phone",        PA_DEVICE_PORT_TYPE_PHONE },
2254        { "network",      PA_DEVICE_PORT_TYPE_NETWORK },
2255        { "analog",       PA_DEVICE_PORT_TYPE_ANALOG },
2256    };
2257    pa_alsa_path *path;
2258    unsigned int idx;
2259
2260    path = state->userdata;
2261
2262    for (idx = 0; idx < PA_ELEMENTSOF(device_port_types); idx++)
2263        if (pa_streq(state->rvalue, device_port_types[idx].name)) {
2264            path->device_port_type = device_port_types[idx].type;
2265            return 0;
2266        }
2267
2268    pa_log("[%s:%u] Invalid value for option 'type': %s", state->filename, state->lineno, state->rvalue);
2269    return -1;
2270}
2271
2272static int parse_eld_device(pa_config_parser_state *state) {
2273    pa_alsa_path *path;
2274    uint32_t eld_device;
2275
2276    path = state->userdata;
2277
2278    if (pa_atou(state->rvalue, &eld_device) >= 0) {
2279        path->autodetect_eld_device = false;
2280        path->eld_device = eld_device;
2281        return 0;
2282    }
2283
2284    if (pa_streq(state->rvalue, "auto")) {
2285        path->autodetect_eld_device = true;
2286        path->eld_device = -1;
2287        return 0;
2288    }
2289
2290    pa_log("[%s:%u] Invalid value for option 'eld-device': %s", state->filename, state->lineno, state->rvalue);
2291    return -1;
2292}
2293
2294static int option_parse_priority(pa_config_parser_state *state) {
2295    pa_alsa_path *p;
2296    pa_alsa_option *o;
2297    uint32_t prio;
2298
2299    pa_assert(state);
2300
2301    p = state->userdata;
2302
2303    if (!(o = option_get(p, state->section))) {
2304        pa_log("[%s:%u] Priority makes no sense in '%s'", state->filename, state->lineno, state->section);
2305        return -1;
2306    }
2307
2308    if (pa_atou(state->rvalue, &prio) < 0) {
2309        pa_log("[%s:%u] Priority invalid of '%s'", state->filename, state->lineno, state->section);
2310        return -1;
2311    }
2312
2313    o->priority = prio;
2314    return 0;
2315}
2316
2317static int option_parse_name(pa_config_parser_state *state) {
2318    pa_alsa_path *p;
2319    pa_alsa_option *o;
2320
2321    pa_assert(state);
2322
2323    p = state->userdata;
2324
2325    if (!(o = option_get(p, state->section))) {
2326        pa_log("[%s:%u] Name makes no sense in '%s'", state->filename, state->lineno, state->section);
2327        return -1;
2328    }
2329
2330    pa_xfree(o->name);
2331    o->name = pa_xstrdup(state->rvalue);
2332
2333    return 0;
2334}
2335
2336static int element_parse_required(pa_config_parser_state *state) {
2337    pa_alsa_path *p;
2338    pa_alsa_element *e;
2339    pa_alsa_option *o;
2340    pa_alsa_jack *j;
2341    pa_alsa_required_t req;
2342
2343    pa_assert(state);
2344
2345    p = state->userdata;
2346
2347    e = pa_alsa_element_get(p, state->section, true);
2348    o = option_get(p, state->section);
2349    j = jack_get(p, state->section);
2350    if (!e && !o && !j) {
2351        pa_log("[%s:%u] Required makes no sense in '%s'", state->filename, state->lineno, state->section);
2352        return -1;
2353    }
2354
2355    if (pa_streq(state->rvalue, "ignore"))
2356        req = PA_ALSA_REQUIRED_IGNORE;
2357    else if (pa_streq(state->rvalue, "switch") && e)
2358        req = PA_ALSA_REQUIRED_SWITCH;
2359    else if (pa_streq(state->rvalue, "volume") && e)
2360        req = PA_ALSA_REQUIRED_VOLUME;
2361    else if (pa_streq(state->rvalue, "enumeration"))
2362        req = PA_ALSA_REQUIRED_ENUMERATION;
2363    else if (pa_streq(state->rvalue, "any"))
2364        req = PA_ALSA_REQUIRED_ANY;
2365    else {
2366        pa_log("[%s:%u] Required invalid of '%s'", state->filename, state->lineno, state->section);
2367        return -1;
2368    }
2369
2370    if (pa_streq(state->lvalue, "required-absent")) {
2371        if (e)
2372            e->required_absent = req;
2373        if (o)
2374            o->required_absent = req;
2375        if (j)
2376            j->required_absent = req;
2377    }
2378    else if (pa_streq(state->lvalue, "required-any")) {
2379        if (e) {
2380            e->required_any = req;
2381            e->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
2382        }
2383        if (o) {
2384            o->required_any = req;
2385            o->element->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
2386        }
2387        if (j) {
2388            j->required_any = req;
2389            j->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
2390        }
2391
2392    }
2393    else {
2394        if (e)
2395            e->required = req;
2396        if (o)
2397            o->required = req;
2398        if (j)
2399            j->required = req;
2400    }
2401
2402    return 0;
2403}
2404
2405static int element_parse_direction(pa_config_parser_state *state) {
2406    pa_alsa_path *p;
2407    pa_alsa_element *e;
2408
2409    pa_assert(state);
2410
2411    p = state->userdata;
2412
2413    if (!(e = pa_alsa_element_get(p, state->section, true))) {
2414        pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section);
2415        return -1;
2416    }
2417
2418    if (pa_streq(state->rvalue, "playback"))
2419        e->direction = PA_ALSA_DIRECTION_OUTPUT;
2420    else if (pa_streq(state->rvalue, "capture"))
2421        e->direction = PA_ALSA_DIRECTION_INPUT;
2422    else {
2423        pa_log("[%s:%u] Direction invalid of '%s'", state->filename, state->lineno, state->section);
2424        return -1;
2425    }
2426
2427    return 0;
2428}
2429
2430static int element_parse_direction_try_other(pa_config_parser_state *state) {
2431    pa_alsa_path *p;
2432    pa_alsa_element *e;
2433    int yes;
2434
2435    pa_assert(state);
2436
2437    p = state->userdata;
2438
2439    if (!(e = pa_alsa_element_get(p, state->section, true))) {
2440        pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section);
2441        return -1;
2442    }
2443
2444    if ((yes = pa_parse_boolean(state->rvalue)) < 0) {
2445        pa_log("[%s:%u] Direction invalid of '%s'", state->filename, state->lineno, state->section);
2446        return -1;
2447    }
2448
2449    e->direction_try_other = !!yes;
2450    return 0;
2451}
2452
2453static int element_parse_volume_limit(pa_config_parser_state *state) {
2454    pa_alsa_path *p;
2455    pa_alsa_element *e;
2456    long volume_limit;
2457
2458    pa_assert(state);
2459
2460    p = state->userdata;
2461
2462    if (!(e = pa_alsa_element_get(p, state->section, true))) {
2463        pa_log("[%s:%u] volume-limit makes no sense in '%s'", state->filename, state->lineno, state->section);
2464        return -1;
2465    }
2466
2467    if (pa_atol(state->rvalue, &volume_limit) < 0 || volume_limit < 0) {
2468        pa_log("[%s:%u] Invalid value for volume-limit", state->filename, state->lineno);
2469        return -1;
2470    }
2471
2472    e->volume_limit = volume_limit;
2473    return 0;
2474}
2475
2476static unsigned int parse_channel_position(const char *m)
2477{
2478    pa_channel_position_t p;
2479
2480    if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
2481        return SND_MIXER_SCHN_UNKNOWN;
2482
2483    return alsa_channel_ids[p];
2484}
2485
2486static pa_channel_position_mask_t parse_mask(const char *m) {
2487    pa_channel_position_mask_t v;
2488
2489    if (pa_streq(m, "all-left"))
2490        v = PA_CHANNEL_POSITION_MASK_LEFT;
2491    else if (pa_streq(m, "all-right"))
2492        v = PA_CHANNEL_POSITION_MASK_RIGHT;
2493    else if (pa_streq(m, "all-center"))
2494        v = PA_CHANNEL_POSITION_MASK_CENTER;
2495    else if (pa_streq(m, "all-front"))
2496        v = PA_CHANNEL_POSITION_MASK_FRONT;
2497    else if (pa_streq(m, "all-rear"))
2498        v = PA_CHANNEL_POSITION_MASK_REAR;
2499    else if (pa_streq(m, "all-side"))
2500        v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER;
2501    else if (pa_streq(m, "all-top"))
2502        v = PA_CHANNEL_POSITION_MASK_TOP;
2503    else if (pa_streq(m, "all-no-lfe"))
2504        v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE);
2505    else if (pa_streq(m, "all"))
2506        v = PA_CHANNEL_POSITION_MASK_ALL;
2507    else {
2508        pa_channel_position_t p;
2509
2510        if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
2511            return 0;
2512
2513        v = PA_CHANNEL_POSITION_MASK(p);
2514    }
2515
2516    return v;
2517}
2518
2519static int element_parse_override_map(pa_config_parser_state *state) {
2520    pa_alsa_path *p;
2521    pa_alsa_element *e;
2522    const char *split_state = NULL;
2523    char *s;
2524    unsigned i = 0;
2525    int channel_count = 0;
2526    char *n;
2527
2528    pa_assert(state);
2529
2530    p = state->userdata;
2531
2532    if (!(e = pa_alsa_element_get(p, state->section, true))) {
2533        pa_log("[%s:%u] Override map makes no sense in '%s'", state->filename, state->lineno, state->section);
2534        return -1;
2535    }
2536
2537    s = strstr(state->lvalue, ".");
2538    if (s) {
2539        pa_atoi(s + 1, &channel_count);
2540        if (channel_count < 1 || channel_count > POSITION_MASK_CHANNELS) {
2541            pa_log("[%s:%u] Override map index '%s' invalid in '%s'", state->filename, state->lineno, state->lvalue, state->section);
2542            return 0;
2543        }
2544    } else {
2545        pa_log("[%s:%u] Invalid override map syntax '%s' in '%s'", state->filename, state->lineno, state->lvalue, state->section);
2546        return -1;
2547    }
2548
2549    while ((n = pa_split(state->rvalue, ",", &split_state))) {
2550        pa_channel_position_mask_t m;
2551        snd_mixer_selem_channel_id_t channel_position;
2552
2553        if (i >= (unsigned)channel_count) {
2554            pa_log("[%s:%u] Invalid override map size (>%d) in '%s'", state->filename, state->lineno, channel_count, state->section);
2555            return -1;
2556        }
2557        channel_position = alsa_channel_positions[i];
2558
2559        if (!*n)
2560            m = 0;
2561        else {
2562            s = strstr(n, ":");
2563            if (s) {
2564                *s = '\0';
2565                s++;
2566                channel_position = parse_channel_position(n);
2567                if (channel_position == SND_MIXER_SCHN_UNKNOWN) {
2568                    pa_log("[%s:%u] Override map position '%s' invalid in '%s'", state->filename, state->lineno, n, state->section);
2569                    pa_xfree(n);
2570                    return -1;
2571                }
2572            }
2573            if ((m = parse_mask(s ? s : n)) == 0) {
2574                pa_log("[%s:%u] Override map '%s' invalid in '%s'", state->filename, state->lineno, s ? s : n, state->section);
2575                pa_xfree(n);
2576                return -1;
2577            }
2578        }
2579
2580        if (e->masks[channel_position][channel_count-1]) {
2581            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);
2582            pa_xfree(n);
2583            return -1;
2584        }
2585        e->override_map |= (1 << (channel_count - 1));
2586        e->masks[channel_position][channel_count-1] = m;
2587        pa_xfree(n);
2588        i++;
2589    }
2590
2591    return 0;
2592}
2593
2594static int jack_parse_state(pa_config_parser_state *state) {
2595    pa_alsa_path *p;
2596    pa_alsa_jack *j;
2597    pa_available_t pa;
2598
2599    pa_assert(state);
2600
2601    p = state->userdata;
2602
2603    if (!(j = jack_get(p, state->section))) {
2604        pa_log("[%s:%u] state makes no sense in '%s'", state->filename, state->lineno, state->section);
2605        return -1;
2606    }
2607
2608    if (pa_streq(state->rvalue, "yes"))
2609        pa = PA_AVAILABLE_YES;
2610    else if (pa_streq(state->rvalue, "no"))
2611        pa = PA_AVAILABLE_NO;
2612    else if (pa_streq(state->rvalue, "unknown"))
2613        pa = PA_AVAILABLE_UNKNOWN;
2614    else {
2615        pa_log("[%s:%u] state must be 'yes', 'no' or 'unknown' in '%s'", state->filename, state->lineno, state->section);
2616        return -1;
2617    }
2618
2619    if (pa_streq(state->lvalue, "state.unplugged"))
2620        j->state_unplugged = pa;
2621    else {
2622        j->state_plugged = pa;
2623        pa_assert(pa_streq(state->lvalue, "state.plugged"));
2624    }
2625
2626    return 0;
2627}
2628
2629static int jack_parse_append_pcm_to_name(pa_config_parser_state *state) {
2630    pa_alsa_path *path;
2631    pa_alsa_jack *jack;
2632    int b;
2633
2634    pa_assert(state);
2635
2636    path = state->userdata;
2637    if (!(jack = jack_get(path, state->section))) {
2638        pa_log("[%s:%u] Option 'append_pcm_to_name' not expected in section '%s'",
2639               state->filename, state->lineno, state->section);
2640        return -1;
2641    }
2642
2643    b = pa_parse_boolean(state->rvalue);
2644    if (b < 0) {
2645        pa_log("[%s:%u] Invalid value for 'append_pcm_to_name': %s", state->filename, state->lineno, state->rvalue);
2646        return -1;
2647    }
2648
2649    jack->append_pcm_to_name = b;
2650    return 0;
2651}
2652
2653static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
2654    snd_mixer_selem_id_t *sid;
2655    snd_mixer_elem_t *me;
2656    char buf[64];
2657    int r;
2658
2659    pa_assert(e);
2660    pa_assert(m);
2661
2662    SELEM_INIT(sid, &e->alsa_id);
2663    if (!(me = snd_mixer_find_selem(m, sid))) {
2664        pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
2665        pa_log_warn("Element %s seems to have disappeared.", buf);
2666        return -1;
2667    }
2668
2669    if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
2670
2671        if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
2672            r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
2673        else
2674            r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
2675
2676        if (r < 0) {
2677            pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
2678            pa_log_warn("Failed to set switch of %s: %s", buf, pa_alsa_strerror(errno));
2679        }
2680
2681    } else {
2682        pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
2683
2684        if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0) {
2685            pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
2686            pa_log_warn("Failed to set enumeration of %s: %s", buf, pa_alsa_strerror(errno));
2687        }
2688    }
2689
2690    return r;
2691}
2692
2693static int setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
2694    pa_alsa_option *o;
2695    uint32_t idx;
2696
2697    pa_assert(s);
2698    pa_assert(m);
2699
2700    PA_IDXSET_FOREACH(o, s->options, idx)
2701        element_set_option(o->element, m, o->alsa_idx);
2702
2703    return 0;
2704}
2705
2706static int option_verify(pa_alsa_option *o) {
2707    static const struct description_map well_known_descriptions[] = {
2708        { "input",                     N_("Input") },
2709        { "input-docking",             N_("Docking Station Input") },
2710        { "input-docking-microphone",  N_("Docking Station Microphone") },
2711        { "input-docking-linein",      N_("Docking Station Line In") },
2712        { "input-linein",              N_("Line In") },
2713        { "input-microphone",          N_("Microphone") },
2714        { "input-microphone-front",    N_("Front Microphone") },
2715        { "input-microphone-rear",     N_("Rear Microphone") },
2716        { "input-microphone-external", N_("External Microphone") },
2717        { "input-microphone-internal", N_("Internal Microphone") },
2718        { "input-radio",               N_("Radio") },
2719        { "input-video",               N_("Video") },
2720        { "input-agc-on",              N_("Automatic Gain Control") },
2721        { "input-agc-off",             N_("No Automatic Gain Control") },
2722        { "input-boost-on",            N_("Boost") },
2723        { "input-boost-off",           N_("No Boost") },
2724        { "output-amplifier-on",       N_("Amplifier") },
2725        { "output-amplifier-off",      N_("No Amplifier") },
2726        { "output-bass-boost-on",      N_("Bass Boost") },
2727        { "output-bass-boost-off",     N_("No Bass Boost") },
2728        { "output-speaker",            N_("Speaker") },
2729        { "output-headphones",         N_("Headphones") }
2730    };
2731    char buf[64];
2732
2733    pa_assert(o);
2734
2735    if (!o->name) {
2736        pa_log("No name set for option %s", o->alsa_name);
2737        return -1;
2738    }
2739
2740    if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
2741        o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
2742        pa_alsa_mixer_id_to_string(buf, sizeof(buf), &o->element->alsa_id);
2743        pa_log("Element %s of option %s not set for select.", buf, o->name);
2744        return -1;
2745    }
2746
2747    if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
2748        !pa_streq(o->alsa_name, "on") &&
2749        !pa_streq(o->alsa_name, "off")) {
2750        pa_alsa_mixer_id_to_string(buf, sizeof(buf), &o->element->alsa_id);
2751        pa_log("Switch %s options need be named off or on ", buf);
2752        return -1;
2753    }
2754
2755    if (!o->description)
2756        o->description = pa_xstrdup(lookup_description(o->name,
2757                                                       well_known_descriptions,
2758                                                       PA_ELEMENTSOF(well_known_descriptions)));
2759    if (!o->description)
2760        o->description = pa_xstrdup(o->name);
2761
2762    return 0;
2763}
2764
2765static int element_verify(pa_alsa_element *e) {
2766    pa_alsa_option *o;
2767    char buf[64];
2768
2769    pa_assert(e);
2770
2771//    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);
2772    if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
2773        (e->required_any != PA_ALSA_REQUIRED_IGNORE && e->required_any == e->required_absent) ||
2774        (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required_any != PA_ALSA_REQUIRED_IGNORE) ||
2775        (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
2776        pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
2777        pa_log("Element %s cannot be required and absent at the same time.", buf);
2778        return -1;
2779    }
2780
2781    if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
2782        pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
2783        pa_log("Element %s cannot set select for both switch and enumeration.", buf);
2784        return -1;
2785    }
2786
2787    PA_LLIST_FOREACH(o, e->options)
2788        if (option_verify(o) < 0)
2789            return -1;
2790
2791    return 0;
2792}
2793
2794static int path_verify(pa_alsa_path *p) {
2795    static const struct description2_map well_known_descriptions[] = {
2796        { "analog-input",                     N_("Analog Input"),                 PA_DEVICE_PORT_TYPE_ANALOG },
2797        { "analog-input-microphone",          N_("Microphone"),                   PA_DEVICE_PORT_TYPE_MIC },
2798        { "analog-input-microphone-front",    N_("Front Microphone"),             PA_DEVICE_PORT_TYPE_MIC },
2799        { "analog-input-microphone-rear",     N_("Rear Microphone"),              PA_DEVICE_PORT_TYPE_MIC },
2800        { "analog-input-microphone-dock",     N_("Dock Microphone"),              PA_DEVICE_PORT_TYPE_MIC },
2801        { "analog-input-microphone-internal", N_("Internal Microphone"),          PA_DEVICE_PORT_TYPE_MIC },
2802        { "analog-input-microphone-headset",  N_("Headset Microphone"),           PA_DEVICE_PORT_TYPE_HEADSET },
2803        { "analog-input-linein",              N_("Line In"),                      PA_DEVICE_PORT_TYPE_LINE },
2804        { "analog-input-radio",               N_("Radio"),                        PA_DEVICE_PORT_TYPE_RADIO },
2805        { "analog-input-video",               N_("Video"),                        PA_DEVICE_PORT_TYPE_VIDEO },
2806        { "analog-output",                    N_("Analog Output"),                PA_DEVICE_PORT_TYPE_ANALOG },
2807        { "analog-output-headphones",         N_("Headphones"),                   PA_DEVICE_PORT_TYPE_HEADPHONES },
2808        { "analog-output-headphones-2",       N_("Headphones 2"),                 PA_DEVICE_PORT_TYPE_HEADPHONES },
2809        { "analog-output-headphones-mono",    N_("Headphones Mono Output"),       PA_DEVICE_PORT_TYPE_HEADPHONES },
2810        { "analog-output-lineout",            N_("Line Out"),                     PA_DEVICE_PORT_TYPE_LINE },
2811        { "analog-output-mono",               N_("Analog Mono Output"),           PA_DEVICE_PORT_TYPE_ANALOG },
2812        { "analog-output-speaker",            N_("Speakers"),                     PA_DEVICE_PORT_TYPE_SPEAKER },
2813        { "hdmi-output",                      N_("HDMI / DisplayPort"),           PA_DEVICE_PORT_TYPE_HDMI },
2814        { "iec958-stereo-output",             N_("Digital Output (S/PDIF)"),      PA_DEVICE_PORT_TYPE_SPDIF },
2815        { "iec958-stereo-input",              N_("Digital Input (S/PDIF)"),       PA_DEVICE_PORT_TYPE_SPDIF },
2816        { "multichannel-input",               N_("Multichannel Input"),           PA_DEVICE_PORT_TYPE_LINE },
2817        { "multichannel-output",              N_("Multichannel Output"),          PA_DEVICE_PORT_TYPE_LINE },
2818        { "steelseries-arctis-output-game-common", N_("Game Output"),             PA_DEVICE_PORT_TYPE_HEADSET },
2819        { "steelseries-arctis-output-chat-common", N_("Chat Output"),             PA_DEVICE_PORT_TYPE_HEADSET },
2820        { "analog-chat-output",               N_("Chat Output"),                  PA_DEVICE_PORT_TYPE_HEADSET },
2821        { "analog-chat-input",                N_("Chat Input"),                   PA_DEVICE_PORT_TYPE_HEADSET },
2822        { "virtual-surround-7.1",             N_("Virtual Surround 7.1"),         PA_DEVICE_PORT_TYPE_HEADPHONES },
2823    };
2824
2825    pa_alsa_element *e;
2826    const char *key = p->description_key ? p->description_key : p->name;
2827    const struct description2_map *map = lookup_description2(key,
2828                                                             well_known_descriptions,
2829                                                             PA_ELEMENTSOF(well_known_descriptions));
2830
2831    pa_assert(p);
2832
2833    PA_LLIST_FOREACH(e, p->elements)
2834        if (element_verify(e) < 0)
2835            return -1;
2836
2837    if (map) {
2838        if (p->device_port_type == PA_DEVICE_PORT_TYPE_UNKNOWN)
2839            p->device_port_type = map->type;
2840        if (!p->description)
2841            p->description = pa_xstrdup(map->description);
2842    }
2843
2844    if (!p->description) {
2845        if (p->description_key)
2846            pa_log_warn("Path %s: Unrecognized description key: %s", p->name, p->description_key);
2847
2848        p->description = pa_xstrdup(p->name);
2849    }
2850
2851    return 0;
2852}
2853
2854static char *get_path_config_path(const char *paths_dir, const char *fname) {
2855    char *path_config_path;
2856    char *dir;
2857    char *data_home;
2858    pa_dynarray *data_dirs;
2859
2860    if (paths_dir) {
2861        path_config_path = pa_maybe_prefix_path(fname, paths_dir);
2862        if (access(path_config_path, R_OK) == 0)
2863            return path_config_path;
2864        else
2865            pa_xfree(path_config_path);
2866    }
2867
2868#ifdef HAVE_RUNNING_FROM_BUILD_TREE
2869    if (pa_run_from_build_tree()) {
2870        path_config_path = pa_maybe_prefix_path(fname, PA_SRCDIR "/modules/alsa/mixer/paths/");
2871        if (access(path_config_path, R_OK) == 0)
2872            return path_config_path;
2873        else
2874            pa_xfree(path_config_path);
2875    }
2876#endif
2877
2878    if (pa_get_data_home_dir(&data_home) == 0) {
2879        dir = pa_sprintf_malloc("%s" PA_PATH_SEP "alsa-mixer" PA_PATH_SEP "paths", data_home);
2880        pa_xfree(data_home);
2881
2882        path_config_path = pa_maybe_prefix_path(fname, dir);
2883        pa_xfree(dir);
2884
2885        if (access(path_config_path, R_OK) == 0)
2886            return path_config_path;
2887        else
2888            pa_xfree(path_config_path);
2889    }
2890
2891    if (pa_get_data_dirs(&data_dirs) == 0) {
2892        int idx;
2893        const char *n;
2894
2895        PA_DYNARRAY_FOREACH(n, data_dirs, idx) {
2896            dir = pa_sprintf_malloc("%s" PA_PATH_SEP "alsa-mixer" PA_PATH_SEP "paths", n);
2897            path_config_path = pa_maybe_prefix_path(fname, dir);
2898            pa_xfree(dir);
2899
2900            if (access(path_config_path, R_OK) == 0) {
2901                pa_dynarray_free(data_dirs);
2902                return path_config_path;
2903            }
2904            else {
2905                pa_xfree(path_config_path);
2906            }
2907        }
2908
2909        pa_dynarray_free(data_dirs);
2910    }
2911
2912    path_config_path = pa_maybe_prefix_path(fname, PA_ALSA_PATHS_DIR);
2913    return path_config_path;
2914}
2915
2916pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa_direction_t direction) {
2917    pa_alsa_path *p;
2918    char *fn;
2919    int r;
2920    const char *n;
2921    bool mute_during_activation = false;
2922
2923    pa_config_item items[] = {
2924        /* [General] */
2925        { "priority",            pa_config_parse_unsigned,          NULL, "General" },
2926        { "description-key",     pa_config_parse_string,            NULL, "General" },
2927        { "description",         pa_config_parse_string,            NULL, "General" },
2928        { "mute-during-activation", pa_config_parse_bool,           NULL, "General" },
2929        { "type",                parse_type,                        NULL, "General" },
2930        { "eld-device",          parse_eld_device,                  NULL, "General" },
2931
2932        /* [Option ...] */
2933        { "priority",            option_parse_priority,             NULL, NULL },
2934        { "name",                option_parse_name,                 NULL, NULL },
2935
2936        /* [Jack ...] */
2937        { "state.plugged",       jack_parse_state,                  NULL, NULL },
2938        { "state.unplugged",     jack_parse_state,                  NULL, NULL },
2939        { "append-pcm-to-name",  jack_parse_append_pcm_to_name,     NULL, NULL },
2940
2941        /* [Element ...] */
2942        { "switch",              element_parse_switch,              NULL, NULL },
2943        { "volume",              element_parse_volume,              NULL, NULL },
2944        { "enumeration",         element_parse_enumeration,         NULL, NULL },
2945        { "override-map.1",      element_parse_override_map,        NULL, NULL },
2946        { "override-map.2",      element_parse_override_map,        NULL, NULL },
2947        { "override-map.3",      element_parse_override_map,        NULL, NULL },
2948        { "override-map.4",      element_parse_override_map,        NULL, NULL },
2949        { "override-map.5",      element_parse_override_map,        NULL, NULL },
2950        { "override-map.6",      element_parse_override_map,        NULL, NULL },
2951        { "override-map.7",      element_parse_override_map,        NULL, NULL },
2952        { "override-map.8",      element_parse_override_map,        NULL, NULL },
2953#if POSITION_MASK_CHANNELS > 8
2954#error "Add override-map.9+ definitions"
2955#endif
2956        /* ... later on we might add override-map.3 and so on here ... */
2957        { "required",            element_parse_required,            NULL, NULL },
2958        { "required-any",        element_parse_required,            NULL, NULL },
2959        { "required-absent",     element_parse_required,            NULL, NULL },
2960        { "direction",           element_parse_direction,           NULL, NULL },
2961        { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
2962        { "volume-limit",        element_parse_volume_limit,        NULL, NULL },
2963        { NULL, NULL, NULL, NULL }
2964    };
2965
2966    pa_assert(fname);
2967
2968    p = pa_xnew0(pa_alsa_path, 1);
2969    n = pa_path_get_filename(fname);
2970    p->name = pa_xstrndup(n, strcspn(n, "."));
2971    p->proplist = pa_proplist_new();
2972    p->direction = direction;
2973    p->eld_device = -1;
2974
2975    items[0].data = &p->priority;
2976    items[1].data = &p->description_key;
2977    items[2].data = &p->description;
2978    items[3].data = &mute_during_activation;
2979
2980    fn = get_path_config_path(paths_dir, fname);
2981
2982    pa_log_info("Loading path config: %s", fn);
2983
2984    r = pa_config_parse(fn, NULL, items, p->proplist, false, p);
2985    pa_xfree(fn);
2986
2987    if (r < 0)
2988        goto fail;
2989
2990    p->mute_during_activation = mute_during_activation;
2991
2992    if (path_verify(p) < 0)
2993        goto fail;
2994
2995    return p;
2996
2997fail:
2998    pa_alsa_path_free(p);
2999    return NULL;
3000}
3001
3002pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction) {
3003    pa_alsa_path *p;
3004    pa_alsa_element *e;
3005    char *name;
3006    int index;
3007
3008    pa_assert(element);
3009
3010    name = alloca(strlen(element) + 1);
3011    if (alsa_id_decode(element, name, &index))
3012        return NULL;
3013
3014    p = pa_xnew0(pa_alsa_path, 1);
3015    p->name = pa_xstrdup(element);
3016    p->direction = direction;
3017    p->proplist = pa_proplist_new();
3018
3019    e = pa_xnew0(pa_alsa_element, 1);
3020    e->path = p;
3021    e->alsa_id.name = pa_xstrdup(name);
3022    e->alsa_id.index = index;
3023    e->direction = direction;
3024    e->volume_limit = -1;
3025
3026    e->switch_use = PA_ALSA_SWITCH_MUTE;
3027    e->volume_use = PA_ALSA_VOLUME_MERGE;
3028
3029    PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
3030    p->last_element = e;
3031    return p;
3032}
3033
3034static bool element_drop_unsupported(pa_alsa_element *e) {
3035    pa_alsa_option *o, *n;
3036
3037    pa_assert(e);
3038
3039    for (o = e->options; o; o = n) {
3040        n = o->next;
3041
3042        if (o->alsa_idx < 0) {
3043            PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
3044            option_free(o);
3045        }
3046    }
3047
3048    return
3049        e->switch_use != PA_ALSA_SWITCH_IGNORE ||
3050        e->volume_use != PA_ALSA_VOLUME_IGNORE ||
3051        e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
3052}
3053
3054static void path_drop_unsupported(pa_alsa_path *p) {
3055    pa_alsa_element *e, *n;
3056
3057    pa_assert(p);
3058
3059    for (e = p->elements; e; e = n) {
3060        n = e->next;
3061
3062        if (!element_drop_unsupported(e)) {
3063            PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
3064            element_free(e);
3065        }
3066    }
3067}
3068
3069static void path_make_options_unique(pa_alsa_path *p) {
3070    pa_alsa_element *e;
3071    pa_alsa_option *o, *u;
3072
3073    PA_LLIST_FOREACH(e, p->elements) {
3074        PA_LLIST_FOREACH(o, e->options) {
3075            unsigned i;
3076            char *m;
3077
3078            for (u = o->next; u; u = u->next)
3079                if (pa_streq(u->name, o->name))
3080                    break;
3081
3082            if (!u)
3083                continue;
3084
3085            m = pa_xstrdup(o->name);
3086
3087            /* OK, this name is not unique, hence let's rename */
3088            for (i = 1, u = o; u; u = u->next) {
3089                char *nn, *nd;
3090
3091                if (!pa_streq(u->name, m))
3092                    continue;
3093
3094                nn = pa_sprintf_malloc("%s-%u", m, i);
3095                pa_xfree(u->name);
3096                u->name = nn;
3097
3098                nd = pa_sprintf_malloc("%s %u", u->description, i);
3099                pa_xfree(u->description);
3100                u->description = nd;
3101
3102                i++;
3103            }
3104
3105            pa_xfree(m);
3106        }
3107    }
3108}
3109
3110static bool element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
3111    pa_alsa_option *o;
3112
3113    for (; e; e = e->next)
3114        if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
3115            e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
3116            break;
3117
3118    if (!e)
3119        return false;
3120
3121    for (o = e->options; o; o = o->next) {
3122        pa_alsa_setting *s;
3123
3124        if (template) {
3125            s = pa_xnewdup(pa_alsa_setting, template, 1);
3126            s->options = pa_idxset_copy(template->options, NULL);
3127            s->name = pa_sprintf_malloc("%s+%s", template->name, o->name);
3128            s->description =
3129                (template->description[0] && o->description[0])
3130                ? pa_sprintf_malloc("%s / %s", template->description, o->description)
3131                : (template->description[0]
3132                   ? pa_xstrdup(template->description)
3133                   : pa_xstrdup(o->description));
3134
3135            s->priority = PA_MAX(template->priority, o->priority);
3136        } else {
3137            s = pa_xnew0(pa_alsa_setting, 1);
3138            s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3139            s->name = pa_xstrdup(o->name);
3140            s->description = pa_xstrdup(o->description);
3141            s->priority = o->priority;
3142        }
3143
3144        pa_idxset_put(s->options, o, NULL);
3145
3146        if (element_create_settings(e->next, s))
3147            /* This is not a leaf, so let's get rid of it */
3148            setting_free(s);
3149        else {
3150            /* This is a leaf, so let's add it */
3151            PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
3152
3153            e->path->last_setting = s;
3154        }
3155    }
3156
3157    return true;
3158}
3159
3160static void path_create_settings(pa_alsa_path *p) {
3161    pa_assert(p);
3162
3163    element_create_settings(p->elements, NULL);
3164}
3165
3166int pa_alsa_path_probe(pa_alsa_path *p, pa_alsa_mapping *mapping, snd_mixer_t *m, bool ignore_dB) {
3167    pa_alsa_element *e;
3168    pa_alsa_jack *j;
3169    double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
3170    pa_channel_position_t t;
3171    pa_channel_position_mask_t path_volume_channels = 0;
3172    bool min_dB_set, max_dB_set;
3173    char buf[64];
3174
3175    pa_assert(p);
3176    pa_assert(m);
3177
3178    if (p->probed)
3179        return p->supported ? 0 : -1;
3180    p->probed = true;
3181
3182    pa_zero(min_dB);
3183    pa_zero(max_dB);
3184
3185    pa_log_debug("Probing path '%s'", p->name);
3186
3187    PA_LLIST_FOREACH(j, p->jacks) {
3188        pa_alsa_mixer_id_to_string(buf, sizeof(buf), &j->alsa_id);
3189        if (jack_probe(j, mapping, m) < 0) {
3190            p->supported = false;
3191            pa_log_debug("Probe of jack %s failed.", buf);
3192            return -1;
3193        }
3194        pa_log_debug("Probe of jack %s succeeded (%s)", buf, j->has_control ? "found!" : "not found");
3195    }
3196
3197    PA_LLIST_FOREACH(e, p->elements) {
3198        pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
3199        if (element_probe(e, m) < 0) {
3200            p->supported = false;
3201            pa_log_debug("Probe of element %s failed.", buf);
3202            return -1;
3203        }
3204        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);
3205
3206        if (ignore_dB)
3207            e->has_dB = false;
3208
3209        if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
3210
3211            if (!p->has_volume) {
3212                p->min_volume = e->min_volume;
3213                p->max_volume = e->max_volume;
3214            }
3215
3216            if (e->has_dB) {
3217                if (!p->has_volume) {
3218                    for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
3219                        if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
3220                            min_dB[t] = e->min_dB;
3221                            max_dB[t] = e->max_dB;
3222                            path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
3223                        }
3224
3225                    p->has_dB = true;
3226                } else {
3227
3228                    if (p->has_dB) {
3229                        for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
3230                            if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
3231                                min_dB[t] += e->min_dB;
3232                                max_dB[t] += e->max_dB;
3233                                path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
3234                            }
3235                    } else {
3236                        /* Hmm, there's another element before us
3237                         * which cannot do dB volumes, so we we need
3238                         * to 'neutralize' this slider */
3239                        e->volume_use = PA_ALSA_VOLUME_ZERO;
3240                        pa_log_info("Zeroing volume of %s on path '%s'", buf, p->name);
3241                    }
3242                }
3243            } else if (p->has_volume) {
3244                /* We can't use this volume, so let's ignore it */
3245                e->volume_use = PA_ALSA_VOLUME_IGNORE;
3246                pa_log_info("Ignoring volume of %s on path '%s' (missing dB info)", buf, p->name);
3247            }
3248            p->has_volume = true;
3249        }
3250
3251        if (e->switch_use == PA_ALSA_SWITCH_MUTE)
3252            p->has_mute = true;
3253    }
3254
3255    if (p->has_req_any && !p->req_any_present) {
3256        p->supported = false;
3257        pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name);
3258        return -1;
3259    }
3260
3261    path_drop_unsupported(p);
3262    path_make_options_unique(p);
3263    path_create_settings(p);
3264
3265    p->supported = true;
3266
3267    p->min_dB = INFINITY;
3268    min_dB_set = false;
3269    p->max_dB = -INFINITY;
3270    max_dB_set = false;
3271
3272    for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
3273        if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) {
3274            if (p->min_dB > min_dB[t]) {
3275                p->min_dB = min_dB[t];
3276                min_dB_set = true;
3277            }
3278
3279            if (p->max_dB < max_dB[t]) {
3280                p->max_dB = max_dB[t];
3281                max_dB_set = true;
3282            }
3283        }
3284    }
3285
3286    /* this is probably a wrong prediction, but it should be safe */
3287    if (!min_dB_set)
3288        p->min_dB = -INFINITY;
3289    if (!max_dB_set)
3290        p->max_dB = 0;
3291
3292    return 0;
3293}
3294
3295void pa_alsa_setting_dump(pa_alsa_setting *s) {
3296    pa_assert(s);
3297
3298    pa_log_debug("Setting %s (%s) priority=%u",
3299                 s->name,
3300                 pa_strnull(s->description),
3301                 s->priority);
3302}
3303
3304void pa_alsa_jack_dump(pa_alsa_jack *j) {
3305    pa_assert(j);
3306
3307    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");
3308}
3309
3310void pa_alsa_option_dump(pa_alsa_option *o) {
3311    pa_assert(o);
3312
3313    pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
3314                 o->alsa_name,
3315                 pa_strnull(o->name),
3316                 pa_strnull(o->description),
3317                 o->alsa_idx,
3318                 o->priority);
3319}
3320
3321void pa_alsa_element_dump(pa_alsa_element *e) {
3322    char buf[64];
3323
3324    pa_alsa_option *o;
3325    pa_assert(e);
3326
3327    pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
3328    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",
3329                 buf,
3330                 e->direction,
3331                 e->switch_use,
3332                 e->volume_use,
3333                 e->volume_limit,
3334                 e->enumeration_use,
3335                 e->required,
3336                 e->required_any,
3337                 e->required_absent,
3338                 (long long unsigned) e->merged_mask,
3339                 e->n_channels,
3340                 e->override_map);
3341
3342    PA_LLIST_FOREACH(o, e->options)
3343        pa_alsa_option_dump(o);
3344}
3345
3346void pa_alsa_path_dump(pa_alsa_path *p) {
3347    pa_alsa_element *e;
3348    pa_alsa_jack *j;
3349    pa_alsa_setting *s;
3350    pa_assert(p);
3351
3352    pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
3353                 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
3354                 p->name,
3355                 pa_strnull(p->description),
3356                 p->direction,
3357                 p->priority,
3358                 pa_yes_no(p->probed),
3359                 pa_yes_no(p->supported),
3360                 pa_yes_no(p->has_mute),
3361                 pa_yes_no(p->has_volume),
3362                 pa_yes_no(p->has_dB),
3363                 p->min_volume, p->max_volume,
3364                 p->min_dB, p->max_dB);
3365
3366    PA_LLIST_FOREACH(e, p->elements)
3367        pa_alsa_element_dump(e);
3368
3369    PA_LLIST_FOREACH(j, p->jacks)
3370        pa_alsa_jack_dump(j);
3371
3372    PA_LLIST_FOREACH(s, p->settings)
3373        pa_alsa_setting_dump(s);
3374}
3375
3376static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
3377    snd_mixer_selem_id_t *sid;
3378    snd_mixer_elem_t *me;
3379    char buf[64];
3380
3381    pa_assert(e);
3382    pa_assert(m);
3383    pa_assert(cb);
3384
3385    SELEM_INIT(sid, &e->alsa_id);
3386    if (!(me = snd_mixer_find_selem(m, sid))) {
3387        pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
3388        pa_log_warn("Element %s seems to have disappeared.", buf);
3389        return;
3390    }
3391
3392    snd_mixer_elem_set_callback(me, cb);
3393    snd_mixer_elem_set_callback_private(me, userdata);
3394}
3395
3396void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
3397    pa_alsa_element *e;
3398
3399    pa_assert(p);
3400    pa_assert(m);
3401    pa_assert(cb);
3402
3403    PA_LLIST_FOREACH(e, p->elements)
3404        element_set_callback(e, m, cb, userdata);
3405}
3406
3407void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
3408    pa_alsa_path *p;
3409    void *state;
3410
3411    pa_assert(ps);
3412    pa_assert(m);
3413    pa_assert(cb);
3414
3415    PA_HASHMAP_FOREACH(p, ps->paths, state)
3416        pa_alsa_path_set_callback(p, m, cb, userdata);
3417}
3418
3419static pa_alsa_path *profile_set_get_path(pa_alsa_profile_set *ps, const char *path_name) {
3420    pa_alsa_path *path;
3421
3422    pa_assert(ps);
3423    pa_assert(path_name);
3424
3425    if ((path = pa_hashmap_get(ps->output_paths, path_name)))
3426        return path;
3427
3428    return pa_hashmap_get(ps->input_paths, path_name);
3429}
3430
3431static void profile_set_add_path(pa_alsa_profile_set *ps, pa_alsa_path *path) {
3432    pa_assert(ps);
3433    pa_assert(path);
3434
3435    switch (path->direction) {
3436        case PA_ALSA_DIRECTION_OUTPUT:
3437            pa_assert_se(pa_hashmap_put(ps->output_paths, path->name, path) >= 0);
3438            break;
3439
3440        case PA_ALSA_DIRECTION_INPUT:
3441            pa_assert_se(pa_hashmap_put(ps->input_paths, path->name, path) >= 0);
3442            break;
3443
3444        default:
3445            pa_assert_not_reached();
3446    }
3447}
3448
3449pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction, const char *paths_dir) {
3450    pa_alsa_path_set *ps;
3451    char **pn = NULL, **en = NULL, **ie;
3452    pa_alsa_decibel_fix *db_fix;
3453    void *state, *state2;
3454    char name[64];
3455    int index;
3456
3457    pa_assert(m);
3458    pa_assert(m->profile_set);
3459    pa_assert(m->profile_set->decibel_fixes);
3460    pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
3461
3462    if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
3463        return NULL;
3464
3465    ps = pa_xnew0(pa_alsa_path_set, 1);
3466    ps->direction = direction;
3467    ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3468
3469    if (direction == PA_ALSA_DIRECTION_OUTPUT)
3470        pn = m->output_path_names;
3471    else
3472        pn = m->input_path_names;
3473
3474    if (pn) {
3475        char **in;
3476
3477        for (in = pn; *in; in++) {
3478            pa_alsa_path *p = NULL;
3479            bool duplicate = false;
3480            char **kn;
3481
3482            for (kn = pn; kn < in; kn++)
3483                if (pa_streq(*kn, *in)) {
3484                    duplicate = true;
3485                    break;
3486                }
3487
3488            if (duplicate)
3489                continue;
3490
3491            p = profile_set_get_path(m->profile_set, *in);
3492
3493            if (p && p->direction != direction) {
3494                pa_log("Configuration error: Path %s is used both as an input and as an output path.", p->name);
3495                goto fail;
3496            }
3497
3498            if (!p) {
3499                char *fn = pa_sprintf_malloc("%s.conf", *in);
3500                p = pa_alsa_path_new(paths_dir, fn, direction);
3501                pa_xfree(fn);
3502                if (p)
3503                    profile_set_add_path(m->profile_set, p);
3504            }
3505
3506            if (p)
3507                pa_hashmap_put(ps->paths, p, p);
3508
3509        }
3510
3511        goto finish;
3512    }
3513
3514    if (direction == PA_ALSA_DIRECTION_OUTPUT)
3515        en = m->output_element;
3516    else
3517        en = m->input_element;
3518
3519    if (!en)
3520        goto fail;
3521
3522    for (ie = en; *ie; ie++) {
3523        char **je;
3524        pa_alsa_path *p;
3525
3526        p = pa_alsa_path_synthesize(*ie, direction);
3527
3528        /* Mark all other passed elements for require-absent */
3529        for (je = en; *je; je++) {
3530            pa_alsa_element *e;
3531
3532            if (je == ie)
3533                continue;
3534
3535            if (strlen(*je) + 1 >= sizeof(name)) {
3536                pa_log("Element identifier %s is too long!", *je);
3537                continue;
3538            }
3539
3540            if (alsa_id_decode(*je, name, &index))
3541                continue;
3542
3543            e = pa_xnew0(pa_alsa_element, 1);
3544            e->path = p;
3545            e->alsa_id.name = pa_xstrdup(name);
3546            e->alsa_id.index = index;
3547            e->direction = direction;
3548            e->required_absent = PA_ALSA_REQUIRED_ANY;
3549            e->volume_limit = -1;
3550
3551            PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
3552            p->last_element = e;
3553        }
3554
3555        pa_hashmap_put(ps->paths, *ie, p);
3556    }
3557
3558finish:
3559    /* Assign decibel fixes to elements. */
3560    PA_HASHMAP_FOREACH(db_fix, m->profile_set->decibel_fixes, state) {
3561        pa_alsa_path *p;
3562
3563        PA_HASHMAP_FOREACH(p, ps->paths, state2) {
3564            pa_alsa_element *e;
3565
3566            PA_LLIST_FOREACH(e, p->elements) {
3567                if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_id.name) &&
3568                    db_fix->index == e->alsa_id.index) {
3569                    /* The profile set that contains the dB fix may be freed
3570                     * before the element, so we have to copy the dB fix
3571                     * object. */
3572                    e->db_fix = pa_xnewdup(pa_alsa_decibel_fix, db_fix, 1);
3573                    e->db_fix->profile_set = NULL;
3574                    e->db_fix->key = pa_xstrdup(db_fix->key);
3575                    e->db_fix->name = pa_xstrdup(db_fix->name);
3576                    e->db_fix->db_values = pa_xmemdup(db_fix->db_values, (db_fix->max_step - db_fix->min_step + 1) * sizeof(long));
3577                }
3578            }
3579        }
3580    }
3581
3582    return ps;
3583
3584fail:
3585    if (ps)
3586        pa_alsa_path_set_free(ps);
3587
3588    return NULL;
3589}
3590
3591void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
3592    pa_alsa_path *p;
3593    void *state;
3594    pa_assert(ps);
3595
3596    pa_log_debug("Path Set %p, direction=%i",
3597                 (void*) ps,
3598                 ps->direction);
3599
3600    PA_HASHMAP_FOREACH(p, ps->paths, state)
3601        pa_alsa_path_dump(p);
3602}
3603
3604static bool options_have_option(pa_alsa_option *options, const char *alsa_name) {
3605    pa_alsa_option *o;
3606
3607    pa_assert(options);
3608    pa_assert(alsa_name);
3609
3610    PA_LLIST_FOREACH(o, options) {
3611        if (pa_streq(o->alsa_name, alsa_name))
3612            return true;
3613    }
3614    return false;
3615}
3616
3617static bool enumeration_is_subset(pa_alsa_option *a_options, pa_alsa_option *b_options) {
3618    pa_alsa_option *oa, *ob;
3619
3620    if (!a_options) return true;
3621    if (!b_options) return false;
3622
3623    /* If there is an option A offers that B does not, then A is not a subset of B. */
3624    PA_LLIST_FOREACH(oa, a_options) {
3625        bool found = false;
3626        PA_LLIST_FOREACH(ob, b_options) {
3627            if (pa_streq(oa->alsa_name, ob->alsa_name)) {
3628                found = true;
3629                break;
3630            }
3631        }
3632        if (!found)
3633            return false;
3634    }
3635    return true;
3636}
3637
3638/**
3639 *  Compares two elements to see if a is a subset of b
3640 */
3641static bool element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_t *m) {
3642    char buf[64];
3643
3644    pa_assert(a);
3645    pa_assert(b);
3646    pa_assert(m);
3647
3648    /* General rules:
3649     * Every state is a subset of itself (with caveats for volume_limits and options)
3650     * IGNORE is a subset of every other state */
3651
3652    /* Check the volume_use */
3653    if (a->volume_use != PA_ALSA_VOLUME_IGNORE) {
3654
3655        /* "Constant" is subset of "Constant" only when their constant values are equal */
3656        if (a->volume_use == PA_ALSA_VOLUME_CONSTANT && b->volume_use == PA_ALSA_VOLUME_CONSTANT && a->constant_volume != b->constant_volume)
3657            return false;
3658
3659        /* Different volume uses when b is not "Merge" means we are definitely not a subset */
3660        if (a->volume_use != b->volume_use && b->volume_use != PA_ALSA_VOLUME_MERGE)
3661            return false;
3662
3663        /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
3664         * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
3665         * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
3666        if (b->volume_use == PA_ALSA_VOLUME_MERGE && b->volume_limit >= 0) {
3667            long a_limit;
3668
3669            if (a->volume_use == PA_ALSA_VOLUME_CONSTANT)
3670                a_limit = a->constant_volume;
3671            else if (a->volume_use == PA_ALSA_VOLUME_ZERO) {
3672                long dB = 0;
3673
3674                if (a->db_fix) {
3675                    int rounding = (a->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1);
3676                    a_limit = decibel_fix_get_step(a->db_fix, &dB, rounding);
3677                } else {
3678                    snd_mixer_selem_id_t *sid;
3679                    snd_mixer_elem_t *me;
3680
3681                    SELEM_INIT(sid, &a->alsa_id);
3682                    if (!(me = snd_mixer_find_selem(m, sid))) {
3683                        pa_alsa_mixer_id_to_string(buf, sizeof(buf), &a->alsa_id);
3684                        pa_log_warn("Element %s seems to have disappeared.", buf);
3685                        return false;
3686                    }
3687
3688                    if (a->direction == PA_ALSA_DIRECTION_OUTPUT) {
3689                        if (snd_mixer_selem_ask_playback_dB_vol(me, dB, +1, &a_limit) < 0)
3690                            return false;
3691                    } else {
3692                        if (snd_mixer_selem_ask_capture_dB_vol(me, dB, -1, &a_limit) < 0)
3693                            return false;
3694                    }
3695                }
3696            } else if (a->volume_use == PA_ALSA_VOLUME_OFF)
3697                a_limit = a->min_volume;
3698            else if (a->volume_use == PA_ALSA_VOLUME_MERGE)
3699                a_limit = a->volume_limit;
3700            else
3701                pa_assert_not_reached();
3702
3703            if (a_limit > b->volume_limit)
3704                return false;
3705        }
3706
3707        if (a->volume_use == PA_ALSA_VOLUME_MERGE) {
3708            int s;
3709            /* If override-maps are different, they're not subsets */
3710            if (a->n_channels != b->n_channels)
3711                return false;
3712            for (s = 0; s <= SND_MIXER_SCHN_LAST; s++)
3713                if (a->masks[s][a->n_channels-1] != b->masks[s][b->n_channels-1]) {
3714                    pa_alsa_mixer_id_to_string(buf, sizeof(buf), &a->alsa_id);
3715                    pa_log_debug("Element %s is not a subset - mask a: 0x%" PRIx64 ", mask b: 0x%" PRIx64 ", at channel %d",
3716                                 buf, a->masks[s][a->n_channels-1], b->masks[s][b->n_channels-1], s);
3717                    return false;
3718               }
3719        }
3720    }
3721
3722    if (a->switch_use != PA_ALSA_SWITCH_IGNORE) {
3723        /* "On" is a subset of "Mute".
3724         * "Off" is a subset of "Mute".
3725         * "On" is a subset of "Select", if there is an "Option:On" in B.
3726         * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3727         * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3728
3729        if (a->switch_use != b->switch_use) {
3730
3731            if (a->switch_use == PA_ALSA_SWITCH_SELECT || a->switch_use == PA_ALSA_SWITCH_MUTE
3732                || b->switch_use == PA_ALSA_SWITCH_OFF || b->switch_use == PA_ALSA_SWITCH_ON)
3733                return false;
3734
3735            if (b->switch_use == PA_ALSA_SWITCH_SELECT) {
3736                if (a->switch_use == PA_ALSA_SWITCH_ON) {
3737                    if (!options_have_option(b->options, "on"))
3738                        return false;
3739                } else if (a->switch_use == PA_ALSA_SWITCH_OFF) {
3740                    if (!options_have_option(b->options, "off"))
3741                        return false;
3742                }
3743            }
3744        } else if (a->switch_use == PA_ALSA_SWITCH_SELECT) {
3745            if (!enumeration_is_subset(a->options, b->options))
3746                return false;
3747        }
3748    }
3749
3750    if (a->enumeration_use != PA_ALSA_ENUMERATION_IGNORE) {
3751        if (b->enumeration_use == PA_ALSA_ENUMERATION_IGNORE)
3752            return false;
3753        if (!enumeration_is_subset(a->options, b->options))
3754            return false;
3755    }
3756
3757    return true;
3758}
3759
3760static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
3761    pa_alsa_path *p;
3762    void *state;
3763
3764    pa_assert(ps);
3765    pa_assert(m);
3766
3767    /* If we only have one path, then don't bother */
3768    if (pa_hashmap_size(ps->paths) < 2)
3769        return;
3770
3771    PA_HASHMAP_FOREACH(p, ps->paths, state) {
3772        pa_alsa_path *p2;
3773        void *state2;
3774
3775        PA_HASHMAP_FOREACH(p2, ps->paths, state2) {
3776            pa_alsa_element *ea, *eb;
3777            pa_alsa_jack *ja, *jb;
3778            bool is_subset = true;
3779
3780            if (p == p2)
3781                continue;
3782
3783            /* If a has a jack that b does not have, a is not a subset */
3784            PA_LLIST_FOREACH(ja, p->jacks) {
3785                bool exists = false;
3786
3787                if (!ja->has_control)
3788                    continue;
3789
3790                PA_LLIST_FOREACH(jb, p2->jacks) {
3791                    if (jb->has_control && pa_streq(ja->alsa_id.name, jb->alsa_id.name) &&
3792                       (ja->alsa_id.index == jb->alsa_id.index) &&
3793                       (ja->state_plugged == jb->state_plugged) &&
3794                       (ja->state_unplugged == jb->state_unplugged)) {
3795                        exists = true;
3796                        break;
3797                    }
3798                }
3799
3800                if (!exists) {
3801                    is_subset = false;
3802                    break;
3803                }
3804            }
3805
3806            /* Compare the elements of each set... */
3807            PA_LLIST_FOREACH(ea, p->elements) {
3808                bool found_matching_element = false;
3809
3810                if (!is_subset)
3811                    break;
3812
3813                PA_LLIST_FOREACH(eb, p2->elements) {
3814                    if (pa_streq(ea->alsa_id.name, eb->alsa_id.name) &&
3815                        ea->alsa_id.index == eb->alsa_id.index) {
3816                        found_matching_element = true;
3817                        is_subset = element_is_subset(ea, eb, m);
3818                        break;
3819                    }
3820                }
3821
3822                if (!found_matching_element)
3823                    is_subset = false;
3824            }
3825
3826            if (is_subset) {
3827                pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p->name, p2->name);
3828                pa_hashmap_remove(ps->paths, p);
3829                break;
3830            }
3831        }
3832    }
3833}
3834
3835static pa_alsa_path* path_set_find_path_by_description(pa_alsa_path_set *ps, const char* description, pa_alsa_path *ignore) {
3836    pa_alsa_path* p;
3837    void *state;
3838
3839    PA_HASHMAP_FOREACH(p, ps->paths, state)
3840        if (p != ignore && pa_streq(p->description, description))
3841            return p;
3842
3843    return NULL;
3844}
3845
3846static void path_set_make_path_descriptions_unique(pa_alsa_path_set *ps) {
3847    pa_alsa_path *p, *q;
3848    void *state, *state2;
3849
3850    PA_HASHMAP_FOREACH(p, ps->paths, state) {
3851        unsigned i;
3852        char *old_description;
3853
3854        q = path_set_find_path_by_description(ps, p->description, p);
3855
3856        if (!q)
3857            continue;
3858
3859        old_description = pa_xstrdup(p->description);
3860
3861        /* OK, this description is not unique, hence let's rename */
3862        i = 1;
3863        PA_HASHMAP_FOREACH(q, ps->paths, state2) {
3864            char *new_description;
3865
3866            if (!pa_streq(q->description, old_description))
3867                continue;
3868
3869            new_description = pa_sprintf_malloc("%s %u", q->description, i);
3870            pa_xfree(q->description);
3871            q->description = new_description;
3872
3873            i++;
3874        }
3875
3876        pa_xfree(old_description);
3877    }
3878}
3879
3880static void mapping_free(pa_alsa_mapping *m) {
3881    pa_assert(m);
3882
3883    pa_xfree(m->name);
3884    pa_xfree(m->description);
3885    pa_xfree(m->description_key);
3886
3887    pa_proplist_free(m->proplist);
3888
3889    pa_xstrfreev(m->device_strings);
3890    pa_xstrfreev(m->input_path_names);
3891    pa_xstrfreev(m->output_path_names);
3892    pa_xstrfreev(m->input_element);
3893    pa_xstrfreev(m->output_element);
3894    if (m->input_path_set)
3895        pa_alsa_path_set_free(m->input_path_set);
3896    if (m->output_path_set)
3897        pa_alsa_path_set_free(m->output_path_set);
3898
3899    pa_assert(!m->input_pcm);
3900    pa_assert(!m->output_pcm);
3901
3902    pa_alsa_ucm_mapping_context_free(&m->ucm_context);
3903
3904    pa_xfree(m);
3905}
3906
3907static void profile_free(pa_alsa_profile *p) {
3908    pa_assert(p);
3909
3910    pa_xfree(p->name);
3911    pa_xfree(p->description);
3912    pa_xfree(p->description_key);
3913    pa_xfree(p->input_name);
3914    pa_xfree(p->output_name);
3915
3916    pa_xstrfreev(p->input_mapping_names);
3917    pa_xstrfreev(p->output_mapping_names);
3918
3919    if (p->input_mappings)
3920        pa_idxset_free(p->input_mappings, NULL);
3921
3922    if (p->output_mappings)
3923        pa_idxset_free(p->output_mappings, NULL);
3924
3925    pa_xfree(p);
3926}
3927
3928void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
3929    pa_assert(ps);
3930
3931    if (ps->input_paths)
3932        pa_hashmap_free(ps->input_paths);
3933
3934    if (ps->output_paths)
3935        pa_hashmap_free(ps->output_paths);
3936
3937    if (ps->profiles)
3938        pa_hashmap_free(ps->profiles);
3939
3940    if (ps->mappings)
3941        pa_hashmap_free(ps->mappings);
3942
3943    if (ps->decibel_fixes)
3944        pa_hashmap_free(ps->decibel_fixes);
3945
3946    pa_xfree(ps);
3947}
3948
3949pa_alsa_mapping *pa_alsa_mapping_get(pa_alsa_profile_set *ps, const char *name) {
3950    pa_alsa_mapping *m;
3951
3952    if (!pa_startswith(name, "Mapping "))
3953        return NULL;
3954
3955    name += 8;
3956
3957    if ((m = pa_hashmap_get(ps->mappings, name)))
3958        return m;
3959
3960    m = pa_xnew0(pa_alsa_mapping, 1);
3961    m->profile_set = ps;
3962    m->exact_channels = true;
3963    m->name = pa_xstrdup(name);
3964    pa_sample_spec_init(&m->sample_spec);
3965    pa_channel_map_init(&m->channel_map);
3966    m->proplist = pa_proplist_new();
3967    m->hw_device_index = -1;
3968
3969    pa_hashmap_put(ps->mappings, m->name, m);
3970
3971    return m;
3972}
3973
3974static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
3975    pa_alsa_profile *p;
3976
3977    if (!pa_startswith(name, "Profile "))
3978        return NULL;
3979
3980    name += 8;
3981
3982    if ((p = pa_hashmap_get(ps->profiles, name)))
3983        return p;
3984
3985    p = pa_xnew0(pa_alsa_profile, 1);
3986    p->profile_set = ps;
3987    p->name = pa_xstrdup(name);
3988
3989    pa_hashmap_put(ps->profiles, p->name, p);
3990
3991    return p;
3992}
3993
3994static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *alsa_id) {
3995    pa_alsa_decibel_fix *db_fix;
3996    char *name;
3997    int index;
3998
3999    if (!pa_startswith(alsa_id, "DecibelFix "))
4000        return NULL;
4001
4002    alsa_id += 11;
4003
4004    if ((db_fix = pa_hashmap_get(ps->decibel_fixes, alsa_id)))
4005        return db_fix;
4006
4007    name = alloca(strlen(alsa_id) + 1);
4008    if (alsa_id_decode(alsa_id, name, &index))
4009        return NULL;
4010
4011    db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
4012    db_fix->profile_set = ps;
4013    db_fix->name = pa_xstrdup(name);
4014    db_fix->index = index;
4015    db_fix->key = pa_xstrdup(alsa_id);
4016
4017    pa_hashmap_put(ps->decibel_fixes, db_fix->key, db_fix);
4018
4019    return db_fix;
4020}
4021
4022static int mapping_parse_device_strings(pa_config_parser_state *state) {
4023    pa_alsa_profile_set *ps;
4024    pa_alsa_mapping *m;
4025
4026    pa_assert(state);
4027
4028    ps = state->userdata;
4029
4030    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
4031        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4032        return -1;
4033    }
4034
4035    pa_xstrfreev(m->device_strings);
4036    if (!(m->device_strings = pa_split_spaces_strv(state->rvalue))) {
4037        pa_log("[%s:%u] Device string list empty of '%s'", state->filename, state->lineno, state->section);
4038        return -1;
4039    }
4040
4041    return 0;
4042}
4043
4044static int mapping_parse_channel_map(pa_config_parser_state *state) {
4045    pa_alsa_profile_set *ps;
4046    pa_alsa_mapping *m;
4047
4048    pa_assert(state);
4049
4050    ps = state->userdata;
4051
4052    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
4053        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4054        return -1;
4055    }
4056
4057    if (!(pa_channel_map_parse(&m->channel_map, state->rvalue))) {
4058        pa_log("[%s:%u] Channel map invalid of '%s'", state->filename, state->lineno, state->section);
4059        return -1;
4060    }
4061
4062    return 0;
4063}
4064
4065static int mapping_parse_paths(pa_config_parser_state *state) {
4066    pa_alsa_profile_set *ps;
4067    pa_alsa_mapping *m;
4068
4069    pa_assert(state);
4070
4071    ps = state->userdata;
4072
4073    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
4074        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4075        return -1;
4076    }
4077
4078    if (pa_streq(state->lvalue, "paths-input")) {
4079        pa_xstrfreev(m->input_path_names);
4080        m->input_path_names = pa_split_spaces_strv(state->rvalue);
4081    } else {
4082        pa_xstrfreev(m->output_path_names);
4083        m->output_path_names = pa_split_spaces_strv(state->rvalue);
4084    }
4085
4086    return 0;
4087}
4088
4089static int mapping_parse_exact_channels(pa_config_parser_state *state) {
4090    pa_alsa_profile_set *ps;
4091    pa_alsa_mapping *m;
4092    int b;
4093
4094    pa_assert(state);
4095
4096    ps = state->userdata;
4097
4098    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
4099        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4100        return -1;
4101    }
4102
4103    if ((b = pa_parse_boolean(state->rvalue)) < 0) {
4104        pa_log("[%s:%u] %s has invalid value '%s'", state->filename, state->lineno, state->lvalue, state->section);
4105        return -1;
4106    }
4107
4108    m->exact_channels = b;
4109
4110    return 0;
4111}
4112
4113static int mapping_parse_element(pa_config_parser_state *state) {
4114    pa_alsa_profile_set *ps;
4115    pa_alsa_mapping *m;
4116
4117    pa_assert(state);
4118
4119    ps = state->userdata;
4120
4121    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
4122        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4123        return -1;
4124    }
4125
4126    if (pa_streq(state->lvalue, "element-input")) {
4127        pa_xstrfreev(m->input_element);
4128        m->input_element = pa_split_spaces_strv(state->rvalue);
4129    } else {
4130        pa_xstrfreev(m->output_element);
4131        m->output_element = pa_split_spaces_strv(state->rvalue);
4132    }
4133
4134    return 0;
4135}
4136
4137static int mapping_parse_direction(pa_config_parser_state *state) {
4138    pa_alsa_profile_set *ps;
4139    pa_alsa_mapping *m;
4140
4141    pa_assert(state);
4142
4143    ps = state->userdata;
4144
4145    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
4146        pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
4147        return -1;
4148    }
4149
4150    if (pa_streq(state->rvalue, "input"))
4151        m->direction = PA_ALSA_DIRECTION_INPUT;
4152    else if (pa_streq(state->rvalue, "output"))
4153        m->direction = PA_ALSA_DIRECTION_OUTPUT;
4154    else if (pa_streq(state->rvalue, "any"))
4155        m->direction = PA_ALSA_DIRECTION_ANY;
4156    else {
4157        pa_log("[%s:%u] Direction %s invalid.", state->filename, state->lineno, state->rvalue);
4158        return -1;
4159    }
4160
4161    return 0;
4162}
4163
4164static int mapping_parse_description(pa_config_parser_state *state) {
4165    pa_alsa_profile_set *ps;
4166    pa_alsa_profile *p;
4167    pa_alsa_mapping *m;
4168
4169    pa_assert(state);
4170
4171    ps = state->userdata;
4172
4173    if ((m = pa_alsa_mapping_get(ps, state->section))) {
4174        pa_xfree(m->description);
4175        m->description = pa_xstrdup(state->rvalue);
4176    } else if ((p = profile_get(ps, state->section))) {
4177        pa_xfree(p->description);
4178        p->description = pa_xstrdup(state->rvalue);
4179    } else {
4180        pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
4181        return -1;
4182    }
4183
4184    return 0;
4185}
4186
4187static int mapping_parse_description_key(pa_config_parser_state *state) {
4188    pa_alsa_profile_set *ps;
4189    pa_alsa_profile *p;
4190    pa_alsa_mapping *m;
4191
4192    pa_assert(state);
4193
4194    ps = state->userdata;
4195
4196    if ((m = pa_alsa_mapping_get(ps, state->section))) {
4197        pa_xfree(m->description_key);
4198        m->description_key = pa_xstrdup(state->rvalue);
4199    } else if ((p = profile_get(ps, state->section))) {
4200        pa_xfree(p->description_key);
4201        p->description_key = pa_xstrdup(state->rvalue);
4202    } else {
4203        pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
4204        return -1;
4205    }
4206
4207    return 0;
4208}
4209
4210
4211static int mapping_parse_priority(pa_config_parser_state *state) {
4212    pa_alsa_profile_set *ps;
4213    pa_alsa_profile *p;
4214    pa_alsa_mapping *m;
4215    uint32_t prio;
4216
4217    pa_assert(state);
4218
4219    ps = state->userdata;
4220
4221    if (pa_atou(state->rvalue, &prio) < 0) {
4222        pa_log("[%s:%u] Priority invalid of '%s'", state->filename, state->lineno, state->section);
4223        return -1;
4224    }
4225
4226    if ((m = pa_alsa_mapping_get(ps, state->section)))
4227        m->priority = prio;
4228    else if ((p = profile_get(ps, state->section)))
4229        p->priority = prio;
4230    else {
4231        pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
4232        return -1;
4233    }
4234
4235    return 0;
4236}
4237
4238static int mapping_parse_fallback(pa_config_parser_state *state) {
4239    pa_alsa_profile_set *ps;
4240    pa_alsa_profile *p;
4241    pa_alsa_mapping *m;
4242    int k;
4243
4244    pa_assert(state);
4245
4246    ps = state->userdata;
4247
4248    if ((k = pa_parse_boolean(state->rvalue)) < 0) {
4249        pa_log("[%s:%u] Fallback invalid of '%s'", state->filename, state->lineno, state->section);
4250        return -1;
4251    }
4252
4253    if ((m = pa_alsa_mapping_get(ps, state->section)))
4254        m->fallback = k;
4255    else if ((p = profile_get(ps, state->section)))
4256        p->fallback_input = p->fallback_output = k;
4257    else {
4258        pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
4259        return -1;
4260    }
4261
4262    return 0;
4263}
4264
4265static int mapping_parse_intended_roles(pa_config_parser_state *state) {
4266    pa_alsa_profile_set *ps;
4267    pa_alsa_mapping *m;
4268
4269    pa_assert(state);
4270
4271    ps = state->userdata;
4272
4273    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
4274        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4275        return -1;
4276    }
4277
4278    pa_proplist_sets(m->proplist, PA_PROP_DEVICE_INTENDED_ROLES, state->rvalue);
4279
4280    return 0;
4281}
4282
4283
4284static int profile_parse_mappings(pa_config_parser_state *state) {
4285    pa_alsa_profile_set *ps;
4286    pa_alsa_profile *p;
4287
4288    pa_assert(state);
4289
4290    ps = state->userdata;
4291
4292    if (!(p = profile_get(ps, state->section))) {
4293        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4294        return -1;
4295    }
4296
4297    if (pa_streq(state->lvalue, "input-mappings")) {
4298        pa_xstrfreev(p->input_mapping_names);
4299        p->input_mapping_names = pa_split_spaces_strv(state->rvalue);
4300    } else {
4301        pa_xstrfreev(p->output_mapping_names);
4302        p->output_mapping_names = pa_split_spaces_strv(state->rvalue);
4303    }
4304
4305    return 0;
4306}
4307
4308static int profile_parse_skip_probe(pa_config_parser_state *state) {
4309    pa_alsa_profile_set *ps;
4310    pa_alsa_profile *p;
4311    int b;
4312
4313    pa_assert(state);
4314
4315    ps = state->userdata;
4316
4317    if (!(p = profile_get(ps, state->section))) {
4318        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4319        return -1;
4320    }
4321
4322    if ((b = pa_parse_boolean(state->rvalue)) < 0) {
4323        pa_log("[%s:%u] Skip probe invalid of '%s'", state->filename, state->lineno, state->section);
4324        return -1;
4325    }
4326
4327    p->supported = b;
4328
4329    return 0;
4330}
4331
4332static int decibel_fix_parse_db_values(pa_config_parser_state *state) {
4333    pa_alsa_profile_set *ps;
4334    pa_alsa_decibel_fix *db_fix;
4335    char **items;
4336    char *item;
4337    long *db_values;
4338    unsigned n = 8; /* Current size of the db_values table. */
4339    unsigned min_step = 0;
4340    unsigned max_step = 0;
4341    unsigned i = 0; /* Index to the items table. */
4342    unsigned prev_step = 0;
4343    double prev_db = 0;
4344
4345    pa_assert(state);
4346
4347    ps = state->userdata;
4348
4349    if (!(db_fix = decibel_fix_get(ps, state->section))) {
4350        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4351        return -1;
4352    }
4353
4354    if (!(items = pa_split_spaces_strv(state->rvalue))) {
4355        pa_log("[%s:%u] Value missing", state->filename, state->lineno);
4356        return -1;
4357    }
4358
4359    db_values = pa_xnew(long, n);
4360
4361    while ((item = items[i++])) {
4362        char *s = item; /* Step value string. */
4363        char *d = item; /* dB value string. */
4364        uint32_t step;
4365        double db;
4366
4367        /* Move d forward until it points to a colon or to the end of the item. */
4368        for (; *d && *d != ':'; ++d);
4369
4370        if (d == s) {
4371            /* item started with colon. */
4372            pa_log("[%s:%u] No step value found in %s", state->filename, state->lineno, item);
4373            goto fail;
4374        }
4375
4376        if (!*d || !*(d + 1)) {
4377            /* No colon found, or it was the last character in item. */
4378            pa_log("[%s:%u] No dB value found in %s", state->filename, state->lineno, item);
4379            goto fail;
4380        }
4381
4382        /* pa_atou() needs a null-terminating string. Let's replace the colon
4383         * with a zero byte. */
4384        *d++ = '\0';
4385
4386        if (pa_atou(s, &step) < 0) {
4387            pa_log("[%s:%u] Invalid step value: %s", state->filename, state->lineno, s);
4388            goto fail;
4389        }
4390
4391        if (pa_atod(d, &db) < 0) {
4392            pa_log("[%s:%u] Invalid dB value: %s", state->filename, state->lineno, d);
4393            goto fail;
4394        }
4395
4396        if (step <= prev_step && i != 1) {
4397            pa_log("[%s:%u] Step value %u not greater than the previous value %u", state->filename, state->lineno, step, prev_step);
4398            goto fail;
4399        }
4400
4401        if (db < prev_db && i != 1) {
4402            pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", state->filename, state->lineno, db, prev_db);
4403            goto fail;
4404        }
4405
4406        if (i == 1) {
4407            min_step = step;
4408            db_values[0] = (long) (db * 100.0);
4409            prev_step = step;
4410            prev_db = db;
4411        } else {
4412            /* Interpolate linearly. */
4413            double db_increment = (db - prev_db) / (step - prev_step);
4414
4415            for (; prev_step < step; ++prev_step, prev_db += db_increment) {
4416
4417                /* Reallocate the db_values table if it's about to overflow. */
4418                if (prev_step + 1 - min_step == n) {
4419                    n *= 2;
4420                    db_values = pa_xrenew(long, db_values, n);
4421                }
4422
4423                db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
4424            }
4425        }
4426
4427        max_step = step;
4428    }
4429
4430    db_fix->min_step = min_step;
4431    db_fix->max_step = max_step;
4432    pa_xfree(db_fix->db_values);
4433    db_fix->db_values = db_values;
4434
4435    pa_xstrfreev(items);
4436
4437    return 0;
4438
4439fail:
4440    pa_xstrfreev(items);
4441    pa_xfree(db_values);
4442
4443    return -1;
4444}
4445
4446/* the logic is simple: if we see the jack in multiple paths */
4447/* assign all those paths to one availability_group */
4448static void profile_set_set_availability_groups(pa_alsa_profile_set *ps) {
4449    pa_dynarray *paths;
4450    pa_alsa_path *p;
4451    void *state;
4452    unsigned idx1;
4453    uint32_t num = 1;
4454
4455    /* Merge ps->input_paths and ps->output_paths into one dynarray. */
4456    paths = pa_dynarray_new(NULL);
4457    PA_HASHMAP_FOREACH(p, ps->input_paths, state)
4458        pa_dynarray_append(paths, p);
4459    PA_HASHMAP_FOREACH(p, ps->output_paths, state)
4460        pa_dynarray_append(paths, p);
4461
4462    PA_DYNARRAY_FOREACH(p, paths, idx1) {
4463        pa_alsa_jack *j;
4464        const char *found = NULL;
4465        bool has_control = false;
4466
4467        PA_LLIST_FOREACH(j, p->jacks) {
4468            pa_alsa_path *p2;
4469            unsigned idx2;
4470
4471            if (!j->has_control || j->state_plugged == PA_AVAILABLE_NO)
4472                continue;
4473            has_control = true;
4474            PA_DYNARRAY_FOREACH(p2, paths, idx2) {
4475                pa_alsa_jack *j2;
4476
4477                if (p2 == p)
4478                    break;
4479                PA_LLIST_FOREACH(j2, p2->jacks) {
4480                    if (!j2->has_control || j2->state_plugged == PA_AVAILABLE_NO)
4481                        continue;
4482                    if (pa_streq(j->alsa_id.name, j2->alsa_id.name) &&
4483                        j->alsa_id.index == j2->alsa_id.index) {
4484                        j->state_plugged = PA_AVAILABLE_UNKNOWN;
4485                        j2->state_plugged = PA_AVAILABLE_UNKNOWN;
4486                        found = p2->availability_group;
4487                        break;
4488                    }
4489                }
4490            }
4491            if (found)
4492                break;
4493        }
4494        if (!has_control)
4495            continue;
4496        if (!found) {
4497            p->availability_group = pa_sprintf_malloc("Legacy %d", num);
4498        } else {
4499            p->availability_group = pa_xstrdup(found);
4500        }
4501        if (!found)
4502            num++;
4503    }
4504
4505    pa_dynarray_free(paths);
4506}
4507
4508static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
4509                                pa_alsa_direction_t direction, pa_hashmap *used_paths,
4510                                pa_hashmap *mixers) {
4511
4512    pa_alsa_path *p;
4513    void *state;
4514    snd_pcm_t *pcm_handle;
4515    pa_alsa_path_set *ps;
4516    snd_mixer_t *mixer_handle;
4517
4518    if (direction == PA_ALSA_DIRECTION_OUTPUT) {
4519        if (m->output_path_set)
4520            return; /* Already probed */
4521        m->output_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
4522        pcm_handle = m->output_pcm;
4523    } else {
4524        if (m->input_path_set)
4525            return; /* Already probed */
4526        m->input_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
4527        pcm_handle = m->input_pcm;
4528    }
4529
4530    if (!ps)
4531        return; /* No paths */
4532
4533    pa_assert(pcm_handle);
4534
4535    mixer_handle = pa_alsa_open_mixer_for_pcm(mixers, pcm_handle, true);
4536    if (!mixer_handle) {
4537        /* Cannot open mixer, remove all entries */
4538        pa_hashmap_remove_all(ps->paths);
4539        return;
4540    }
4541
4542    PA_HASHMAP_FOREACH(p, ps->paths, state) {
4543        if (p->autodetect_eld_device)
4544            p->eld_device = m->hw_device_index;
4545
4546        if (pa_alsa_path_probe(p, m, mixer_handle, m->profile_set->ignore_dB) < 0)
4547            pa_hashmap_remove(ps->paths, p);
4548    }
4549
4550    path_set_condense(ps, mixer_handle);
4551    path_set_make_path_descriptions_unique(ps);
4552
4553    PA_HASHMAP_FOREACH(p, ps->paths, state)
4554        pa_hashmap_put(used_paths, p, p);
4555
4556    pa_log_debug("Available mixer paths (after tidying):");
4557    pa_alsa_path_set_dump(ps);
4558}
4559
4560static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
4561
4562    static const struct description_map well_known_descriptions[] = {
4563        { "analog-mono",            N_("Analog Mono") },
4564        { "analog-mono-left",       N_("Analog Mono (Left)") },
4565        { "analog-mono-right",      N_("Analog Mono (Right)") },
4566        { "analog-stereo",          N_("Analog Stereo") },
4567        { "mono-fallback",          N_("Mono") },
4568        { "stereo-fallback",        N_("Stereo") },
4569        /* Note: Not translated to "Analog Stereo Input", because the source
4570         * name gets "Input" appended to it automatically, so adding "Input"
4571         * here would lead to the source name to become "Analog Stereo Input
4572         * Input". The same logic applies to analog-stereo-output,
4573         * multichannel-input and multichannel-output. */
4574        { "analog-stereo-input",    N_("Analog Stereo") },
4575        { "analog-stereo-output",   N_("Analog Stereo") },
4576        { "analog-stereo-headset",  N_("Headset") },
4577        { "analog-stereo-speakerphone",  N_("Speakerphone") },
4578        { "multichannel-input",     N_("Multichannel") },
4579        { "multichannel-output",    N_("Multichannel") },
4580        { "analog-surround-21",     N_("Analog Surround 2.1") },
4581        { "analog-surround-30",     N_("Analog Surround 3.0") },
4582        { "analog-surround-31",     N_("Analog Surround 3.1") },
4583        { "analog-surround-40",     N_("Analog Surround 4.0") },
4584        { "analog-surround-41",     N_("Analog Surround 4.1") },
4585        { "analog-surround-50",     N_("Analog Surround 5.0") },
4586        { "analog-surround-51",     N_("Analog Surround 5.1") },
4587        { "analog-surround-61",     N_("Analog Surround 6.0") },
4588        { "analog-surround-61",     N_("Analog Surround 6.1") },
4589        { "analog-surround-70",     N_("Analog Surround 7.0") },
4590        { "analog-surround-71",     N_("Analog Surround 7.1") },
4591        { "iec958-stereo",          N_("Digital Stereo (IEC958)") },
4592        { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
4593        { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
4594        { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
4595        { "hdmi-stereo",            N_("Digital Stereo (HDMI)") },
4596        { "hdmi-surround-51",       N_("Digital Surround 5.1 (HDMI)") },
4597        { "gaming-headset-chat",    N_("Chat") },
4598        { "gaming-headset-game",    N_("Game") },
4599    };
4600    const char *description_key = m->description_key ? m->description_key : m->name;
4601
4602    pa_assert(m);
4603
4604    if (!pa_channel_map_valid(&m->channel_map)) {
4605        pa_log("Mapping %s is missing channel map.", m->name);
4606        return -1;
4607    }
4608
4609    if (!m->device_strings) {
4610        pa_log("Mapping %s is missing device strings.", m->name);
4611        return -1;
4612    }
4613
4614    if ((m->input_path_names && m->input_element) ||
4615        (m->output_path_names && m->output_element)) {
4616        pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
4617        return -1;
4618    }
4619
4620    if (!m->description)
4621        m->description = pa_xstrdup(lookup_description(description_key,
4622                                                       well_known_descriptions,
4623                                                       PA_ELEMENTSOF(well_known_descriptions)));
4624
4625    if (!m->description)
4626        m->description = pa_xstrdup(m->name);
4627
4628    if (bonus) {
4629        if (pa_channel_map_equal(&m->channel_map, bonus))
4630            m->priority += 50;
4631        else if (m->channel_map.channels == bonus->channels)
4632            m->priority += 30;
4633    }
4634
4635    return 0;
4636}
4637
4638void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
4639    char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
4640
4641    pa_assert(m);
4642
4643    pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
4644                 m->name,
4645                 pa_strnull(m->description),
4646                 m->priority,
4647                 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
4648                 pa_yes_no(m->supported),
4649                 m->direction);
4650}
4651
4652static void profile_set_add_auto_pair(
4653        pa_alsa_profile_set *ps,
4654        pa_alsa_mapping *m, /* output */
4655        pa_alsa_mapping *n  /* input */) {
4656
4657    char *name;
4658    pa_alsa_profile *p;
4659
4660    pa_assert(ps);
4661    pa_assert(m || n);
4662
4663    if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
4664        return;
4665
4666    if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
4667        return;
4668
4669    if (m && n)
4670        name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
4671    else if (m)
4672        name = pa_sprintf_malloc("output:%s", m->name);
4673    else
4674        name = pa_sprintf_malloc("input:%s", n->name);
4675
4676    if (pa_hashmap_get(ps->profiles, name)) {
4677        pa_xfree(name);
4678        return;
4679    }
4680
4681    p = pa_xnew0(pa_alsa_profile, 1);
4682    p->profile_set = ps;
4683    p->name = name;
4684
4685    if (m) {
4686        p->output_name = pa_xstrdup(m->name);
4687        p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4688        pa_idxset_put(p->output_mappings, m, NULL);
4689        p->priority += m->priority * 100;
4690        p->fallback_output = m->fallback;
4691    }
4692
4693    if (n) {
4694        p->input_name = pa_xstrdup(n->name);
4695        p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4696        pa_idxset_put(p->input_mappings, n, NULL);
4697        p->priority += n->priority;
4698        p->fallback_input = n->fallback;
4699    }
4700
4701    pa_hashmap_put(ps->profiles, p->name, p);
4702}
4703
4704static void profile_set_add_auto(pa_alsa_profile_set *ps) {
4705    pa_alsa_mapping *m, *n;
4706    void *m_state, *n_state;
4707
4708    pa_assert(ps);
4709
4710    /* The order is important here:
4711       1) try single inputs and outputs before trying their
4712          combination, because if the half-duplex test failed, we don't have
4713          to try full duplex.
4714       2) try the output right before the input combinations with
4715          that output, because then the output_pcm is not closed between tests.
4716    */
4717    PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
4718        profile_set_add_auto_pair(ps, NULL, n);
4719
4720    PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
4721        profile_set_add_auto_pair(ps, m, NULL);
4722
4723        PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
4724            profile_set_add_auto_pair(ps, m, n);
4725    }
4726
4727}
4728
4729static int profile_verify(pa_alsa_profile *p) {
4730
4731    static const struct description_map well_known_descriptions[] = {
4732        { "output:analog-mono+input:analog-mono",     N_("Analog Mono Duplex") },
4733        { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
4734        { "output:analog-stereo-headset+input:analog-stereo-headset", N_("Headset") },
4735        { "output:analog-stereo-speakerphone+input:analog-stereo-speakerphone", N_("Speakerphone") },
4736        { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
4737        { "output:multichannel-output+input:multichannel-input", N_("Multichannel Duplex") },
4738        { "output:unknown-stereo+input:unknown-stereo", N_("Stereo Duplex") },
4739        { "output:analog-output-surround71+output:analog-output-chat+input:analog-input", N_("Mono Chat + 7.1 Surround") },
4740        { "off",                                      N_("Off") }
4741    };
4742    const char *description_key = p->description_key ? p->description_key : p->name;
4743
4744    pa_assert(p);
4745
4746    /* Replace the output mapping names by the actual mappings */
4747    if (p->output_mapping_names) {
4748        char **name;
4749
4750        pa_assert(!p->output_mappings);
4751        p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4752
4753        for (name = p->output_mapping_names; *name; name++) {
4754            pa_alsa_mapping *m;
4755            char **in;
4756            bool duplicate = false;
4757
4758            for (in = name + 1; *in; in++)
4759                if (pa_streq(*name, *in)) {
4760                    duplicate = true;
4761                    break;
4762                }
4763
4764            if (duplicate)
4765                continue;
4766
4767            if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
4768                pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
4769                return -1;
4770            }
4771
4772            pa_idxset_put(p->output_mappings, m, NULL);
4773
4774            if (p->supported)
4775                m->supported++;
4776        }
4777
4778        pa_xstrfreev(p->output_mapping_names);
4779        p->output_mapping_names = NULL;
4780    }
4781
4782    /* Replace the input mapping names by the actual mappings */
4783    if (p->input_mapping_names) {
4784        char **name;
4785
4786        pa_assert(!p->input_mappings);
4787        p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4788
4789        for (name = p->input_mapping_names; *name; name++) {
4790            pa_alsa_mapping *m;
4791            char **in;
4792            bool duplicate = false;
4793
4794            for (in = name + 1; *in; in++)
4795                if (pa_streq(*name, *in)) {
4796                    duplicate = true;
4797                    break;
4798                }
4799
4800            if (duplicate)
4801                continue;
4802
4803            if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
4804                pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
4805                return -1;
4806            }
4807
4808            pa_idxset_put(p->input_mappings, m, NULL);
4809
4810            if (p->supported)
4811                m->supported++;
4812        }
4813
4814        pa_xstrfreev(p->input_mapping_names);
4815        p->input_mapping_names = NULL;
4816    }
4817
4818    if (!p->input_mappings && !p->output_mappings) {
4819        pa_log("Profile '%s' lacks mappings.", p->name);
4820        return -1;
4821    }
4822
4823    if (!p->description)
4824        p->description = pa_xstrdup(lookup_description(description_key,
4825                                                       well_known_descriptions,
4826                                                       PA_ELEMENTSOF(well_known_descriptions)));
4827
4828    if (!p->description) {
4829        pa_strbuf *sb;
4830        uint32_t idx;
4831        pa_alsa_mapping *m;
4832
4833        sb = pa_strbuf_new();
4834
4835        if (p->output_mappings)
4836            PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4837                if (!pa_strbuf_isempty(sb))
4838                    pa_strbuf_puts(sb, " + ");
4839
4840                pa_strbuf_printf(sb, _("%s Output"), m->description);
4841            }
4842
4843        if (p->input_mappings)
4844            PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4845                if (!pa_strbuf_isempty(sb))
4846                    pa_strbuf_puts(sb, " + ");
4847
4848                pa_strbuf_printf(sb, _("%s Input"), m->description);
4849            }
4850
4851        p->description = pa_strbuf_to_string_free(sb);
4852    }
4853
4854    return 0;
4855}
4856
4857void pa_alsa_profile_dump(pa_alsa_profile *p) {
4858    uint32_t idx;
4859    pa_alsa_mapping *m;
4860    pa_assert(p);
4861
4862    pa_log_debug("Profile %s (%s), input=%s, output=%s priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4863                 p->name,
4864                 pa_strnull(p->description),
4865                 pa_strnull(p->input_name),
4866                 pa_strnull(p->output_name),
4867                 p->priority,
4868                 pa_yes_no(p->supported),
4869                 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
4870                 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
4871
4872    if (p->input_mappings)
4873        PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4874            pa_log_debug("Input %s", m->name);
4875
4876    if (p->output_mappings)
4877        PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4878            pa_log_debug("Output %s", m->name);
4879}
4880
4881static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
4882    pa_assert(db_fix);
4883
4884    /* Check that the dB mapping has been configured. Since "db-values" is
4885     * currently the only option in the DecibelFix section, and decibel fix
4886     * objects don't get created if a DecibelFix section is empty, this is
4887     * actually a redundant check. Having this may prevent future bugs,
4888     * however. */
4889    if (!db_fix->db_values) {
4890        pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
4891        return -1;
4892    }
4893
4894    return 0;
4895}
4896
4897void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
4898    char *db_values = NULL;
4899
4900    pa_assert(db_fix);
4901
4902    if (db_fix->db_values) {
4903        pa_strbuf *buf;
4904        unsigned long i, nsteps;
4905
4906        pa_assert(db_fix->min_step <= db_fix->max_step);
4907        nsteps = db_fix->max_step - db_fix->min_step + 1;
4908
4909        buf = pa_strbuf_new();
4910        for (i = 0; i < nsteps; ++i)
4911            pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
4912
4913        db_values = pa_strbuf_to_string_free(buf);
4914    }
4915
4916    pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4917                 db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
4918
4919    pa_xfree(db_values);
4920}
4921
4922pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
4923    pa_alsa_profile_set *ps;
4924    pa_alsa_profile *p;
4925    pa_alsa_mapping *m;
4926    pa_alsa_decibel_fix *db_fix;
4927    char *fn;
4928    int r;
4929    void *state;
4930
4931    static pa_config_item items[] = {
4932        /* [General] */
4933        { "auto-profiles",          pa_config_parse_bool,         NULL, "General" },
4934
4935        /* [Mapping ...] */
4936        { "device-strings",         mapping_parse_device_strings, NULL, NULL },
4937        { "channel-map",            mapping_parse_channel_map,    NULL, NULL },
4938        { "paths-input",            mapping_parse_paths,          NULL, NULL },
4939        { "paths-output",           mapping_parse_paths,          NULL, NULL },
4940        { "element-input",          mapping_parse_element,        NULL, NULL },
4941        { "element-output",         mapping_parse_element,        NULL, NULL },
4942        { "direction",              mapping_parse_direction,      NULL, NULL },
4943        { "exact-channels",         mapping_parse_exact_channels, NULL, NULL },
4944        { "intended-roles",         mapping_parse_intended_roles, NULL, NULL },
4945
4946        /* Shared by [Mapping ...] and [Profile ...] */
4947        { "description",            mapping_parse_description,    NULL, NULL },
4948        { "description-key",        mapping_parse_description_key,NULL, NULL },
4949        { "priority",               mapping_parse_priority,       NULL, NULL },
4950        { "fallback",               mapping_parse_fallback,       NULL, NULL },
4951
4952        /* [Profile ...] */
4953        { "input-mappings",         profile_parse_mappings,       NULL, NULL },
4954        { "output-mappings",        profile_parse_mappings,       NULL, NULL },
4955        { "skip-probe",             profile_parse_skip_probe,     NULL, NULL },
4956
4957        /* [DecibelFix ...] */
4958        { "db-values",              decibel_fix_parse_db_values,  NULL, NULL },
4959        { NULL, NULL, NULL, NULL }
4960    };
4961
4962    ps = pa_xnew0(pa_alsa_profile_set, 1);
4963    ps->mappings = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) mapping_free);
4964    ps->profiles = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) profile_free);
4965    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);
4966    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);
4967    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);
4968
4969    items[0].data = &ps->auto_profiles;
4970
4971    if (!fname)
4972        fname = "default.conf";
4973
4974    fn = pa_maybe_prefix_path(fname,
4975#ifdef HAVE_RUNNING_FROM_BUILD_TREE
4976                              pa_run_from_build_tree() ? PA_SRCDIR "/modules/alsa/mixer/profile-sets/" :
4977#endif
4978                              PA_ALSA_PROFILE_SETS_DIR);
4979
4980    r = pa_config_parse(fn, NULL, items, NULL, false, ps);
4981    pa_xfree(fn);
4982
4983    if (r < 0)
4984        goto fail;
4985
4986    PA_HASHMAP_FOREACH(m, ps->mappings, state)
4987        if (mapping_verify(m, bonus) < 0)
4988            goto fail;
4989
4990    if (ps->auto_profiles)
4991        profile_set_add_auto(ps);
4992
4993    PA_HASHMAP_FOREACH(p, ps->profiles, state)
4994        if (profile_verify(p) < 0)
4995            goto fail;
4996
4997    PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4998        if (decibel_fix_verify(db_fix) < 0)
4999            goto fail;
5000
5001    return ps;
5002
5003fail:
5004    pa_alsa_profile_set_free(ps);
5005    return NULL;
5006}
5007
5008static void profile_finalize_probing(pa_alsa_profile *to_be_finalized, pa_alsa_profile *next) {
5009    pa_alsa_mapping *m;
5010    uint32_t idx;
5011
5012    if (!to_be_finalized)
5013        return;
5014
5015    if (to_be_finalized->output_mappings)
5016        PA_IDXSET_FOREACH(m, to_be_finalized->output_mappings, idx) {
5017
5018            if (!m->output_pcm)
5019                continue;
5020
5021            if (to_be_finalized->supported)
5022                m->supported++;
5023
5024            /* If this mapping is also in the next profile, we won't close the
5025             * pcm handle here, because it would get immediately reopened
5026             * anyway. */
5027            if (next && next->output_mappings && pa_idxset_get_by_data(next->output_mappings, m, NULL))
5028                continue;
5029
5030            snd_pcm_close(m->output_pcm);
5031            m->output_pcm = NULL;
5032        }
5033
5034    if (to_be_finalized->input_mappings)
5035        PA_IDXSET_FOREACH(m, to_be_finalized->input_mappings, idx) {
5036
5037            if (!m->input_pcm)
5038                continue;
5039
5040            if (to_be_finalized->supported)
5041                m->supported++;
5042
5043            /* If this mapping is also in the next profile, we won't close the
5044             * pcm handle here, because it would get immediately reopened
5045             * anyway. */
5046            if (next && next->input_mappings && pa_idxset_get_by_data(next->input_mappings, m, NULL))
5047                continue;
5048
5049            snd_pcm_close(m->input_pcm);
5050            m->input_pcm = NULL;
5051        }
5052}
5053
5054static snd_pcm_t* mapping_open_pcm(pa_alsa_mapping *m,
5055                                   const pa_sample_spec *ss,
5056                                   const char *dev_id,
5057                                   bool exact_channels,
5058                                   int mode,
5059                                   unsigned default_n_fragments,
5060                                   unsigned default_fragment_size_msec) {
5061
5062    snd_pcm_t* handle;
5063    pa_sample_spec try_ss = *ss;
5064    pa_channel_map try_map = m->channel_map;
5065    snd_pcm_uframes_t try_period_size, try_buffer_size;
5066
5067    try_ss.channels = try_map.channels;
5068
5069    try_period_size =
5070        pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
5071        pa_frame_size(&try_ss);
5072    try_buffer_size = default_n_fragments * try_period_size;
5073
5074    handle = pa_alsa_open_by_template(
5075                              m->device_strings, dev_id, NULL, &try_ss,
5076                              &try_map, mode, &try_period_size,
5077                              &try_buffer_size, 0, NULL, NULL, exact_channels);
5078    if (handle && !exact_channels && m->channel_map.channels != try_map.channels) {
5079        char buf[PA_CHANNEL_MAP_SNPRINT_MAX];
5080        pa_log_debug("Channel map for mapping '%s' permanently changed to '%s'", m->name,
5081                     pa_channel_map_snprint(buf, sizeof(buf), &try_map));
5082        m->channel_map = try_map;
5083    }
5084    return handle;
5085}
5086
5087static void paths_drop_unused(pa_hashmap* h, pa_hashmap *keep) {
5088
5089    void* state = NULL;
5090    const void* key;
5091    pa_alsa_path* p;
5092
5093    pa_assert(h);
5094    pa_assert(keep);
5095
5096    p = pa_hashmap_iterate(h, &state, &key);
5097    while (p) {
5098        if (pa_hashmap_get(keep, p) == NULL)
5099            pa_hashmap_remove_and_free(h, key);
5100        p = pa_hashmap_iterate(h, &state, &key);
5101    }
5102}
5103
5104static int add_profiles_to_probe(
5105        pa_alsa_profile **list,
5106        pa_hashmap *profiles,
5107        bool fallback_output,
5108        bool fallback_input) {
5109
5110    int i = 0;
5111    void *state;
5112    pa_alsa_profile *p;
5113    PA_HASHMAP_FOREACH(p, profiles, state)
5114        if (p->fallback_input == fallback_input && p->fallback_output == fallback_output) {
5115            *list = p;
5116            list++;
5117            i++;
5118        }
5119    return i;
5120}
5121
5122static void mapping_query_hw_device(pa_alsa_mapping *mapping, snd_pcm_t *pcm) {
5123    int r;
5124    snd_pcm_info_t* pcm_info;
5125    snd_pcm_info_alloca(&pcm_info);
5126
5127    r = snd_pcm_info(pcm, pcm_info);
5128    if (r < 0) {
5129        pa_log("Mapping %s: snd_pcm_info() failed %s: ", mapping->name, pa_alsa_strerror(r));
5130        return;
5131    }
5132
5133    /* XXX: It's not clear what snd_pcm_info_get_device() does if the device is
5134     * not backed by a hw device or if it's backed by multiple hw devices. We
5135     * only use hw_device_index for HDMI devices, however, and for those the
5136     * return value is expected to be always valid, so this shouldn't be a
5137     * significant problem. */
5138    mapping->hw_device_index = snd_pcm_info_get_device(pcm_info);
5139}
5140
5141void pa_alsa_profile_set_probe(
5142        pa_alsa_profile_set *ps,
5143        pa_hashmap *mixers,
5144        const char *dev_id,
5145        const pa_sample_spec *ss,
5146        unsigned default_n_fragments,
5147        unsigned default_fragment_size_msec) {
5148
5149    bool found_output = false, found_input = false;
5150
5151    pa_alsa_profile *p, *last = NULL;
5152    pa_alsa_profile **pp, **probe_order;
5153    pa_alsa_mapping *m;
5154    pa_hashmap *broken_inputs, *broken_outputs, *used_paths;
5155    pa_alsa_mapping *selected_fallback_input = NULL, *selected_fallback_output = NULL;
5156
5157    pa_assert(ps);
5158    pa_assert(dev_id);
5159    pa_assert(ss);
5160
5161    if (ps->probed)
5162        return;
5163
5164    broken_inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
5165    broken_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
5166    used_paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
5167    pp = probe_order = pa_xnew0(pa_alsa_profile *, pa_hashmap_size(ps->profiles) + 1);
5168
5169    pp += add_profiles_to_probe(pp, ps->profiles, false, false);
5170    pp += add_profiles_to_probe(pp, ps->profiles, false, true);
5171    pp += add_profiles_to_probe(pp, ps->profiles, true, false);
5172    pp += add_profiles_to_probe(pp, ps->profiles, true, true);
5173
5174    for (pp = probe_order; *pp; pp++) {
5175        uint32_t idx;
5176        p = *pp;
5177
5178        /* Skip if fallback and already found something, but still probe already selected fallbacks.
5179         * If UCM is used then both fallback_input and fallback_output flags are false.
5180         * If UCM is not used then there will be only a single entry in mappings.
5181         */
5182        if (found_input && p->fallback_input)
5183            if (selected_fallback_input == NULL || pa_idxset_get_by_index(p->input_mappings, 0) != selected_fallback_input)
5184                continue;
5185        if (found_output && p->fallback_output)
5186            if (selected_fallback_output == NULL || pa_idxset_get_by_index(p->output_mappings, 0) != selected_fallback_output)
5187                continue;
5188
5189        /* Skip if this is already marked that it is supported (i.e. from the config file) */
5190        if (!p->supported) {
5191
5192            profile_finalize_probing(last, p);
5193            p->supported = true;
5194
5195            if (p->output_mappings) {
5196                PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
5197                    if (pa_hashmap_get(broken_outputs, m) == m) {
5198                        pa_log_debug("Skipping profile %s - will not be able to open output:%s", p->name, m->name);
5199                        p->supported = false;
5200                        break;
5201                    }
5202                }
5203            }
5204
5205            if (p->input_mappings && p->supported) {
5206                PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
5207                    if (pa_hashmap_get(broken_inputs, m) == m) {
5208                        pa_log_debug("Skipping profile %s - will not be able to open input:%s", p->name, m->name);
5209                        p->supported = false;
5210                        break;
5211                    }
5212                }
5213            }
5214
5215            if (p->supported)
5216                pa_log_debug("Looking at profile %s", p->name);
5217
5218            /* Check if we can open all new ones */
5219            if (p->output_mappings && p->supported)
5220                PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
5221
5222                    if (m->output_pcm)
5223                        continue;
5224
5225                    pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
5226                    if (!(m->output_pcm = mapping_open_pcm(m, ss, dev_id, m->exact_channels,
5227                                                           SND_PCM_STREAM_PLAYBACK,
5228                                                           default_n_fragments,
5229                                                           default_fragment_size_msec))) {
5230                        p->supported = false;
5231                        if (pa_idxset_size(p->output_mappings) == 1 &&
5232                            ((!p->input_mappings) || pa_idxset_size(p->input_mappings) == 0)) {
5233                            pa_log_debug("Caching failure to open output:%s", m->name);
5234                            pa_hashmap_put(broken_outputs, m, m);
5235                        }
5236                        break;
5237                    }
5238
5239                    if (m->hw_device_index < 0)
5240                        mapping_query_hw_device(m, m->output_pcm);
5241                }
5242
5243            if (p->input_mappings && p->supported)
5244                PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
5245
5246                    if (m->input_pcm)
5247                        continue;
5248
5249                    pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
5250                    if (!(m->input_pcm = mapping_open_pcm(m, ss, dev_id, m->exact_channels,
5251                                                          SND_PCM_STREAM_CAPTURE,
5252                                                          default_n_fragments,
5253                                                          default_fragment_size_msec))) {
5254                        p->supported = false;
5255                        if (pa_idxset_size(p->input_mappings) == 1 &&
5256                            ((!p->output_mappings) || pa_idxset_size(p->output_mappings) == 0)) {
5257                            pa_log_debug("Caching failure to open input:%s", m->name);
5258                            pa_hashmap_put(broken_inputs, m, m);
5259                        }
5260                        break;
5261                    }
5262
5263                    if (m->hw_device_index < 0)
5264                        mapping_query_hw_device(m, m->input_pcm);
5265                }
5266
5267            last = p;
5268
5269            if (!p->supported)
5270                continue;
5271        }
5272
5273        pa_log_debug("Profile %s supported.", p->name);
5274
5275        if (p->output_mappings)
5276            PA_IDXSET_FOREACH(m, p->output_mappings, idx)
5277                if (m->output_pcm) {
5278                    found_output = true;
5279                    if (p->fallback_output && selected_fallback_output == NULL) {
5280                        selected_fallback_output = m;
5281                    }
5282                    mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT, used_paths, mixers);
5283                }
5284
5285        if (p->input_mappings)
5286            PA_IDXSET_FOREACH(m, p->input_mappings, idx)
5287                if (m->input_pcm) {
5288                    found_input = true;
5289                    if (p->fallback_input && selected_fallback_input == NULL) {
5290                        selected_fallback_input = m;
5291                    }
5292                    mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT, used_paths, mixers);
5293                }
5294    }
5295
5296    /* Clean up */
5297    profile_finalize_probing(last, NULL);
5298
5299    pa_alsa_profile_set_drop_unsupported(ps);
5300
5301    paths_drop_unused(ps->input_paths, used_paths);
5302    paths_drop_unused(ps->output_paths, used_paths);
5303    pa_hashmap_free(broken_inputs);
5304    pa_hashmap_free(broken_outputs);
5305    pa_hashmap_free(used_paths);
5306    pa_xfree(probe_order);
5307
5308    profile_set_set_availability_groups(ps);
5309
5310    ps->probed = true;
5311}
5312
5313void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
5314    pa_alsa_profile *p;
5315    pa_alsa_mapping *m;
5316    pa_alsa_decibel_fix *db_fix;
5317    void *state;
5318
5319    pa_assert(ps);
5320
5321    pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
5322                 (void*)
5323                 ps,
5324                 pa_yes_no(ps->auto_profiles),
5325                 pa_yes_no(ps->probed),
5326                 pa_hashmap_size(ps->mappings),
5327                 pa_hashmap_size(ps->profiles),
5328                 pa_hashmap_size(ps->decibel_fixes));
5329
5330    PA_HASHMAP_FOREACH(m, ps->mappings, state)
5331        pa_alsa_mapping_dump(m);
5332
5333    PA_HASHMAP_FOREACH(p, ps->profiles, state)
5334        pa_alsa_profile_dump(p);
5335
5336    PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
5337        pa_alsa_decibel_fix_dump(db_fix);
5338}
5339
5340void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set *ps) {
5341    pa_alsa_profile *p;
5342    pa_alsa_mapping *m;
5343    void *state;
5344
5345    PA_HASHMAP_FOREACH(p, ps->profiles, state) {
5346        if (!p->supported)
5347            pa_hashmap_remove_and_free(ps->profiles, p->name);
5348    }
5349
5350    PA_HASHMAP_FOREACH(m, ps->mappings, state) {
5351        if (m->supported <= 0)
5352            pa_hashmap_remove_and_free(ps->mappings, m->name);
5353    }
5354}
5355
5356static pa_device_port* device_port_alsa_init(pa_hashmap *ports, /* card ports */
5357    const char* name,
5358    const char* description,
5359    pa_alsa_path *path,
5360    pa_alsa_setting *setting,
5361    pa_card_profile *cp,
5362    pa_hashmap *extra, /* sink/source ports */
5363    pa_core *core) {
5364
5365    pa_device_port *p;
5366
5367    pa_assert(path);
5368
5369    p = pa_hashmap_get(ports, name);
5370
5371    if (!p) {
5372        pa_alsa_port_data *data;
5373        pa_device_port_new_data port_data;
5374
5375        pa_device_port_new_data_init(&port_data);
5376        pa_device_port_new_data_set_name(&port_data, name);
5377        pa_device_port_new_data_set_description(&port_data, description);
5378        pa_device_port_new_data_set_direction(&port_data, path->direction == PA_ALSA_DIRECTION_OUTPUT ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT);
5379        pa_device_port_new_data_set_type(&port_data, path->device_port_type);
5380        pa_device_port_new_data_set_availability_group(&port_data, path->availability_group);
5381
5382        p = pa_device_port_new(core, &port_data, sizeof(pa_alsa_port_data));
5383        pa_device_port_new_data_done(&port_data);
5384        pa_assert(p);
5385        pa_hashmap_put(ports, p->name, p);
5386        pa_proplist_update(p->proplist, PA_UPDATE_REPLACE, path->proplist);
5387
5388        data = PA_DEVICE_PORT_DATA(p);
5389        /* Ownership of the path and setting is not transferred to the port data, so we don't deal with freeing them */
5390        data->path = path;
5391        data->setting = setting;
5392        path->port = p;
5393    }
5394
5395    if (cp)
5396        pa_hashmap_put(p->profiles, cp->name, cp);
5397
5398    if (extra) {
5399        pa_hashmap_put(extra, p->name, p);
5400        pa_device_port_ref(p);
5401    }
5402
5403    return p;
5404}
5405
5406void pa_alsa_path_set_add_ports(
5407        pa_alsa_path_set *ps,
5408        pa_card_profile *cp,
5409        pa_hashmap *ports, /* card ports */
5410        pa_hashmap *extra, /* sink/source ports */
5411        pa_core *core) {
5412
5413    pa_alsa_path *path;
5414    void *state;
5415
5416    pa_assert(ports);
5417
5418    if (!ps)
5419        return;
5420
5421    PA_HASHMAP_FOREACH(path, ps->paths, state) {
5422        if (!path->settings || !path->settings->next) {
5423            /* If there is no or just one setting we only need a
5424             * single entry */
5425            pa_device_port *port = device_port_alsa_init(ports, path->name,
5426                path->description, path, path->settings, cp, extra, core);
5427            port->priority = path->priority * 100;
5428
5429        } else {
5430            pa_alsa_setting *s;
5431            PA_LLIST_FOREACH(s, path->settings) {
5432                pa_device_port *port;
5433                char *n, *d;
5434
5435                n = pa_sprintf_malloc("%s;%s", path->name, s->name);
5436
5437                if (s->description[0])
5438                    d = pa_sprintf_malloc("%s / %s", path->description, s->description);
5439                else
5440                    d = pa_xstrdup(path->description);
5441
5442                port = device_port_alsa_init(ports, n, d, path, s, cp, extra, core);
5443                port->priority = path->priority * 100 + s->priority;
5444
5445                pa_xfree(n);
5446                pa_xfree(d);
5447            }
5448        }
5449    }
5450}
5451
5452void pa_alsa_add_ports(void *sink_or_source_new_data, pa_alsa_path_set *ps, pa_card *card) {
5453    pa_hashmap *ports;
5454
5455    pa_assert(sink_or_source_new_data);
5456    pa_assert(ps);
5457
5458    if (ps->direction == PA_ALSA_DIRECTION_OUTPUT)
5459        ports = ((pa_sink_new_data *) sink_or_source_new_data)->ports;
5460    else
5461        ports = ((pa_source_new_data *) sink_or_source_new_data)->ports;
5462
5463    if (ps->paths && pa_hashmap_size(ps->paths) > 0) {
5464        pa_assert(card);
5465        pa_alsa_path_set_add_ports(ps, NULL, card->ports, ports, card->core);
5466    }
5467
5468    pa_log_debug("Added %u ports", pa_hashmap_size(ports));
5469}
5470