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