alsa-mixer: When figuring out the max_dB of a path, use only channels that are used...
[platform/upstream/pulseaudio.git] / src / modules / alsa / alsa-mixer.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2004-2009 Lennart Poettering
5   Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7   PulseAudio is free software; you can redistribute it and/or modify
8   it under the terms of the GNU Lesser General Public License as published
9   by the Free Software Foundation; either version 2.1 of the License,
10   or (at your option) any later version.
11
12   PulseAudio is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with PulseAudio; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20   USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <sys/types.h>
28 #include <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 static int element_mute_volume(pa_alsa_element *e, snd_mixer_t *m) {
1088     snd_mixer_elem_t *me;
1089     snd_mixer_selem_id_t *sid;
1090     int r;
1091
1092     pa_assert(m);
1093     pa_assert(e);
1094
1095     SELEM_INIT(sid, e->alsa_name);
1096     if (!(me = snd_mixer_find_selem(m, sid))) {
1097         pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1098         return -1;
1099     }
1100
1101     if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1102         r = snd_mixer_selem_set_playback_volume_all(me, e->min_volume);
1103     else
1104         r = snd_mixer_selem_set_capture_volume_all(me, e->min_volume);
1105
1106     if (r < 0)
1107         pa_log_warn("Failed to set volume to muted of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1108
1109     return r;
1110 }
1111
1112 /* The volume to 0dB */
1113 static int element_zero_volume(pa_alsa_element *e, snd_mixer_t *m) {
1114     snd_mixer_elem_t *me;
1115     snd_mixer_selem_id_t *sid;
1116     int r;
1117
1118     pa_assert(m);
1119     pa_assert(e);
1120
1121     SELEM_INIT(sid, e->alsa_name);
1122     if (!(me = snd_mixer_find_selem(m, sid))) {
1123         pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1124         return -1;
1125     }
1126
1127     if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1128         if (e->db_fix) {
1129             long value = 0;
1130
1131             r = snd_mixer_selem_set_playback_volume_all(me, decibel_fix_get_step(e->db_fix, &value, +1));
1132         } else
1133             r = snd_mixer_selem_set_playback_dB_all(me, 0, +1);
1134     else
1135         if (e->db_fix) {
1136             long value = 0;
1137
1138             r = snd_mixer_selem_set_capture_volume_all(me, decibel_fix_get_step(e->db_fix, &value, +1));
1139         } else
1140             r = snd_mixer_selem_set_capture_dB_all(me, 0, +1);
1141
1142     if (r < 0)
1143         pa_log_warn("Failed to set volume to 0dB of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1144
1145     return r;
1146 }
1147
1148 int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
1149     pa_alsa_element *e;
1150     int r = 0;
1151
1152     pa_assert(m);
1153     pa_assert(p);
1154
1155     pa_log_debug("Activating path %s", p->name);
1156     pa_alsa_path_dump(p);
1157
1158     PA_LLIST_FOREACH(e, p->elements) {
1159
1160         switch (e->switch_use) {
1161             case PA_ALSA_SWITCH_OFF:
1162                 r = element_set_switch(e, m, FALSE);
1163                 break;
1164
1165             case PA_ALSA_SWITCH_ON:
1166                 r = element_set_switch(e, m, TRUE);
1167                 break;
1168
1169             case PA_ALSA_SWITCH_MUTE:
1170             case PA_ALSA_SWITCH_IGNORE:
1171             case PA_ALSA_SWITCH_SELECT:
1172                 r = 0;
1173                 break;
1174         }
1175
1176         if (r < 0)
1177             return -1;
1178
1179         switch (e->volume_use) {
1180             case PA_ALSA_VOLUME_OFF:
1181                 r = element_mute_volume(e, m);
1182                 break;
1183
1184             case PA_ALSA_VOLUME_ZERO:
1185                 r = element_zero_volume(e, m);
1186                 break;
1187
1188             case PA_ALSA_VOLUME_MERGE:
1189             case PA_ALSA_VOLUME_IGNORE:
1190                 r = 0;
1191                 break;
1192         }
1193
1194         if (r < 0)
1195             return -1;
1196     }
1197
1198     return 0;
1199 }
1200
1201 static int check_required(pa_alsa_element *e, snd_mixer_elem_t *me) {
1202     pa_bool_t has_switch;
1203     pa_bool_t has_enumeration;
1204     pa_bool_t has_volume;
1205
1206     pa_assert(e);
1207     pa_assert(me);
1208
1209     if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1210         has_switch =
1211             snd_mixer_selem_has_playback_switch(me) ||
1212             (e->direction_try_other && snd_mixer_selem_has_capture_switch(me));
1213     } else {
1214         has_switch =
1215             snd_mixer_selem_has_capture_switch(me) ||
1216             (e->direction_try_other && snd_mixer_selem_has_playback_switch(me));
1217     }
1218
1219     if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1220         has_volume =
1221             snd_mixer_selem_has_playback_volume(me) ||
1222             (e->direction_try_other && snd_mixer_selem_has_capture_volume(me));
1223     } else {
1224         has_volume =
1225             snd_mixer_selem_has_capture_volume(me) ||
1226             (e->direction_try_other && snd_mixer_selem_has_playback_volume(me));
1227     }
1228
1229     has_enumeration = snd_mixer_selem_is_enumerated(me);
1230
1231     if ((e->required == PA_ALSA_REQUIRED_SWITCH && !has_switch) ||
1232         (e->required == PA_ALSA_REQUIRED_VOLUME && !has_volume) ||
1233         (e->required == PA_ALSA_REQUIRED_ENUMERATION && !has_enumeration))
1234         return -1;
1235
1236     if (e->required == PA_ALSA_REQUIRED_ANY && !(has_switch || has_volume || has_enumeration))
1237         return -1;
1238
1239     if ((e->required_absent == PA_ALSA_REQUIRED_SWITCH && has_switch) ||
1240         (e->required_absent == PA_ALSA_REQUIRED_VOLUME && has_volume) ||
1241         (e->required_absent == PA_ALSA_REQUIRED_ENUMERATION && has_enumeration))
1242         return -1;
1243
1244     if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration))
1245         return -1;
1246
1247     if (e->required_any != PA_ALSA_REQUIRED_IGNORE) {
1248         switch (e->required_any) {
1249             case PA_ALSA_REQUIRED_VOLUME:
1250                 e->path->req_any_present |= (e->volume_use != PA_ALSA_VOLUME_IGNORE);
1251                 break;
1252             case PA_ALSA_REQUIRED_SWITCH:
1253                 e->path->req_any_present |= (e->switch_use != PA_ALSA_SWITCH_IGNORE);
1254                 break;
1255             case PA_ALSA_REQUIRED_ENUMERATION:
1256                 e->path->req_any_present |= (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
1257                 break;
1258             case PA_ALSA_REQUIRED_ANY:
1259                 e->path->req_any_present |=
1260                     (e->volume_use != PA_ALSA_VOLUME_IGNORE) ||
1261                     (e->switch_use != PA_ALSA_SWITCH_IGNORE) ||
1262                     (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
1263                 break;
1264             default:
1265                 pa_assert_not_reached();
1266         }
1267     }
1268
1269     if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1270         pa_alsa_option *o;
1271         PA_LLIST_FOREACH(o, e->options) {
1272             e->path->req_any_present |= (o->required_any != PA_ALSA_REQUIRED_IGNORE) &&
1273                 (o->alsa_idx >= 0);
1274             if (o->required != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx < 0)
1275                 return -1;
1276             if (o->required_absent != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx >= 0)
1277                 return -1;
1278         }
1279     }
1280
1281     return 0;
1282 }
1283
1284 static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
1285     snd_mixer_selem_id_t *sid;
1286     snd_mixer_elem_t *me;
1287
1288     pa_assert(m);
1289     pa_assert(e);
1290     pa_assert(e->path);
1291
1292     SELEM_INIT(sid, e->alsa_name);
1293
1294     if (!(me = snd_mixer_find_selem(m, sid))) {
1295
1296         if (e->required != PA_ALSA_REQUIRED_IGNORE)
1297             return -1;
1298
1299         e->switch_use = PA_ALSA_SWITCH_IGNORE;
1300         e->volume_use = PA_ALSA_VOLUME_IGNORE;
1301         e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1302
1303         return 0;
1304     }
1305
1306     if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
1307         if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1308
1309             if (!snd_mixer_selem_has_playback_switch(me)) {
1310                 if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
1311                     e->direction = PA_ALSA_DIRECTION_INPUT;
1312                 else
1313                     e->switch_use = PA_ALSA_SWITCH_IGNORE;
1314             }
1315
1316         } else {
1317
1318             if (!snd_mixer_selem_has_capture_switch(me)) {
1319                 if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
1320                     e->direction = PA_ALSA_DIRECTION_OUTPUT;
1321                 else
1322                     e->switch_use = PA_ALSA_SWITCH_IGNORE;
1323             }
1324         }
1325
1326         if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
1327             e->direction_try_other = FALSE;
1328     }
1329
1330     if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1331
1332         if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1333
1334             if (!snd_mixer_selem_has_playback_volume(me)) {
1335                 if (e->direction_try_other && snd_mixer_selem_has_capture_volume(me))
1336                     e->direction = PA_ALSA_DIRECTION_INPUT;
1337                 else
1338                     e->volume_use = PA_ALSA_VOLUME_IGNORE;
1339             }
1340
1341         } else {
1342
1343             if (!snd_mixer_selem_has_capture_volume(me)) {
1344                 if (e->direction_try_other && snd_mixer_selem_has_playback_volume(me))
1345                     e->direction = PA_ALSA_DIRECTION_OUTPUT;
1346                 else
1347                     e->volume_use = PA_ALSA_VOLUME_IGNORE;
1348             }
1349         }
1350
1351         if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1352             long min_dB = 0, max_dB = 0;
1353             int r;
1354
1355             e->direction_try_other = FALSE;
1356
1357             if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1358                 r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume);
1359             else
1360                 r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
1361
1362             if (r < 0) {
1363                 pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1364                 return -1;
1365             }
1366
1367             if (e->min_volume >= e->max_volume) {
1368                 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);
1369                 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1370
1371             } else {
1372                 pa_bool_t is_mono;
1373                 pa_channel_position_t p;
1374
1375                 if (e->db_fix &&
1376                         ((e->min_volume > e->db_fix->min_step) ||
1377                          (e->max_volume < e->db_fix->max_step))) {
1378                     pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1379                                 "real hardware range (%li-%li). Disabling the decibel fix.", e->alsa_name,
1380                                 e->db_fix->min_step, e->db_fix->max_step,
1381                                 e->min_volume, e->max_volume);
1382
1383                     decibel_fix_free(e->db_fix);
1384                     e->db_fix = NULL;
1385                 }
1386
1387                 if (e->db_fix) {
1388                     e->has_dB = TRUE;
1389                     e->min_volume = e->db_fix->min_step;
1390                     e->max_volume = e->db_fix->max_step;
1391                     min_dB = e->db_fix->db_values[0];
1392                     max_dB = e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step];
1393                 } else if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1394                     e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0;
1395                 else
1396                     e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0;
1397
1398                 if (e->has_dB) {
1399 #ifdef HAVE_VALGRIND_MEMCHECK_H
1400                     VALGRIND_MAKE_MEM_DEFINED(&min_dB, sizeof(min_dB));
1401                     VALGRIND_MAKE_MEM_DEFINED(&max_dB, sizeof(max_dB));
1402 #endif
1403
1404                     e->min_dB = ((double) min_dB) / 100.0;
1405                     e->max_dB = ((double) max_dB) / 100.0;
1406
1407                     if (min_dB >= max_dB) {
1408                         pa_assert(!e->db_fix);
1409                         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);
1410                         e->has_dB = FALSE;
1411                     }
1412                 }
1413
1414                 if (e->volume_limit >= 0) {
1415                     if (e->volume_limit <= e->min_volume || e->volume_limit > e->max_volume)
1416                         pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1417                                     "%li-%li. The volume limit is ignored.",
1418                                     e->alsa_name, e->path->name, e->volume_limit, e->min_volume + 1, e->max_volume);
1419
1420                     else {
1421                         e->max_volume = e->volume_limit;
1422
1423                         if (e->has_dB) {
1424                             if (e->db_fix) {
1425                                 e->db_fix->max_step = e->max_volume;
1426                                 e->max_dB = ((double) e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step]) / 100.0;
1427
1428                             } else {
1429                                 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1430                                     r = snd_mixer_selem_ask_playback_vol_dB(me, e->max_volume, &max_dB);
1431                                 else
1432                                     r = snd_mixer_selem_ask_capture_vol_dB(me, e->max_volume, &max_dB);
1433
1434                                 if (r < 0) {
1435                                     pa_log_warn("Failed to get dB value of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1436                                     e->has_dB = FALSE;
1437                                 } else
1438                                     e->max_dB = ((double) max_dB) / 100.0;
1439                             }
1440                         }
1441                     }
1442                 }
1443
1444                 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1445                     is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
1446                 else
1447                     is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
1448
1449                 if (is_mono) {
1450                     e->n_channels = 1;
1451
1452                     if (!e->override_map) {
1453                         for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++)
1454                             e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
1455                         e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
1456                     }
1457
1458                     e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
1459                 } else {
1460                     e->n_channels = 0;
1461                     for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1462
1463                         if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1464                             continue;
1465
1466                         if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1467                             e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1468                         else
1469                             e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1470                     }
1471
1472                     if (e->n_channels <= 0) {
1473                         pa_log_warn("Volume element %s with no channels?", e->alsa_name);
1474                         return -1;
1475                     }
1476
1477                     if (!e->override_map) {
1478                         for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1479                             pa_bool_t has_channel;
1480
1481                             if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1482                                 continue;
1483
1484                             if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1485                                 has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1486                             else
1487                                 has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1488
1489                             e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
1490                         }
1491                     }
1492
1493                     e->merged_mask = 0;
1494                     for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++)
1495                         e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
1496                 }
1497             }
1498         }
1499
1500     }
1501
1502     if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1503         pa_alsa_option *o;
1504
1505         PA_LLIST_FOREACH(o, e->options)
1506             o->alsa_idx = pa_streq(o->alsa_name, "on") ? 1 : 0;
1507     } else if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1508         int n;
1509         pa_alsa_option *o;
1510
1511         if ((n = snd_mixer_selem_get_enum_items(me)) < 0) {
1512             pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n));
1513             return -1;
1514         }
1515
1516         PA_LLIST_FOREACH(o, e->options) {
1517             int i;
1518
1519             for (i = 0; i < n; i++) {
1520                 char buf[128];
1521
1522                 if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
1523                     continue;
1524
1525                 if (!pa_streq(buf, o->alsa_name))
1526                     continue;
1527
1528                 o->alsa_idx = i;
1529             }
1530         }
1531     }
1532
1533     if (check_required(e, me) < 0)
1534         return -1;
1535
1536     return 0;
1537 }
1538
1539 static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, pa_bool_t prefixed) {
1540     pa_alsa_element *e;
1541
1542     pa_assert(p);
1543     pa_assert(section);
1544
1545     if (prefixed) {
1546         if (!pa_startswith(section, "Element "))
1547             return NULL;
1548
1549         section += 8;
1550     }
1551
1552     /* This is not an element section, but an enum section? */
1553     if (strchr(section, ':'))
1554         return NULL;
1555
1556     if (p->last_element && pa_streq(p->last_element->alsa_name, section))
1557         return p->last_element;
1558
1559     PA_LLIST_FOREACH(e, p->elements)
1560         if (pa_streq(e->alsa_name, section))
1561             goto finish;
1562
1563     e = pa_xnew0(pa_alsa_element, 1);
1564     e->path = p;
1565     e->alsa_name = pa_xstrdup(section);
1566     e->direction = p->direction;
1567     e->volume_limit = -1;
1568
1569     PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
1570
1571 finish:
1572     p->last_element = e;
1573     return e;
1574 }
1575
1576 static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
1577     char *en;
1578     const char *on;
1579     pa_alsa_option *o;
1580     pa_alsa_element *e;
1581
1582     if (!pa_startswith(section, "Option "))
1583         return NULL;
1584
1585     section += 7;
1586
1587     /* This is not an enum section, but an element section? */
1588     if (!(on = strchr(section, ':')))
1589         return NULL;
1590
1591     en = pa_xstrndup(section, on - section);
1592     on++;
1593
1594     if (p->last_option &&
1595         pa_streq(p->last_option->element->alsa_name, en) &&
1596         pa_streq(p->last_option->alsa_name, on)) {
1597         pa_xfree(en);
1598         return p->last_option;
1599     }
1600
1601     pa_assert_se(e = element_get(p, en, FALSE));
1602     pa_xfree(en);
1603
1604     PA_LLIST_FOREACH(o, e->options)
1605         if (pa_streq(o->alsa_name, on))
1606             goto finish;
1607
1608     o = pa_xnew0(pa_alsa_option, 1);
1609     o->element = e;
1610     o->alsa_name = pa_xstrdup(on);
1611     o->alsa_idx = -1;
1612
1613     if (p->last_option && p->last_option->element == e)
1614         PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
1615     else
1616         PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
1617
1618 finish:
1619     p->last_option = o;
1620     return o;
1621 }
1622
1623 static int element_parse_switch(
1624         const char *filename,
1625         unsigned line,
1626         const char *section,
1627         const char *lvalue,
1628         const char *rvalue,
1629         void *data,
1630         void *userdata) {
1631
1632     pa_alsa_path *p = userdata;
1633     pa_alsa_element *e;
1634
1635     pa_assert(p);
1636
1637     if (!(e = element_get(p, section, TRUE))) {
1638         pa_log("[%s:%u] Switch makes no sense in '%s'", filename, line, section);
1639         return -1;
1640     }
1641
1642     if (pa_streq(rvalue, "ignore"))
1643         e->switch_use = PA_ALSA_SWITCH_IGNORE;
1644     else if (pa_streq(rvalue, "mute"))
1645         e->switch_use = PA_ALSA_SWITCH_MUTE;
1646     else if (pa_streq(rvalue, "off"))
1647         e->switch_use = PA_ALSA_SWITCH_OFF;
1648     else if (pa_streq(rvalue, "on"))
1649         e->switch_use = PA_ALSA_SWITCH_ON;
1650     else if (pa_streq(rvalue, "select"))
1651         e->switch_use = PA_ALSA_SWITCH_SELECT;
1652     else {
1653         pa_log("[%s:%u] Switch invalid of '%s'", filename, line, section);
1654         return -1;
1655     }
1656
1657     return 0;
1658 }
1659
1660 static int element_parse_volume(
1661         const char *filename,
1662         unsigned line,
1663         const char *section,
1664         const char *lvalue,
1665         const char *rvalue,
1666         void *data,
1667         void *userdata) {
1668
1669     pa_alsa_path *p = userdata;
1670     pa_alsa_element *e;
1671
1672     pa_assert(p);
1673
1674     if (!(e = element_get(p, section, TRUE))) {
1675         pa_log("[%s:%u] Volume makes no sense in '%s'", filename, line, section);
1676         return -1;
1677     }
1678
1679     if (pa_streq(rvalue, "ignore"))
1680         e->volume_use = PA_ALSA_VOLUME_IGNORE;
1681     else if (pa_streq(rvalue, "merge"))
1682         e->volume_use = PA_ALSA_VOLUME_MERGE;
1683     else if (pa_streq(rvalue, "off"))
1684         e->volume_use = PA_ALSA_VOLUME_OFF;
1685     else if (pa_streq(rvalue, "zero"))
1686         e->volume_use = PA_ALSA_VOLUME_ZERO;
1687     else {
1688         pa_log("[%s:%u] Volume invalid of '%s'", filename, line, section);
1689         return -1;
1690     }
1691
1692     return 0;
1693 }
1694
1695 static int element_parse_enumeration(
1696         const char *filename,
1697         unsigned line,
1698         const char *section,
1699         const char *lvalue,
1700         const char *rvalue,
1701         void *data,
1702         void *userdata) {
1703
1704     pa_alsa_path *p = userdata;
1705     pa_alsa_element *e;
1706
1707     pa_assert(p);
1708
1709     if (!(e = element_get(p, section, TRUE))) {
1710         pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename, line, section);
1711         return -1;
1712     }
1713
1714     if (pa_streq(rvalue, "ignore"))
1715         e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1716     else if (pa_streq(rvalue, "select"))
1717         e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
1718     else {
1719         pa_log("[%s:%u] Enumeration invalid of '%s'", filename, line, section);
1720         return -1;
1721     }
1722
1723     return 0;
1724 }
1725
1726 static int option_parse_priority(
1727         const char *filename,
1728         unsigned line,
1729         const char *section,
1730         const char *lvalue,
1731         const char *rvalue,
1732         void *data,
1733         void *userdata) {
1734
1735     pa_alsa_path *p = userdata;
1736     pa_alsa_option *o;
1737     uint32_t prio;
1738
1739     pa_assert(p);
1740
1741     if (!(o = option_get(p, section))) {
1742         pa_log("[%s:%u] Priority makes no sense in '%s'", filename, line, section);
1743         return -1;
1744     }
1745
1746     if (pa_atou(rvalue, &prio) < 0) {
1747         pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
1748         return -1;
1749     }
1750
1751     o->priority = prio;
1752     return 0;
1753 }
1754
1755 static int option_parse_name(
1756         const char *filename,
1757         unsigned line,
1758         const char *section,
1759         const char *lvalue,
1760         const char *rvalue,
1761         void *data,
1762         void *userdata) {
1763
1764     pa_alsa_path *p = userdata;
1765     pa_alsa_option *o;
1766
1767     pa_assert(p);
1768
1769     if (!(o = option_get(p, section))) {
1770         pa_log("[%s:%u] Name makes no sense in '%s'", filename, line, section);
1771         return -1;
1772     }
1773
1774     pa_xfree(o->name);
1775     o->name = pa_xstrdup(rvalue);
1776
1777     return 0;
1778 }
1779
1780 static int element_parse_required(
1781         const char *filename,
1782         unsigned line,
1783         const char *section,
1784         const char *lvalue,
1785         const char *rvalue,
1786         void *data,
1787         void *userdata) {
1788
1789     pa_alsa_path *p = userdata;
1790     pa_alsa_element *e;
1791     pa_alsa_option *o;
1792     pa_alsa_required_t req;
1793
1794     pa_assert(p);
1795
1796     e = element_get(p, section, TRUE);
1797     o = option_get(p, section);
1798     if (!e && !o) {
1799         pa_log("[%s:%u] Required makes no sense in '%s'", filename, line, section);
1800         return -1;
1801     }
1802
1803     if (pa_streq(rvalue, "ignore"))
1804         req = PA_ALSA_REQUIRED_IGNORE;
1805     else if (pa_streq(rvalue, "switch") && e)
1806         req = PA_ALSA_REQUIRED_SWITCH;
1807     else if (pa_streq(rvalue, "volume") && e)
1808         req = PA_ALSA_REQUIRED_VOLUME;
1809     else if (pa_streq(rvalue, "enumeration"))
1810         req = PA_ALSA_REQUIRED_ENUMERATION;
1811     else if (pa_streq(rvalue, "any"))
1812         req = PA_ALSA_REQUIRED_ANY;
1813     else {
1814         pa_log("[%s:%u] Required invalid of '%s'", filename, line, section);
1815         return -1;
1816     }
1817
1818     if (pa_streq(lvalue, "required-absent")) {
1819         if (e)
1820             e->required_absent = req;
1821         if (o)
1822             o->required_absent = req;
1823     }
1824     else if (pa_streq(lvalue, "required-any")) {
1825         if (e) {
1826             e->required_any = req;
1827             e->path->has_req_any = TRUE;
1828         }
1829         if (o) {
1830             o->required_any = req;
1831             o->element->path->has_req_any = TRUE;
1832         }
1833     }
1834     else {
1835         if (e)
1836             e->required = req;
1837         if (o)
1838             o->required = req;
1839     }
1840
1841     return 0;
1842 }
1843
1844 static int element_parse_direction(
1845         const char *filename,
1846         unsigned line,
1847         const char *section,
1848         const char *lvalue,
1849         const char *rvalue,
1850         void *data,
1851         void *userdata) {
1852
1853     pa_alsa_path *p = userdata;
1854     pa_alsa_element *e;
1855
1856     pa_assert(p);
1857
1858     if (!(e = element_get(p, section, TRUE))) {
1859         pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1860         return -1;
1861     }
1862
1863     if (pa_streq(rvalue, "playback"))
1864         e->direction = PA_ALSA_DIRECTION_OUTPUT;
1865     else if (pa_streq(rvalue, "capture"))
1866         e->direction = PA_ALSA_DIRECTION_INPUT;
1867     else {
1868         pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1869         return -1;
1870     }
1871
1872     return 0;
1873 }
1874
1875 static int element_parse_direction_try_other(
1876         const char *filename,
1877         unsigned line,
1878         const char *section,
1879         const char *lvalue,
1880         const char *rvalue,
1881         void *data,
1882         void *userdata) {
1883
1884     pa_alsa_path *p = userdata;
1885     pa_alsa_element *e;
1886     int yes;
1887
1888     if (!(e = element_get(p, section, TRUE))) {
1889         pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1890         return -1;
1891     }
1892
1893     if ((yes = pa_parse_boolean(rvalue)) < 0) {
1894         pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1895         return -1;
1896     }
1897
1898     e->direction_try_other = !!yes;
1899     return 0;
1900 }
1901
1902 static int element_parse_volume_limit(
1903         const char *filename,
1904         unsigned line,
1905         const char *section,
1906         const char *lvalue,
1907         const char *rvalue,
1908         void *data,
1909         void *userdata) {
1910
1911     pa_alsa_path *p = userdata;
1912     pa_alsa_element *e;
1913     uint32_t volume_limit;
1914
1915     if (!(e = element_get(p, section, TRUE))) {
1916         pa_log("[%s:%u] volume-limit makes no sense in '%s'", filename, line, section);
1917         return -1;
1918     }
1919
1920     if (pa_atou(rvalue, &volume_limit) < 0 || volume_limit > LONG_MAX) {
1921         pa_log("[%s:%u] Invalid value for volume-limit", filename, line);
1922         return -1;
1923     }
1924
1925     e->volume_limit = volume_limit;
1926     return 0;
1927 }
1928
1929 static pa_channel_position_mask_t parse_mask(const char *m) {
1930     pa_channel_position_mask_t v;
1931
1932     if (pa_streq(m, "all-left"))
1933         v = PA_CHANNEL_POSITION_MASK_LEFT;
1934     else if (pa_streq(m, "all-right"))
1935         v = PA_CHANNEL_POSITION_MASK_RIGHT;
1936     else if (pa_streq(m, "all-center"))
1937         v = PA_CHANNEL_POSITION_MASK_CENTER;
1938     else if (pa_streq(m, "all-front"))
1939         v = PA_CHANNEL_POSITION_MASK_FRONT;
1940     else if (pa_streq(m, "all-rear"))
1941         v = PA_CHANNEL_POSITION_MASK_REAR;
1942     else if (pa_streq(m, "all-side"))
1943         v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER;
1944     else if (pa_streq(m, "all-top"))
1945         v = PA_CHANNEL_POSITION_MASK_TOP;
1946     else if (pa_streq(m, "all-no-lfe"))
1947         v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE);
1948     else if (pa_streq(m, "all"))
1949         v = PA_CHANNEL_POSITION_MASK_ALL;
1950     else {
1951         pa_channel_position_t p;
1952
1953         if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
1954             return 0;
1955
1956         v = PA_CHANNEL_POSITION_MASK(p);
1957     }
1958
1959     return v;
1960 }
1961
1962 static int element_parse_override_map(
1963         const char *filename,
1964         unsigned line,
1965         const char *section,
1966         const char *lvalue,
1967         const char *rvalue,
1968         void *data,
1969         void *userdata) {
1970
1971     pa_alsa_path *p = userdata;
1972     pa_alsa_element *e;
1973     const char *state = NULL;
1974     unsigned i = 0;
1975     char *n;
1976
1977     if (!(e = element_get(p, section, TRUE))) {
1978         pa_log("[%s:%u] Override map makes no sense in '%s'", filename, line, section);
1979         return -1;
1980     }
1981
1982     while ((n = pa_split(rvalue, ",", &state))) {
1983         pa_channel_position_mask_t m;
1984
1985         if (!*n)
1986             m = 0;
1987         else {
1988             if ((m = parse_mask(n)) == 0) {
1989                 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename, line, n, section);
1990                 pa_xfree(n);
1991                 return -1;
1992             }
1993         }
1994
1995         if (pa_streq(lvalue, "override-map.1"))
1996             e->masks[i++][0] = m;
1997         else
1998             e->masks[i++][1] = m;
1999
2000         /* Later on we might add override-map.3 and so on here ... */
2001
2002         pa_xfree(n);
2003     }
2004
2005     e->override_map = TRUE;
2006
2007     return 0;
2008 }
2009
2010 static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
2011     snd_mixer_selem_id_t *sid;
2012     snd_mixer_elem_t *me;
2013     int r;
2014
2015     pa_assert(e);
2016     pa_assert(m);
2017
2018     SELEM_INIT(sid, e->alsa_name);
2019     if (!(me = snd_mixer_find_selem(m, sid))) {
2020         pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2021         return -1;
2022     }
2023
2024     if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
2025
2026         if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
2027             r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
2028         else
2029             r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
2030
2031         if (r < 0)
2032             pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
2033
2034     } else {
2035         pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
2036
2037         if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0)
2038             pa_log_warn("Failed to set enumeration of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
2039     }
2040
2041     return r;
2042 }
2043
2044 int pa_alsa_setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
2045     pa_alsa_option *o;
2046     uint32_t idx;
2047
2048     pa_assert(s);
2049     pa_assert(m);
2050
2051     PA_IDXSET_FOREACH(o, s->options, idx)
2052         element_set_option(o->element, m, o->alsa_idx);
2053
2054     return 0;
2055 }
2056
2057 static int option_verify(pa_alsa_option *o) {
2058     static const struct description_map well_known_descriptions[] = {
2059         { "input",                     N_("Input") },
2060         { "input-docking",             N_("Docking Station Input") },
2061         { "input-docking-microphone",  N_("Docking Station Microphone") },
2062         { "input-docking-linein",      N_("Docking Station Line-In") },
2063         { "input-linein",              N_("Line-In") },
2064         { "input-microphone",          N_("Microphone") },
2065         { "input-microphone-front",    N_("Front Microphone") },
2066         { "input-microphone-rear",     N_("Rear Microphone") },
2067         { "input-microphone-external", N_("External Microphone") },
2068         { "input-microphone-internal", N_("Internal Microphone") },
2069         { "input-radio",               N_("Radio") },
2070         { "input-video",               N_("Video") },
2071         { "input-agc-on",              N_("Automatic Gain Control") },
2072         { "input-agc-off",             N_("No Automatic Gain Control") },
2073         { "input-boost-on",            N_("Boost") },
2074         { "input-boost-off",           N_("No Boost") },
2075         { "output-amplifier-on",       N_("Amplifier") },
2076         { "output-amplifier-off",      N_("No Amplifier") },
2077         { "output-bass-boost-on",      N_("Bass Boost") },
2078         { "output-bass-boost-off",     N_("No Bass Boost") },
2079         { "output-speaker",            N_("Speaker") },
2080         { "output-headphones",         N_("Headphones") }
2081     };
2082
2083     pa_assert(o);
2084
2085     if (!o->name) {
2086         pa_log("No name set for option %s", o->alsa_name);
2087         return -1;
2088     }
2089
2090     if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
2091         o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
2092         pa_log("Element %s of option %s not set for select.", o->element->alsa_name, o->name);
2093         return -1;
2094     }
2095
2096     if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
2097         !pa_streq(o->alsa_name, "on") &&
2098         !pa_streq(o->alsa_name, "off")) {
2099         pa_log("Switch %s options need be named off or on ", o->element->alsa_name);
2100         return -1;
2101     }
2102
2103     if (!o->description)
2104         o->description = pa_xstrdup(lookup_description(o->name,
2105                                                        well_known_descriptions,
2106                                                        PA_ELEMENTSOF(well_known_descriptions)));
2107     if (!o->description)
2108         o->description = pa_xstrdup(o->name);
2109
2110     return 0;
2111 }
2112
2113 static int element_verify(pa_alsa_element *e) {
2114     pa_alsa_option *o;
2115
2116     pa_assert(e);
2117
2118 //    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);
2119     if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
2120         (e->required_any != PA_ALSA_REQUIRED_IGNORE && e->required_any == e->required_absent) ||
2121         (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required_any != PA_ALSA_REQUIRED_IGNORE) ||
2122         (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
2123         pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name);
2124         return -1;
2125     }
2126
2127     if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
2128         pa_log("Element %s cannot set select for both switch and enumeration.", e->alsa_name);
2129         return -1;
2130     }
2131
2132     PA_LLIST_FOREACH(o, e->options)
2133         if (option_verify(o) < 0)
2134             return -1;
2135
2136     return 0;
2137 }
2138
2139 static int path_verify(pa_alsa_path *p) {
2140     static const struct description_map well_known_descriptions[] = {
2141         { "analog-input",               N_("Analog Input") },
2142         { "analog-input-microphone",    N_("Analog Microphone") },
2143         { "analog-input-microphone-front",    N_("Front Microphone") },
2144         { "analog-input-microphone-rear",     N_("Rear Microphone") },
2145         { "analog-input-microphone-dock",     N_("Docking Station Microphone") },
2146         { "analog-input-microphone-internal", N_("Internal Microphone") },
2147         { "analog-input-linein",        N_("Analog Line-In") },
2148         { "analog-input-radio",         N_("Analog Radio") },
2149         { "analog-input-video",         N_("Analog Video") },
2150         { "analog-output",              N_("Analog Output") },
2151         { "analog-output-headphones",   N_("Analog Headphones") },
2152         { "analog-output-lfe-on-mono",  N_("Analog Output (LFE)") },
2153         { "analog-output-mono",         N_("Analog Mono Output") },
2154         { "analog-output-speaker",      N_("Analog Speakers") },
2155         { "iec958-stereo-output",       N_("Digital Output (IEC958)") },
2156         { "iec958-passthrough-output",  N_("Digital Passthrough (IEC958)") }
2157     };
2158
2159     pa_alsa_element *e;
2160
2161     pa_assert(p);
2162
2163     PA_LLIST_FOREACH(e, p->elements)
2164         if (element_verify(e) < 0)
2165             return -1;
2166
2167     if (!p->description)
2168         p->description = pa_xstrdup(lookup_description(p->name,
2169                                                        well_known_descriptions,
2170                                                        PA_ELEMENTSOF(well_known_descriptions)));
2171
2172     if (!p->description)
2173         p->description = pa_xstrdup(p->name);
2174
2175     return 0;
2176 }
2177
2178 pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction) {
2179     pa_alsa_path *p;
2180     char *fn;
2181     int r;
2182     const char *n;
2183
2184     pa_config_item items[] = {
2185         /* [General] */
2186         { "priority",            pa_config_parse_unsigned,          NULL, "General" },
2187         { "description",         pa_config_parse_string,            NULL, "General" },
2188         { "name",                pa_config_parse_string,            NULL, "General" },
2189
2190         /* [Option ...] */
2191         { "priority",            option_parse_priority,             NULL, NULL },
2192         { "name",                option_parse_name,                 NULL, NULL },
2193
2194         /* [Element ...] */
2195         { "switch",              element_parse_switch,              NULL, NULL },
2196         { "volume",              element_parse_volume,              NULL, NULL },
2197         { "enumeration",         element_parse_enumeration,         NULL, NULL },
2198         { "override-map.1",      element_parse_override_map,        NULL, NULL },
2199         { "override-map.2",      element_parse_override_map,        NULL, NULL },
2200         /* ... later on we might add override-map.3 and so on here ... */
2201         { "required",            element_parse_required,            NULL, NULL },
2202         { "required-any",        element_parse_required,            NULL, NULL },
2203         { "required-absent",     element_parse_required,            NULL, NULL },
2204         { "direction",           element_parse_direction,           NULL, NULL },
2205         { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
2206         { "volume-limit",        element_parse_volume_limit,        NULL, NULL },
2207         { NULL, NULL, NULL, NULL }
2208     };
2209
2210     pa_assert(fname);
2211
2212     p = pa_xnew0(pa_alsa_path, 1);
2213     n = pa_path_get_filename(fname);
2214     p->name = pa_xstrndup(n, strcspn(n, "."));
2215     p->direction = direction;
2216
2217     items[0].data = &p->priority;
2218     items[1].data = &p->description;
2219     items[2].data = &p->name;
2220
2221     fn = pa_maybe_prefix_path(fname,
2222 #if defined(__linux__) && !defined(__OPTIMIZE__)
2223                               pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/paths/" :
2224 #endif
2225                               PA_ALSA_PATHS_DIR);
2226
2227     r = pa_config_parse(fn, NULL, items, p);
2228     pa_xfree(fn);
2229
2230     if (r < 0)
2231         goto fail;
2232
2233     if (path_verify(p) < 0)
2234         goto fail;
2235
2236     return p;
2237
2238 fail:
2239     pa_alsa_path_free(p);
2240     return NULL;
2241 }
2242
2243 pa_alsa_path* pa_alsa_path_synthesize(const char*element, pa_alsa_direction_t direction) {
2244     pa_alsa_path *p;
2245     pa_alsa_element *e;
2246
2247     pa_assert(element);
2248
2249     p = pa_xnew0(pa_alsa_path, 1);
2250     p->name = pa_xstrdup(element);
2251     p->direction = direction;
2252
2253     e = pa_xnew0(pa_alsa_element, 1);
2254     e->path = p;
2255     e->alsa_name = pa_xstrdup(element);
2256     e->direction = direction;
2257     e->volume_limit = -1;
2258
2259     e->switch_use = PA_ALSA_SWITCH_MUTE;
2260     e->volume_use = PA_ALSA_VOLUME_MERGE;
2261
2262     PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
2263     p->last_element = e;
2264     return p;
2265 }
2266
2267 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
2268     pa_alsa_option *o, *n;
2269
2270     pa_assert(e);
2271
2272     for (o = e->options; o; o = n) {
2273         n = o->next;
2274
2275         if (o->alsa_idx < 0) {
2276             PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
2277             option_free(o);
2278         }
2279     }
2280
2281     return
2282         e->switch_use != PA_ALSA_SWITCH_IGNORE ||
2283         e->volume_use != PA_ALSA_VOLUME_IGNORE ||
2284         e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
2285 }
2286
2287 static void path_drop_unsupported(pa_alsa_path *p) {
2288     pa_alsa_element *e, *n;
2289
2290     pa_assert(p);
2291
2292     for (e = p->elements; e; e = n) {
2293         n = e->next;
2294
2295         if (!element_drop_unsupported(e)) {
2296             PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
2297             element_free(e);
2298         }
2299     }
2300 }
2301
2302 static void path_make_options_unique(pa_alsa_path *p) {
2303     pa_alsa_element *e;
2304     pa_alsa_option *o, *u;
2305
2306     PA_LLIST_FOREACH(e, p->elements) {
2307         PA_LLIST_FOREACH(o, e->options) {
2308             unsigned i;
2309             char *m;
2310
2311             for (u = o->next; u; u = u->next)
2312                 if (pa_streq(u->name, o->name))
2313                     break;
2314
2315             if (!u)
2316                 continue;
2317
2318             m = pa_xstrdup(o->name);
2319
2320             /* OK, this name is not unique, hence let's rename */
2321             for (i = 1, u = o; u; u = u->next) {
2322                 char *nn, *nd;
2323
2324                 if (!pa_streq(u->name, m))
2325                     continue;
2326
2327                 nn = pa_sprintf_malloc("%s-%u", m, i);
2328                 pa_xfree(u->name);
2329                 u->name = nn;
2330
2331                 nd = pa_sprintf_malloc("%s %u", u->description, i);
2332                 pa_xfree(u->description);
2333                 u->description = nd;
2334
2335                 i++;
2336             }
2337
2338             pa_xfree(m);
2339         }
2340     }
2341 }
2342
2343 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
2344     pa_alsa_option *o;
2345
2346     for (; e; e = e->next)
2347         if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
2348             e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
2349             break;
2350
2351     if (!e)
2352         return FALSE;
2353
2354     for (o = e->options; o; o = o->next) {
2355         pa_alsa_setting *s;
2356
2357         if (template) {
2358             s = pa_xnewdup(pa_alsa_setting, template, 1);
2359             s->options = pa_idxset_copy(template->options);
2360             s->name = pa_sprintf_malloc(_("%s+%s"), template->name, o->name);
2361             s->description =
2362                 (template->description[0] && o->description[0])
2363                 ? pa_sprintf_malloc(_("%s / %s"), template->description, o->description)
2364                 : (template->description[0]
2365                    ? pa_xstrdup(template->description)
2366                    : pa_xstrdup(o->description));
2367
2368             s->priority = PA_MAX(template->priority, o->priority);
2369         } else {
2370             s = pa_xnew0(pa_alsa_setting, 1);
2371             s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2372             s->name = pa_xstrdup(o->name);
2373             s->description = pa_xstrdup(o->description);
2374             s->priority = o->priority;
2375         }
2376
2377         pa_idxset_put(s->options, o, NULL);
2378
2379         if (element_create_settings(e->next, s))
2380             /* This is not a leaf, so let's get rid of it */
2381             setting_free(s);
2382         else {
2383             /* This is a leaf, so let's add it */
2384             PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2385
2386             e->path->last_setting = s;
2387         }
2388     }
2389
2390     return TRUE;
2391 }
2392
2393 static void path_create_settings(pa_alsa_path *p) {
2394     pa_assert(p);
2395
2396     element_create_settings(p->elements, NULL);
2397 }
2398
2399 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) {
2400     pa_alsa_element *e;
2401     double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2402     pa_channel_position_t t;
2403     pa_channel_position_mask_t path_volume_channels = 0;
2404
2405     pa_assert(p);
2406     pa_assert(m);
2407
2408     if (p->probed)
2409         return 0;
2410
2411     pa_zero(min_dB);
2412     pa_zero(max_dB);
2413
2414     pa_log_debug("Probing path '%s'", p->name);
2415
2416     PA_LLIST_FOREACH(e, p->elements) {
2417         if (element_probe(e, m) < 0) {
2418             p->supported = FALSE;
2419             pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2420             return -1;
2421         }
2422         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);
2423
2424         if (ignore_dB)
2425             e->has_dB = FALSE;
2426
2427         if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2428
2429             if (!p->has_volume) {
2430                 p->min_volume = e->min_volume;
2431                 p->max_volume = e->max_volume;
2432             }
2433
2434             if (e->has_dB) {
2435                 if (!p->has_volume) {
2436                     for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2437                         if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2438                             min_dB[t] = e->min_dB;
2439                             max_dB[t] = e->max_dB;
2440                             path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2441                         }
2442
2443                     p->has_dB = TRUE;
2444                 } else {
2445
2446                     if (p->has_dB) {
2447                         for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2448                             if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2449                                 min_dB[t] += e->min_dB;
2450                                 max_dB[t] += e->max_dB;
2451                                 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2452                             }
2453                     } else {
2454                         /* Hmm, there's another element before us
2455                          * which cannot do dB volumes, so we we need
2456                          * to 'neutralize' this slider */
2457                         e->volume_use = PA_ALSA_VOLUME_ZERO;
2458                         pa_log_info("Zeroing volume of '%s' on path '%s'", e->alsa_name, p->name);
2459                     }
2460                 }
2461             } else if (p->has_volume) {
2462                 /* We can't use this volume, so let's ignore it */
2463                 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2464                 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e->alsa_name, p->name);
2465             }
2466             p->has_volume = TRUE;
2467         }
2468
2469         if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2470             p->has_mute = TRUE;
2471     }
2472
2473     if (p->has_req_any && !p->req_any_present) {
2474         p->supported = FALSE;
2475         pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name);
2476         return -1;
2477     }
2478
2479     path_drop_unsupported(p);
2480     path_make_options_unique(p);
2481     path_create_settings(p);
2482
2483     p->supported = TRUE;
2484     p->probed = TRUE;
2485
2486     p->min_dB = INFINITY;
2487     p->max_dB = -INFINITY;
2488
2489     for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2490         if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) {
2491             if (p->min_dB > min_dB[t])
2492                 p->min_dB = min_dB[t];
2493
2494             if (p->max_dB < max_dB[t])
2495                 p->max_dB = max_dB[t];
2496         }
2497     }
2498
2499     return 0;
2500 }
2501
2502 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2503     pa_assert(s);
2504
2505     pa_log_debug("Setting %s (%s) priority=%u",
2506                  s->name,
2507                  pa_strnull(s->description),
2508                  s->priority);
2509 }
2510
2511 void pa_alsa_option_dump(pa_alsa_option *o) {
2512     pa_assert(o);
2513
2514     pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2515                  o->alsa_name,
2516                  pa_strnull(o->name),
2517                  pa_strnull(o->description),
2518                  o->alsa_idx,
2519                  o->priority);
2520 }
2521
2522 void pa_alsa_element_dump(pa_alsa_element *e) {
2523     pa_alsa_option *o;
2524     pa_assert(e);
2525
2526     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",
2527                  e->alsa_name,
2528                  e->direction,
2529                  e->switch_use,
2530                  e->volume_use,
2531                  e->volume_limit,
2532                  e->enumeration_use,
2533                  e->required,
2534                  e->required_any,
2535                  e->required_absent,
2536                  (long long unsigned) e->merged_mask,
2537                  e->n_channels,
2538                  pa_yes_no(e->override_map));
2539
2540     PA_LLIST_FOREACH(o, e->options)
2541         pa_alsa_option_dump(o);
2542 }
2543
2544 void pa_alsa_path_dump(pa_alsa_path *p) {
2545     pa_alsa_element *e;
2546     pa_alsa_setting *s;
2547     pa_assert(p);
2548
2549     pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2550                  "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2551                  p->name,
2552                  pa_strnull(p->description),
2553                  p->direction,
2554                  p->priority,
2555                  pa_yes_no(p->probed),
2556                  pa_yes_no(p->supported),
2557                  pa_yes_no(p->has_mute),
2558                  pa_yes_no(p->has_volume),
2559                  pa_yes_no(p->has_dB),
2560                  p->min_volume, p->max_volume,
2561                  p->min_dB, p->max_dB);
2562
2563     PA_LLIST_FOREACH(e, p->elements)
2564         pa_alsa_element_dump(e);
2565
2566     PA_LLIST_FOREACH(s, p->settings)
2567         pa_alsa_setting_dump(s);
2568 }
2569
2570 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2571     snd_mixer_selem_id_t *sid;
2572     snd_mixer_elem_t *me;
2573
2574     pa_assert(e);
2575     pa_assert(m);
2576     pa_assert(cb);
2577
2578     SELEM_INIT(sid, e->alsa_name);
2579     if (!(me = snd_mixer_find_selem(m, sid))) {
2580         pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2581         return;
2582     }
2583
2584     snd_mixer_elem_set_callback(me, cb);
2585     snd_mixer_elem_set_callback_private(me, userdata);
2586 }
2587
2588 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2589     pa_alsa_element *e;
2590
2591     pa_assert(p);
2592     pa_assert(m);
2593     pa_assert(cb);
2594
2595     PA_LLIST_FOREACH(e, p->elements)
2596         element_set_callback(e, m, cb, userdata);
2597 }
2598
2599 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2600     pa_alsa_path *p;
2601
2602     pa_assert(ps);
2603     pa_assert(m);
2604     pa_assert(cb);
2605
2606     PA_LLIST_FOREACH(p, ps->paths)
2607         pa_alsa_path_set_callback(p, m, cb, userdata);
2608 }
2609
2610 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction) {
2611     pa_alsa_path_set *ps;
2612     char **pn = NULL, **en = NULL, **ie;
2613     pa_alsa_decibel_fix *db_fix;
2614     void *state;
2615
2616     pa_assert(m);
2617     pa_assert(m->profile_set);
2618     pa_assert(m->profile_set->decibel_fixes);
2619     pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2620
2621     if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2622         return NULL;
2623
2624     ps = pa_xnew0(pa_alsa_path_set, 1);
2625     ps->direction = direction;
2626
2627     if (direction == PA_ALSA_DIRECTION_OUTPUT)
2628         pn = m->output_path_names;
2629     else if (direction == PA_ALSA_DIRECTION_INPUT)
2630         pn = m->input_path_names;
2631
2632     if (pn) {
2633         char **in;
2634
2635         for (in = pn; *in; in++) {
2636             pa_alsa_path *p;
2637             pa_bool_t duplicate = FALSE;
2638             char **kn, *fn;
2639
2640             for (kn = pn; kn != in; kn++)
2641                 if (pa_streq(*kn, *in)) {
2642                     duplicate = TRUE;
2643                     break;
2644                 }
2645
2646             if (duplicate)
2647                 continue;
2648
2649             fn = pa_sprintf_malloc("%s.conf", *in);
2650
2651             if ((p = pa_alsa_path_new(fn, direction))) {
2652                 p->path_set = ps;
2653                 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2654                 ps->last_path = p;
2655             }
2656
2657             pa_xfree(fn);
2658         }
2659
2660         goto finish;
2661     }
2662
2663     if (direction == PA_ALSA_DIRECTION_OUTPUT)
2664         en = m->output_element;
2665     else if (direction == PA_ALSA_DIRECTION_INPUT)
2666         en = m->input_element;
2667
2668     if (!en) {
2669         pa_alsa_path_set_free(ps);
2670         return NULL;
2671     }
2672
2673     for (ie = en; *ie; ie++) {
2674         char **je;
2675         pa_alsa_path *p;
2676
2677         p = pa_alsa_path_synthesize(*ie, direction);
2678         p->path_set = ps;
2679
2680         /* Mark all other passed elements for require-absent */
2681         for (je = en; *je; je++) {
2682             pa_alsa_element *e;
2683
2684             if (je == ie)
2685                 continue;
2686
2687             e = pa_xnew0(pa_alsa_element, 1);
2688             e->path = p;
2689             e->alsa_name = pa_xstrdup(*je);
2690             e->direction = direction;
2691             e->required_absent = PA_ALSA_REQUIRED_ANY;
2692             e->volume_limit = -1;
2693
2694             PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2695             p->last_element = e;
2696         }
2697
2698         PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2699         ps->last_path = p;
2700     }
2701
2702 finish:
2703     /* Assign decibel fixes to elements. */
2704     PA_HASHMAP_FOREACH(db_fix, m->profile_set->decibel_fixes, state) {
2705         pa_alsa_path *p;
2706
2707         PA_LLIST_FOREACH(p, ps->paths) {
2708             pa_alsa_element *e;
2709
2710             PA_LLIST_FOREACH(e, p->elements) {
2711                 if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_name)) {
2712                     /* The profile set that contains the dB fix may be freed
2713                      * before the element, so we have to copy the dB fix
2714                      * object. */
2715                     e->db_fix = pa_xnewdup(pa_alsa_decibel_fix, db_fix, 1);
2716                     e->db_fix->profile_set = NULL;
2717                     e->db_fix->name = pa_xstrdup(db_fix->name);
2718                     e->db_fix->db_values = pa_xmemdup(db_fix->db_values, (db_fix->max_step - db_fix->min_step + 1) * sizeof(long));
2719                 }
2720             }
2721         }
2722     }
2723
2724     return ps;
2725 }
2726
2727 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2728     pa_alsa_path *p;
2729     pa_assert(ps);
2730
2731     pa_log_debug("Path Set %p, direction=%i, probed=%s",
2732                  (void*) ps,
2733                  ps->direction,
2734                  pa_yes_no(ps->probed));
2735
2736     PA_LLIST_FOREACH(p, ps->paths)
2737         pa_alsa_path_dump(p);
2738 }
2739
2740 static void path_set_unify(pa_alsa_path_set *ps) {
2741     pa_alsa_path *p;
2742     pa_bool_t has_dB = TRUE, has_volume = TRUE, has_mute = TRUE;
2743     pa_assert(ps);
2744
2745     /* We have issues dealing with paths that vary too wildly. That
2746      * means for now we have to have all paths support volume/mute/dB
2747      * or none. */
2748
2749     PA_LLIST_FOREACH(p, ps->paths) {
2750         pa_assert(p->probed);
2751
2752         if (!p->has_volume)
2753             has_volume = FALSE;
2754         else if (!p->has_dB)
2755             has_dB = FALSE;
2756
2757         if (!p->has_mute)
2758             has_mute = FALSE;
2759     }
2760
2761     if (!has_volume || !has_dB || !has_mute) {
2762
2763         if (!has_volume)
2764             pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2765         else if (!has_dB)
2766             pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2767
2768         if (!has_mute)
2769             pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2770
2771         PA_LLIST_FOREACH(p, ps->paths) {
2772             if (!has_volume)
2773                 p->has_volume = FALSE;
2774             else if (!has_dB)
2775                 p->has_dB = FALSE;
2776
2777             if (!has_mute)
2778                 p->has_mute = FALSE;
2779         }
2780     }
2781 }
2782
2783 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
2784     pa_alsa_path *p, *q;
2785
2786     PA_LLIST_FOREACH(p, ps->paths) {
2787         unsigned i;
2788         char *m;
2789
2790         for (q = p->next; q; q = q->next)
2791             if (pa_streq(q->name, p->name))
2792                 break;
2793
2794         if (!q)
2795             continue;
2796
2797         m = pa_xstrdup(p->name);
2798
2799         /* OK, this name is not unique, hence let's rename */
2800         for (i = 1, q = p; q; q = q->next) {
2801             char *nn, *nd;
2802
2803             if (!pa_streq(q->name, m))
2804                 continue;
2805
2806             nn = pa_sprintf_malloc("%s-%u", m, i);
2807             pa_xfree(q->name);
2808             q->name = nn;
2809
2810             nd = pa_sprintf_malloc("%s %u", q->description, i);
2811             pa_xfree(q->description);
2812             q->description = nd;
2813
2814             i++;
2815         }
2816
2817         pa_xfree(m);
2818     }
2819 }
2820
2821 void pa_alsa_path_set_probe(pa_alsa_path_set *ps, snd_mixer_t *m, pa_bool_t ignore_dB) {
2822     pa_alsa_path *p, *n;
2823
2824     pa_assert(ps);
2825
2826     if (ps->probed)
2827         return;
2828
2829     for (p = ps->paths; p; p = n) {
2830         n = p->next;
2831
2832         if (pa_alsa_path_probe(p, m, ignore_dB) < 0) {
2833             PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
2834             pa_alsa_path_free(p);
2835         }
2836     }
2837
2838     path_set_unify(ps);
2839     path_set_make_paths_unique(ps);
2840     ps->probed = TRUE;
2841 }
2842
2843 static void mapping_free(pa_alsa_mapping *m) {
2844     pa_assert(m);
2845
2846     pa_xfree(m->name);
2847     pa_xfree(m->description);
2848
2849     pa_xstrfreev(m->device_strings);
2850     pa_xstrfreev(m->input_path_names);
2851     pa_xstrfreev(m->output_path_names);
2852     pa_xstrfreev(m->input_element);
2853     pa_xstrfreev(m->output_element);
2854
2855     pa_assert(!m->input_pcm);
2856     pa_assert(!m->output_pcm);
2857
2858     pa_xfree(m);
2859 }
2860
2861 static void profile_free(pa_alsa_profile *p) {
2862     pa_assert(p);
2863
2864     pa_xfree(p->name);
2865     pa_xfree(p->description);
2866
2867     pa_xstrfreev(p->input_mapping_names);
2868     pa_xstrfreev(p->output_mapping_names);
2869
2870     if (p->input_mappings)
2871         pa_idxset_free(p->input_mappings, NULL, NULL);
2872
2873     if (p->output_mappings)
2874         pa_idxset_free(p->output_mappings, NULL, NULL);
2875
2876     pa_xfree(p);
2877 }
2878
2879 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
2880     pa_assert(ps);
2881
2882     if (ps->profiles) {
2883         pa_alsa_profile *p;
2884
2885         while ((p = pa_hashmap_steal_first(ps->profiles)))
2886             profile_free(p);
2887
2888         pa_hashmap_free(ps->profiles, NULL, NULL);
2889     }
2890
2891     if (ps->mappings) {
2892         pa_alsa_mapping *m;
2893
2894         while ((m = pa_hashmap_steal_first(ps->mappings)))
2895             mapping_free(m);
2896
2897         pa_hashmap_free(ps->mappings, NULL, NULL);
2898     }
2899
2900     if (ps->decibel_fixes) {
2901         pa_alsa_decibel_fix *db_fix;
2902
2903         while ((db_fix = pa_hashmap_steal_first(ps->decibel_fixes)))
2904             decibel_fix_free(db_fix);
2905
2906         pa_hashmap_free(ps->decibel_fixes, NULL, NULL);
2907     }
2908
2909     pa_xfree(ps);
2910 }
2911
2912 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
2913     pa_alsa_mapping *m;
2914
2915     if (!pa_startswith(name, "Mapping "))
2916         return NULL;
2917
2918     name += 8;
2919
2920     if ((m = pa_hashmap_get(ps->mappings, name)))
2921         return m;
2922
2923     m = pa_xnew0(pa_alsa_mapping, 1);
2924     m->profile_set = ps;
2925     m->name = pa_xstrdup(name);
2926     pa_channel_map_init(&m->channel_map);
2927
2928     pa_hashmap_put(ps->mappings, m->name, m);
2929
2930     return m;
2931 }
2932
2933 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
2934     pa_alsa_profile *p;
2935
2936     if (!pa_startswith(name, "Profile "))
2937         return NULL;
2938
2939     name += 8;
2940
2941     if ((p = pa_hashmap_get(ps->profiles, name)))
2942         return p;
2943
2944     p = pa_xnew0(pa_alsa_profile, 1);
2945     p->profile_set = ps;
2946     p->name = pa_xstrdup(name);
2947
2948     pa_hashmap_put(ps->profiles, p->name, p);
2949
2950     return p;
2951 }
2952
2953 static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) {
2954     pa_alsa_decibel_fix *db_fix;
2955
2956     if (!pa_startswith(name, "DecibelFix "))
2957         return NULL;
2958
2959     name += 11;
2960
2961     if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name)))
2962         return db_fix;
2963
2964     db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
2965     db_fix->profile_set = ps;
2966     db_fix->name = pa_xstrdup(name);
2967
2968     pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix);
2969
2970     return db_fix;
2971 }
2972
2973 static int mapping_parse_device_strings(
2974         const char *filename,
2975         unsigned line,
2976         const char *section,
2977         const char *lvalue,
2978         const char *rvalue,
2979         void *data,
2980         void *userdata) {
2981
2982     pa_alsa_profile_set *ps = userdata;
2983     pa_alsa_mapping *m;
2984
2985     pa_assert(ps);
2986
2987     if (!(m = mapping_get(ps, section))) {
2988         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2989         return -1;
2990     }
2991
2992     pa_xstrfreev(m->device_strings);
2993     if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
2994         pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
2995         return -1;
2996     }
2997
2998     return 0;
2999 }
3000
3001 static int mapping_parse_channel_map(
3002         const char *filename,
3003         unsigned line,
3004         const char *section,
3005         const char *lvalue,
3006         const char *rvalue,
3007         void *data,
3008         void *userdata) {
3009
3010     pa_alsa_profile_set *ps = userdata;
3011     pa_alsa_mapping *m;
3012
3013     pa_assert(ps);
3014
3015     if (!(m = mapping_get(ps, section))) {
3016         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3017         return -1;
3018     }
3019
3020     if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
3021         pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
3022         return -1;
3023     }
3024
3025     return 0;
3026 }
3027
3028 static int mapping_parse_paths(
3029         const char *filename,
3030         unsigned line,
3031         const char *section,
3032         const char *lvalue,
3033         const char *rvalue,
3034         void *data,
3035         void *userdata) {
3036
3037     pa_alsa_profile_set *ps = userdata;
3038     pa_alsa_mapping *m;
3039
3040     pa_assert(ps);
3041
3042     if (!(m = mapping_get(ps, section))) {
3043         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3044         return -1;
3045     }
3046
3047     if (pa_streq(lvalue, "paths-input")) {
3048         pa_xstrfreev(m->input_path_names);
3049         m->input_path_names = pa_split_spaces_strv(rvalue);
3050     } else {
3051         pa_xstrfreev(m->output_path_names);
3052         m->output_path_names = pa_split_spaces_strv(rvalue);
3053     }
3054
3055     return 0;
3056 }
3057
3058 static int mapping_parse_element(
3059         const char *filename,
3060         unsigned line,
3061         const char *section,
3062         const char *lvalue,
3063         const char *rvalue,
3064         void *data,
3065         void *userdata) {
3066
3067     pa_alsa_profile_set *ps = userdata;
3068     pa_alsa_mapping *m;
3069
3070     pa_assert(ps);
3071
3072     if (!(m = mapping_get(ps, section))) {
3073         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3074         return -1;
3075     }
3076
3077     if (pa_streq(lvalue, "element-input")) {
3078         pa_xstrfreev(m->input_element);
3079         m->input_element = pa_split_spaces_strv(rvalue);
3080     } else {
3081         pa_xstrfreev(m->output_element);
3082         m->output_element = pa_split_spaces_strv(rvalue);
3083     }
3084
3085     return 0;
3086 }
3087
3088 static int mapping_parse_direction(
3089         const char *filename,
3090         unsigned line,
3091         const char *section,
3092         const char *lvalue,
3093         const char *rvalue,
3094         void *data,
3095         void *userdata) {
3096
3097     pa_alsa_profile_set *ps = userdata;
3098     pa_alsa_mapping *m;
3099
3100     pa_assert(ps);
3101
3102     if (!(m = mapping_get(ps, section))) {
3103         pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3104         return -1;
3105     }
3106
3107     if (pa_streq(rvalue, "input"))
3108         m->direction = PA_ALSA_DIRECTION_INPUT;
3109     else if (pa_streq(rvalue, "output"))
3110         m->direction = PA_ALSA_DIRECTION_OUTPUT;
3111     else if (pa_streq(rvalue, "any"))
3112         m->direction = PA_ALSA_DIRECTION_ANY;
3113     else {
3114         pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
3115         return -1;
3116     }
3117
3118     return 0;
3119 }
3120
3121 static int mapping_parse_description(
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_profile *p;
3132     pa_alsa_mapping *m;
3133
3134     pa_assert(ps);
3135
3136     if ((m = mapping_get(ps, section))) {
3137         pa_xfree(m->description);
3138         m->description = pa_xstrdup(rvalue);
3139     } else if ((p = profile_get(ps, section))) {
3140         pa_xfree(p->description);
3141         p->description = pa_xstrdup(rvalue);
3142     } else {
3143         pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3144         return -1;
3145     }
3146
3147     return 0;
3148 }
3149
3150 static int mapping_parse_priority(
3151         const char *filename,
3152         unsigned line,
3153         const char *section,
3154         const char *lvalue,
3155         const char *rvalue,
3156         void *data,
3157         void *userdata) {
3158
3159     pa_alsa_profile_set *ps = userdata;
3160     pa_alsa_profile *p;
3161     pa_alsa_mapping *m;
3162     uint32_t prio;
3163
3164     pa_assert(ps);
3165
3166     if (pa_atou(rvalue, &prio) < 0) {
3167         pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
3168         return -1;
3169     }
3170
3171     if ((m = mapping_get(ps, section)))
3172         m->priority = prio;
3173     else if ((p = profile_get(ps, section)))
3174         p->priority = prio;
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 profile_parse_mappings(
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
3195     pa_assert(ps);
3196
3197     if (!(p = profile_get(ps, section))) {
3198         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3199         return -1;
3200     }
3201
3202     if (pa_streq(lvalue, "input-mappings")) {
3203         pa_xstrfreev(p->input_mapping_names);
3204         p->input_mapping_names = pa_split_spaces_strv(rvalue);
3205     } else {
3206         pa_xstrfreev(p->output_mapping_names);
3207         p->output_mapping_names = pa_split_spaces_strv(rvalue);
3208     }
3209
3210     return 0;
3211 }
3212
3213 static int profile_parse_skip_probe(
3214         const char *filename,
3215         unsigned line,
3216         const char *section,
3217         const char *lvalue,
3218         const char *rvalue,
3219         void *data,
3220         void *userdata) {
3221
3222     pa_alsa_profile_set *ps = userdata;
3223     pa_alsa_profile *p;
3224     int b;
3225
3226     pa_assert(ps);
3227
3228     if (!(p = profile_get(ps, section))) {
3229         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3230         return -1;
3231     }
3232
3233     if ((b = pa_parse_boolean(rvalue)) < 0) {
3234         pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
3235         return -1;
3236     }
3237
3238     p->supported = b;
3239
3240     return 0;
3241 }
3242
3243 static int decibel_fix_parse_db_values(
3244         const char *filename,
3245         unsigned line,
3246         const char *section,
3247         const char *lvalue,
3248         const char *rvalue,
3249         void *data,
3250         void *userdata) {
3251
3252     pa_alsa_profile_set *ps = userdata;
3253     pa_alsa_decibel_fix *db_fix;
3254     char **items;
3255     char *item;
3256     long *db_values;
3257     unsigned n = 8; /* Current size of the db_values table. */
3258     unsigned min_step = 0;
3259     unsigned max_step = 0;
3260     unsigned i = 0; /* Index to the items table. */
3261     unsigned prev_step = 0;
3262     double prev_db = 0;
3263
3264     pa_assert(filename);
3265     pa_assert(section);
3266     pa_assert(lvalue);
3267     pa_assert(rvalue);
3268     pa_assert(ps);
3269
3270     if (!(db_fix = decibel_fix_get(ps, section))) {
3271         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3272         return -1;
3273     }
3274
3275     if (!(items = pa_split_spaces_strv(rvalue))) {
3276         pa_log("[%s:%u] Value missing", pa_strnull(filename), line);
3277         return -1;
3278     }
3279
3280     db_values = pa_xnew(long, n);
3281
3282     while ((item = items[i++])) {
3283         char *s = item; /* Step value string. */
3284         char *d = item; /* dB value string. */
3285         uint32_t step;
3286         double db;
3287
3288         /* Move d forward until it points to a colon or to the end of the item. */
3289         for (; *d && *d != ':'; ++d);
3290
3291         if (d == s) {
3292             /* item started with colon. */
3293             pa_log("[%s:%u] No step value found in %s", filename, line, item);
3294             goto fail;
3295         }
3296
3297         if (!*d || !*(d + 1)) {
3298             /* No colon found, or it was the last character in item. */
3299             pa_log("[%s:%u] No dB value found in %s", filename, line, item);
3300             goto fail;
3301         }
3302
3303         /* pa_atou() needs a null-terminating string. Let's replace the colon
3304          * with a zero byte. */
3305         *d++ = '\0';
3306
3307         if (pa_atou(s, &step) < 0) {
3308             pa_log("[%s:%u] Invalid step value: %s", filename, line, s);
3309             goto fail;
3310         }
3311
3312         if (pa_atod(d, &db) < 0) {
3313             pa_log("[%s:%u] Invalid dB value: %s", filename, line, d);
3314             goto fail;
3315         }
3316
3317         if (step <= prev_step && i != 1) {
3318             pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename, line, step, prev_step);
3319             goto fail;
3320         }
3321
3322         if (db < prev_db && i != 1) {
3323             pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename, line, db, prev_db);
3324             goto fail;
3325         }
3326
3327         if (i == 1) {
3328             min_step = step;
3329             db_values[0] = (long) (db * 100.0);
3330             prev_step = step;
3331             prev_db = db;
3332         } else {
3333             /* Interpolate linearly. */
3334             double db_increment = (db - prev_db) / (step - prev_step);
3335
3336             for (; prev_step < step; ++prev_step, prev_db += db_increment) {
3337
3338                 /* Reallocate the db_values table if it's about to overflow. */
3339                 if (prev_step + 1 - min_step == n) {
3340                     n *= 2;
3341                     db_values = pa_xrenew(long, db_values, n);
3342                 }
3343
3344                 db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
3345             }
3346         }
3347
3348         max_step = step;
3349     }
3350
3351     db_fix->min_step = min_step;
3352     db_fix->max_step = max_step;
3353     pa_xfree(db_fix->db_values);
3354     db_fix->db_values = db_values;
3355
3356     pa_xstrfreev(items);
3357
3358     return 0;
3359
3360 fail:
3361     pa_xstrfreev(items);
3362     pa_xfree(db_values);
3363
3364     return -1;
3365 }
3366
3367 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
3368
3369     static const struct description_map well_known_descriptions[] = {
3370         { "analog-mono",            N_("Analog Mono") },
3371         { "analog-stereo",          N_("Analog Stereo") },
3372         { "analog-surround-21",     N_("Analog Surround 2.1") },
3373         { "analog-surround-30",     N_("Analog Surround 3.0") },
3374         { "analog-surround-31",     N_("Analog Surround 3.1") },
3375         { "analog-surround-40",     N_("Analog Surround 4.0") },
3376         { "analog-surround-41",     N_("Analog Surround 4.1") },
3377         { "analog-surround-50",     N_("Analog Surround 5.0") },
3378         { "analog-surround-51",     N_("Analog Surround 5.1") },
3379         { "analog-surround-61",     N_("Analog Surround 6.0") },
3380         { "analog-surround-61",     N_("Analog Surround 6.1") },
3381         { "analog-surround-70",     N_("Analog Surround 7.0") },
3382         { "analog-surround-71",     N_("Analog Surround 7.1") },
3383         { "iec958-stereo",          N_("Digital Stereo (IEC958)") },
3384         { "iec958-passthrough",     N_("Digital Passthrough  (IEC958)") },
3385         { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3386         { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3387         { "hdmi-stereo",            N_("Digital Stereo (HDMI)") }
3388     };
3389
3390     pa_assert(m);
3391
3392     if (!pa_channel_map_valid(&m->channel_map)) {
3393         pa_log("Mapping %s is missing channel map.", m->name);
3394         return -1;
3395     }
3396
3397     if (!m->device_strings) {
3398         pa_log("Mapping %s is missing device strings.", m->name);
3399         return -1;
3400     }
3401
3402     if ((m->input_path_names && m->input_element) ||
3403         (m->output_path_names && m->output_element)) {
3404         pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
3405         return -1;
3406     }
3407
3408     if (!m->description)
3409         m->description = pa_xstrdup(lookup_description(m->name,
3410                                                        well_known_descriptions,
3411                                                        PA_ELEMENTSOF(well_known_descriptions)));
3412
3413     if (!m->description)
3414         m->description = pa_xstrdup(m->name);
3415
3416     if (bonus) {
3417         if (pa_channel_map_equal(&m->channel_map, bonus))
3418             m->priority += 50;
3419         else if (m->channel_map.channels == bonus->channels)
3420             m->priority += 30;
3421     }
3422
3423     return 0;
3424 }
3425
3426 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
3427     char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
3428
3429     pa_assert(m);
3430
3431     pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3432                  m->name,
3433                  pa_strnull(m->description),
3434                  m->priority,
3435                  pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
3436                  pa_yes_no(m->supported),
3437                  m->direction);
3438 }
3439
3440 static void profile_set_add_auto_pair(
3441         pa_alsa_profile_set *ps,
3442         pa_alsa_mapping *m, /* output */
3443         pa_alsa_mapping *n  /* input */) {
3444
3445     char *name;
3446     pa_alsa_profile *p;
3447
3448     pa_assert(ps);
3449     pa_assert(m || n);
3450
3451     if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
3452         return;
3453
3454     if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
3455         return;
3456
3457     if (m && n)
3458         name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
3459     else if (m)
3460         name = pa_sprintf_malloc("output:%s", m->name);
3461     else
3462         name = pa_sprintf_malloc("input:%s", n->name);
3463
3464     if (pa_hashmap_get(ps->profiles, name)) {
3465         pa_xfree(name);
3466         return;
3467     }
3468
3469     p = pa_xnew0(pa_alsa_profile, 1);
3470     p->profile_set = ps;
3471     p->name = name;
3472
3473     if (m) {
3474         p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3475         pa_idxset_put(p->output_mappings, m, NULL);
3476         p->priority += m->priority * 100;
3477     }
3478
3479     if (n) {
3480         p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3481         pa_idxset_put(p->input_mappings, n, NULL);
3482         p->priority += n->priority;
3483     }
3484
3485     pa_hashmap_put(ps->profiles, p->name, p);
3486 }
3487
3488 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
3489     pa_alsa_mapping *m, *n;
3490     void *m_state, *n_state;
3491
3492     pa_assert(ps);
3493
3494     PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
3495         profile_set_add_auto_pair(ps, m, NULL);
3496
3497         PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3498             profile_set_add_auto_pair(ps, m, n);
3499     }
3500
3501     PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3502         profile_set_add_auto_pair(ps, NULL, n);
3503 }
3504
3505 static int profile_verify(pa_alsa_profile *p) {
3506
3507     static const struct description_map well_known_descriptions[] = {
3508         { "output:analog-mono+input:analog-mono",     N_("Analog Mono Duplex") },
3509         { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3510         { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3511         { "off",                                      N_("Off") }
3512     };
3513
3514     pa_assert(p);
3515
3516     /* Replace the output mapping names by the actual mappings */
3517     if (p->output_mapping_names) {
3518         char **name;
3519
3520         pa_assert(!p->output_mappings);
3521         p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3522
3523         for (name = p->output_mapping_names; *name; name++) {
3524             pa_alsa_mapping *m;
3525             char **in;
3526             pa_bool_t duplicate = FALSE;
3527
3528             for (in = name + 1; *in; in++)
3529                 if (pa_streq(*name, *in)) {
3530                     duplicate = TRUE;
3531                     break;
3532                 }
3533
3534             if (duplicate)
3535                 continue;
3536
3537             if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
3538                 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
3539                 return -1;
3540             }
3541
3542             pa_idxset_put(p->output_mappings, m, NULL);
3543
3544             if (p->supported)
3545                 m->supported++;
3546         }
3547
3548         pa_xstrfreev(p->output_mapping_names);
3549         p->output_mapping_names = NULL;
3550     }
3551
3552     /* Replace the input mapping names by the actual mappings */
3553     if (p->input_mapping_names) {
3554         char **name;
3555
3556         pa_assert(!p->input_mappings);
3557         p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3558
3559         for (name = p->input_mapping_names; *name; name++) {
3560             pa_alsa_mapping *m;
3561             char **in;
3562             pa_bool_t duplicate = FALSE;
3563
3564             for (in = name + 1; *in; in++)
3565                 if (pa_streq(*name, *in)) {
3566                     duplicate = TRUE;
3567                     break;
3568                 }
3569
3570             if (duplicate)
3571                 continue;
3572
3573             if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
3574                 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
3575                 return -1;
3576             }
3577
3578             pa_idxset_put(p->input_mappings, m, NULL);
3579
3580             if (p->supported)
3581                 m->supported++;
3582         }
3583
3584         pa_xstrfreev(p->input_mapping_names);
3585         p->input_mapping_names = NULL;
3586     }
3587
3588     if (!p->input_mappings && !p->output_mappings) {
3589         pa_log("Profile '%s' lacks mappings.", p->name);
3590         return -1;
3591     }
3592
3593     if (!p->description)
3594         p->description = pa_xstrdup(lookup_description(p->name,
3595                                                        well_known_descriptions,
3596                                                        PA_ELEMENTSOF(well_known_descriptions)));
3597
3598     if (!p->description) {
3599         pa_strbuf *sb;
3600         uint32_t idx;
3601         pa_alsa_mapping *m;
3602
3603         sb = pa_strbuf_new();
3604
3605         if (p->output_mappings)
3606             PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3607                 if (!pa_strbuf_isempty(sb))
3608                     pa_strbuf_puts(sb, " + ");
3609
3610                 pa_strbuf_printf(sb, _("%s Output"), m->description);
3611             }
3612
3613         if (p->input_mappings)
3614             PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3615                 if (!pa_strbuf_isempty(sb))
3616                     pa_strbuf_puts(sb, " + ");
3617
3618                 pa_strbuf_printf(sb, _("%s Input"), m->description);
3619             }
3620
3621         p->description = pa_strbuf_tostring_free(sb);
3622     }
3623
3624     return 0;
3625 }
3626
3627 void pa_alsa_profile_dump(pa_alsa_profile *p) {
3628     uint32_t idx;
3629     pa_alsa_mapping *m;
3630     pa_assert(p);
3631
3632     pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3633                  p->name,
3634                  pa_strnull(p->description),
3635                  p->priority,
3636                  pa_yes_no(p->supported),
3637                  p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
3638                  p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
3639
3640     if (p->input_mappings)
3641         PA_IDXSET_FOREACH(m, p->input_mappings, idx)
3642             pa_log_debug("Input %s", m->name);
3643
3644     if (p->output_mappings)
3645         PA_IDXSET_FOREACH(m, p->output_mappings, idx)
3646             pa_log_debug("Output %s", m->name);
3647 }
3648
3649 static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
3650     pa_assert(db_fix);
3651
3652     /* Check that the dB mapping has been configured. Since "db-values" is
3653      * currently the only option in the DecibelFix section, and decibel fix
3654      * objects don't get created if a DecibelFix section is empty, this is
3655      * actually a redundant check. Having this may prevent future bugs,
3656      * however. */
3657     if (!db_fix->db_values) {
3658         pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
3659         return -1;
3660     }
3661
3662     return 0;
3663 }
3664
3665 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
3666     char *db_values = NULL;
3667
3668     pa_assert(db_fix);
3669
3670     if (db_fix->db_values) {
3671         pa_strbuf *buf;
3672         long i;
3673         long max_i = db_fix->max_step - db_fix->min_step;
3674
3675         buf = pa_strbuf_new();
3676         pa_strbuf_printf(buf, "[%li]:%0.2f", db_fix->min_step, db_fix->db_values[0] / 100.0);
3677
3678         for (i = 1; i <= max_i; ++i)
3679             pa_strbuf_printf(buf, " [%li]:%0.2f", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
3680
3681         db_values = pa_strbuf_tostring_free(buf);
3682     }
3683
3684     pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
3685                  db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
3686
3687     pa_xfree(db_values);
3688 }
3689
3690 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
3691     pa_alsa_profile_set *ps;
3692     pa_alsa_profile *p;
3693     pa_alsa_mapping *m;
3694     pa_alsa_decibel_fix *db_fix;
3695     char *fn;
3696     int r;
3697     void *state;
3698
3699     static pa_config_item items[] = {
3700         /* [General] */
3701         { "auto-profiles",          pa_config_parse_bool,         NULL, "General" },
3702
3703         /* [Mapping ...] */
3704         { "device-strings",         mapping_parse_device_strings, NULL, NULL },
3705         { "channel-map",            mapping_parse_channel_map,    NULL, NULL },
3706         { "paths-input",            mapping_parse_paths,          NULL, NULL },
3707         { "paths-output",           mapping_parse_paths,          NULL, NULL },
3708         { "element-input",          mapping_parse_element,        NULL, NULL },
3709         { "element-output",         mapping_parse_element,        NULL, NULL },
3710         { "direction",              mapping_parse_direction,      NULL, NULL },
3711
3712         /* Shared by [Mapping ...] and [Profile ...] */
3713         { "description",            mapping_parse_description,    NULL, NULL },
3714         { "priority",               mapping_parse_priority,       NULL, NULL },
3715
3716         /* [Profile ...] */
3717         { "input-mappings",         profile_parse_mappings,       NULL, NULL },
3718         { "output-mappings",        profile_parse_mappings,       NULL, NULL },
3719         { "skip-probe",             profile_parse_skip_probe,     NULL, NULL },
3720
3721         /* [DecibelFix ...] */
3722         { "db-values",              decibel_fix_parse_db_values,  NULL, NULL },
3723         { NULL, NULL, NULL, NULL }
3724     };
3725
3726     ps = pa_xnew0(pa_alsa_profile_set, 1);
3727     ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3728     ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3729     ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3730
3731     items[0].data = &ps->auto_profiles;
3732
3733     if (!fname)
3734         fname = "default.conf";
3735
3736     fn = pa_maybe_prefix_path(fname,
3737 #if defined(__linux__) && !defined(__OPTIMIZE__)
3738                               pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
3739 #endif
3740                               PA_ALSA_PROFILE_SETS_DIR);
3741
3742     r = pa_config_parse(fn, NULL, items, ps);
3743     pa_xfree(fn);
3744
3745     if (r < 0)
3746         goto fail;
3747
3748     PA_HASHMAP_FOREACH(m, ps->mappings, state)
3749         if (mapping_verify(m, bonus) < 0)
3750             goto fail;
3751
3752     if (ps->auto_profiles)
3753         profile_set_add_auto(ps);
3754
3755     PA_HASHMAP_FOREACH(p, ps->profiles, state)
3756         if (profile_verify(p) < 0)
3757             goto fail;
3758
3759     PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
3760         if (decibel_fix_verify(db_fix) < 0)
3761             goto fail;
3762
3763     return ps;
3764
3765 fail:
3766     pa_alsa_profile_set_free(ps);
3767     return NULL;
3768 }
3769
3770 void pa_alsa_profile_set_probe(
3771         pa_alsa_profile_set *ps,
3772         const char *dev_id,
3773         const pa_sample_spec *ss,
3774         unsigned default_n_fragments,
3775         unsigned default_fragment_size_msec) {
3776
3777     void *state;
3778     pa_alsa_profile *p, *last = NULL;
3779     pa_alsa_mapping *m;
3780
3781     pa_assert(ps);
3782     pa_assert(dev_id);
3783     pa_assert(ss);
3784
3785     if (ps->probed)
3786         return;
3787
3788     PA_HASHMAP_FOREACH(p, ps->profiles, state) {
3789         pa_sample_spec try_ss;
3790         pa_channel_map try_map;
3791         snd_pcm_uframes_t try_period_size, try_buffer_size;
3792         uint32_t idx;
3793
3794         /* Is this already marked that it is supported? (i.e. from the config file) */
3795         if (p->supported)
3796             continue;
3797
3798         pa_log_debug("Looking at profile %s", p->name);
3799
3800         /* Close PCMs from the last iteration we don't need anymore */
3801         if (last && last->output_mappings)
3802             PA_IDXSET_FOREACH(m, last->output_mappings, idx) {
3803
3804                 if (!m->output_pcm)
3805                     break;
3806
3807                 if (last->supported)
3808                     m->supported++;
3809
3810                 if (!p->output_mappings || !pa_idxset_get_by_data(p->output_mappings, m, NULL)) {
3811                     snd_pcm_close(m->output_pcm);
3812                     m->output_pcm = NULL;
3813                 }
3814             }
3815
3816         if (last && last->input_mappings)
3817             PA_IDXSET_FOREACH(m, last->input_mappings, idx) {
3818
3819                 if (!m->input_pcm)
3820                     break;
3821
3822                 if (last->supported)
3823                     m->supported++;
3824
3825                 if (!p->input_mappings || !pa_idxset_get_by_data(p->input_mappings, m, NULL)) {
3826                     snd_pcm_close(m->input_pcm);
3827                     m->input_pcm = NULL;
3828                 }
3829             }
3830
3831         p->supported = TRUE;
3832
3833         /* Check if we can open all new ones */
3834         if (p->output_mappings)
3835             PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3836
3837                 if (m->output_pcm)
3838                     continue;
3839
3840                 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
3841                 try_map = m->channel_map;
3842                 try_ss = *ss;
3843                 try_ss.channels = try_map.channels;
3844
3845                 try_period_size =
3846                     pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
3847                     pa_frame_size(&try_ss);
3848                 try_buffer_size = default_n_fragments * try_period_size;
3849
3850                 if (!(m ->output_pcm = pa_alsa_open_by_template(
3851                               m->device_strings,
3852                               dev_id,
3853                               NULL,
3854                               &try_ss, &try_map,
3855                               SND_PCM_STREAM_PLAYBACK,
3856                               &try_period_size, &try_buffer_size, 0, NULL, NULL,
3857                               TRUE))) {
3858                     p->supported = FALSE;
3859                     break;
3860                 }
3861             }
3862
3863         if (p->input_mappings && p->supported)
3864             PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3865
3866                 if (m->input_pcm)
3867                     continue;
3868
3869                 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
3870                 try_map = m->channel_map;
3871                 try_ss = *ss;
3872                 try_ss.channels = try_map.channels;
3873
3874                 try_period_size =
3875                     pa_usec_to_bytes(default_fragment_size_msec*PA_USEC_PER_MSEC, &try_ss) /
3876                     pa_frame_size(&try_ss);
3877                 try_buffer_size = default_n_fragments * try_period_size;
3878
3879                 if (!(m ->input_pcm = pa_alsa_open_by_template(
3880                               m->device_strings,
3881                               dev_id,
3882                               NULL,
3883                               &try_ss, &try_map,
3884                               SND_PCM_STREAM_CAPTURE,
3885                               &try_period_size, &try_buffer_size, 0, NULL, NULL,
3886                               TRUE))) {
3887                     p->supported = FALSE;
3888                     break;
3889                 }
3890             }
3891
3892         last = p;
3893
3894         if (p->supported)
3895             pa_log_debug("Profile %s supported.", p->name);
3896     }
3897
3898     /* Clean up */
3899     if (last) {
3900         uint32_t idx;
3901
3902         if (last->output_mappings)
3903             PA_IDXSET_FOREACH(m, last->output_mappings, idx)
3904                 if (m->output_pcm) {
3905
3906                     if (last->supported)
3907                         m->supported++;
3908
3909                     snd_pcm_close(m->output_pcm);
3910                     m->output_pcm = NULL;
3911                 }
3912
3913         if (last->input_mappings)
3914             PA_IDXSET_FOREACH(m, last->input_mappings, idx)
3915                 if (m->input_pcm) {
3916
3917                     if (last->supported)
3918                         m->supported++;
3919
3920                     snd_pcm_close(m->input_pcm);
3921                     m->input_pcm = NULL;
3922                 }
3923     }
3924
3925     PA_HASHMAP_FOREACH(p, ps->profiles, state)
3926         if (!p->supported) {
3927             pa_hashmap_remove(ps->profiles, p->name);
3928             profile_free(p);
3929         }
3930
3931     PA_HASHMAP_FOREACH(m, ps->mappings, state)
3932         if (m->supported <= 0) {
3933             pa_hashmap_remove(ps->mappings, m->name);
3934             mapping_free(m);
3935         }
3936
3937     ps->probed = TRUE;
3938 }
3939
3940 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
3941     pa_alsa_profile *p;
3942     pa_alsa_mapping *m;
3943     pa_alsa_decibel_fix *db_fix;
3944     void *state;
3945
3946     pa_assert(ps);
3947
3948     pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
3949                  (void*)
3950                  ps,
3951                  pa_yes_no(ps->auto_profiles),
3952                  pa_yes_no(ps->probed),
3953                  pa_hashmap_size(ps->mappings),
3954                  pa_hashmap_size(ps->profiles),
3955                  pa_hashmap_size(ps->decibel_fixes));
3956
3957     PA_HASHMAP_FOREACH(m, ps->mappings, state)
3958         pa_alsa_mapping_dump(m);
3959
3960     PA_HASHMAP_FOREACH(p, ps->profiles, state)
3961         pa_alsa_profile_dump(p);
3962
3963     PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
3964         pa_alsa_decibel_fix_dump(db_fix);
3965 }
3966
3967 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps) {
3968     pa_alsa_path *path;
3969
3970     pa_assert(p);
3971     pa_assert(!*p);
3972     pa_assert(ps);
3973
3974     /* if there is no path, we don't want a port list */
3975     if (!ps->paths)
3976         return;
3977
3978     if (!ps->paths->next){
3979         pa_alsa_setting *s;
3980
3981         /* If there is only one path, but no or only one setting, then
3982          * we want a port list either */
3983         if (!ps->paths->settings || !ps->paths->settings->next)
3984             return;
3985
3986         /* Ok, there is only one path, however with multiple settings,
3987          * so let's create a port for each setting */
3988         *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3989
3990         PA_LLIST_FOREACH(s, ps->paths->settings) {
3991             pa_device_port *port;
3992             pa_alsa_port_data *data;
3993
3994             port = pa_device_port_new(s->name, s->description, sizeof(pa_alsa_port_data));
3995             port->priority = s->priority;
3996
3997             data = PA_DEVICE_PORT_DATA(port);
3998             data->path = ps->paths;
3999             data->setting = s;
4000
4001             pa_hashmap_put(*p, port->name, port);
4002         }
4003
4004     } else {
4005
4006         /* We have multiple paths, so let's create a port for each
4007          * one, and each of each settings */
4008         *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4009
4010         PA_LLIST_FOREACH(path, ps->paths) {
4011
4012             if (!path->settings || !path->settings->next) {
4013                 pa_device_port *port;
4014                 pa_alsa_port_data *data;
4015
4016                 /* If there is no or just one setting we only need a
4017                  * single entry */
4018
4019                 port = pa_device_port_new(path->name, path->description, sizeof(pa_alsa_port_data));
4020                 port->priority = path->priority * 100;
4021
4022
4023                 data = PA_DEVICE_PORT_DATA(port);
4024                 data->path = path;
4025                 data->setting = path->settings;
4026
4027                 pa_hashmap_put(*p, port->name, port);
4028             } else {
4029                 pa_alsa_setting *s;
4030
4031                 PA_LLIST_FOREACH(s, path->settings) {
4032                     pa_device_port *port;
4033                     pa_alsa_port_data *data;
4034                     char *n, *d;
4035
4036                     n = pa_sprintf_malloc("%s;%s", path->name, s->name);
4037
4038                     if (s->description[0])
4039                         d = pa_sprintf_malloc(_("%s / %s"), path->description, s->description);
4040                     else
4041                         d = pa_xstrdup(path->description);
4042
4043                     port = pa_device_port_new(n, d, sizeof(pa_alsa_port_data));
4044                     port->priority = path->priority * 100 + s->priority;
4045
4046                     pa_xfree(n);
4047                     pa_xfree(d);
4048
4049                     data = PA_DEVICE_PORT_DATA(port);
4050                     data->path = path;
4051                     data->setting = s;
4052
4053                     pa_hashmap_put(*p, port->name, port);
4054                 }
4055             }
4056         }
4057     }
4058
4059     pa_log_debug("Added %u ports", pa_hashmap_size(*p));
4060 }