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