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