alsa: Fix mixer path when running from build tree
[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-microphone-headset",  N_("Headset Microphone") },
2340         { "analog-input-linein",        N_("Line In") },
2341         { "analog-input-radio",         N_("Radio") },
2342         { "analog-input-video",         N_("Video") },
2343         { "analog-output",              N_("Analog Output") },
2344         { "analog-output-headphones",   N_("Headphones") },
2345         { "analog-output-lfe-on-mono",  N_("LFE on Separate Mono Output") },
2346         { "analog-output-lineout",      N_("Line Out") },
2347         { "analog-output-mono",         N_("Analog Mono Output") },
2348         { "analog-output-speaker",      N_("Speakers") },
2349         { "hdmi-output",                N_("HDMI / DisplayPort") },
2350         { "iec958-stereo-output",       N_("Digital Output (S/PDIF)") },
2351         { "iec958-stereo-input",        N_("Digital Input (S/PDIF)") },
2352         { "iec958-passthrough-output",  N_("Digital Passthrough (S/PDIF)") }
2353     };
2354
2355     pa_alsa_element *e;
2356
2357     pa_assert(p);
2358
2359     PA_LLIST_FOREACH(e, p->elements)
2360         if (element_verify(e) < 0)
2361             return -1;
2362
2363     if (!p->description)
2364         p->description = pa_xstrdup(lookup_description(p->name,
2365                                                        well_known_descriptions,
2366                                                        PA_ELEMENTSOF(well_known_descriptions)));
2367
2368     if (!p->description)
2369         p->description = pa_xstrdup(p->name);
2370
2371     return 0;
2372 }
2373
2374 static const char *get_default_paths_dir(void) {
2375     if (pa_run_from_build_tree())
2376         return PA_SRCDIR "/modules/alsa/mixer/paths/";
2377     else
2378         return PA_ALSA_PATHS_DIR;
2379 }
2380
2381 pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa_direction_t direction) {
2382     pa_alsa_path *p;
2383     char *fn;
2384     int r;
2385     const char *n;
2386     bool mute_during_activation = false;
2387
2388     pa_config_item items[] = {
2389         /* [General] */
2390         { "priority",            pa_config_parse_unsigned,          NULL, "General" },
2391         { "description",         pa_config_parse_string,            NULL, "General" },
2392         { "name",                pa_config_parse_string,            NULL, "General" },
2393         { "mute-during-activation", pa_config_parse_bool,           NULL, "General" },
2394         { "eld-device",          pa_config_parse_int,               NULL, "General" },
2395
2396         /* [Option ...] */
2397         { "priority",            option_parse_priority,             NULL, NULL },
2398         { "name",                option_parse_name,                 NULL, NULL },
2399
2400         /* [Jack ...] */
2401         { "state.plugged",       jack_parse_state,                  NULL, NULL },
2402         { "state.unplugged",     jack_parse_state,                  NULL, NULL },
2403
2404         /* [Element ...] */
2405         { "switch",              element_parse_switch,              NULL, NULL },
2406         { "volume",              element_parse_volume,              NULL, NULL },
2407         { "enumeration",         element_parse_enumeration,         NULL, NULL },
2408         { "override-map.1",      element_parse_override_map,        NULL, NULL },
2409         { "override-map.2",      element_parse_override_map,        NULL, NULL },
2410         /* ... later on we might add override-map.3 and so on here ... */
2411         { "required",            element_parse_required,            NULL, NULL },
2412         { "required-any",        element_parse_required,            NULL, NULL },
2413         { "required-absent",     element_parse_required,            NULL, NULL },
2414         { "direction",           element_parse_direction,           NULL, NULL },
2415         { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
2416         { "volume-limit",        element_parse_volume_limit,        NULL, NULL },
2417         { NULL, NULL, NULL, NULL }
2418     };
2419
2420     pa_assert(fname);
2421
2422     p = pa_xnew0(pa_alsa_path, 1);
2423     n = pa_path_get_filename(fname);
2424     p->name = pa_xstrndup(n, strcspn(n, "."));
2425     p->proplist = pa_proplist_new();
2426     p->direction = direction;
2427     p->eld_device = -1;
2428
2429     items[0].data = &p->priority;
2430     items[1].data = &p->description;
2431     items[2].data = &p->name;
2432     items[3].data = &mute_during_activation;
2433     items[4].data = &p->eld_device;
2434
2435     if (!paths_dir)
2436         paths_dir = get_default_paths_dir();
2437
2438     fn = pa_maybe_prefix_path(fname, paths_dir);
2439
2440     r = pa_config_parse(fn, NULL, items, p->proplist, p);
2441     pa_xfree(fn);
2442
2443     if (r < 0)
2444         goto fail;
2445
2446     p->mute_during_activation = mute_during_activation;
2447
2448     if (path_verify(p) < 0)
2449         goto fail;
2450
2451     return p;
2452
2453 fail:
2454     pa_alsa_path_free(p);
2455     return NULL;
2456 }
2457
2458 pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction) {
2459     pa_alsa_path *p;
2460     pa_alsa_element *e;
2461
2462     pa_assert(element);
2463
2464     p = pa_xnew0(pa_alsa_path, 1);
2465     p->name = pa_xstrdup(element);
2466     p->direction = direction;
2467
2468     e = pa_xnew0(pa_alsa_element, 1);
2469     e->path = p;
2470     e->alsa_name = pa_xstrdup(element);
2471     e->direction = direction;
2472     e->volume_limit = -1;
2473
2474     e->switch_use = PA_ALSA_SWITCH_MUTE;
2475     e->volume_use = PA_ALSA_VOLUME_MERGE;
2476
2477     PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
2478     p->last_element = e;
2479     return p;
2480 }
2481
2482 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
2483     pa_alsa_option *o, *n;
2484
2485     pa_assert(e);
2486
2487     for (o = e->options; o; o = n) {
2488         n = o->next;
2489
2490         if (o->alsa_idx < 0) {
2491             PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
2492             option_free(o);
2493         }
2494     }
2495
2496     return
2497         e->switch_use != PA_ALSA_SWITCH_IGNORE ||
2498         e->volume_use != PA_ALSA_VOLUME_IGNORE ||
2499         e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
2500 }
2501
2502 static void path_drop_unsupported(pa_alsa_path *p) {
2503     pa_alsa_element *e, *n;
2504
2505     pa_assert(p);
2506
2507     for (e = p->elements; e; e = n) {
2508         n = e->next;
2509
2510         if (!element_drop_unsupported(e)) {
2511             PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
2512             element_free(e);
2513         }
2514     }
2515 }
2516
2517 static void path_make_options_unique(pa_alsa_path *p) {
2518     pa_alsa_element *e;
2519     pa_alsa_option *o, *u;
2520
2521     PA_LLIST_FOREACH(e, p->elements) {
2522         PA_LLIST_FOREACH(o, e->options) {
2523             unsigned i;
2524             char *m;
2525
2526             for (u = o->next; u; u = u->next)
2527                 if (pa_streq(u->name, o->name))
2528                     break;
2529
2530             if (!u)
2531                 continue;
2532
2533             m = pa_xstrdup(o->name);
2534
2535             /* OK, this name is not unique, hence let's rename */
2536             for (i = 1, u = o; u; u = u->next) {
2537                 char *nn, *nd;
2538
2539                 if (!pa_streq(u->name, m))
2540                     continue;
2541
2542                 nn = pa_sprintf_malloc("%s-%u", m, i);
2543                 pa_xfree(u->name);
2544                 u->name = nn;
2545
2546                 nd = pa_sprintf_malloc("%s %u", u->description, i);
2547                 pa_xfree(u->description);
2548                 u->description = nd;
2549
2550                 i++;
2551             }
2552
2553             pa_xfree(m);
2554         }
2555     }
2556 }
2557
2558 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
2559     pa_alsa_option *o;
2560
2561     for (; e; e = e->next)
2562         if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
2563             e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
2564             break;
2565
2566     if (!e)
2567         return FALSE;
2568
2569     for (o = e->options; o; o = o->next) {
2570         pa_alsa_setting *s;
2571
2572         if (template) {
2573             s = pa_xnewdup(pa_alsa_setting, template, 1);
2574             s->options = pa_idxset_copy(template->options);
2575             s->name = pa_sprintf_malloc("%s+%s", template->name, o->name);
2576             s->description =
2577                 (template->description[0] && o->description[0])
2578                 ? pa_sprintf_malloc("%s / %s", template->description, o->description)
2579                 : (template->description[0]
2580                    ? pa_xstrdup(template->description)
2581                    : pa_xstrdup(o->description));
2582
2583             s->priority = PA_MAX(template->priority, o->priority);
2584         } else {
2585             s = pa_xnew0(pa_alsa_setting, 1);
2586             s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2587             s->name = pa_xstrdup(o->name);
2588             s->description = pa_xstrdup(o->description);
2589             s->priority = o->priority;
2590         }
2591
2592         pa_idxset_put(s->options, o, NULL);
2593
2594         if (element_create_settings(e->next, s))
2595             /* This is not a leaf, so let's get rid of it */
2596             setting_free(s);
2597         else {
2598             /* This is a leaf, so let's add it */
2599             PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2600
2601             e->path->last_setting = s;
2602         }
2603     }
2604
2605     return TRUE;
2606 }
2607
2608 static void path_create_settings(pa_alsa_path *p) {
2609     pa_assert(p);
2610
2611     element_create_settings(p->elements, NULL);
2612 }
2613
2614 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, snd_hctl_t *hctl, pa_bool_t ignore_dB) {
2615     pa_alsa_element *e;
2616     pa_alsa_jack *j;
2617     double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2618     pa_channel_position_t t;
2619     pa_channel_position_mask_t path_volume_channels = 0;
2620
2621     pa_assert(p);
2622     pa_assert(m);
2623
2624     if (p->probed)
2625         return p->supported ? 0 : -1;
2626     p->probed = TRUE;
2627
2628     pa_zero(min_dB);
2629     pa_zero(max_dB);
2630
2631     pa_log_debug("Probing path '%s'", p->name);
2632
2633     PA_LLIST_FOREACH(j, p->jacks) {
2634         if (jack_probe(j, hctl) < 0) {
2635             p->supported = FALSE;
2636             pa_log_debug("Probe of jack '%s' failed.", j->alsa_name);
2637             return -1;
2638         }
2639         pa_log_debug("Probe of jack '%s' succeeded (%s)", j->alsa_name, j->has_control ? "found!" : "not found");
2640     }
2641
2642     PA_LLIST_FOREACH(e, p->elements) {
2643         if (element_probe(e, m) < 0) {
2644             p->supported = FALSE;
2645             pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2646             return -1;
2647         }
2648         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);
2649
2650         if (ignore_dB)
2651             e->has_dB = FALSE;
2652
2653         if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2654
2655             if (!p->has_volume) {
2656                 p->min_volume = e->min_volume;
2657                 p->max_volume = e->max_volume;
2658             }
2659
2660             if (e->has_dB) {
2661                 if (!p->has_volume) {
2662                     for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2663                         if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2664                             min_dB[t] = e->min_dB;
2665                             max_dB[t] = e->max_dB;
2666                             path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2667                         }
2668
2669                     p->has_dB = TRUE;
2670                 } else {
2671
2672                     if (p->has_dB) {
2673                         for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2674                             if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2675                                 min_dB[t] += e->min_dB;
2676                                 max_dB[t] += e->max_dB;
2677                                 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2678                             }
2679                     } else {
2680                         /* Hmm, there's another element before us
2681                          * which cannot do dB volumes, so we we need
2682                          * to 'neutralize' this slider */
2683                         e->volume_use = PA_ALSA_VOLUME_ZERO;
2684                         pa_log_info("Zeroing volume of '%s' on path '%s'", e->alsa_name, p->name);
2685                     }
2686                 }
2687             } else if (p->has_volume) {
2688                 /* We can't use this volume, so let's ignore it */
2689                 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2690                 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e->alsa_name, p->name);
2691             }
2692             p->has_volume = TRUE;
2693         }
2694
2695         if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2696             p->has_mute = TRUE;
2697     }
2698
2699     if (p->has_req_any && !p->req_any_present) {
2700         p->supported = FALSE;
2701         pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name);
2702         return -1;
2703     }
2704
2705     path_drop_unsupported(p);
2706     path_make_options_unique(p);
2707     path_create_settings(p);
2708
2709     p->supported = TRUE;
2710
2711     p->min_dB = INFINITY;
2712     p->max_dB = -INFINITY;
2713
2714     for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2715         if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) {
2716             if (p->min_dB > min_dB[t])
2717                 p->min_dB = min_dB[t];
2718
2719             if (p->max_dB < max_dB[t])
2720                 p->max_dB = max_dB[t];
2721         }
2722     }
2723
2724     return 0;
2725 }
2726
2727 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2728     pa_assert(s);
2729
2730     pa_log_debug("Setting %s (%s) priority=%u",
2731                  s->name,
2732                  pa_strnull(s->description),
2733                  s->priority);
2734 }
2735
2736 void pa_alsa_jack_dump(pa_alsa_jack *j) {
2737     pa_assert(j);
2738
2739     pa_log_debug("Jack %s, alsa_name='%s', detection %s", j->name, j->alsa_name, j->has_control ? "possible" : "unavailable");
2740 }
2741
2742 void pa_alsa_option_dump(pa_alsa_option *o) {
2743     pa_assert(o);
2744
2745     pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2746                  o->alsa_name,
2747                  pa_strnull(o->name),
2748                  pa_strnull(o->description),
2749                  o->alsa_idx,
2750                  o->priority);
2751 }
2752
2753 void pa_alsa_element_dump(pa_alsa_element *e) {
2754     pa_alsa_option *o;
2755     pa_assert(e);
2756
2757     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",
2758                  e->alsa_name,
2759                  e->direction,
2760                  e->switch_use,
2761                  e->volume_use,
2762                  e->volume_limit,
2763                  e->enumeration_use,
2764                  e->required,
2765                  e->required_any,
2766                  e->required_absent,
2767                  (long long unsigned) e->merged_mask,
2768                  e->n_channels,
2769                  pa_yes_no(e->override_map));
2770
2771     PA_LLIST_FOREACH(o, e->options)
2772         pa_alsa_option_dump(o);
2773 }
2774
2775 void pa_alsa_path_dump(pa_alsa_path *p) {
2776     pa_alsa_element *e;
2777     pa_alsa_jack *j;
2778     pa_alsa_setting *s;
2779     pa_assert(p);
2780
2781     pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2782                  "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2783                  p->name,
2784                  pa_strnull(p->description),
2785                  p->direction,
2786                  p->priority,
2787                  pa_yes_no(p->probed),
2788                  pa_yes_no(p->supported),
2789                  pa_yes_no(p->has_mute),
2790                  pa_yes_no(p->has_volume),
2791                  pa_yes_no(p->has_dB),
2792                  p->min_volume, p->max_volume,
2793                  p->min_dB, p->max_dB);
2794
2795     PA_LLIST_FOREACH(e, p->elements)
2796         pa_alsa_element_dump(e);
2797
2798     PA_LLIST_FOREACH(j, p->jacks)
2799         pa_alsa_jack_dump(j);
2800
2801     PA_LLIST_FOREACH(s, p->settings)
2802         pa_alsa_setting_dump(s);
2803 }
2804
2805 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2806     snd_mixer_selem_id_t *sid;
2807     snd_mixer_elem_t *me;
2808
2809     pa_assert(e);
2810     pa_assert(m);
2811     pa_assert(cb);
2812
2813     SELEM_INIT(sid, e->alsa_name);
2814     if (!(me = snd_mixer_find_selem(m, sid))) {
2815         pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2816         return;
2817     }
2818
2819     snd_mixer_elem_set_callback(me, cb);
2820     snd_mixer_elem_set_callback_private(me, userdata);
2821 }
2822
2823 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2824     pa_alsa_element *e;
2825
2826     pa_assert(p);
2827     pa_assert(m);
2828     pa_assert(cb);
2829
2830     PA_LLIST_FOREACH(e, p->elements)
2831         element_set_callback(e, m, cb, userdata);
2832 }
2833
2834 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2835     pa_alsa_path *p;
2836     void *state;
2837
2838     pa_assert(ps);
2839     pa_assert(m);
2840     pa_assert(cb);
2841
2842     PA_HASHMAP_FOREACH(p, ps->paths, state)
2843         pa_alsa_path_set_callback(p, m, cb, userdata);
2844 }
2845
2846 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction, const char *paths_dir) {
2847     pa_alsa_path_set *ps;
2848     char **pn = NULL, **en = NULL, **ie;
2849     pa_alsa_decibel_fix *db_fix;
2850     void *state, *state2;
2851     pa_hashmap *cache;
2852
2853     pa_assert(m);
2854     pa_assert(m->profile_set);
2855     pa_assert(m->profile_set->decibel_fixes);
2856     pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2857
2858     if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2859         return NULL;
2860
2861     ps = pa_xnew0(pa_alsa_path_set, 1);
2862     ps->direction = direction;
2863     ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2864
2865     if (direction == PA_ALSA_DIRECTION_OUTPUT) {
2866         pn = m->output_path_names;
2867         cache = m->profile_set->output_paths;
2868     }
2869     else if (direction == PA_ALSA_DIRECTION_INPUT) {
2870         pn = m->input_path_names;
2871         cache = m->profile_set->input_paths;
2872     }
2873
2874     if (pn) {
2875         char **in;
2876
2877         for (in = pn; *in; in++) {
2878             pa_alsa_path *p = NULL;
2879             pa_bool_t duplicate = FALSE;
2880             char **kn;
2881
2882             for (kn = pn; kn < in; kn++)
2883                 if (pa_streq(*kn, *in)) {
2884                     duplicate = TRUE;
2885                     break;
2886                 }
2887
2888             if (duplicate)
2889                 continue;
2890
2891             p = pa_hashmap_get(cache, *in);
2892             if (!p) {
2893                 char *fn = pa_sprintf_malloc("%s.conf", *in);
2894                 p = pa_alsa_path_new(paths_dir, fn, direction);
2895                 pa_xfree(fn);
2896                 if (p)
2897                     pa_hashmap_put(cache, *in, p);
2898             }
2899             pa_assert(pa_hashmap_get(cache, *in) == p);
2900             if (p)
2901                 pa_hashmap_put(ps->paths, p, p);
2902
2903         }
2904
2905         goto finish;
2906     }
2907
2908     if (direction == PA_ALSA_DIRECTION_OUTPUT)
2909         en = m->output_element;
2910     else if (direction == PA_ALSA_DIRECTION_INPUT)
2911         en = m->input_element;
2912
2913     if (!en) {
2914         pa_alsa_path_set_free(ps);
2915         return NULL;
2916     }
2917
2918     for (ie = en; *ie; ie++) {
2919         char **je;
2920         pa_alsa_path *p;
2921
2922         p = pa_alsa_path_synthesize(*ie, direction);
2923
2924         /* Mark all other passed elements for require-absent */
2925         for (je = en; *je; je++) {
2926             pa_alsa_element *e;
2927
2928             if (je == ie)
2929                 continue;
2930
2931             e = pa_xnew0(pa_alsa_element, 1);
2932             e->path = p;
2933             e->alsa_name = pa_xstrdup(*je);
2934             e->direction = direction;
2935             e->required_absent = PA_ALSA_REQUIRED_ANY;
2936             e->volume_limit = -1;
2937
2938             PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2939             p->last_element = e;
2940         }
2941
2942         pa_hashmap_put(ps->paths, *ie, p);
2943     }
2944
2945 finish:
2946     /* Assign decibel fixes to elements. */
2947     PA_HASHMAP_FOREACH(db_fix, m->profile_set->decibel_fixes, state) {
2948         pa_alsa_path *p;
2949
2950         PA_HASHMAP_FOREACH(p, ps->paths, state2) {
2951             pa_alsa_element *e;
2952
2953             PA_LLIST_FOREACH(e, p->elements) {
2954                 if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_name)) {
2955                     /* The profile set that contains the dB fix may be freed
2956                      * before the element, so we have to copy the dB fix
2957                      * object. */
2958                     e->db_fix = pa_xnewdup(pa_alsa_decibel_fix, db_fix, 1);
2959                     e->db_fix->profile_set = NULL;
2960                     e->db_fix->name = pa_xstrdup(db_fix->name);
2961                     e->db_fix->db_values = pa_xmemdup(db_fix->db_values, (db_fix->max_step - db_fix->min_step + 1) * sizeof(long));
2962                 }
2963             }
2964         }
2965     }
2966
2967     return ps;
2968 }
2969
2970 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2971     pa_alsa_path *p;
2972     void *state;
2973     pa_assert(ps);
2974
2975     pa_log_debug("Path Set %p, direction=%i",
2976                  (void*) ps,
2977                  ps->direction);
2978
2979     PA_HASHMAP_FOREACH(p, ps->paths, state)
2980         pa_alsa_path_dump(p);
2981 }
2982
2983
2984 static pa_bool_t options_have_option(pa_alsa_option *options, const char *alsa_name) {
2985     pa_alsa_option *o;
2986
2987     pa_assert(options);
2988     pa_assert(alsa_name);
2989
2990     PA_LLIST_FOREACH(o, options) {
2991         if (pa_streq(o->alsa_name, alsa_name))
2992             return TRUE;
2993     }
2994     return FALSE;
2995 }
2996
2997 static pa_bool_t enumeration_is_subset(pa_alsa_option *a_options, pa_alsa_option *b_options) {
2998     pa_alsa_option *oa, *ob;
2999
3000     if (!a_options) return TRUE;
3001     if (!b_options) return FALSE;
3002
3003     /* If there is an option A offers that B does not, then A is not a subset of B. */
3004     PA_LLIST_FOREACH(oa, a_options) {
3005         pa_bool_t found = FALSE;
3006         PA_LLIST_FOREACH(ob, b_options) {
3007             if (pa_streq(oa->alsa_name, ob->alsa_name)) {
3008                 found = TRUE;
3009                 break;
3010             }
3011         }
3012         if (!found)
3013             return FALSE;
3014     }
3015     return TRUE;
3016 }
3017
3018 /**
3019  *  Compares two elements to see if a is a subset of b
3020  */
3021 static pa_bool_t element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_t *m) {
3022     pa_assert(a);
3023     pa_assert(b);
3024     pa_assert(m);
3025
3026     /* General rules:
3027      * Every state is a subset of itself (with caveats for volume_limits and options)
3028      * IGNORE is a subset of every other state */
3029
3030     /* Check the volume_use */
3031     if (a->volume_use != PA_ALSA_VOLUME_IGNORE) {
3032
3033         /* "Constant" is subset of "Constant" only when their constant values are equal */
3034         if (a->volume_use == PA_ALSA_VOLUME_CONSTANT && b->volume_use == PA_ALSA_VOLUME_CONSTANT && a->constant_volume != b->constant_volume)
3035             return FALSE;
3036
3037         /* Different volume uses when b is not "Merge" means we are definitely not a subset */
3038         if (a->volume_use != b->volume_use && b->volume_use != PA_ALSA_VOLUME_MERGE)
3039             return FALSE;
3040
3041         /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
3042          * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
3043          * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
3044         if (b->volume_use == PA_ALSA_VOLUME_MERGE && b->volume_limit >= 0) {
3045             long a_limit;
3046
3047             if (a->volume_use == PA_ALSA_VOLUME_CONSTANT)
3048                 a_limit = a->constant_volume;
3049             else if (a->volume_use == PA_ALSA_VOLUME_ZERO) {
3050                 long dB = 0;
3051
3052                 if (a->db_fix) {
3053                     int rounding = (a->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1);
3054                     a_limit = decibel_fix_get_step(a->db_fix, &dB, rounding);
3055                 } else {
3056                     snd_mixer_selem_id_t *sid;
3057                     snd_mixer_elem_t *me;
3058
3059                     SELEM_INIT(sid, a->alsa_name);
3060                     if (!(me = snd_mixer_find_selem(m, sid))) {
3061                         pa_log_warn("Element %s seems to have disappeared.", a->alsa_name);
3062                         return FALSE;
3063                     }
3064
3065                     if (a->direction == PA_ALSA_DIRECTION_OUTPUT) {
3066                         if (snd_mixer_selem_ask_playback_dB_vol(me, dB, +1, &a_limit) < 0)
3067                             return FALSE;
3068                     } else {
3069                         if (snd_mixer_selem_ask_capture_dB_vol(me, dB, -1, &a_limit) < 0)
3070                             return FALSE;
3071                     }
3072                 }
3073             } else if (a->volume_use == PA_ALSA_VOLUME_OFF)
3074                 a_limit = a->min_volume;
3075             else if (a->volume_use == PA_ALSA_VOLUME_MERGE)
3076                 a_limit = a->volume_limit;
3077             else
3078                 /* This should never be reached */
3079                 pa_assert(FALSE);
3080
3081             if (a_limit > b->volume_limit)
3082                 return FALSE;
3083         }
3084
3085         if (a->volume_use == PA_ALSA_VOLUME_MERGE) {
3086             int s;
3087             /* If override-maps are different, they're not subsets */
3088             if (a->n_channels != b->n_channels)
3089                 return FALSE;
3090             for (s = 0; s <= SND_MIXER_SCHN_LAST; s++)
3091                 if (a->masks[s][a->n_channels-1] != b->masks[s][b->n_channels-1]) {
3092                     pa_log_debug("Element %s is not a subset - mask a: 0x%" PRIx64 ", mask b: 0x%" PRIx64 ", at channel %d",
3093                         a->alsa_name, a->masks[s][a->n_channels-1], b->masks[s][b->n_channels-1], s);
3094                     return FALSE;
3095                }
3096         }
3097     }
3098
3099     if (a->switch_use != PA_ALSA_SWITCH_IGNORE) {
3100         /* "On" is a subset of "Mute".
3101          * "Off" is a subset of "Mute".
3102          * "On" is a subset of "Select", if there is an "Option:On" in B.
3103          * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3104          * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3105
3106         if (a->switch_use != b->switch_use) {
3107
3108             if (a->switch_use == PA_ALSA_SWITCH_SELECT || a->switch_use == PA_ALSA_SWITCH_MUTE
3109                 || b->switch_use == PA_ALSA_SWITCH_OFF || b->switch_use == PA_ALSA_SWITCH_ON)
3110                 return FALSE;
3111
3112             if (b->switch_use == PA_ALSA_SWITCH_SELECT) {
3113                 if (a->switch_use == PA_ALSA_SWITCH_ON) {
3114                     if (!options_have_option(b->options, "on"))
3115                         return FALSE;
3116                 } else if (a->switch_use == PA_ALSA_SWITCH_OFF) {
3117                     if (!options_have_option(b->options, "off"))
3118                         return FALSE;
3119                 }
3120             }
3121         } else if (a->switch_use == PA_ALSA_SWITCH_SELECT) {
3122             if (!enumeration_is_subset(a->options, b->options))
3123                 return FALSE;
3124         }
3125     }
3126
3127     if (a->enumeration_use != PA_ALSA_ENUMERATION_IGNORE) {
3128         if (b->enumeration_use == PA_ALSA_ENUMERATION_IGNORE)
3129             return FALSE;
3130         if (!enumeration_is_subset(a->options, b->options))
3131             return FALSE;
3132     }
3133
3134     return TRUE;
3135 }
3136
3137 static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
3138     pa_alsa_path *p;
3139     void *state;
3140
3141     pa_assert(ps);
3142     pa_assert(m);
3143
3144     /* If we only have one path, then don't bother */
3145     if (pa_hashmap_size(ps->paths) < 2)
3146         return;
3147
3148     PA_HASHMAP_FOREACH(p, ps->paths, state) {
3149         pa_alsa_path *p2;
3150         void *state2;
3151
3152         PA_HASHMAP_FOREACH(p2, ps->paths, state2) {
3153             pa_alsa_element *ea, *eb;
3154             pa_alsa_jack *ja, *jb;
3155             bool is_subset = true;
3156
3157             if (p == p2)
3158                 continue;
3159
3160             /* If a has a jack that b does not have, a is not a subset */
3161             PA_LLIST_FOREACH(ja, p->jacks) {
3162                 bool exists = false;
3163
3164                 if (!ja->has_control)
3165                     continue;
3166
3167                 PA_LLIST_FOREACH(jb, p2->jacks) {
3168                     if (jb->has_control && pa_streq(jb->alsa_name, ja->alsa_name) &&
3169                        (ja->state_plugged == jb->state_plugged) &&
3170                        (ja->state_unplugged == jb->state_unplugged)) {
3171                         exists = true;
3172                         break;
3173                     }
3174                 }
3175
3176                 if (!exists) {
3177                     is_subset = false;
3178                     break;
3179                 }
3180             }
3181
3182             /* Compare the elements of each set... */
3183             ea = p->elements;
3184             eb = p2->elements;
3185
3186             while (is_subset) {
3187                 if (!ea && !eb)
3188                     break;
3189                 else if ((ea && !eb) || (!ea && eb))
3190                     is_subset = false;
3191                 else if (pa_streq(ea->alsa_name, eb->alsa_name)) {
3192                     if (element_is_subset(ea, eb, m)) {
3193                         ea = ea->next;
3194                         eb = eb->next;
3195                     } else
3196                         is_subset = false;
3197                 } else
3198                     is_subset = false;
3199             }
3200
3201             if (is_subset) {
3202                 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p->name, p2->name);
3203                 pa_hashmap_remove(ps->paths, p);
3204                 break;
3205             }
3206         }
3207     }
3208 }
3209
3210 static pa_alsa_path* path_set_find_path_by_name(pa_alsa_path_set *ps, const char* name, pa_alsa_path *ignore)
3211 {
3212     pa_alsa_path* p;
3213     void *state;
3214
3215     PA_HASHMAP_FOREACH(p, ps->paths, state)
3216         if (p != ignore && pa_streq(p->name, name))
3217             return p;
3218     return NULL;
3219 }
3220
3221 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
3222     pa_alsa_path *p, *q;
3223     void *state, *state2;
3224
3225     PA_HASHMAP_FOREACH(p, ps->paths, state) {
3226         unsigned i;
3227         char *m;
3228
3229         q = path_set_find_path_by_name(ps, p->name, p);
3230
3231         if (!q)
3232             continue;
3233
3234         m = pa_xstrdup(p->name);
3235
3236         /* OK, this name is not unique, hence let's rename */
3237         i = 1;
3238         PA_HASHMAP_FOREACH(q, ps->paths, state2) {
3239             char *nn, *nd;
3240
3241             if (!pa_streq(q->name, m))
3242                 continue;
3243
3244             nn = pa_sprintf_malloc("%s-%u", m, i);
3245             pa_xfree(q->name);
3246             q->name = nn;
3247
3248             nd = pa_sprintf_malloc("%s %u", q->description, i);
3249             pa_xfree(q->description);
3250             q->description = nd;
3251
3252             i++;
3253         }
3254
3255         pa_xfree(m);
3256     }
3257 }
3258
3259 static void mapping_free(pa_alsa_mapping *m) {
3260     pa_assert(m);
3261
3262     pa_xfree(m->name);
3263     pa_xfree(m->description);
3264
3265     pa_proplist_free(m->proplist);
3266
3267     pa_xstrfreev(m->device_strings);
3268     pa_xstrfreev(m->input_path_names);
3269     pa_xstrfreev(m->output_path_names);
3270     pa_xstrfreev(m->input_element);
3271     pa_xstrfreev(m->output_element);
3272     if (m->input_path_set)
3273         pa_alsa_path_set_free(m->input_path_set);
3274     if (m->output_path_set)
3275         pa_alsa_path_set_free(m->output_path_set);
3276
3277     pa_assert(!m->input_pcm);
3278     pa_assert(!m->output_pcm);
3279
3280     pa_alsa_ucm_mapping_context_free(&m->ucm_context);
3281
3282     pa_xfree(m);
3283 }
3284
3285 static void profile_free(pa_alsa_profile *p) {
3286     pa_assert(p);
3287
3288     pa_xfree(p->name);
3289     pa_xfree(p->description);
3290
3291     pa_xstrfreev(p->input_mapping_names);
3292     pa_xstrfreev(p->output_mapping_names);
3293
3294     if (p->input_mappings)
3295         pa_idxset_free(p->input_mappings, NULL);
3296
3297     if (p->output_mappings)
3298         pa_idxset_free(p->output_mappings, NULL);
3299
3300     pa_xfree(p);
3301 }
3302
3303 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
3304     pa_assert(ps);
3305
3306     if (ps->input_paths)
3307         pa_hashmap_free(ps->input_paths, (pa_free_cb_t) pa_alsa_path_free);
3308
3309     if (ps->output_paths)
3310         pa_hashmap_free(ps->output_paths, (pa_free_cb_t) pa_alsa_path_free);
3311
3312     if (ps->profiles)
3313         pa_hashmap_free(ps->profiles, (pa_free_cb_t) profile_free);
3314
3315     if (ps->mappings)
3316         pa_hashmap_free(ps->mappings, (pa_free_cb_t) mapping_free);
3317
3318     if (ps->decibel_fixes)
3319         pa_hashmap_free(ps->decibel_fixes, (pa_free_cb_t) decibel_fix_free);
3320
3321     pa_xfree(ps);
3322 }
3323
3324 pa_alsa_mapping *pa_alsa_mapping_get(pa_alsa_profile_set *ps, const char *name) {
3325     pa_alsa_mapping *m;
3326
3327     if (!pa_startswith(name, "Mapping "))
3328         return NULL;
3329
3330     name += 8;
3331
3332     if ((m = pa_hashmap_get(ps->mappings, name)))
3333         return m;
3334
3335     m = pa_xnew0(pa_alsa_mapping, 1);
3336     m->profile_set = ps;
3337     m->name = pa_xstrdup(name);
3338     pa_channel_map_init(&m->channel_map);
3339     m->proplist = pa_proplist_new();
3340
3341     pa_hashmap_put(ps->mappings, m->name, m);
3342
3343     return m;
3344 }
3345
3346 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
3347     pa_alsa_profile *p;
3348
3349     if (!pa_startswith(name, "Profile "))
3350         return NULL;
3351
3352     name += 8;
3353
3354     if ((p = pa_hashmap_get(ps->profiles, name)))
3355         return p;
3356
3357     p = pa_xnew0(pa_alsa_profile, 1);
3358     p->profile_set = ps;
3359     p->name = pa_xstrdup(name);
3360
3361     pa_hashmap_put(ps->profiles, p->name, p);
3362
3363     return p;
3364 }
3365
3366 static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) {
3367     pa_alsa_decibel_fix *db_fix;
3368
3369     if (!pa_startswith(name, "DecibelFix "))
3370         return NULL;
3371
3372     name += 11;
3373
3374     if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name)))
3375         return db_fix;
3376
3377     db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
3378     db_fix->profile_set = ps;
3379     db_fix->name = pa_xstrdup(name);
3380
3381     pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix);
3382
3383     return db_fix;
3384 }
3385
3386 static int mapping_parse_device_strings(pa_config_parser_state *state) {
3387     pa_alsa_profile_set *ps;
3388     pa_alsa_mapping *m;
3389
3390     pa_assert(state);
3391
3392     ps = state->userdata;
3393
3394     if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3395         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3396         return -1;
3397     }
3398
3399     pa_xstrfreev(m->device_strings);
3400     if (!(m->device_strings = pa_split_spaces_strv(state->rvalue))) {
3401         pa_log("[%s:%u] Device string list empty of '%s'", state->filename, state->lineno, state->section);
3402         return -1;
3403     }
3404
3405     return 0;
3406 }
3407
3408 static int mapping_parse_channel_map(pa_config_parser_state *state) {
3409     pa_alsa_profile_set *ps;
3410     pa_alsa_mapping *m;
3411
3412     pa_assert(state);
3413
3414     ps = state->userdata;
3415
3416     if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3417         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3418         return -1;
3419     }
3420
3421     if (!(pa_channel_map_parse(&m->channel_map, state->rvalue))) {
3422         pa_log("[%s:%u] Channel map invalid of '%s'", state->filename, state->lineno, state->section);
3423         return -1;
3424     }
3425
3426     return 0;
3427 }
3428
3429 static int mapping_parse_paths(pa_config_parser_state *state) {
3430     pa_alsa_profile_set *ps;
3431     pa_alsa_mapping *m;
3432
3433     pa_assert(state);
3434
3435     ps = state->userdata;
3436
3437     if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3438         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3439         return -1;
3440     }
3441
3442     if (pa_streq(state->lvalue, "paths-input")) {
3443         pa_xstrfreev(m->input_path_names);
3444         m->input_path_names = pa_split_spaces_strv(state->rvalue);
3445     } else {
3446         pa_xstrfreev(m->output_path_names);
3447         m->output_path_names = pa_split_spaces_strv(state->rvalue);
3448     }
3449
3450     return 0;
3451 }
3452
3453 static int mapping_parse_element(pa_config_parser_state *state) {
3454     pa_alsa_profile_set *ps;
3455     pa_alsa_mapping *m;
3456
3457     pa_assert(state);
3458
3459     ps = state->userdata;
3460
3461     if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3462         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3463         return -1;
3464     }
3465
3466     if (pa_streq(state->lvalue, "element-input")) {
3467         pa_xstrfreev(m->input_element);
3468         m->input_element = pa_split_spaces_strv(state->rvalue);
3469     } else {
3470         pa_xstrfreev(m->output_element);
3471         m->output_element = pa_split_spaces_strv(state->rvalue);
3472     }
3473
3474     return 0;
3475 }
3476
3477 static int mapping_parse_direction(pa_config_parser_state *state) {
3478     pa_alsa_profile_set *ps;
3479     pa_alsa_mapping *m;
3480
3481     pa_assert(state);
3482
3483     ps = state->userdata;
3484
3485     if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3486         pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
3487         return -1;
3488     }
3489
3490     if (pa_streq(state->rvalue, "input"))
3491         m->direction = PA_ALSA_DIRECTION_INPUT;
3492     else if (pa_streq(state->rvalue, "output"))
3493         m->direction = PA_ALSA_DIRECTION_OUTPUT;
3494     else if (pa_streq(state->rvalue, "any"))
3495         m->direction = PA_ALSA_DIRECTION_ANY;
3496     else {
3497         pa_log("[%s:%u] Direction %s invalid.", state->filename, state->lineno, state->rvalue);
3498         return -1;
3499     }
3500
3501     return 0;
3502 }
3503
3504 static int mapping_parse_description(pa_config_parser_state *state) {
3505     pa_alsa_profile_set *ps;
3506     pa_alsa_profile *p;
3507     pa_alsa_mapping *m;
3508
3509     pa_assert(state);
3510
3511     ps = state->userdata;
3512
3513     if ((m = pa_alsa_mapping_get(ps, state->section))) {
3514         pa_xfree(m->description);
3515         m->description = pa_xstrdup(state->rvalue);
3516     } else if ((p = profile_get(ps, state->section))) {
3517         pa_xfree(p->description);
3518         p->description = pa_xstrdup(state->rvalue);
3519     } else {
3520         pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
3521         return -1;
3522     }
3523
3524     return 0;
3525 }
3526
3527 static int mapping_parse_priority(pa_config_parser_state *state) {
3528     pa_alsa_profile_set *ps;
3529     pa_alsa_profile *p;
3530     pa_alsa_mapping *m;
3531     uint32_t prio;
3532
3533     pa_assert(state);
3534
3535     ps = state->userdata;
3536
3537     if (pa_atou(state->rvalue, &prio) < 0) {
3538         pa_log("[%s:%u] Priority invalid of '%s'", state->filename, state->lineno, state->section);
3539         return -1;
3540     }
3541
3542     if ((m = pa_alsa_mapping_get(ps, state->section)))
3543         m->priority = prio;
3544     else if ((p = profile_get(ps, state->section)))
3545         p->priority = prio;
3546     else {
3547         pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
3548         return -1;
3549     }
3550
3551     return 0;
3552 }
3553
3554 static int profile_parse_mappings(pa_config_parser_state *state) {
3555     pa_alsa_profile_set *ps;
3556     pa_alsa_profile *p;
3557
3558     pa_assert(state);
3559
3560     ps = state->userdata;
3561
3562     if (!(p = profile_get(ps, state->section))) {
3563         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3564         return -1;
3565     }
3566
3567     if (pa_streq(state->lvalue, "input-mappings")) {
3568         pa_xstrfreev(p->input_mapping_names);
3569         p->input_mapping_names = pa_split_spaces_strv(state->rvalue);
3570     } else {
3571         pa_xstrfreev(p->output_mapping_names);
3572         p->output_mapping_names = pa_split_spaces_strv(state->rvalue);
3573     }
3574
3575     return 0;
3576 }
3577
3578 static int profile_parse_skip_probe(pa_config_parser_state *state) {
3579     pa_alsa_profile_set *ps;
3580     pa_alsa_profile *p;
3581     int b;
3582
3583     pa_assert(state);
3584
3585     ps = state->userdata;
3586
3587     if (!(p = profile_get(ps, state->section))) {
3588         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3589         return -1;
3590     }
3591
3592     if ((b = pa_parse_boolean(state->rvalue)) < 0) {
3593         pa_log("[%s:%u] Skip probe invalid of '%s'", state->filename, state->lineno, state->section);
3594         return -1;
3595     }
3596
3597     p->supported = b;
3598
3599     return 0;
3600 }
3601
3602 static int decibel_fix_parse_db_values(pa_config_parser_state *state) {
3603     pa_alsa_profile_set *ps;
3604     pa_alsa_decibel_fix *db_fix;
3605     char **items;
3606     char *item;
3607     long *db_values;
3608     unsigned n = 8; /* Current size of the db_values table. */
3609     unsigned min_step = 0;
3610     unsigned max_step = 0;
3611     unsigned i = 0; /* Index to the items table. */
3612     unsigned prev_step = 0;
3613     double prev_db = 0;
3614
3615     pa_assert(state);
3616
3617     ps = state->userdata;
3618
3619     if (!(db_fix = decibel_fix_get(ps, state->section))) {
3620         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3621         return -1;
3622     }
3623
3624     if (!(items = pa_split_spaces_strv(state->rvalue))) {
3625         pa_log("[%s:%u] Value missing", state->filename, state->lineno);
3626         return -1;
3627     }
3628
3629     db_values = pa_xnew(long, n);
3630
3631     while ((item = items[i++])) {
3632         char *s = item; /* Step value string. */
3633         char *d = item; /* dB value string. */
3634         uint32_t step;
3635         double db;
3636
3637         /* Move d forward until it points to a colon or to the end of the item. */
3638         for (; *d && *d != ':'; ++d);
3639
3640         if (d == s) {
3641             /* item started with colon. */
3642             pa_log("[%s:%u] No step value found in %s", state->filename, state->lineno, item);
3643             goto fail;
3644         }
3645
3646         if (!*d || !*(d + 1)) {
3647             /* No colon found, or it was the last character in item. */
3648             pa_log("[%s:%u] No dB value found in %s", state->filename, state->lineno, item);
3649             goto fail;
3650         }
3651
3652         /* pa_atou() needs a null-terminating string. Let's replace the colon
3653          * with a zero byte. */
3654         *d++ = '\0';
3655
3656         if (pa_atou(s, &step) < 0) {
3657             pa_log("[%s:%u] Invalid step value: %s", state->filename, state->lineno, s);
3658             goto fail;
3659         }
3660
3661         if (pa_atod(d, &db) < 0) {
3662             pa_log("[%s:%u] Invalid dB value: %s", state->filename, state->lineno, d);
3663             goto fail;
3664         }
3665
3666         if (step <= prev_step && i != 1) {
3667             pa_log("[%s:%u] Step value %u not greater than the previous value %u", state->filename, state->lineno, step, prev_step);
3668             goto fail;
3669         }
3670
3671         if (db < prev_db && i != 1) {
3672             pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", state->filename, state->lineno, db, prev_db);
3673             goto fail;
3674         }
3675
3676         if (i == 1) {
3677             min_step = step;
3678             db_values[0] = (long) (db * 100.0);
3679             prev_step = step;
3680             prev_db = db;
3681         } else {
3682             /* Interpolate linearly. */
3683             double db_increment = (db - prev_db) / (step - prev_step);
3684
3685             for (; prev_step < step; ++prev_step, prev_db += db_increment) {
3686
3687                 /* Reallocate the db_values table if it's about to overflow. */
3688                 if (prev_step + 1 - min_step == n) {
3689                     n *= 2;
3690                     db_values = pa_xrenew(long, db_values, n);
3691                 }
3692
3693                 db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
3694             }
3695         }
3696
3697         max_step = step;
3698     }
3699
3700     db_fix->min_step = min_step;
3701     db_fix->max_step = max_step;
3702     pa_xfree(db_fix->db_values);
3703     db_fix->db_values = db_values;
3704
3705     pa_xstrfreev(items);
3706
3707     return 0;
3708
3709 fail:
3710     pa_xstrfreev(items);
3711     pa_xfree(db_values);
3712
3713     return -1;
3714 }
3715
3716 static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
3717                                 pa_alsa_direction_t direction) {
3718
3719     pa_alsa_path *p;
3720     void *state;
3721     snd_pcm_t *pcm_handle;
3722     pa_alsa_path_set *ps;
3723     snd_mixer_t *mixer_handle;
3724     snd_hctl_t *hctl_handle;
3725
3726     if (direction == PA_ALSA_DIRECTION_OUTPUT) {
3727         if (m->output_path_set)
3728             return; /* Already probed */
3729         m->output_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3730         pcm_handle = m->output_pcm;
3731     } else {
3732         if (m->input_path_set)
3733             return; /* Already probed */
3734         m->input_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3735         pcm_handle = m->input_pcm;
3736     }
3737
3738     if (!ps)
3739         return; /* No paths */
3740
3741     pa_assert(pcm_handle);
3742
3743     mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL, &hctl_handle);
3744     if (!mixer_handle || !hctl_handle) {
3745          /* Cannot open mixer, remove all entries */
3746         pa_hashmap_remove_all(ps->paths, NULL);
3747         return;
3748     }
3749
3750
3751     PA_HASHMAP_FOREACH(p, ps->paths, state) {
3752         if (pa_alsa_path_probe(p, mixer_handle, hctl_handle, m->profile_set->ignore_dB) < 0) {
3753             pa_hashmap_remove(ps->paths, p);
3754         }
3755     }
3756
3757     path_set_condense(ps, mixer_handle);
3758     path_set_make_paths_unique(ps);
3759
3760     if (mixer_handle)
3761         snd_mixer_close(mixer_handle);
3762
3763     pa_log_debug("Available mixer paths (after tidying):");
3764     pa_alsa_path_set_dump(ps);
3765 }
3766
3767 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
3768
3769     static const struct description_map well_known_descriptions[] = {
3770         { "analog-mono",            N_("Analog Mono") },
3771         { "analog-stereo",          N_("Analog Stereo") },
3772         { "analog-surround-21",     N_("Analog Surround 2.1") },
3773         { "analog-surround-30",     N_("Analog Surround 3.0") },
3774         { "analog-surround-31",     N_("Analog Surround 3.1") },
3775         { "analog-surround-40",     N_("Analog Surround 4.0") },
3776         { "analog-surround-41",     N_("Analog Surround 4.1") },
3777         { "analog-surround-50",     N_("Analog Surround 5.0") },
3778         { "analog-surround-51",     N_("Analog Surround 5.1") },
3779         { "analog-surround-61",     N_("Analog Surround 6.0") },
3780         { "analog-surround-61",     N_("Analog Surround 6.1") },
3781         { "analog-surround-70",     N_("Analog Surround 7.0") },
3782         { "analog-surround-71",     N_("Analog Surround 7.1") },
3783         { "analog-4-channel-input", N_("Analog 4-channel Input") },
3784         { "iec958-stereo",          N_("Digital Stereo (IEC958)") },
3785         { "iec958-passthrough",     N_("Digital Passthrough  (IEC958)") },
3786         { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3787         { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3788         { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
3789         { "hdmi-stereo",            N_("Digital Stereo (HDMI)") },
3790         { "hdmi-surround-51",       N_("Digital Surround 5.1 (HDMI)") }
3791     };
3792
3793     pa_assert(m);
3794
3795     if (!pa_channel_map_valid(&m->channel_map)) {
3796         pa_log("Mapping %s is missing channel map.", m->name);
3797         return -1;
3798     }
3799
3800     if (!m->device_strings) {
3801         pa_log("Mapping %s is missing device strings.", m->name);
3802         return -1;
3803     }
3804
3805     if ((m->input_path_names && m->input_element) ||
3806         (m->output_path_names && m->output_element)) {
3807         pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
3808         return -1;
3809     }
3810
3811     if (!m->description)
3812         m->description = pa_xstrdup(lookup_description(m->name,
3813                                                        well_known_descriptions,
3814                                                        PA_ELEMENTSOF(well_known_descriptions)));
3815
3816     if (!m->description)
3817         m->description = pa_xstrdup(m->name);
3818
3819     if (bonus) {
3820         if (pa_channel_map_equal(&m->channel_map, bonus))
3821             m->priority += 50;
3822         else if (m->channel_map.channels == bonus->channels)
3823             m->priority += 30;
3824     }
3825
3826     return 0;
3827 }
3828
3829 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
3830     char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
3831
3832     pa_assert(m);
3833
3834     pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3835                  m->name,
3836                  pa_strnull(m->description),
3837                  m->priority,
3838                  pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
3839                  pa_yes_no(m->supported),
3840                  m->direction);
3841 }
3842
3843 static void profile_set_add_auto_pair(
3844         pa_alsa_profile_set *ps,
3845         pa_alsa_mapping *m, /* output */
3846         pa_alsa_mapping *n  /* input */) {
3847
3848     char *name;
3849     pa_alsa_profile *p;
3850
3851     pa_assert(ps);
3852     pa_assert(m || n);
3853
3854     if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
3855         return;
3856
3857     if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
3858         return;
3859
3860     if (m && n)
3861         name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
3862     else if (m)
3863         name = pa_sprintf_malloc("output:%s", m->name);
3864     else
3865         name = pa_sprintf_malloc("input:%s", n->name);
3866
3867     if (pa_hashmap_get(ps->profiles, name)) {
3868         pa_xfree(name);
3869         return;
3870     }
3871
3872     p = pa_xnew0(pa_alsa_profile, 1);
3873     p->profile_set = ps;
3874     p->name = name;
3875
3876     if (m) {
3877         p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3878         pa_idxset_put(p->output_mappings, m, NULL);
3879         p->priority += m->priority * 100;
3880     }
3881
3882     if (n) {
3883         p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3884         pa_idxset_put(p->input_mappings, n, NULL);
3885         p->priority += n->priority;
3886     }
3887
3888     pa_hashmap_put(ps->profiles, p->name, p);
3889 }
3890
3891 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
3892     pa_alsa_mapping *m, *n;
3893     void *m_state, *n_state;
3894
3895     pa_assert(ps);
3896
3897     /* The order is important here:
3898        1) try single inputs and outputs before trying their
3899           combination, because if the half-duplex test failed, we don't have
3900           to try full duplex.
3901        2) try the output right before the input combinations with
3902           that output, because then the output_pcm is not closed between tests.
3903     */
3904     PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3905         profile_set_add_auto_pair(ps, NULL, n);
3906
3907     PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
3908         profile_set_add_auto_pair(ps, m, NULL);
3909
3910         PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3911             profile_set_add_auto_pair(ps, m, n);
3912     }
3913
3914 }
3915
3916 static int profile_verify(pa_alsa_profile *p) {
3917
3918     static const struct description_map well_known_descriptions[] = {
3919         { "output:analog-mono+input:analog-mono",     N_("Analog Mono Duplex") },
3920         { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3921         { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3922         { "off",                                      N_("Off") }
3923     };
3924
3925     pa_assert(p);
3926
3927     /* Replace the output mapping names by the actual mappings */
3928     if (p->output_mapping_names) {
3929         char **name;
3930
3931         pa_assert(!p->output_mappings);
3932         p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3933
3934         for (name = p->output_mapping_names; *name; name++) {
3935             pa_alsa_mapping *m;
3936             char **in;
3937             pa_bool_t duplicate = FALSE;
3938
3939             for (in = name + 1; *in; in++)
3940                 if (pa_streq(*name, *in)) {
3941                     duplicate = TRUE;
3942                     break;
3943                 }
3944
3945             if (duplicate)
3946                 continue;
3947
3948             if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
3949                 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
3950                 return -1;
3951             }
3952
3953             pa_idxset_put(p->output_mappings, m, NULL);
3954
3955             if (p->supported)
3956                 m->supported++;
3957         }
3958
3959         pa_xstrfreev(p->output_mapping_names);
3960         p->output_mapping_names = NULL;
3961     }
3962
3963     /* Replace the input mapping names by the actual mappings */
3964     if (p->input_mapping_names) {
3965         char **name;
3966
3967         pa_assert(!p->input_mappings);
3968         p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3969
3970         for (name = p->input_mapping_names; *name; name++) {
3971             pa_alsa_mapping *m;
3972             char **in;
3973             pa_bool_t duplicate = FALSE;
3974
3975             for (in = name + 1; *in; in++)
3976                 if (pa_streq(*name, *in)) {
3977                     duplicate = TRUE;
3978                     break;
3979                 }
3980
3981             if (duplicate)
3982                 continue;
3983
3984             if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
3985                 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
3986                 return -1;
3987             }
3988
3989             pa_idxset_put(p->input_mappings, m, NULL);
3990
3991             if (p->supported)
3992                 m->supported++;
3993         }
3994
3995         pa_xstrfreev(p->input_mapping_names);
3996         p->input_mapping_names = NULL;
3997     }
3998
3999     if (!p->input_mappings && !p->output_mappings) {
4000         pa_log("Profile '%s' lacks mappings.", p->name);
4001         return -1;
4002     }
4003
4004     if (!p->description)
4005         p->description = pa_xstrdup(lookup_description(p->name,
4006                                                        well_known_descriptions,
4007                                                        PA_ELEMENTSOF(well_known_descriptions)));
4008
4009     if (!p->description) {
4010         pa_strbuf *sb;
4011         uint32_t idx;
4012         pa_alsa_mapping *m;
4013
4014         sb = pa_strbuf_new();
4015
4016         if (p->output_mappings)
4017             PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4018                 if (!pa_strbuf_isempty(sb))
4019                     pa_strbuf_puts(sb, " + ");
4020
4021                 pa_strbuf_printf(sb, _("%s Output"), m->description);
4022             }
4023
4024         if (p->input_mappings)
4025             PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4026                 if (!pa_strbuf_isempty(sb))
4027                     pa_strbuf_puts(sb, " + ");
4028
4029                 pa_strbuf_printf(sb, _("%s Input"), m->description);
4030             }
4031
4032         p->description = pa_strbuf_tostring_free(sb);
4033     }
4034
4035     return 0;
4036 }
4037
4038 void pa_alsa_profile_dump(pa_alsa_profile *p) {
4039     uint32_t idx;
4040     pa_alsa_mapping *m;
4041     pa_assert(p);
4042
4043     pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4044                  p->name,
4045                  pa_strnull(p->description),
4046                  p->priority,
4047                  pa_yes_no(p->supported),
4048                  p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
4049                  p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
4050
4051     if (p->input_mappings)
4052         PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4053             pa_log_debug("Input %s", m->name);
4054
4055     if (p->output_mappings)
4056         PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4057             pa_log_debug("Output %s", m->name);
4058 }
4059
4060 static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
4061     pa_assert(db_fix);
4062
4063     /* Check that the dB mapping has been configured. Since "db-values" is
4064      * currently the only option in the DecibelFix section, and decibel fix
4065      * objects don't get created if a DecibelFix section is empty, this is
4066      * actually a redundant check. Having this may prevent future bugs,
4067      * however. */
4068     if (!db_fix->db_values) {
4069         pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
4070         return -1;
4071     }
4072
4073     return 0;
4074 }
4075
4076 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
4077     char *db_values = NULL;
4078
4079     pa_assert(db_fix);
4080
4081     if (db_fix->db_values) {
4082         pa_strbuf *buf;
4083         unsigned long i, nsteps;
4084
4085         pa_assert(db_fix->min_step <= db_fix->max_step);
4086         nsteps = db_fix->max_step - db_fix->min_step + 1;
4087
4088         buf = pa_strbuf_new();
4089         for (i = 0; i < nsteps; ++i)
4090             pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
4091
4092         db_values = pa_strbuf_tostring_free(buf);
4093     }
4094
4095     pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4096                  db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
4097
4098     pa_xfree(db_values);
4099 }
4100
4101 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
4102     pa_alsa_profile_set *ps;
4103     pa_alsa_profile *p;
4104     pa_alsa_mapping *m;
4105     pa_alsa_decibel_fix *db_fix;
4106     char *fn;
4107     int r;
4108     void *state;
4109
4110     static pa_config_item items[] = {
4111         /* [General] */
4112         { "auto-profiles",          pa_config_parse_bool,         NULL, "General" },
4113
4114         /* [Mapping ...] */
4115         { "device-strings",         mapping_parse_device_strings, NULL, NULL },
4116         { "channel-map",            mapping_parse_channel_map,    NULL, NULL },
4117         { "paths-input",            mapping_parse_paths,          NULL, NULL },
4118         { "paths-output",           mapping_parse_paths,          NULL, NULL },
4119         { "element-input",          mapping_parse_element,        NULL, NULL },
4120         { "element-output",         mapping_parse_element,        NULL, NULL },
4121         { "direction",              mapping_parse_direction,      NULL, NULL },
4122
4123         /* Shared by [Mapping ...] and [Profile ...] */
4124         { "description",            mapping_parse_description,    NULL, NULL },
4125         { "priority",               mapping_parse_priority,       NULL, NULL },
4126
4127         /* [Profile ...] */
4128         { "input-mappings",         profile_parse_mappings,       NULL, NULL },
4129         { "output-mappings",        profile_parse_mappings,       NULL, NULL },
4130         { "skip-probe",             profile_parse_skip_probe,     NULL, NULL },
4131
4132         /* [DecibelFix ...] */
4133         { "db-values",              decibel_fix_parse_db_values,  NULL, NULL },
4134         { NULL, NULL, NULL, NULL }
4135     };
4136
4137     ps = pa_xnew0(pa_alsa_profile_set, 1);
4138     ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4139     ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4140     ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4141     ps->input_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4142     ps->output_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4143
4144     items[0].data = &ps->auto_profiles;
4145
4146     if (!fname)
4147         fname = "default.conf";
4148
4149     fn = pa_maybe_prefix_path(fname,
4150                               pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
4151                               PA_ALSA_PROFILE_SETS_DIR);
4152
4153     r = pa_config_parse(fn, NULL, items, NULL, ps);
4154     pa_xfree(fn);
4155
4156     if (r < 0)
4157         goto fail;
4158
4159     PA_HASHMAP_FOREACH(m, ps->mappings, state)
4160         if (mapping_verify(m, bonus) < 0)
4161             goto fail;
4162
4163     if (ps->auto_profiles)
4164         profile_set_add_auto(ps);
4165
4166     PA_HASHMAP_FOREACH(p, ps->profiles, state)
4167         if (profile_verify(p) < 0)
4168             goto fail;
4169
4170     PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4171         if (decibel_fix_verify(db_fix) < 0)
4172             goto fail;
4173
4174     return ps;
4175
4176 fail:
4177     pa_alsa_profile_set_free(ps);
4178     return NULL;
4179 }
4180
4181 static void profile_finalize_probing(pa_alsa_profile *to_be_finalized, pa_alsa_profile *next) {
4182     pa_alsa_mapping *m;
4183     uint32_t idx;
4184
4185     if (!to_be_finalized)
4186         return;
4187
4188     if (to_be_finalized->output_mappings)
4189         PA_IDXSET_FOREACH(m, to_be_finalized->output_mappings, idx) {
4190
4191             if (!m->output_pcm)
4192                 continue;
4193
4194             if (to_be_finalized->supported)
4195                 m->supported++;
4196
4197             /* If this mapping is also in the next profile, we won't close the
4198              * pcm handle here, because it would get immediately reopened
4199              * anyway. */
4200             if (next && next->output_mappings && pa_idxset_get_by_data(next->output_mappings, m, NULL))
4201                 continue;
4202
4203             snd_pcm_close(m->output_pcm);
4204             m->output_pcm = NULL;
4205         }
4206
4207     if (to_be_finalized->input_mappings)
4208         PA_IDXSET_FOREACH(m, to_be_finalized->input_mappings, idx) {
4209
4210             if (!m->input_pcm)
4211                 continue;
4212
4213             if (to_be_finalized->supported)
4214                 m->supported++;
4215
4216             /* If this mapping is also in the next profile, we won't close the
4217              * pcm handle here, because it would get immediately reopened
4218              * anyway. */
4219             if (next && next->input_mappings && pa_idxset_get_by_data(next->input_mappings, m, NULL))
4220                 continue;
4221
4222             snd_pcm_close(m->input_pcm);
4223             m->input_pcm = NULL;
4224         }
4225 }
4226
4227 static snd_pcm_t* mapping_open_pcm(pa_alsa_mapping *m,
4228                                    const pa_sample_spec *ss,
4229                                    const char *dev_id,
4230                                    int mode,
4231                                    unsigned default_n_fragments,
4232                                    unsigned default_fragment_size_msec) {
4233
4234     pa_sample_spec try_ss = *ss;
4235     pa_channel_map try_map = m->channel_map;
4236     snd_pcm_uframes_t try_period_size, try_buffer_size;
4237
4238     try_ss.channels = try_map.channels;
4239
4240     try_period_size =
4241         pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
4242         pa_frame_size(&try_ss);
4243     try_buffer_size = default_n_fragments * try_period_size;
4244
4245     return pa_alsa_open_by_template(
4246                               m->device_strings, dev_id, NULL, &try_ss,
4247                               &try_map, mode, &try_period_size,
4248                               &try_buffer_size, 0, NULL, NULL, TRUE);
4249 }
4250
4251 static void paths_drop_unsupported(pa_hashmap* h) {
4252
4253     void* state = NULL;
4254     const void* key;
4255     pa_alsa_path* p;
4256
4257     pa_assert(h);
4258     p = pa_hashmap_iterate(h, &state, &key);
4259     while (p) {
4260         if (p->supported <= 0) {
4261             pa_hashmap_remove(h, key);
4262             pa_alsa_path_free(p);
4263         }
4264         p = pa_hashmap_iterate(h, &state, &key);
4265     }
4266 }
4267
4268 void pa_alsa_profile_set_probe(
4269         pa_alsa_profile_set *ps,
4270         const char *dev_id,
4271         const pa_sample_spec *ss,
4272         unsigned default_n_fragments,
4273         unsigned default_fragment_size_msec) {
4274
4275     void *state;
4276     pa_alsa_profile *p, *last = NULL;
4277     pa_alsa_mapping *m;
4278     pa_hashmap *broken_inputs, *broken_outputs;
4279
4280     pa_assert(ps);
4281     pa_assert(dev_id);
4282     pa_assert(ss);
4283
4284     if (ps->probed)
4285         return;
4286
4287     broken_inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4288     broken_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4289
4290     PA_HASHMAP_FOREACH(p, ps->profiles, state) {
4291         uint32_t idx;
4292
4293         /* Skip if this is already marked that it is supported (i.e. from the config file) */
4294         if (!p->supported) {
4295
4296             profile_finalize_probing(last, p);
4297             p->supported = TRUE;
4298
4299             if (p->output_mappings) {
4300                 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4301                     if (pa_hashmap_get(broken_outputs, m) == m) {
4302                         pa_log_debug("Skipping profile %s - will not be able to open output:%s", p->name, m->name);
4303                         p->supported = FALSE;
4304                         break;
4305                     }
4306                 }
4307             }
4308
4309             if (p->input_mappings && p->supported) {
4310                 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4311                     if (pa_hashmap_get(broken_inputs, m) == m) {
4312                         pa_log_debug("Skipping profile %s - will not be able to open input:%s", p->name, m->name);
4313                         p->supported = FALSE;
4314                         break;
4315                     }
4316                 }
4317             }
4318
4319             if (p->supported)
4320                 pa_log_debug("Looking at profile %s", p->name);
4321
4322             /* Check if we can open all new ones */
4323             if (p->output_mappings && p->supported)
4324                 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4325
4326                     if (m->output_pcm)
4327                         continue;
4328
4329                     pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
4330                     if (!(m->output_pcm = mapping_open_pcm(m, ss, dev_id,
4331                                                            SND_PCM_STREAM_PLAYBACK,
4332                                                            default_n_fragments,
4333                                                            default_fragment_size_msec))) {
4334                         p->supported = FALSE;
4335                         if (pa_idxset_size(p->output_mappings) == 1 &&
4336                             ((!p->input_mappings) || pa_idxset_size(p->input_mappings) == 0)) {
4337                             pa_log_debug("Caching failure to open output:%s", m->name);
4338                             pa_hashmap_put(broken_outputs, m, m);
4339                         }
4340                         break;
4341                     }
4342                 }
4343
4344             if (p->input_mappings && p->supported)
4345                 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4346
4347                     if (m->input_pcm)
4348                         continue;
4349
4350                     pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
4351                     if (!(m->input_pcm = mapping_open_pcm(m, ss, dev_id,
4352                                                           SND_PCM_STREAM_CAPTURE,
4353                                                           default_n_fragments,
4354                                                           default_fragment_size_msec))) {
4355                         p->supported = FALSE;
4356                         if (pa_idxset_size(p->input_mappings) == 1 &&
4357                             ((!p->output_mappings) || pa_idxset_size(p->output_mappings) == 0)) {
4358                             pa_log_debug("Caching failure to open input:%s", m->name);
4359                             pa_hashmap_put(broken_inputs, m, m);
4360                         }
4361                         break;
4362                     }
4363                 }
4364
4365             last = p;
4366
4367             if (!p->supported)
4368                 continue;
4369         }
4370
4371         pa_log_debug("Profile %s supported.", p->name);
4372
4373         if (p->output_mappings)
4374             PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4375                 if (m->output_pcm)
4376                     mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT);
4377
4378         if (p->input_mappings)
4379             PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4380                 if (m->input_pcm)
4381                     mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT);
4382     }
4383
4384     /* Clean up */
4385     profile_finalize_probing(last, NULL);
4386
4387     pa_alsa_profile_set_drop_unsupported(ps);
4388
4389     paths_drop_unsupported(ps->input_paths);
4390     paths_drop_unsupported(ps->output_paths);
4391     pa_hashmap_free(broken_inputs, NULL);
4392     pa_hashmap_free(broken_outputs, NULL);
4393
4394     ps->probed = TRUE;
4395 }
4396
4397 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
4398     pa_alsa_profile *p;
4399     pa_alsa_mapping *m;
4400     pa_alsa_decibel_fix *db_fix;
4401     void *state;
4402
4403     pa_assert(ps);
4404
4405     pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4406                  (void*)
4407                  ps,
4408                  pa_yes_no(ps->auto_profiles),
4409                  pa_yes_no(ps->probed),
4410                  pa_hashmap_size(ps->mappings),
4411                  pa_hashmap_size(ps->profiles),
4412                  pa_hashmap_size(ps->decibel_fixes));
4413
4414     PA_HASHMAP_FOREACH(m, ps->mappings, state)
4415         pa_alsa_mapping_dump(m);
4416
4417     PA_HASHMAP_FOREACH(p, ps->profiles, state)
4418         pa_alsa_profile_dump(p);
4419
4420     PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4421         pa_alsa_decibel_fix_dump(db_fix);
4422 }
4423
4424 void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set *ps) {
4425     pa_alsa_profile *p;
4426     pa_alsa_mapping *m;
4427     void *state;
4428
4429     PA_HASHMAP_FOREACH(p, ps->profiles, state) {
4430         if (!p->supported) {
4431             pa_hashmap_remove(ps->profiles, p->name);
4432             profile_free(p);
4433         }
4434     }
4435
4436     PA_HASHMAP_FOREACH(m, ps->mappings, state) {
4437         if (m->supported <= 0) {
4438             pa_hashmap_remove(ps->mappings, m->name);
4439             mapping_free(m);
4440         }
4441     }
4442 }
4443
4444 static pa_device_port* device_port_alsa_init(pa_hashmap *ports,
4445     const char* name,
4446     const char* description,
4447     pa_alsa_path *path,
4448     pa_alsa_setting *setting,
4449     pa_card_profile *cp,
4450     pa_hashmap *extra,
4451     pa_core *core) {
4452
4453     pa_device_port *p;
4454
4455     pa_assert(path);
4456
4457     p = pa_hashmap_get(ports, name);
4458
4459     if (!p) {
4460         pa_alsa_port_data *data;
4461
4462         p = pa_device_port_new(core, name, description, sizeof(pa_alsa_port_data));
4463         pa_assert(p);
4464         pa_hashmap_put(ports, p->name, p);
4465         pa_proplist_update(p->proplist, PA_UPDATE_REPLACE, path->proplist);
4466
4467         data = PA_DEVICE_PORT_DATA(p);
4468         data->path = path;
4469         data->setting = setting;
4470         path->port = p;
4471     }
4472
4473     p->is_input |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_INPUT;
4474     p->is_output |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_OUTPUT;
4475
4476     if (cp)
4477         pa_hashmap_put(p->profiles, cp->name, cp);
4478
4479     if (extra) {
4480         pa_hashmap_put(extra, p->name, p);
4481         pa_device_port_ref(p);
4482     }
4483
4484     return p;
4485 }
4486
4487 void pa_alsa_path_set_add_ports(
4488         pa_alsa_path_set *ps,
4489         pa_card_profile *cp,
4490         pa_hashmap *ports,
4491         pa_hashmap *extra,
4492         pa_core *core) {
4493
4494     pa_alsa_path *path;
4495     void *state;
4496
4497     pa_assert(ports);
4498
4499     if (!ps)
4500         return;
4501
4502     PA_HASHMAP_FOREACH(path, ps->paths, state) {
4503         if (!path->settings || !path->settings->next) {
4504             /* If there is no or just one setting we only need a
4505              * single entry */
4506             pa_device_port *port = device_port_alsa_init(ports, path->name,
4507                 path->description, path, path->settings, cp, extra, core);
4508             port->priority = path->priority * 100;
4509
4510         } else {
4511             pa_alsa_setting *s;
4512             PA_LLIST_FOREACH(s, path->settings) {
4513                 pa_device_port *port;
4514                 char *n, *d;
4515
4516                 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
4517
4518                 if (s->description[0])
4519                     d = pa_sprintf_malloc("%s / %s", path->description, s->description);
4520                 else
4521                     d = pa_xstrdup(path->description);
4522
4523                 port = device_port_alsa_init(ports, n, d, path, s, cp, extra, core);
4524                 port->priority = path->priority * 100 + s->priority;
4525
4526                 pa_xfree(n);
4527                 pa_xfree(d);
4528             }
4529         }
4530     }
4531 }
4532
4533 void pa_alsa_add_ports(void *sink_or_source_new_data, pa_alsa_path_set *ps, pa_card *card) {
4534     pa_hashmap *ports;
4535
4536     pa_assert(sink_or_source_new_data);
4537     pa_assert(ps);
4538
4539     if (ps->direction == PA_ALSA_DIRECTION_OUTPUT)
4540         ports = ((pa_sink_new_data *) sink_or_source_new_data)->ports;
4541     else
4542         ports = ((pa_source_new_data *) sink_or_source_new_data)->ports;
4543
4544     if (ps->paths && pa_hashmap_size(ps->paths) > 0) {
4545         pa_assert(card);
4546         pa_alsa_path_set_add_ports(ps, NULL, card->ports, ports, card->core);
4547     }
4548
4549     pa_log_debug("Added %u ports", pa_hashmap_size(ports));
4550 }