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