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