device-port: Create the profiles hashmap at initialization.
[profile/ivi/pulseaudio-panda.git] / src / modules / alsa / alsa-mixer.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2004-2009 Lennart Poettering
5   Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7   PulseAudio is free software; you can redistribute it and/or modify
8   it under the terms of the GNU Lesser General Public License as published
9   by the Free Software Foundation; either version 2.1 of the License,
10   or (at your option) any later version.
11
12   PulseAudio is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with PulseAudio; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20   USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <sys/types.h>
28 #include <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 + 1][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 |= (req != PA_ALSA_REQUIRED_IGNORE);
2003         }
2004         if (o) {
2005             o->required_any = req;
2006             o->element->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
2007         }
2008         if (j) {
2009             j->required_any = req;
2010             j->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
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%" PRIx64 ", mask b: 0x%" PRIx64 ", 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                        (ja->state_plugged == jb->state_plugged) &&
3188                        (ja->state_unplugged == jb->state_unplugged)) {
3189                         exists = TRUE;
3190                         break;
3191                     }
3192                 }
3193
3194                 if (!exists) {
3195                     is_subset = FALSE;
3196                     break;
3197                 }
3198             }
3199
3200             /* Compare the elements of each set... */
3201             pa_assert_se(ea = p->elements);
3202             pa_assert_se(eb = p2->elements);
3203
3204             while (is_subset) {
3205                 if (pa_streq(ea->alsa_name, eb->alsa_name)) {
3206                     if (element_is_subset(ea, eb, m)) {
3207                         ea = ea->next;
3208                         eb = eb->next;
3209                         if ((ea && !eb) || (!ea && eb))
3210                             is_subset = FALSE;
3211                         else if (!ea && !eb)
3212                             break;
3213                     } else
3214                         is_subset = FALSE;
3215
3216                 } else
3217                     is_subset = FALSE;
3218             }
3219
3220             if (is_subset) {
3221                 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p->name, p2->name);
3222                 pa_hashmap_remove(ps->paths, p);
3223                 break;
3224             }
3225         }
3226     }
3227 }
3228
3229 static pa_alsa_path* path_set_find_path_by_name(pa_alsa_path_set *ps, const char* name, pa_alsa_path *ignore)
3230 {
3231     pa_alsa_path* p;
3232     void *state;
3233
3234     PA_HASHMAP_FOREACH(p, ps->paths, state)
3235         if (p != ignore && pa_streq(p->name, name))
3236             return p;
3237     return NULL;
3238 }
3239
3240 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
3241     pa_alsa_path *p, *q;
3242     void *state, *state2;
3243
3244     PA_HASHMAP_FOREACH(p, ps->paths, state) {
3245         unsigned i;
3246         char *m;
3247
3248         q = path_set_find_path_by_name(ps, p->name, p);
3249
3250         if (!q)
3251             continue;
3252
3253         m = pa_xstrdup(p->name);
3254
3255         /* OK, this name is not unique, hence let's rename */
3256         i = 1;
3257         PA_HASHMAP_FOREACH(q, ps->paths, state2) {
3258             char *nn, *nd;
3259
3260             if (!pa_streq(q->name, m))
3261                 continue;
3262
3263             nn = pa_sprintf_malloc("%s-%u", m, i);
3264             pa_xfree(q->name);
3265             q->name = nn;
3266
3267             nd = pa_sprintf_malloc("%s %u", q->description, i);
3268             pa_xfree(q->description);
3269             q->description = nd;
3270
3271             i++;
3272         }
3273
3274         pa_xfree(m);
3275     }
3276 }
3277
3278 static void mapping_free(pa_alsa_mapping *m) {
3279     pa_assert(m);
3280
3281     pa_xfree(m->name);
3282     pa_xfree(m->description);
3283
3284     pa_xstrfreev(m->device_strings);
3285     pa_xstrfreev(m->input_path_names);
3286     pa_xstrfreev(m->output_path_names);
3287     pa_xstrfreev(m->input_element);
3288     pa_xstrfreev(m->output_element);
3289     if (m->input_path_set)
3290         pa_alsa_path_set_free(m->input_path_set);
3291     if (m->output_path_set)
3292         pa_alsa_path_set_free(m->output_path_set);
3293
3294     pa_assert(!m->input_pcm);
3295     pa_assert(!m->output_pcm);
3296
3297     pa_xfree(m);
3298 }
3299
3300 static void profile_free(pa_alsa_profile *p) {
3301     pa_assert(p);
3302
3303     pa_xfree(p->name);
3304     pa_xfree(p->description);
3305
3306     pa_xstrfreev(p->input_mapping_names);
3307     pa_xstrfreev(p->output_mapping_names);
3308
3309     if (p->input_mappings)
3310         pa_idxset_free(p->input_mappings, NULL, NULL);
3311
3312     if (p->output_mappings)
3313         pa_idxset_free(p->output_mappings, NULL, NULL);
3314
3315     pa_xfree(p);
3316 }
3317
3318 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
3319     pa_assert(ps);
3320
3321     if (ps->input_paths) {
3322         pa_alsa_path *p;
3323
3324         while ((p = pa_hashmap_steal_first(ps->input_paths)))
3325             pa_alsa_path_free(p);
3326
3327         pa_hashmap_free(ps->input_paths, NULL, NULL);
3328     }
3329
3330     if (ps->output_paths) {
3331         pa_alsa_path *p;
3332
3333         while ((p = pa_hashmap_steal_first(ps->output_paths)))
3334             pa_alsa_path_free(p);
3335
3336         pa_hashmap_free(ps->output_paths, NULL, NULL);
3337     }
3338
3339     if (ps->profiles) {
3340         pa_alsa_profile *p;
3341
3342         while ((p = pa_hashmap_steal_first(ps->profiles)))
3343             profile_free(p);
3344
3345         pa_hashmap_free(ps->profiles, NULL, NULL);
3346     }
3347
3348     if (ps->mappings) {
3349         pa_alsa_mapping *m;
3350
3351         while ((m = pa_hashmap_steal_first(ps->mappings)))
3352             mapping_free(m);
3353
3354         pa_hashmap_free(ps->mappings, NULL, NULL);
3355     }
3356
3357     if (ps->decibel_fixes) {
3358         pa_alsa_decibel_fix *db_fix;
3359
3360         while ((db_fix = pa_hashmap_steal_first(ps->decibel_fixes)))
3361             decibel_fix_free(db_fix);
3362
3363         pa_hashmap_free(ps->decibel_fixes, NULL, NULL);
3364     }
3365
3366     pa_xfree(ps);
3367 }
3368
3369 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
3370     pa_alsa_mapping *m;
3371
3372     if (!pa_startswith(name, "Mapping "))
3373         return NULL;
3374
3375     name += 8;
3376
3377     if ((m = pa_hashmap_get(ps->mappings, name)))
3378         return m;
3379
3380     m = pa_xnew0(pa_alsa_mapping, 1);
3381     m->profile_set = ps;
3382     m->name = pa_xstrdup(name);
3383     pa_channel_map_init(&m->channel_map);
3384
3385     pa_hashmap_put(ps->mappings, m->name, m);
3386
3387     return m;
3388 }
3389
3390 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
3391     pa_alsa_profile *p;
3392
3393     if (!pa_startswith(name, "Profile "))
3394         return NULL;
3395
3396     name += 8;
3397
3398     if ((p = pa_hashmap_get(ps->profiles, name)))
3399         return p;
3400
3401     p = pa_xnew0(pa_alsa_profile, 1);
3402     p->profile_set = ps;
3403     p->name = pa_xstrdup(name);
3404
3405     pa_hashmap_put(ps->profiles, p->name, p);
3406
3407     return p;
3408 }
3409
3410 static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) {
3411     pa_alsa_decibel_fix *db_fix;
3412
3413     if (!pa_startswith(name, "DecibelFix "))
3414         return NULL;
3415
3416     name += 11;
3417
3418     if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name)))
3419         return db_fix;
3420
3421     db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
3422     db_fix->profile_set = ps;
3423     db_fix->name = pa_xstrdup(name);
3424
3425     pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix);
3426
3427     return db_fix;
3428 }
3429
3430 static int mapping_parse_device_strings(
3431         const char *filename,
3432         unsigned line,
3433         const char *section,
3434         const char *lvalue,
3435         const char *rvalue,
3436         void *data,
3437         void *userdata) {
3438
3439     pa_alsa_profile_set *ps = userdata;
3440     pa_alsa_mapping *m;
3441
3442     pa_assert(ps);
3443
3444     if (!(m = mapping_get(ps, section))) {
3445         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3446         return -1;
3447     }
3448
3449     pa_xstrfreev(m->device_strings);
3450     if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
3451         pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
3452         return -1;
3453     }
3454
3455     return 0;
3456 }
3457
3458 static int mapping_parse_channel_map(
3459         const char *filename,
3460         unsigned line,
3461         const char *section,
3462         const char *lvalue,
3463         const char *rvalue,
3464         void *data,
3465         void *userdata) {
3466
3467     pa_alsa_profile_set *ps = userdata;
3468     pa_alsa_mapping *m;
3469
3470     pa_assert(ps);
3471
3472     if (!(m = mapping_get(ps, section))) {
3473         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3474         return -1;
3475     }
3476
3477     if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
3478         pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
3479         return -1;
3480     }
3481
3482     return 0;
3483 }
3484
3485 static int mapping_parse_paths(
3486         const char *filename,
3487         unsigned line,
3488         const char *section,
3489         const char *lvalue,
3490         const char *rvalue,
3491         void *data,
3492         void *userdata) {
3493
3494     pa_alsa_profile_set *ps = userdata;
3495     pa_alsa_mapping *m;
3496
3497     pa_assert(ps);
3498
3499     if (!(m = mapping_get(ps, section))) {
3500         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3501         return -1;
3502     }
3503
3504     if (pa_streq(lvalue, "paths-input")) {
3505         pa_xstrfreev(m->input_path_names);
3506         m->input_path_names = pa_split_spaces_strv(rvalue);
3507     } else {
3508         pa_xstrfreev(m->output_path_names);
3509         m->output_path_names = pa_split_spaces_strv(rvalue);
3510     }
3511
3512     return 0;
3513 }
3514
3515 static int mapping_parse_element(
3516         const char *filename,
3517         unsigned line,
3518         const char *section,
3519         const char *lvalue,
3520         const char *rvalue,
3521         void *data,
3522         void *userdata) {
3523
3524     pa_alsa_profile_set *ps = userdata;
3525     pa_alsa_mapping *m;
3526
3527     pa_assert(ps);
3528
3529     if (!(m = mapping_get(ps, section))) {
3530         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3531         return -1;
3532     }
3533
3534     if (pa_streq(lvalue, "element-input")) {
3535         pa_xstrfreev(m->input_element);
3536         m->input_element = pa_split_spaces_strv(rvalue);
3537     } else {
3538         pa_xstrfreev(m->output_element);
3539         m->output_element = pa_split_spaces_strv(rvalue);
3540     }
3541
3542     return 0;
3543 }
3544
3545 static int mapping_parse_direction(
3546         const char *filename,
3547         unsigned line,
3548         const char *section,
3549         const char *lvalue,
3550         const char *rvalue,
3551         void *data,
3552         void *userdata) {
3553
3554     pa_alsa_profile_set *ps = userdata;
3555     pa_alsa_mapping *m;
3556
3557     pa_assert(ps);
3558
3559     if (!(m = mapping_get(ps, section))) {
3560         pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3561         return -1;
3562     }
3563
3564     if (pa_streq(rvalue, "input"))
3565         m->direction = PA_ALSA_DIRECTION_INPUT;
3566     else if (pa_streq(rvalue, "output"))
3567         m->direction = PA_ALSA_DIRECTION_OUTPUT;
3568     else if (pa_streq(rvalue, "any"))
3569         m->direction = PA_ALSA_DIRECTION_ANY;
3570     else {
3571         pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
3572         return -1;
3573     }
3574
3575     return 0;
3576 }
3577
3578 static int mapping_parse_description(
3579         const char *filename,
3580         unsigned line,
3581         const char *section,
3582         const char *lvalue,
3583         const char *rvalue,
3584         void *data,
3585         void *userdata) {
3586
3587     pa_alsa_profile_set *ps = userdata;
3588     pa_alsa_profile *p;
3589     pa_alsa_mapping *m;
3590
3591     pa_assert(ps);
3592
3593     if ((m = mapping_get(ps, section))) {
3594         pa_xfree(m->description);
3595         m->description = pa_xstrdup(rvalue);
3596     } else if ((p = profile_get(ps, section))) {
3597         pa_xfree(p->description);
3598         p->description = pa_xstrdup(rvalue);
3599     } else {
3600         pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3601         return -1;
3602     }
3603
3604     return 0;
3605 }
3606
3607 static int mapping_parse_priority(
3608         const char *filename,
3609         unsigned line,
3610         const char *section,
3611         const char *lvalue,
3612         const char *rvalue,
3613         void *data,
3614         void *userdata) {
3615
3616     pa_alsa_profile_set *ps = userdata;
3617     pa_alsa_profile *p;
3618     pa_alsa_mapping *m;
3619     uint32_t prio;
3620
3621     pa_assert(ps);
3622
3623     if (pa_atou(rvalue, &prio) < 0) {
3624         pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
3625         return -1;
3626     }
3627
3628     if ((m = mapping_get(ps, section)))
3629         m->priority = prio;
3630     else if ((p = profile_get(ps, section)))
3631         p->priority = prio;
3632     else {
3633         pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3634         return -1;
3635     }
3636
3637     return 0;
3638 }
3639
3640 static int profile_parse_mappings(
3641         const char *filename,
3642         unsigned line,
3643         const char *section,
3644         const char *lvalue,
3645         const char *rvalue,
3646         void *data,
3647         void *userdata) {
3648
3649     pa_alsa_profile_set *ps = userdata;
3650     pa_alsa_profile *p;
3651
3652     pa_assert(ps);
3653
3654     if (!(p = profile_get(ps, section))) {
3655         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3656         return -1;
3657     }
3658
3659     if (pa_streq(lvalue, "input-mappings")) {
3660         pa_xstrfreev(p->input_mapping_names);
3661         p->input_mapping_names = pa_split_spaces_strv(rvalue);
3662     } else {
3663         pa_xstrfreev(p->output_mapping_names);
3664         p->output_mapping_names = pa_split_spaces_strv(rvalue);
3665     }
3666
3667     return 0;
3668 }
3669
3670 static int profile_parse_skip_probe(
3671         const char *filename,
3672         unsigned line,
3673         const char *section,
3674         const char *lvalue,
3675         const char *rvalue,
3676         void *data,
3677         void *userdata) {
3678
3679     pa_alsa_profile_set *ps = userdata;
3680     pa_alsa_profile *p;
3681     int b;
3682
3683     pa_assert(ps);
3684
3685     if (!(p = profile_get(ps, section))) {
3686         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3687         return -1;
3688     }
3689
3690     if ((b = pa_parse_boolean(rvalue)) < 0) {
3691         pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
3692         return -1;
3693     }
3694
3695     p->supported = b;
3696
3697     return 0;
3698 }
3699
3700 static int decibel_fix_parse_db_values(
3701         const char *filename,
3702         unsigned line,
3703         const char *section,
3704         const char *lvalue,
3705         const char *rvalue,
3706         void *data,
3707         void *userdata) {
3708
3709     pa_alsa_profile_set *ps = userdata;
3710     pa_alsa_decibel_fix *db_fix;
3711     char **items;
3712     char *item;
3713     long *db_values;
3714     unsigned n = 8; /* Current size of the db_values table. */
3715     unsigned min_step = 0;
3716     unsigned max_step = 0;
3717     unsigned i = 0; /* Index to the items table. */
3718     unsigned prev_step = 0;
3719     double prev_db = 0;
3720
3721     pa_assert(filename);
3722     pa_assert(section);
3723     pa_assert(lvalue);
3724     pa_assert(rvalue);
3725     pa_assert(ps);
3726
3727     if (!(db_fix = decibel_fix_get(ps, section))) {
3728         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3729         return -1;
3730     }
3731
3732     if (!(items = pa_split_spaces_strv(rvalue))) {
3733         pa_log("[%s:%u] Value missing", pa_strnull(filename), line);
3734         return -1;
3735     }
3736
3737     db_values = pa_xnew(long, n);
3738
3739     while ((item = items[i++])) {
3740         char *s = item; /* Step value string. */
3741         char *d = item; /* dB value string. */
3742         uint32_t step;
3743         double db;
3744
3745         /* Move d forward until it points to a colon or to the end of the item. */
3746         for (; *d && *d != ':'; ++d);
3747
3748         if (d == s) {
3749             /* item started with colon. */
3750             pa_log("[%s:%u] No step value found in %s", filename, line, item);
3751             goto fail;
3752         }
3753
3754         if (!*d || !*(d + 1)) {
3755             /* No colon found, or it was the last character in item. */
3756             pa_log("[%s:%u] No dB value found in %s", filename, line, item);
3757             goto fail;
3758         }
3759
3760         /* pa_atou() needs a null-terminating string. Let's replace the colon
3761          * with a zero byte. */
3762         *d++ = '\0';
3763
3764         if (pa_atou(s, &step) < 0) {
3765             pa_log("[%s:%u] Invalid step value: %s", filename, line, s);
3766             goto fail;
3767         }
3768
3769         if (pa_atod(d, &db) < 0) {
3770             pa_log("[%s:%u] Invalid dB value: %s", filename, line, d);
3771             goto fail;
3772         }
3773
3774         if (step <= prev_step && i != 1) {
3775             pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename, line, step, prev_step);
3776             goto fail;
3777         }
3778
3779         if (db < prev_db && i != 1) {
3780             pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename, line, db, prev_db);
3781             goto fail;
3782         }
3783
3784         if (i == 1) {
3785             min_step = step;
3786             db_values[0] = (long) (db * 100.0);
3787             prev_step = step;
3788             prev_db = db;
3789         } else {
3790             /* Interpolate linearly. */
3791             double db_increment = (db - prev_db) / (step - prev_step);
3792
3793             for (; prev_step < step; ++prev_step, prev_db += db_increment) {
3794
3795                 /* Reallocate the db_values table if it's about to overflow. */
3796                 if (prev_step + 1 - min_step == n) {
3797                     n *= 2;
3798                     db_values = pa_xrenew(long, db_values, n);
3799                 }
3800
3801                 db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
3802             }
3803         }
3804
3805         max_step = step;
3806     }
3807
3808     db_fix->min_step = min_step;
3809     db_fix->max_step = max_step;
3810     pa_xfree(db_fix->db_values);
3811     db_fix->db_values = db_values;
3812
3813     pa_xstrfreev(items);
3814
3815     return 0;
3816
3817 fail:
3818     pa_xstrfreev(items);
3819     pa_xfree(db_values);
3820
3821     return -1;
3822 }
3823
3824 static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
3825                                 pa_alsa_direction_t direction) {
3826
3827     pa_alsa_path *p;
3828     void *state;
3829     snd_pcm_t *pcm_handle;
3830     pa_alsa_path_set *ps;
3831     snd_mixer_t *mixer_handle;
3832     snd_hctl_t *hctl_handle;
3833
3834     if (direction == PA_ALSA_DIRECTION_OUTPUT) {
3835         if (m->output_path_set)
3836             return; /* Already probed */
3837         m->output_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3838         pcm_handle = m->output_pcm;
3839     } else {
3840         if (m->input_path_set)
3841             return; /* Already probed */
3842         m->input_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3843         pcm_handle = m->input_pcm;
3844     }
3845
3846     if (!ps)
3847         return; /* No paths */
3848
3849     pa_assert(pcm_handle);
3850
3851     mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL, &hctl_handle);
3852     if (!mixer_handle || !hctl_handle) {
3853          /* Cannot open mixer, remove all entries */
3854         while (pa_hashmap_steal_first(ps->paths));
3855         return;
3856     }
3857
3858
3859     PA_HASHMAP_FOREACH(p, ps->paths, state) {
3860         if (pa_alsa_path_probe(p, mixer_handle, hctl_handle, m->profile_set->ignore_dB) < 0) {
3861             pa_hashmap_remove(ps->paths, p);
3862         }
3863     }
3864
3865     path_set_condense(ps, mixer_handle);
3866     path_set_make_paths_unique(ps);
3867
3868     if (mixer_handle)
3869         snd_mixer_close(mixer_handle);
3870
3871     pa_log_debug("Available mixer paths (after tidying):");
3872     pa_alsa_path_set_dump(ps);
3873 }
3874
3875 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
3876
3877     static const struct description_map well_known_descriptions[] = {
3878         { "analog-mono",            N_("Analog Mono") },
3879         { "analog-stereo",          N_("Analog Stereo") },
3880         { "analog-surround-21",     N_("Analog Surround 2.1") },
3881         { "analog-surround-30",     N_("Analog Surround 3.0") },
3882         { "analog-surround-31",     N_("Analog Surround 3.1") },
3883         { "analog-surround-40",     N_("Analog Surround 4.0") },
3884         { "analog-surround-41",     N_("Analog Surround 4.1") },
3885         { "analog-surround-50",     N_("Analog Surround 5.0") },
3886         { "analog-surround-51",     N_("Analog Surround 5.1") },
3887         { "analog-surround-61",     N_("Analog Surround 6.0") },
3888         { "analog-surround-61",     N_("Analog Surround 6.1") },
3889         { "analog-surround-70",     N_("Analog Surround 7.0") },
3890         { "analog-surround-71",     N_("Analog Surround 7.1") },
3891         { "analog-4-channel-input", N_("Analog 4-channel Input") },
3892         { "iec958-stereo",          N_("Digital Stereo (IEC958)") },
3893         { "iec958-passthrough",     N_("Digital Passthrough  (IEC958)") },
3894         { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3895         { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3896         { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
3897         { "hdmi-stereo",            N_("Digital Stereo (HDMI)") },
3898         { "hdmi-surround-51",       N_("Digital Surround 5.1 (HDMI)") }
3899     };
3900
3901     pa_assert(m);
3902
3903     if (!pa_channel_map_valid(&m->channel_map)) {
3904         pa_log("Mapping %s is missing channel map.", m->name);
3905         return -1;
3906     }
3907
3908     if (!m->device_strings) {
3909         pa_log("Mapping %s is missing device strings.", m->name);
3910         return -1;
3911     }
3912
3913     if ((m->input_path_names && m->input_element) ||
3914         (m->output_path_names && m->output_element)) {
3915         pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
3916         return -1;
3917     }
3918
3919     if (!m->description)
3920         m->description = pa_xstrdup(lookup_description(m->name,
3921                                                        well_known_descriptions,
3922                                                        PA_ELEMENTSOF(well_known_descriptions)));
3923
3924     if (!m->description)
3925         m->description = pa_xstrdup(m->name);
3926
3927     if (bonus) {
3928         if (pa_channel_map_equal(&m->channel_map, bonus))
3929             m->priority += 50;
3930         else if (m->channel_map.channels == bonus->channels)
3931             m->priority += 30;
3932     }
3933
3934     return 0;
3935 }
3936
3937 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
3938     char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
3939
3940     pa_assert(m);
3941
3942     pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3943                  m->name,
3944                  pa_strnull(m->description),
3945                  m->priority,
3946                  pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
3947                  pa_yes_no(m->supported),
3948                  m->direction);
3949 }
3950
3951 static void profile_set_add_auto_pair(
3952         pa_alsa_profile_set *ps,
3953         pa_alsa_mapping *m, /* output */
3954         pa_alsa_mapping *n  /* input */) {
3955
3956     char *name;
3957     pa_alsa_profile *p;
3958
3959     pa_assert(ps);
3960     pa_assert(m || n);
3961
3962     if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
3963         return;
3964
3965     if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
3966         return;
3967
3968     if (m && n)
3969         name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
3970     else if (m)
3971         name = pa_sprintf_malloc("output:%s", m->name);
3972     else
3973         name = pa_sprintf_malloc("input:%s", n->name);
3974
3975     if (pa_hashmap_get(ps->profiles, name)) {
3976         pa_xfree(name);
3977         return;
3978     }
3979
3980     p = pa_xnew0(pa_alsa_profile, 1);
3981     p->profile_set = ps;
3982     p->name = name;
3983
3984     if (m) {
3985         p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3986         pa_idxset_put(p->output_mappings, m, NULL);
3987         p->priority += m->priority * 100;
3988     }
3989
3990     if (n) {
3991         p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3992         pa_idxset_put(p->input_mappings, n, NULL);
3993         p->priority += n->priority;
3994     }
3995
3996     pa_hashmap_put(ps->profiles, p->name, p);
3997 }
3998
3999 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
4000     pa_alsa_mapping *m, *n;
4001     void *m_state, *n_state;
4002
4003     pa_assert(ps);
4004
4005     PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
4006         profile_set_add_auto_pair(ps, m, NULL);
4007
4008         PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
4009             profile_set_add_auto_pair(ps, m, n);
4010     }
4011
4012     PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
4013         profile_set_add_auto_pair(ps, NULL, n);
4014 }
4015
4016 static int profile_verify(pa_alsa_profile *p) {
4017
4018     static const struct description_map well_known_descriptions[] = {
4019         { "output:analog-mono+input:analog-mono",     N_("Analog Mono Duplex") },
4020         { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
4021         { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
4022         { "off",                                      N_("Off") }
4023     };
4024
4025     pa_assert(p);
4026
4027     /* Replace the output mapping names by the actual mappings */
4028     if (p->output_mapping_names) {
4029         char **name;
4030
4031         pa_assert(!p->output_mappings);
4032         p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4033
4034         for (name = p->output_mapping_names; *name; name++) {
4035             pa_alsa_mapping *m;
4036             char **in;
4037             pa_bool_t duplicate = FALSE;
4038
4039             for (in = name + 1; *in; in++)
4040                 if (pa_streq(*name, *in)) {
4041                     duplicate = TRUE;
4042                     break;
4043                 }
4044
4045             if (duplicate)
4046                 continue;
4047
4048             if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
4049                 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
4050                 return -1;
4051             }
4052
4053             pa_idxset_put(p->output_mappings, m, NULL);
4054
4055             if (p->supported)
4056                 m->supported++;
4057         }
4058
4059         pa_xstrfreev(p->output_mapping_names);
4060         p->output_mapping_names = NULL;
4061     }
4062
4063     /* Replace the input mapping names by the actual mappings */
4064     if (p->input_mapping_names) {
4065         char **name;
4066
4067         pa_assert(!p->input_mappings);
4068         p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4069
4070         for (name = p->input_mapping_names; *name; name++) {
4071             pa_alsa_mapping *m;
4072             char **in;
4073             pa_bool_t duplicate = FALSE;
4074
4075             for (in = name + 1; *in; in++)
4076                 if (pa_streq(*name, *in)) {
4077                     duplicate = TRUE;
4078                     break;
4079                 }
4080
4081             if (duplicate)
4082                 continue;
4083
4084             if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
4085                 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
4086                 return -1;
4087             }
4088
4089             pa_idxset_put(p->input_mappings, m, NULL);
4090
4091             if (p->supported)
4092                 m->supported++;
4093         }
4094
4095         pa_xstrfreev(p->input_mapping_names);
4096         p->input_mapping_names = NULL;
4097     }
4098
4099     if (!p->input_mappings && !p->output_mappings) {
4100         pa_log("Profile '%s' lacks mappings.", p->name);
4101         return -1;
4102     }
4103
4104     if (!p->description)
4105         p->description = pa_xstrdup(lookup_description(p->name,
4106                                                        well_known_descriptions,
4107                                                        PA_ELEMENTSOF(well_known_descriptions)));
4108
4109     if (!p->description) {
4110         pa_strbuf *sb;
4111         uint32_t idx;
4112         pa_alsa_mapping *m;
4113
4114         sb = pa_strbuf_new();
4115
4116         if (p->output_mappings)
4117             PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4118                 if (!pa_strbuf_isempty(sb))
4119                     pa_strbuf_puts(sb, " + ");
4120
4121                 pa_strbuf_printf(sb, _("%s Output"), m->description);
4122             }
4123
4124         if (p->input_mappings)
4125             PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4126                 if (!pa_strbuf_isempty(sb))
4127                     pa_strbuf_puts(sb, " + ");
4128
4129                 pa_strbuf_printf(sb, _("%s Input"), m->description);
4130             }
4131
4132         p->description = pa_strbuf_tostring_free(sb);
4133     }
4134
4135     return 0;
4136 }
4137
4138 void pa_alsa_profile_dump(pa_alsa_profile *p) {
4139     uint32_t idx;
4140     pa_alsa_mapping *m;
4141     pa_assert(p);
4142
4143     pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4144                  p->name,
4145                  pa_strnull(p->description),
4146                  p->priority,
4147                  pa_yes_no(p->supported),
4148                  p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
4149                  p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
4150
4151     if (p->input_mappings)
4152         PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4153             pa_log_debug("Input %s", m->name);
4154
4155     if (p->output_mappings)
4156         PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4157             pa_log_debug("Output %s", m->name);
4158 }
4159
4160 static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
4161     pa_assert(db_fix);
4162
4163     /* Check that the dB mapping has been configured. Since "db-values" is
4164      * currently the only option in the DecibelFix section, and decibel fix
4165      * objects don't get created if a DecibelFix section is empty, this is
4166      * actually a redundant check. Having this may prevent future bugs,
4167      * however. */
4168     if (!db_fix->db_values) {
4169         pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
4170         return -1;
4171     }
4172
4173     return 0;
4174 }
4175
4176 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
4177     char *db_values = NULL;
4178
4179     pa_assert(db_fix);
4180
4181     if (db_fix->db_values) {
4182         pa_strbuf *buf;
4183         unsigned long i, nsteps;
4184
4185         pa_assert(db_fix->min_step <= db_fix->max_step);
4186         nsteps = db_fix->max_step - db_fix->min_step + 1;
4187
4188         buf = pa_strbuf_new();
4189         for (i = 0; i < nsteps; ++i)
4190             pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
4191
4192         db_values = pa_strbuf_tostring_free(buf);
4193     }
4194
4195     pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4196                  db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
4197
4198     pa_xfree(db_values);
4199 }
4200
4201 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
4202     pa_alsa_profile_set *ps;
4203     pa_alsa_profile *p;
4204     pa_alsa_mapping *m;
4205     pa_alsa_decibel_fix *db_fix;
4206     char *fn;
4207     int r;
4208     void *state;
4209
4210     static pa_config_item items[] = {
4211         /* [General] */
4212         { "auto-profiles",          pa_config_parse_bool,         NULL, "General" },
4213
4214         /* [Mapping ...] */
4215         { "device-strings",         mapping_parse_device_strings, NULL, NULL },
4216         { "channel-map",            mapping_parse_channel_map,    NULL, NULL },
4217         { "paths-input",            mapping_parse_paths,          NULL, NULL },
4218         { "paths-output",           mapping_parse_paths,          NULL, NULL },
4219         { "element-input",          mapping_parse_element,        NULL, NULL },
4220         { "element-output",         mapping_parse_element,        NULL, NULL },
4221         { "direction",              mapping_parse_direction,      NULL, NULL },
4222
4223         /* Shared by [Mapping ...] and [Profile ...] */
4224         { "description",            mapping_parse_description,    NULL, NULL },
4225         { "priority",               mapping_parse_priority,       NULL, NULL },
4226
4227         /* [Profile ...] */
4228         { "input-mappings",         profile_parse_mappings,       NULL, NULL },
4229         { "output-mappings",        profile_parse_mappings,       NULL, NULL },
4230         { "skip-probe",             profile_parse_skip_probe,     NULL, NULL },
4231
4232         /* [DecibelFix ...] */
4233         { "db-values",              decibel_fix_parse_db_values,  NULL, NULL },
4234         { NULL, NULL, NULL, NULL }
4235     };
4236
4237     ps = pa_xnew0(pa_alsa_profile_set, 1);
4238     ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4239     ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4240     ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4241     ps->input_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4242     ps->output_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4243
4244     items[0].data = &ps->auto_profiles;
4245
4246     if (!fname)
4247         fname = "default.conf";
4248
4249     fn = pa_maybe_prefix_path(fname,
4250                               pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
4251                               PA_ALSA_PROFILE_SETS_DIR);
4252
4253     r = pa_config_parse(fn, NULL, items, ps);
4254     pa_xfree(fn);
4255
4256     if (r < 0)
4257         goto fail;
4258
4259     PA_HASHMAP_FOREACH(m, ps->mappings, state)
4260         if (mapping_verify(m, bonus) < 0)
4261             goto fail;
4262
4263     if (ps->auto_profiles)
4264         profile_set_add_auto(ps);
4265
4266     PA_HASHMAP_FOREACH(p, ps->profiles, state)
4267         if (profile_verify(p) < 0)
4268             goto fail;
4269
4270     PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4271         if (decibel_fix_verify(db_fix) < 0)
4272             goto fail;
4273
4274     return ps;
4275
4276 fail:
4277     pa_alsa_profile_set_free(ps);
4278     return NULL;
4279 }
4280
4281 static void profile_finalize_probing(pa_alsa_profile *to_be_finalized, pa_alsa_profile *next) {
4282     pa_alsa_mapping *m;
4283     uint32_t idx;
4284
4285     if (!to_be_finalized)
4286         return;
4287
4288     if (to_be_finalized->output_mappings)
4289         PA_IDXSET_FOREACH(m, to_be_finalized->output_mappings, idx) {
4290
4291             if (!m->output_pcm)
4292                 continue;
4293
4294             if (to_be_finalized->supported)
4295                 m->supported++;
4296
4297             /* If this mapping is also in the next profile, we won't close the
4298              * pcm handle here, because it would get immediately reopened
4299              * anyway. */
4300             if (next && next->output_mappings && pa_idxset_get_by_data(next->output_mappings, m, NULL))
4301                 continue;
4302
4303             snd_pcm_close(m->output_pcm);
4304             m->output_pcm = NULL;
4305         }
4306
4307     if (to_be_finalized->input_mappings)
4308         PA_IDXSET_FOREACH(m, to_be_finalized->input_mappings, idx) {
4309
4310             if (!m->input_pcm)
4311                 continue;
4312
4313             if (to_be_finalized->supported)
4314                 m->supported++;
4315
4316             /* If this mapping is also in the next profile, we won't close the
4317              * pcm handle here, because it would get immediately reopened
4318              * anyway. */
4319             if (next && next->input_mappings && pa_idxset_get_by_data(next->input_mappings, m, NULL))
4320                 continue;
4321
4322             snd_pcm_close(m->input_pcm);
4323             m->input_pcm = NULL;
4324         }
4325 }
4326
4327 static snd_pcm_t* mapping_open_pcm(pa_alsa_mapping *m,
4328                                    const pa_sample_spec *ss,
4329                                    const char *dev_id,
4330                                    int mode,
4331                                    unsigned default_n_fragments,
4332                                    unsigned default_fragment_size_msec) {
4333
4334     pa_sample_spec try_ss = *ss;
4335     pa_channel_map try_map = m->channel_map;
4336     snd_pcm_uframes_t try_period_size, try_buffer_size;
4337
4338     try_ss.channels = try_map.channels;
4339
4340     try_period_size =
4341         pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
4342         pa_frame_size(&try_ss);
4343     try_buffer_size = default_n_fragments * try_period_size;
4344
4345     return pa_alsa_open_by_template(
4346                               m->device_strings, dev_id, NULL, &try_ss,
4347                               &try_map, mode, &try_period_size,
4348                               &try_buffer_size, 0, NULL, NULL, TRUE);
4349 }
4350
4351 static void paths_drop_unsupported(pa_hashmap* h) {
4352
4353     void* state = NULL;
4354     const void* key;
4355     pa_alsa_path* p;
4356
4357     pa_assert(h);
4358     p = pa_hashmap_iterate(h, &state, &key);
4359     while (p) {
4360         if (p->supported <= 0) {
4361             pa_hashmap_remove(h, key);
4362             pa_alsa_path_free(p);
4363         }
4364         p = pa_hashmap_iterate(h, &state, &key);
4365     }
4366 }
4367
4368 void pa_alsa_profile_set_probe(
4369         pa_alsa_profile_set *ps,
4370         const char *dev_id,
4371         const pa_sample_spec *ss,
4372         unsigned default_n_fragments,
4373         unsigned default_fragment_size_msec) {
4374
4375     void *state;
4376     pa_alsa_profile *p, *last = NULL;
4377     pa_alsa_mapping *m;
4378
4379     pa_assert(ps);
4380     pa_assert(dev_id);
4381     pa_assert(ss);
4382
4383     if (ps->probed)
4384         return;
4385
4386     PA_HASHMAP_FOREACH(p, ps->profiles, state) {
4387         uint32_t idx;
4388
4389         /* Skip if this is already marked that it is supported (i.e. from the config file) */
4390         if (!p->supported) {
4391
4392             pa_log_debug("Looking at profile %s", p->name);
4393             profile_finalize_probing(last, p);
4394             p->supported = TRUE;
4395
4396             /* Check if we can open all new ones */
4397             if (p->output_mappings)
4398                 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4399
4400                     if (m->output_pcm)
4401                         continue;
4402
4403                     pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
4404                     if (!(m->output_pcm = mapping_open_pcm(m, ss, dev_id,
4405                                                            SND_PCM_STREAM_PLAYBACK,
4406                                                            default_n_fragments,
4407                                                            default_fragment_size_msec))) {
4408                         p->supported = FALSE;
4409                         break;
4410                     }
4411                 }
4412
4413             if (p->input_mappings && p->supported)
4414                 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4415
4416                     if (m->input_pcm)
4417                         continue;
4418
4419                     pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
4420                     if (!(m->input_pcm = mapping_open_pcm(m, ss, dev_id,
4421                                                           SND_PCM_STREAM_CAPTURE,
4422                                                           default_n_fragments,
4423                                                           default_fragment_size_msec))) {
4424                         p->supported = FALSE;
4425                         break;
4426                     }
4427                 }
4428
4429             last = p;
4430
4431             if (!p->supported)
4432                 continue;
4433         }
4434
4435         pa_log_debug("Profile %s supported.", p->name);
4436
4437         if (p->output_mappings)
4438             PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4439                 if (m->output_pcm)
4440                     mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT);
4441
4442         if (p->input_mappings)
4443             PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4444                 if (m->input_pcm)
4445                     mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT);
4446     }
4447
4448     /* Clean up */
4449     profile_finalize_probing(last, NULL);
4450
4451     PA_HASHMAP_FOREACH(p, ps->profiles, state)
4452         if (!p->supported) {
4453             pa_hashmap_remove(ps->profiles, p->name);
4454             profile_free(p);
4455         }
4456
4457     PA_HASHMAP_FOREACH(m, ps->mappings, state)
4458         if (m->supported <= 0) {
4459             pa_hashmap_remove(ps->mappings, m->name);
4460             mapping_free(m);
4461         }
4462
4463     paths_drop_unsupported(ps->input_paths);
4464     paths_drop_unsupported(ps->output_paths);
4465
4466     ps->probed = TRUE;
4467 }
4468
4469 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
4470     pa_alsa_profile *p;
4471     pa_alsa_mapping *m;
4472     pa_alsa_decibel_fix *db_fix;
4473     void *state;
4474
4475     pa_assert(ps);
4476
4477     pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4478                  (void*)
4479                  ps,
4480                  pa_yes_no(ps->auto_profiles),
4481                  pa_yes_no(ps->probed),
4482                  pa_hashmap_size(ps->mappings),
4483                  pa_hashmap_size(ps->profiles),
4484                  pa_hashmap_size(ps->decibel_fixes));
4485
4486     PA_HASHMAP_FOREACH(m, ps->mappings, state)
4487         pa_alsa_mapping_dump(m);
4488
4489     PA_HASHMAP_FOREACH(p, ps->profiles, state)
4490         pa_alsa_profile_dump(p);
4491
4492     PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4493         pa_alsa_decibel_fix_dump(db_fix);
4494 }
4495
4496 static pa_device_port* device_port_alsa_init(pa_hashmap *ports,
4497     const char* name,
4498     const char* description,
4499     pa_alsa_path *path,
4500     pa_alsa_setting *setting,
4501     pa_card_profile *cp,
4502     pa_hashmap *extra,
4503     pa_core *core) {
4504
4505     pa_device_port * p = pa_hashmap_get(ports, name);
4506     if (!p) {
4507         pa_alsa_port_data *data;
4508
4509         p = pa_device_port_new(core, name, description, sizeof(pa_alsa_port_data));
4510         pa_assert(p);
4511         pa_hashmap_put(ports, p->name, p);
4512
4513         data = PA_DEVICE_PORT_DATA(p);
4514         data->path = path;
4515         data->setting = setting;
4516         path->port = p;
4517     }
4518
4519     p->is_input |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_INPUT;
4520     p->is_output |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_OUTPUT;
4521
4522     if (cp)
4523         pa_hashmap_put(p->profiles, cp->name, cp);
4524
4525     if (extra) {
4526         pa_hashmap_put(extra, p->name, p);
4527         pa_device_port_ref(p);
4528     }
4529
4530     return p;
4531 }
4532
4533 void pa_alsa_path_set_add_ports(
4534         pa_alsa_path_set *ps,
4535         pa_card_profile *cp,
4536         pa_hashmap *ports,
4537         pa_hashmap *extra,
4538         pa_core *core) {
4539
4540     pa_alsa_path *path;
4541     void *state;
4542
4543     pa_assert(ports);
4544
4545     if (!ps)
4546         return;
4547
4548     PA_HASHMAP_FOREACH(path, ps->paths, state) {
4549         if (!path->settings || !path->settings->next) {
4550             /* If there is no or just one setting we only need a
4551              * single entry */
4552             pa_device_port *port = device_port_alsa_init(ports, path->name,
4553                 path->description, path, path->settings, cp, extra, core);
4554             port->priority = path->priority * 100;
4555
4556         } else {
4557             pa_alsa_setting *s;
4558             PA_LLIST_FOREACH(s, path->settings) {
4559                 pa_device_port *port;
4560                 char *n, *d;
4561
4562                 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
4563
4564                 if (s->description[0])
4565                     d = pa_sprintf_malloc("%s / %s", path->description, s->description);
4566                 else
4567                     d = pa_xstrdup(path->description);
4568
4569                 port = device_port_alsa_init(ports, n, d, path, s, cp, extra, core);
4570                 port->priority = path->priority * 100 + s->priority;
4571
4572                 pa_xfree(n);
4573                 pa_xfree(d);
4574             }
4575         }
4576     }
4577 }
4578
4579 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps, pa_card *card) {
4580
4581     pa_assert(p);
4582     pa_assert(!*p);
4583     pa_assert(ps);
4584
4585     if (ps->paths && pa_hashmap_size(ps->paths) > 0) {
4586         pa_assert(card);
4587         *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4588         pa_alsa_path_set_add_ports(ps, NULL, card->ports, *p, card->core);
4589     }
4590
4591     pa_log_debug("Added %u ports", *p ? pa_hashmap_size(*p) : 0);
4592 }