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