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