alsa: cover bass boost mixer element
[platform/upstream/pulseaudio.git] / src / modules / alsa / alsa-mixer.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2004-2009 Lennart Poettering
5   Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7   PulseAudio is free software; you can redistribute it and/or modify
8   it under the terms of the GNU Lesser General Public License as published
9   by the Free Software Foundation; either version 2.1 of the License,
10   or (at your option) any later version.
11
12   PulseAudio is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with PulseAudio; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20   USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <sys/types.h>
28 #include <limits.h>
29 #include <asoundlib.h>
30
31 #ifdef HAVE_VALGRIND_MEMCHECK_H
32 #include <valgrind/memcheck.h>
33 #endif
34
35 #include <pulse/sample.h>
36 #include <pulse/xmalloc.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/i18n.h>
40 #include <pulse/utf8.h>
41
42 #include <pulsecore/log.h>
43 #include <pulsecore/macro.h>
44 #include <pulsecore/core-util.h>
45 #include <pulsecore/atomic.h>
46 #include <pulsecore/core-error.h>
47 #include <pulsecore/once.h>
48 #include <pulsecore/thread.h>
49 #include <pulsecore/conf-parser.h>
50 #include <pulsecore/strbuf.h>
51
52 #include "alsa-mixer.h"
53 #include "alsa-util.h"
54
55 struct description_map {
56     const char *name;
57     const char *description;
58 };
59
60 static const char *lookup_description(const char *name, const struct description_map dm[], unsigned n) {
61     unsigned i;
62
63     for (i = 0; i < n; i++)
64         if (pa_streq(dm[i].name, name))
65             return dm[i].description;
66
67     return NULL;
68 }
69
70 struct pa_alsa_fdlist {
71     unsigned num_fds;
72     struct pollfd *fds;
73     /* This is a temporary buffer used to avoid lots of mallocs */
74     struct pollfd *work_fds;
75
76     snd_mixer_t *mixer;
77
78     pa_mainloop_api *m;
79     pa_defer_event *defer;
80     pa_io_event **ios;
81
82     pa_bool_t polled;
83
84     void (*cb)(void *userdata);
85     void *userdata;
86 };
87
88 static void io_cb(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
89
90     struct pa_alsa_fdlist *fdl = userdata;
91     int err;
92     unsigned i;
93     unsigned short revents;
94
95     pa_assert(a);
96     pa_assert(fdl);
97     pa_assert(fdl->mixer);
98     pa_assert(fdl->fds);
99     pa_assert(fdl->work_fds);
100
101     if (fdl->polled)
102         return;
103
104     fdl->polled = TRUE;
105
106     memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds);
107
108     for (i = 0; i < fdl->num_fds; i++) {
109         if (e == fdl->ios[i]) {
110             if (events & PA_IO_EVENT_INPUT)
111                 fdl->work_fds[i].revents |= POLLIN;
112             if (events & PA_IO_EVENT_OUTPUT)
113                 fdl->work_fds[i].revents |= POLLOUT;
114             if (events & PA_IO_EVENT_ERROR)
115                 fdl->work_fds[i].revents |= POLLERR;
116             if (events & PA_IO_EVENT_HANGUP)
117                 fdl->work_fds[i].revents |= POLLHUP;
118             break;
119         }
120     }
121
122     pa_assert(i != fdl->num_fds);
123
124     if ((err = snd_mixer_poll_descriptors_revents(fdl->mixer, fdl->work_fds, fdl->num_fds, &revents)) < 0) {
125         pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
126         return;
127     }
128
129     a->defer_enable(fdl->defer, 1);
130
131     if (revents)
132         snd_mixer_handle_events(fdl->mixer);
133 }
134
135 static void defer_cb(pa_mainloop_api*a, pa_defer_event* e, void *userdata) {
136     struct pa_alsa_fdlist *fdl = userdata;
137     unsigned num_fds, i;
138     int err, n;
139     struct pollfd *temp;
140
141     pa_assert(a);
142     pa_assert(fdl);
143     pa_assert(fdl->mixer);
144
145     a->defer_enable(fdl->defer, 0);
146
147     if ((n = snd_mixer_poll_descriptors_count(fdl->mixer)) < 0) {
148         pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
149         return;
150     }
151     num_fds = (unsigned) n;
152
153     if (num_fds != fdl->num_fds) {
154         if (fdl->fds)
155             pa_xfree(fdl->fds);
156         if (fdl->work_fds)
157             pa_xfree(fdl->work_fds);
158         fdl->fds = pa_xnew0(struct pollfd, num_fds);
159         fdl->work_fds = pa_xnew(struct pollfd, num_fds);
160     }
161
162     memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds);
163
164     if ((err = snd_mixer_poll_descriptors(fdl->mixer, fdl->work_fds, num_fds)) < 0) {
165         pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
166         return;
167     }
168
169     fdl->polled = FALSE;
170
171     if (memcmp(fdl->fds, fdl->work_fds, sizeof(struct pollfd) * num_fds) == 0)
172         return;
173
174     if (fdl->ios) {
175         for (i = 0; i < fdl->num_fds; i++)
176             a->io_free(fdl->ios[i]);
177
178         if (num_fds != fdl->num_fds) {
179             pa_xfree(fdl->ios);
180             fdl->ios = NULL;
181         }
182     }
183
184     if (!fdl->ios)
185         fdl->ios = pa_xnew(pa_io_event*, num_fds);
186
187     /* Swap pointers */
188     temp = fdl->work_fds;
189     fdl->work_fds = fdl->fds;
190     fdl->fds = temp;
191
192     fdl->num_fds = num_fds;
193
194     for (i = 0;i < num_fds;i++)
195         fdl->ios[i] = a->io_new(a, fdl->fds[i].fd,
196             ((fdl->fds[i].events & POLLIN) ? PA_IO_EVENT_INPUT : 0) |
197             ((fdl->fds[i].events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0),
198             io_cb, fdl);
199 }
200
201 struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) {
202     struct pa_alsa_fdlist *fdl;
203
204     fdl = pa_xnew0(struct pa_alsa_fdlist, 1);
205
206     return fdl;
207 }
208
209 void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {
210     pa_assert(fdl);
211
212     if (fdl->defer) {
213         pa_assert(fdl->m);
214         fdl->m->defer_free(fdl->defer);
215     }
216
217     if (fdl->ios) {
218         unsigned i;
219         pa_assert(fdl->m);
220         for (i = 0; i < fdl->num_fds; i++)
221             fdl->m->io_free(fdl->ios[i]);
222         pa_xfree(fdl->ios);
223     }
224
225     if (fdl->fds)
226         pa_xfree(fdl->fds);
227     if (fdl->work_fds)
228         pa_xfree(fdl->work_fds);
229
230     pa_xfree(fdl);
231 }
232
233 int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m) {
234     pa_assert(fdl);
235     pa_assert(mixer_handle);
236     pa_assert(m);
237     pa_assert(!fdl->m);
238
239     fdl->mixer = mixer_handle;
240     fdl->m = m;
241     fdl->defer = m->defer_new(m, defer_cb, fdl);
242
243     return 0;
244 }
245
246 static int prepare_mixer(snd_mixer_t *mixer, const char *dev) {
247     int err;
248
249     pa_assert(mixer);
250     pa_assert(dev);
251
252     if ((err = snd_mixer_attach(mixer, dev)) < 0) {
253         pa_log_info("Unable to attach to mixer %s: %s", dev, pa_alsa_strerror(err));
254         return -1;
255     }
256
257     if ((err = snd_mixer_selem_register(mixer, NULL, NULL)) < 0) {
258         pa_log_warn("Unable to register mixer: %s", pa_alsa_strerror(err));
259         return -1;
260     }
261
262     if ((err = snd_mixer_load(mixer)) < 0) {
263         pa_log_warn("Unable to load mixer: %s", pa_alsa_strerror(err));
264         return -1;
265     }
266
267     pa_log_info("Successfully attached to mixer '%s'", dev);
268     return 0;
269 }
270
271 snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device) {
272     int err;
273     snd_mixer_t *m;
274     const char *dev;
275     snd_pcm_info_t* info;
276     snd_pcm_info_alloca(&info);
277
278     pa_assert(pcm);
279
280     if ((err = snd_mixer_open(&m, 0)) < 0) {
281         pa_log("Error opening mixer: %s", pa_alsa_strerror(err));
282         return NULL;
283     }
284
285     /* First, try by name */
286     if ((dev = snd_pcm_name(pcm)))
287         if (prepare_mixer(m, dev) >= 0) {
288             if (ctl_device)
289                 *ctl_device = pa_xstrdup(dev);
290
291             return m;
292         }
293
294     /* Then, try by card index */
295     if (snd_pcm_info(pcm, info) >= 0) {
296         char *md;
297         int card_idx;
298
299         if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
300
301             md = pa_sprintf_malloc("hw:%i", card_idx);
302
303             if (!dev || !pa_streq(dev, md))
304                 if (prepare_mixer(m, md) >= 0) {
305
306                     if (ctl_device)
307                         *ctl_device = md;
308                     else
309                         pa_xfree(md);
310
311                     return m;
312                 }
313
314             pa_xfree(md);
315         }
316     }
317
318     snd_mixer_close(m);
319     return NULL;
320 }
321
322 static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_MAX] = {
323     [PA_CHANNEL_POSITION_MONO] = SND_MIXER_SCHN_MONO, /* The ALSA name is just an alias! */
324
325     [PA_CHANNEL_POSITION_FRONT_CENTER] = SND_MIXER_SCHN_FRONT_CENTER,
326     [PA_CHANNEL_POSITION_FRONT_LEFT] = SND_MIXER_SCHN_FRONT_LEFT,
327     [PA_CHANNEL_POSITION_FRONT_RIGHT] = SND_MIXER_SCHN_FRONT_RIGHT,
328
329     [PA_CHANNEL_POSITION_REAR_CENTER] = SND_MIXER_SCHN_REAR_CENTER,
330     [PA_CHANNEL_POSITION_REAR_LEFT] = SND_MIXER_SCHN_REAR_LEFT,
331     [PA_CHANNEL_POSITION_REAR_RIGHT] = SND_MIXER_SCHN_REAR_RIGHT,
332
333     [PA_CHANNEL_POSITION_LFE] = SND_MIXER_SCHN_WOOFER,
334
335     [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
336     [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
337
338     [PA_CHANNEL_POSITION_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT,
339     [PA_CHANNEL_POSITION_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT,
340
341     [PA_CHANNEL_POSITION_AUX0] = SND_MIXER_SCHN_UNKNOWN,
342     [PA_CHANNEL_POSITION_AUX1] = SND_MIXER_SCHN_UNKNOWN,
343     [PA_CHANNEL_POSITION_AUX2] = SND_MIXER_SCHN_UNKNOWN,
344     [PA_CHANNEL_POSITION_AUX3] = SND_MIXER_SCHN_UNKNOWN,
345     [PA_CHANNEL_POSITION_AUX4] = SND_MIXER_SCHN_UNKNOWN,
346     [PA_CHANNEL_POSITION_AUX5] = SND_MIXER_SCHN_UNKNOWN,
347     [PA_CHANNEL_POSITION_AUX6] = SND_MIXER_SCHN_UNKNOWN,
348     [PA_CHANNEL_POSITION_AUX7] = SND_MIXER_SCHN_UNKNOWN,
349     [PA_CHANNEL_POSITION_AUX8] = SND_MIXER_SCHN_UNKNOWN,
350     [PA_CHANNEL_POSITION_AUX9] =  SND_MIXER_SCHN_UNKNOWN,
351     [PA_CHANNEL_POSITION_AUX10] = SND_MIXER_SCHN_UNKNOWN,
352     [PA_CHANNEL_POSITION_AUX11] = SND_MIXER_SCHN_UNKNOWN,
353     [PA_CHANNEL_POSITION_AUX12] = SND_MIXER_SCHN_UNKNOWN,
354     [PA_CHANNEL_POSITION_AUX13] = SND_MIXER_SCHN_UNKNOWN,
355     [PA_CHANNEL_POSITION_AUX14] = SND_MIXER_SCHN_UNKNOWN,
356     [PA_CHANNEL_POSITION_AUX15] = SND_MIXER_SCHN_UNKNOWN,
357     [PA_CHANNEL_POSITION_AUX16] = SND_MIXER_SCHN_UNKNOWN,
358     [PA_CHANNEL_POSITION_AUX17] = SND_MIXER_SCHN_UNKNOWN,
359     [PA_CHANNEL_POSITION_AUX18] = SND_MIXER_SCHN_UNKNOWN,
360     [PA_CHANNEL_POSITION_AUX19] = SND_MIXER_SCHN_UNKNOWN,
361     [PA_CHANNEL_POSITION_AUX20] = SND_MIXER_SCHN_UNKNOWN,
362     [PA_CHANNEL_POSITION_AUX21] = SND_MIXER_SCHN_UNKNOWN,
363     [PA_CHANNEL_POSITION_AUX22] = SND_MIXER_SCHN_UNKNOWN,
364     [PA_CHANNEL_POSITION_AUX23] = SND_MIXER_SCHN_UNKNOWN,
365     [PA_CHANNEL_POSITION_AUX24] = SND_MIXER_SCHN_UNKNOWN,
366     [PA_CHANNEL_POSITION_AUX25] = SND_MIXER_SCHN_UNKNOWN,
367     [PA_CHANNEL_POSITION_AUX26] = SND_MIXER_SCHN_UNKNOWN,
368     [PA_CHANNEL_POSITION_AUX27] = SND_MIXER_SCHN_UNKNOWN,
369     [PA_CHANNEL_POSITION_AUX28] = SND_MIXER_SCHN_UNKNOWN,
370     [PA_CHANNEL_POSITION_AUX29] = SND_MIXER_SCHN_UNKNOWN,
371     [PA_CHANNEL_POSITION_AUX30] = SND_MIXER_SCHN_UNKNOWN,
372     [PA_CHANNEL_POSITION_AUX31] = SND_MIXER_SCHN_UNKNOWN,
373
374     [PA_CHANNEL_POSITION_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN,
375
376     [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SND_MIXER_SCHN_UNKNOWN,
377     [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SND_MIXER_SCHN_UNKNOWN,
378     [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SND_MIXER_SCHN_UNKNOWN,
379
380     [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SND_MIXER_SCHN_UNKNOWN,
381     [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SND_MIXER_SCHN_UNKNOWN,
382     [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SND_MIXER_SCHN_UNKNOWN
383 };
384
385 static void setting_free(pa_alsa_setting *s) {
386     pa_assert(s);
387
388     if (s->options)
389         pa_idxset_free(s->options, NULL, NULL);
390
391     pa_xfree(s->name);
392     pa_xfree(s->description);
393     pa_xfree(s);
394 }
395
396 static void option_free(pa_alsa_option *o) {
397     pa_assert(o);
398
399     pa_xfree(o->alsa_name);
400     pa_xfree(o->name);
401     pa_xfree(o->description);
402     pa_xfree(o);
403 }
404
405 static void element_free(pa_alsa_element *e) {
406     pa_alsa_option *o;
407     pa_assert(e);
408
409     while ((o = e->options)) {
410         PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
411         option_free(o);
412     }
413
414     pa_xfree(e->alsa_name);
415     pa_xfree(e);
416 }
417
418 void pa_alsa_path_free(pa_alsa_path *p) {
419     pa_alsa_element *e;
420     pa_alsa_setting *s;
421
422     pa_assert(p);
423
424     while ((e = p->elements)) {
425         PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
426         element_free(e);
427     }
428
429     while ((s = p->settings)) {
430         PA_LLIST_REMOVE(pa_alsa_setting, p->settings, s);
431         setting_free(s);
432     }
433
434     pa_xfree(p->name);
435     pa_xfree(p->description);
436     pa_xfree(p);
437 }
438
439 void pa_alsa_path_set_free(pa_alsa_path_set *ps) {
440     pa_alsa_path *p;
441     pa_assert(ps);
442
443     while ((p = ps->paths)) {
444         PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
445         pa_alsa_path_free(p);
446     }
447
448     pa_xfree(ps);
449 }
450
451 static long to_alsa_dB(pa_volume_t v) {
452     return (long) (pa_sw_volume_to_dB(v) * 100.0);
453 }
454
455 static pa_volume_t from_alsa_dB(long v) {
456     return pa_sw_volume_from_dB((double) v / 100.0);
457 }
458
459 static long to_alsa_volume(pa_volume_t v, long min, long max) {
460     long w;
461
462     w = (long) round(((double) v * (double) (max - min)) / PA_VOLUME_NORM) + min;
463     return PA_CLAMP_UNLIKELY(w, min, max);
464 }
465
466 static pa_volume_t from_alsa_volume(long v, long min, long max) {
467     return (pa_volume_t) round(((double) (v - min) * PA_VOLUME_NORM) / (double) (max - min));
468 }
469
470 #define SELEM_INIT(sid, name)                           \
471     do {                                                \
472         snd_mixer_selem_id_alloca(&(sid));              \
473         snd_mixer_selem_id_set_name((sid), (name));     \
474         snd_mixer_selem_id_set_index((sid), 0);         \
475     } while(FALSE)
476
477 static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
478     snd_mixer_selem_id_t *sid;
479     snd_mixer_elem_t *me;
480     snd_mixer_selem_channel_id_t c;
481     pa_channel_position_mask_t mask = 0;
482     unsigned k;
483
484     pa_assert(m);
485     pa_assert(e);
486     pa_assert(cm);
487     pa_assert(v);
488
489     SELEM_INIT(sid, e->alsa_name);
490     if (!(me = snd_mixer_find_selem(m, sid))) {
491         pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
492         return -1;
493     }
494
495     pa_cvolume_mute(v, cm->channels);
496
497     /* We take the highest volume of all channels that match */
498
499     for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
500         int r;
501         pa_volume_t f;
502
503         if (e->has_dB) {
504             long value = 0;
505
506             if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
507                 if (snd_mixer_selem_has_playback_channel(me, c))
508                     r = snd_mixer_selem_get_playback_dB(me, c, &value);
509                 else
510                     r = -1;
511             } else {
512                 if (snd_mixer_selem_has_capture_channel(me, c))
513                     r = snd_mixer_selem_get_capture_dB(me, c, &value);
514                 else
515                     r = -1;
516             }
517
518             if (r < 0)
519                 continue;
520
521 #ifdef HAVE_VALGRIND_MEMCHECK_H
522                 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
523 #endif
524
525             f = from_alsa_dB(value);
526
527         } else {
528             long value = 0;
529
530             if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
531                 if (snd_mixer_selem_has_playback_channel(me, c))
532                     r = snd_mixer_selem_get_playback_volume(me, c, &value);
533                 else
534                     r = -1;
535             } else {
536                 if (snd_mixer_selem_has_capture_channel(me, c))
537                     r = snd_mixer_selem_get_capture_volume(me, c, &value);
538                 else
539                     r = -1;
540             }
541
542             if (r < 0)
543                 continue;
544
545             f = from_alsa_volume(value, e->min_volume, e->max_volume);
546         }
547
548         for (k = 0; k < cm->channels; k++)
549             if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
550                 if (v->values[k] < f)
551                     v->values[k] = f;
552
553         mask |= e->masks[c][e->n_channels-1];
554     }
555
556     for (k = 0; k < cm->channels; k++)
557         if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
558             v->values[k] = PA_VOLUME_NORM;
559
560     return 0;
561 }
562
563 int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
564     pa_alsa_element *e;
565
566     pa_assert(m);
567     pa_assert(p);
568     pa_assert(cm);
569     pa_assert(v);
570
571     if (!p->has_volume)
572         return -1;
573
574     pa_cvolume_reset(v, cm->channels);
575
576     PA_LLIST_FOREACH(e, p->elements) {
577         pa_cvolume ev;
578
579         if (e->volume_use != PA_ALSA_VOLUME_MERGE)
580             continue;
581
582         pa_assert(!p->has_dB || e->has_dB);
583
584         if (element_get_volume(e, m, cm, &ev) < 0)
585             return -1;
586
587         /* If we have no dB information all we can do is take the first element and leave */
588         if (!p->has_dB) {
589             *v = ev;
590             return 0;
591         }
592
593         pa_sw_cvolume_multiply(v, v, &ev);
594     }
595
596     return 0;
597 }
598
599 static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t *b) {
600     snd_mixer_selem_id_t *sid;
601     snd_mixer_elem_t *me;
602     snd_mixer_selem_channel_id_t c;
603
604     pa_assert(m);
605     pa_assert(e);
606     pa_assert(b);
607
608     SELEM_INIT(sid, e->alsa_name);
609     if (!(me = snd_mixer_find_selem(m, sid))) {
610         pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
611         return -1;
612     }
613
614     /* We return muted if at least one channel is muted */
615
616     for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
617         int r;
618         int value = 0;
619
620         if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
621             if (snd_mixer_selem_has_playback_channel(me, c))
622                 r = snd_mixer_selem_get_playback_switch(me, c, &value);
623             else
624                 r = -1;
625         } else {
626             if (snd_mixer_selem_has_capture_channel(me, c))
627                 r = snd_mixer_selem_get_capture_switch(me, c, &value);
628             else
629                 r = -1;
630         }
631
632         if (r < 0)
633             continue;
634
635         if (!value) {
636             *b = FALSE;
637             return 0;
638         }
639     }
640
641     *b = TRUE;
642     return 0;
643 }
644
645 int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t *muted) {
646     pa_alsa_element *e;
647
648     pa_assert(m);
649     pa_assert(p);
650     pa_assert(muted);
651
652     if (!p->has_mute)
653         return -1;
654
655     PA_LLIST_FOREACH(e, p->elements) {
656         pa_bool_t b;
657
658         if (e->switch_use != PA_ALSA_SWITCH_MUTE)
659             continue;
660
661         if (element_get_switch(e, m, &b) < 0)
662             return -1;
663
664         if (!b) {
665             *muted = TRUE;
666             return 0;
667         }
668     }
669
670     *muted = FALSE;
671     return 0;
672 }
673
674 static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
675     snd_mixer_selem_id_t *sid;
676     pa_cvolume rv;
677     snd_mixer_elem_t *me;
678     snd_mixer_selem_channel_id_t c;
679     pa_channel_position_mask_t mask = 0;
680     unsigned k;
681
682     pa_assert(m);
683     pa_assert(e);
684     pa_assert(cm);
685     pa_assert(v);
686     pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
687
688     SELEM_INIT(sid, e->alsa_name);
689     if (!(me = snd_mixer_find_selem(m, sid))) {
690         pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
691         return -1;
692     }
693
694     pa_cvolume_mute(&rv, cm->channels);
695
696     for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
697         int r;
698         pa_volume_t f = PA_VOLUME_MUTED;
699         pa_bool_t found = FALSE;
700
701         for (k = 0; k < cm->channels; k++)
702             if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) {
703                 found = TRUE;
704                 if (v->values[k] > f)
705                     f = v->values[k];
706             }
707
708         if (!found) {
709             /* Hmm, so this channel does not exist in the volume
710              * struct, so let's bind it to the overall max of the
711              * volume. */
712             f = pa_cvolume_max(v);
713         }
714
715         if (e->has_dB) {
716             long value = to_alsa_dB(f);
717
718             if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
719                 /* If we call set_play_volume() without checking first
720                  * if the channel is available, ALSA behaves ver
721                  * strangely and doesn't fail the call */
722                 if (snd_mixer_selem_has_playback_channel(me, c)) {
723                     if ((r = snd_mixer_selem_set_playback_dB(me, c, value, +1)) >= 0)
724                         r = snd_mixer_selem_get_playback_dB(me, c, &value);
725                 } else
726                     r = -1;
727             } else {
728                 if (snd_mixer_selem_has_capture_channel(me, c)) {
729                     if ((r = snd_mixer_selem_set_capture_dB(me, c, value, +1)) >= 0)
730                         r = snd_mixer_selem_get_capture_dB(me, c, &value);
731                 } else
732                     r = -1;
733             }
734
735             if (r < 0)
736                 continue;
737
738 #ifdef HAVE_VALGRIND_MEMCHECK_H
739             VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
740 #endif
741
742             f = from_alsa_dB(value);
743
744         } else {
745             long value;
746
747             value = to_alsa_volume(f, e->min_volume, e->max_volume);
748
749             if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
750                 if (snd_mixer_selem_has_playback_channel(me, c)) {
751                     if ((r = snd_mixer_selem_set_playback_volume(me, c, value)) >= 0)
752                         r = snd_mixer_selem_get_playback_volume(me, c, &value);
753                 } else
754                     r = -1;
755             } else {
756                 if (snd_mixer_selem_has_capture_channel(me, c)) {
757                     if ((r = snd_mixer_selem_set_capture_volume(me, c, value)) >= 0)
758                         r = snd_mixer_selem_get_capture_volume(me, c, &value);
759                 } else
760                     r = -1;
761             }
762
763             if (r < 0)
764                 continue;
765
766             f = from_alsa_volume(value, e->min_volume, e->max_volume);
767         }
768
769         for (k = 0; k < cm->channels; k++)
770             if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
771                 if (rv.values[k] < f)
772                     rv.values[k] = f;
773
774         mask |= e->masks[c][e->n_channels-1];
775     }
776
777     for (k = 0; k < cm->channels; k++)
778         if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
779             rv.values[k] = PA_VOLUME_NORM;
780
781     *v = rv;
782     return 0;
783 }
784
785 int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
786     pa_alsa_element *e;
787     pa_cvolume rv;
788
789     pa_assert(m);
790     pa_assert(p);
791     pa_assert(cm);
792     pa_assert(v);
793     pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
794
795     if (!p->has_volume)
796         return -1;
797
798     rv = *v; /* Remaining adjustment */
799     pa_cvolume_reset(v, cm->channels); /* Adjustment done */
800
801     PA_LLIST_FOREACH(e, p->elements) {
802         pa_cvolume ev;
803
804         if (e->volume_use != PA_ALSA_VOLUME_MERGE)
805             continue;
806
807         pa_assert(!p->has_dB || e->has_dB);
808
809         ev = rv;
810         if (element_set_volume(e, m, cm, &ev) < 0)
811             return -1;
812
813         if (!p->has_dB) {
814             *v = ev;
815             return 0;
816         }
817
818         pa_sw_cvolume_multiply(v, v, &ev);
819         pa_sw_cvolume_divide(&rv, &rv, &ev);
820     }
821
822     return 0;
823 }
824
825 static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t b) {
826     snd_mixer_elem_t *me;
827     snd_mixer_selem_id_t *sid;
828     int r;
829
830     pa_assert(m);
831     pa_assert(e);
832
833     SELEM_INIT(sid, e->alsa_name);
834     if (!(me = snd_mixer_find_selem(m, sid))) {
835         pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
836         return -1;
837     }
838
839     if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
840         r = snd_mixer_selem_set_playback_switch_all(me, b);
841     else
842         r = snd_mixer_selem_set_capture_switch_all(me, b);
843
844     if (r < 0)
845         pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
846
847     return r;
848 }
849
850 int pa_alsa_path_set_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t muted) {
851     pa_alsa_element *e;
852
853     pa_assert(m);
854     pa_assert(p);
855
856     if (!p->has_mute)
857         return -1;
858
859     PA_LLIST_FOREACH(e, p->elements) {
860
861         if (e->switch_use != PA_ALSA_SWITCH_MUTE)
862             continue;
863
864         if (element_set_switch(e, m, !muted) < 0)
865             return -1;
866     }
867
868     return 0;
869 }
870
871 static int element_mute_volume(pa_alsa_element *e, snd_mixer_t *m) {
872     snd_mixer_elem_t *me;
873     snd_mixer_selem_id_t *sid;
874     int r;
875
876     pa_assert(m);
877     pa_assert(e);
878
879     SELEM_INIT(sid, e->alsa_name);
880     if (!(me = snd_mixer_find_selem(m, sid))) {
881         pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
882         return -1;
883     }
884
885     if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
886         r = snd_mixer_selem_set_playback_volume_all(me, e->min_volume);
887     else
888         r = snd_mixer_selem_set_capture_volume_all(me, e->min_volume);
889
890     if (r < 0)
891         pa_log_warn("Faile to set volume to muted of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
892
893     return r;
894 }
895
896 /* The volume to 0dB */
897 static int element_zero_volume(pa_alsa_element *e, snd_mixer_t *m) {
898     snd_mixer_elem_t *me;
899     snd_mixer_selem_id_t *sid;
900     int r;
901
902     pa_assert(m);
903     pa_assert(e);
904
905     SELEM_INIT(sid, e->alsa_name);
906     if (!(me = snd_mixer_find_selem(m, sid))) {
907         pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
908         return -1;
909     }
910
911     if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
912         r = snd_mixer_selem_set_playback_dB_all(me, 0, +1);
913     else
914         r = snd_mixer_selem_set_capture_dB_all(me, 0, +1);
915
916     if (r < 0)
917         pa_log_warn("Faile to set volume to 0dB of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
918
919     return r;
920 }
921
922 int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
923     pa_alsa_element *e;
924     int r = 0;
925
926     pa_assert(m);
927     pa_assert(p);
928
929     pa_log_debug("Activating path %s", p->name);
930     pa_alsa_path_dump(p);
931
932     PA_LLIST_FOREACH(e, p->elements) {
933
934         switch (e->switch_use) {
935             case PA_ALSA_SWITCH_OFF:
936                 r = element_set_switch(e, m, FALSE);
937                 break;
938
939             case PA_ALSA_SWITCH_ON:
940                 r = element_set_switch(e, m, TRUE);
941                 break;
942
943             case PA_ALSA_SWITCH_MUTE:
944             case PA_ALSA_SWITCH_IGNORE:
945             case PA_ALSA_SWITCH_SELECT:
946                 r = 0;
947                 break;
948         }
949
950         if (r < 0)
951             return -1;
952
953         switch (e->volume_use) {
954             case PA_ALSA_VOLUME_OFF:
955                 r = element_mute_volume(e, m);
956                 break;
957
958             case PA_ALSA_VOLUME_ZERO:
959                 r = element_zero_volume(e, m);
960                 break;
961
962             case PA_ALSA_VOLUME_MERGE:
963             case PA_ALSA_VOLUME_IGNORE:
964                 r = 0;
965                 break;
966         }
967
968         if (r < 0)
969             return -1;
970     }
971
972     return 0;
973 }
974
975 static int check_required(pa_alsa_element *e, snd_mixer_elem_t *me) {
976     pa_bool_t has_switch;
977     pa_bool_t has_enumeration;
978     pa_bool_t has_volume;
979
980     pa_assert(e);
981     pa_assert(me);
982
983     if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
984         has_switch =
985             snd_mixer_selem_has_playback_switch(me) ||
986             (e->direction_try_other && snd_mixer_selem_has_capture_switch(me));
987     } else {
988         has_switch =
989             snd_mixer_selem_has_capture_switch(me) ||
990             (e->direction_try_other && snd_mixer_selem_has_playback_switch(me));
991     }
992
993     if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
994         has_volume =
995             snd_mixer_selem_has_playback_volume(me) ||
996             (e->direction_try_other && snd_mixer_selem_has_capture_volume(me));
997     } else {
998         has_volume =
999             snd_mixer_selem_has_capture_volume(me) ||
1000             (e->direction_try_other && snd_mixer_selem_has_playback_volume(me));
1001     }
1002
1003     has_enumeration = snd_mixer_selem_is_enumerated(me);
1004
1005     if ((e->required == PA_ALSA_REQUIRED_SWITCH && !has_switch) ||
1006         (e->required == PA_ALSA_REQUIRED_VOLUME && !has_volume) ||
1007         (e->required == PA_ALSA_REQUIRED_ENUMERATION && !has_enumeration))
1008         return -1;
1009
1010     if (e->required == PA_ALSA_REQUIRED_ANY && !(has_switch || has_volume || has_enumeration))
1011         return -1;
1012
1013     if ((e->required_absent == PA_ALSA_REQUIRED_SWITCH && has_switch) ||
1014         (e->required_absent == PA_ALSA_REQUIRED_VOLUME && has_volume) ||
1015         (e->required_absent == PA_ALSA_REQUIRED_ENUMERATION && has_enumeration))
1016         return -1;
1017
1018     if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration))
1019         return -1;
1020
1021     return 0;
1022 }
1023
1024 static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
1025     snd_mixer_selem_id_t *sid;
1026     snd_mixer_elem_t *me;
1027
1028     pa_assert(m);
1029     pa_assert(e);
1030
1031     SELEM_INIT(sid, e->alsa_name);
1032
1033     if (!(me = snd_mixer_find_selem(m, sid))) {
1034
1035         if (e->required != PA_ALSA_REQUIRED_IGNORE)
1036             return -1;
1037
1038         e->switch_use = PA_ALSA_SWITCH_IGNORE;
1039         e->volume_use = PA_ALSA_VOLUME_IGNORE;
1040         e->enumeration_use = PA_ALSA_VOLUME_IGNORE;
1041
1042         return 0;
1043     }
1044
1045     if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
1046         if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1047
1048             if (!snd_mixer_selem_has_playback_switch(me)) {
1049                 if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
1050                     e->direction = PA_ALSA_DIRECTION_INPUT;
1051                 else
1052                     e->switch_use = PA_ALSA_SWITCH_IGNORE;
1053             }
1054
1055         } else {
1056
1057             if (!snd_mixer_selem_has_capture_switch(me)) {
1058                 if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
1059                     e->direction = PA_ALSA_DIRECTION_OUTPUT;
1060                 else
1061                     e->switch_use = PA_ALSA_SWITCH_IGNORE;
1062             }
1063         }
1064
1065         if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
1066             e->direction_try_other = FALSE;
1067     }
1068
1069     if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1070
1071         if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1072
1073             if (!snd_mixer_selem_has_playback_volume(me)) {
1074                 if (e->direction_try_other && snd_mixer_selem_has_capture_volume(me))
1075                     e->direction = PA_ALSA_DIRECTION_INPUT;
1076                 else
1077                     e->volume_use = PA_ALSA_VOLUME_IGNORE;
1078             }
1079
1080         } else {
1081
1082             if (!snd_mixer_selem_has_capture_volume(me)) {
1083                 if (e->direction_try_other && snd_mixer_selem_has_playback_volume(me))
1084                     e->direction = PA_ALSA_DIRECTION_OUTPUT;
1085                 else
1086                     e->volume_use = PA_ALSA_VOLUME_IGNORE;
1087             }
1088         }
1089
1090         if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1091             long min_dB = 0, max_dB = 0;
1092             int r;
1093
1094             e->direction_try_other = FALSE;
1095
1096             if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1097                 e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0;
1098             else
1099                 e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0;
1100
1101             if (e->has_dB) {
1102 #ifdef HAVE_VALGRIND_MEMCHECK_H
1103                 VALGRIND_MAKE_MEM_DEFINED(&min_dB, sizeof(min_dB));
1104                 VALGRIND_MAKE_MEM_DEFINED(&max_dB, sizeof(max_dB));
1105 #endif
1106
1107                 e->min_dB = ((double) min_dB) / 100.0;
1108                 e->max_dB = ((double) max_dB) / 100.0;
1109
1110                 if (min_dB >= max_dB) {
1111                     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);
1112                     e->has_dB = FALSE;
1113                 }
1114             }
1115
1116             if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1117                 r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume);
1118             else
1119                 r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
1120
1121             if (r < 0) {
1122                 pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1123                 return -1;
1124             }
1125
1126
1127             if (e->min_volume >= e->max_volume) {
1128                 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);
1129                 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1130
1131             } else {
1132                 pa_bool_t is_mono;
1133                 pa_channel_position_t p;
1134
1135                 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1136                     is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
1137                 else
1138                     is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
1139
1140                 if (is_mono) {
1141                     e->n_channels = 1;
1142
1143                     if (!e->override_map) {
1144                         for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++)
1145                             e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
1146                         e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
1147                     }
1148
1149                     e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
1150                 } else {
1151                     e->n_channels = 0;
1152                     for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1153
1154                         if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1155                             continue;
1156
1157                         if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1158                             e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1159                         else
1160                             e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1161                     }
1162
1163                     if (e->n_channels <= 0) {
1164                         pa_log_warn("Volume element %s with no channels?", e->alsa_name);
1165                         return -1;
1166                     }
1167
1168                     if (!e->override_map) {
1169                         for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1170                             pa_bool_t has_channel;
1171
1172                             if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1173                                 continue;
1174
1175                             if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1176                                 has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1177                             else
1178                                 has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1179
1180                             e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
1181                         }
1182                     }
1183
1184                     e->merged_mask = 0;
1185                     for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++)
1186                         e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
1187                 }
1188             }
1189         }
1190
1191     }
1192
1193     if (check_required(e, me) < 0)
1194         return -1;
1195
1196     if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1197         pa_alsa_option *o;
1198
1199         PA_LLIST_FOREACH(o, e->options)
1200             o->alsa_idx = pa_streq(o->alsa_name, "on") ? 1 : 0;
1201     } else if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1202         int n;
1203         pa_alsa_option *o;
1204
1205         if ((n = snd_mixer_selem_get_enum_items(me)) < 0) {
1206             pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n));
1207             return -1;
1208         }
1209
1210         PA_LLIST_FOREACH(o, e->options) {
1211             int i;
1212
1213             for (i = 0; i < n; i++) {
1214                 char buf[128];
1215
1216                 if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
1217                     continue;
1218
1219                 if (!pa_streq(buf, o->alsa_name))
1220                     continue;
1221
1222                 o->alsa_idx = i;
1223             }
1224         }
1225     }
1226
1227     return 0;
1228 }
1229
1230 static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, pa_bool_t prefixed) {
1231     pa_alsa_element *e;
1232
1233     pa_assert(p);
1234     pa_assert(section);
1235
1236     if (prefixed) {
1237         if (!pa_startswith(section, "Element "))
1238             return NULL;
1239
1240         section += 8;
1241     }
1242
1243     /* This is not an element section, but an enum section? */
1244     if (strchr(section, ':'))
1245         return NULL;
1246
1247     if (p->last_element && pa_streq(p->last_element->alsa_name, section))
1248         return p->last_element;
1249
1250     PA_LLIST_FOREACH(e, p->elements)
1251         if (pa_streq(e->alsa_name, section))
1252             goto finish;
1253
1254     e = pa_xnew0(pa_alsa_element, 1);
1255     e->path = p;
1256     e->alsa_name = pa_xstrdup(section);
1257     e->direction = p->direction;
1258
1259     PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
1260
1261 finish:
1262     p->last_element = e;
1263     return e;
1264 }
1265
1266 static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
1267     char *en;
1268     const char *on;
1269     pa_alsa_option *o;
1270     pa_alsa_element *e;
1271
1272     if (!pa_startswith(section, "Option "))
1273         return NULL;
1274
1275     section += 7;
1276
1277     /* This is not an enum section, but an element section? */
1278     if (!(on = strchr(section, ':')))
1279         return NULL;
1280
1281     en = pa_xstrndup(section, on - section);
1282     on++;
1283
1284     if (p->last_option &&
1285         pa_streq(p->last_option->element->alsa_name, en) &&
1286         pa_streq(p->last_option->alsa_name, on)) {
1287         pa_xfree(en);
1288         return p->last_option;
1289     }
1290
1291     pa_assert_se(e = element_get(p, en, FALSE));
1292     pa_xfree(en);
1293
1294     PA_LLIST_FOREACH(o, e->options)
1295         if (pa_streq(o->alsa_name, on))
1296             goto finish;
1297
1298     o = pa_xnew0(pa_alsa_option, 1);
1299     o->element = e;
1300     o->alsa_name = pa_xstrdup(on);
1301     o->alsa_idx = -1;
1302
1303     if (p->last_option && p->last_option->element == e)
1304         PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
1305     else
1306         PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
1307
1308 finish:
1309     p->last_option = o;
1310     return o;
1311 }
1312
1313 static int element_parse_switch(
1314         const char *filename,
1315         unsigned line,
1316         const char *section,
1317         const char *lvalue,
1318         const char *rvalue,
1319         void *data,
1320         void *userdata) {
1321
1322     pa_alsa_path *p = userdata;
1323     pa_alsa_element *e;
1324
1325     pa_assert(p);
1326
1327     if (!(e = element_get(p, section, TRUE))) {
1328         pa_log("[%s:%u] Switch makes no sense in '%s'", filename, line, section);
1329         return -1;
1330     }
1331
1332     if (pa_streq(rvalue, "ignore"))
1333         e->switch_use = PA_ALSA_SWITCH_IGNORE;
1334     else if (pa_streq(rvalue, "mute"))
1335         e->switch_use = PA_ALSA_SWITCH_MUTE;
1336     else if (pa_streq(rvalue, "off"))
1337         e->switch_use = PA_ALSA_SWITCH_OFF;
1338     else if (pa_streq(rvalue, "on"))
1339         e->switch_use = PA_ALSA_SWITCH_ON;
1340     else if (pa_streq(rvalue, "select"))
1341         e->switch_use = PA_ALSA_SWITCH_SELECT;
1342     else {
1343         pa_log("[%s:%u] Switch invalid of '%s'", filename, line, section);
1344         return -1;
1345     }
1346
1347     return 0;
1348 }
1349
1350 static int element_parse_volume(
1351         const char *filename,
1352         unsigned line,
1353         const char *section,
1354         const char *lvalue,
1355         const char *rvalue,
1356         void *data,
1357         void *userdata) {
1358
1359     pa_alsa_path *p = userdata;
1360     pa_alsa_element *e;
1361
1362     pa_assert(p);
1363
1364     if (!(e = element_get(p, section, TRUE))) {
1365         pa_log("[%s:%u] Volume makes no sense in '%s'", filename, line, section);
1366         return -1;
1367     }
1368
1369     if (pa_streq(rvalue, "ignore"))
1370         e->volume_use = PA_ALSA_VOLUME_IGNORE;
1371     else if (pa_streq(rvalue, "merge"))
1372         e->volume_use = PA_ALSA_VOLUME_MERGE;
1373     else if (pa_streq(rvalue, "off"))
1374         e->volume_use = PA_ALSA_VOLUME_OFF;
1375     else if (pa_streq(rvalue, "zero"))
1376         e->volume_use = PA_ALSA_VOLUME_ZERO;
1377     else {
1378         pa_log("[%s:%u] Volume invalid of '%s'", filename, line, section);
1379         return -1;
1380     }
1381
1382     return 0;
1383 }
1384
1385 static int element_parse_enumeration(
1386         const char *filename,
1387         unsigned line,
1388         const char *section,
1389         const char *lvalue,
1390         const char *rvalue,
1391         void *data,
1392         void *userdata) {
1393
1394     pa_alsa_path *p = userdata;
1395     pa_alsa_element *e;
1396
1397     pa_assert(p);
1398
1399     if (!(e = element_get(p, section, TRUE))) {
1400         pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename, line, section);
1401         return -1;
1402     }
1403
1404     if (pa_streq(rvalue, "ignore"))
1405         e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1406     else if (pa_streq(rvalue, "select"))
1407         e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
1408     else {
1409         pa_log("[%s:%u] Enumeration invalid of '%s'", filename, line, section);
1410         return -1;
1411     }
1412
1413     return 0;
1414 }
1415
1416 static int option_parse_priority(
1417         const char *filename,
1418         unsigned line,
1419         const char *section,
1420         const char *lvalue,
1421         const char *rvalue,
1422         void *data,
1423         void *userdata) {
1424
1425     pa_alsa_path *p = userdata;
1426     pa_alsa_option *o;
1427     uint32_t prio;
1428
1429     pa_assert(p);
1430
1431     if (!(o = option_get(p, section))) {
1432         pa_log("[%s:%u] Priority makes no sense in '%s'", filename, line, section);
1433         return -1;
1434     }
1435
1436     if (pa_atou(rvalue, &prio) < 0) {
1437         pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
1438         return -1;
1439     }
1440
1441     o->priority = prio;
1442     return 0;
1443 }
1444
1445 static int option_parse_name(
1446         const char *filename,
1447         unsigned line,
1448         const char *section,
1449         const char *lvalue,
1450         const char *rvalue,
1451         void *data,
1452         void *userdata) {
1453
1454     pa_alsa_path *p = userdata;
1455     pa_alsa_option *o;
1456
1457     pa_assert(p);
1458
1459     if (!(o = option_get(p, section))) {
1460         pa_log("[%s:%u] Name makes no sense in '%s'", filename, line, section);
1461         return -1;
1462     }
1463
1464     pa_xfree(o->name);
1465     o->name = pa_xstrdup(rvalue);
1466
1467     return 0;
1468 }
1469
1470 static int element_parse_required(
1471         const char *filename,
1472         unsigned line,
1473         const char *section,
1474         const char *lvalue,
1475         const char *rvalue,
1476         void *data,
1477         void *userdata) {
1478
1479     pa_alsa_path *p = userdata;
1480     pa_alsa_element *e;
1481     pa_alsa_required_t req;
1482
1483     pa_assert(p);
1484
1485     if (!(e = element_get(p, section, TRUE))) {
1486         pa_log("[%s:%u] Required makes no sense in '%s'", filename, line, section);
1487         return -1;
1488     }
1489
1490     if (pa_streq(rvalue, "ignore"))
1491         req = PA_ALSA_REQUIRED_IGNORE;
1492     else if (pa_streq(rvalue, "switch"))
1493         req = PA_ALSA_REQUIRED_SWITCH;
1494     else if (pa_streq(rvalue, "volume"))
1495         req = PA_ALSA_REQUIRED_VOLUME;
1496     else if (pa_streq(rvalue, "enumeration"))
1497         req = PA_ALSA_REQUIRED_ENUMERATION;
1498     else if (pa_streq(rvalue, "any"))
1499         req = PA_ALSA_REQUIRED_ANY;
1500     else {
1501         pa_log("[%s:%u] Required invalid of '%s'", filename, line, section);
1502         return -1;
1503     }
1504
1505     if (pa_streq(lvalue, "required-absent"))
1506         e->required_absent = req;
1507     else
1508         e->required = req;
1509
1510     return 0;
1511 }
1512
1513 static int element_parse_direction(
1514         const char *filename,
1515         unsigned line,
1516         const char *section,
1517         const char *lvalue,
1518         const char *rvalue,
1519         void *data,
1520         void *userdata) {
1521
1522     pa_alsa_path *p = userdata;
1523     pa_alsa_element *e;
1524
1525     pa_assert(p);
1526
1527     if (!(e = element_get(p, section, TRUE))) {
1528         pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1529         return -1;
1530     }
1531
1532     if (pa_streq(rvalue, "playback"))
1533         e->direction = PA_ALSA_DIRECTION_OUTPUT;
1534     else if (pa_streq(rvalue, "capture"))
1535         e->direction = PA_ALSA_DIRECTION_INPUT;
1536     else {
1537         pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1538         return -1;
1539     }
1540
1541     return 0;
1542 }
1543
1544 static int element_parse_direction_try_other(
1545         const char *filename,
1546         unsigned line,
1547         const char *section,
1548         const char *lvalue,
1549         const char *rvalue,
1550         void *data,
1551         void *userdata) {
1552
1553     pa_alsa_path *p = userdata;
1554     pa_alsa_element *e;
1555     int yes;
1556
1557     if (!(e = element_get(p, section, TRUE))) {
1558         pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1559         return -1;
1560     }
1561
1562     if ((yes = pa_parse_boolean(rvalue)) < 0) {
1563         pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1564         return -1;
1565     }
1566
1567     e->direction_try_other = !!yes;
1568     return 0;
1569 }
1570
1571 static pa_channel_position_mask_t parse_mask(const char *m) {
1572     pa_channel_position_mask_t v;
1573
1574     if (pa_streq(m, "all-left"))
1575         v = PA_CHANNEL_POSITION_MASK_LEFT;
1576     else if (pa_streq(m, "all-right"))
1577         v = PA_CHANNEL_POSITION_MASK_RIGHT;
1578     else if (pa_streq(m, "all-center"))
1579         v = PA_CHANNEL_POSITION_MASK_CENTER;
1580     else if (pa_streq(m, "all-front"))
1581         v = PA_CHANNEL_POSITION_MASK_FRONT;
1582     else if (pa_streq(m, "all-rear"))
1583         v = PA_CHANNEL_POSITION_MASK_REAR;
1584     else if (pa_streq(m, "all-side"))
1585         v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER;
1586     else if (pa_streq(m, "all-top"))
1587         v = PA_CHANNEL_POSITION_MASK_TOP;
1588     else if (pa_streq(m, "all-no-lfe"))
1589         v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE);
1590     else if (pa_streq(m, "all"))
1591         v = PA_CHANNEL_POSITION_MASK_ALL;
1592     else {
1593         pa_channel_position_t p;
1594
1595         if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
1596             return 0;
1597
1598         v = PA_CHANNEL_POSITION_MASK(p);
1599     }
1600
1601     return v;
1602 }
1603
1604 static int element_parse_override_map(
1605         const char *filename,
1606         unsigned line,
1607         const char *section,
1608         const char *lvalue,
1609         const char *rvalue,
1610         void *data,
1611         void *userdata) {
1612
1613     pa_alsa_path *p = userdata;
1614     pa_alsa_element *e;
1615     const char *state = NULL;
1616     unsigned i = 0;
1617     char *n;
1618
1619     if (!(e = element_get(p, section, TRUE))) {
1620         pa_log("[%s:%u] Override map makes no sense in '%s'", filename, line, section);
1621         return -1;
1622     }
1623
1624     while ((n = pa_split(rvalue, ",", &state))) {
1625         pa_channel_position_mask_t m;
1626
1627         if (!*n)
1628             m = 0;
1629         else {
1630             if ((m = parse_mask(n)) == 0) {
1631                 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename, line, n, section);
1632                 pa_xfree(n);
1633                 return -1;
1634             }
1635         }
1636
1637         if (pa_streq(lvalue, "override-map.1"))
1638             e->masks[i++][0] = m;
1639         else
1640             e->masks[i++][1] = m;
1641
1642         /* Later on we might add override-map.3 and so on here ... */
1643
1644         pa_xfree(n);
1645     }
1646
1647     e->override_map = TRUE;
1648
1649     return 0;
1650 }
1651
1652 static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
1653     snd_mixer_selem_id_t *sid;
1654     snd_mixer_elem_t *me;
1655     int r;
1656
1657     pa_assert(e);
1658     pa_assert(m);
1659
1660     SELEM_INIT(sid, e->alsa_name);
1661     if (!(me = snd_mixer_find_selem(m, sid))) {
1662         pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1663         return -1;
1664     }
1665
1666     if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1667
1668         if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1669             r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
1670         else
1671             r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
1672
1673         if (r < 0)
1674             pa_log_warn("Faile to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1675
1676     } else {
1677         pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
1678
1679         if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0)
1680             pa_log_warn("Faile to set enumeration of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1681     }
1682
1683     return r;
1684 }
1685
1686 int pa_alsa_setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
1687     pa_alsa_option *o;
1688     uint32_t idx;
1689
1690     pa_assert(s);
1691     pa_assert(m);
1692
1693     PA_IDXSET_FOREACH(o, s->options, idx)
1694         element_set_option(o->element, m, o->alsa_idx);
1695
1696     return 0;
1697 }
1698
1699 static int option_verify(pa_alsa_option *o) {
1700     static const struct description_map well_known_descriptions[] = {
1701         { "input",                     N_("Input") },
1702         { "input-docking",             N_("Docking Station Input") },
1703         { "input-docking-microphone",  N_("Docking Station Microphone") },
1704         { "input-linein",              N_("Line-In") },
1705         { "input-microphone",          N_("Microphone") },
1706         { "input-microphone-external", N_("External Microphone") },
1707         { "input-microphone-internal", N_("Internal Microphone") },
1708         { "input-radio",               N_("Radio") },
1709         { "input-video",               N_("Video") },
1710         { "input-agc-on",              N_("Automatic Gain Control") },
1711         { "input-agc-off",             N_("No Automatic Gain Control") },
1712         { "input-boost-on",            N_("Boost") },
1713         { "input-boost-off",           N_("No Boost") },
1714         { "output-amplifier-on",       N_("Amplifier") },
1715         { "output-amplifier-off",      N_("No Amplifier") },
1716         { "output-bass-boost-on",      N_("Bass Boost") },
1717         { "output-bass-boost-off",     N_("No Bass Boost") },
1718         { "output-speaker",            N_("Speaker") },
1719         { "output-headphones",         N_("Headphones") }
1720     };
1721
1722     pa_assert(o);
1723
1724     if (!o->name) {
1725         pa_log("No name set for option %s", o->alsa_name);
1726         return -1;
1727     }
1728
1729     if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
1730         o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
1731         pa_log("Element %s of option %s not set for select.", o->element->alsa_name, o->name);
1732         return -1;
1733     }
1734
1735     if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
1736         !pa_streq(o->alsa_name, "on") &&
1737         !pa_streq(o->alsa_name, "off")) {
1738         pa_log("Switch %s options need be named off or on ", o->element->alsa_name);
1739         return -1;
1740     }
1741
1742     if (!o->description)
1743         o->description = pa_xstrdup(lookup_description(o->name,
1744                                                        well_known_descriptions,
1745                                                        PA_ELEMENTSOF(well_known_descriptions)));
1746     if (!o->description)
1747         o->description = pa_xstrdup(o->name);
1748
1749     return 0;
1750 }
1751
1752 static int element_verify(pa_alsa_element *e) {
1753     pa_alsa_option *o;
1754
1755     pa_assert(e);
1756
1757     if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
1758         (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
1759         pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name);
1760         return -1;
1761     }
1762
1763     if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1764         pa_log("Element %s cannot set select for both switch and enumeration.", e->alsa_name);
1765         return -1;
1766     }
1767
1768     PA_LLIST_FOREACH(o, e->options)
1769         if (option_verify(o) < 0)
1770             return -1;
1771
1772     return 0;
1773 }
1774
1775 static int path_verify(pa_alsa_path *p) {
1776     static const struct description_map well_known_descriptions[] = {
1777         { "analog-input",               N_("Analog Input") },
1778         { "analog-input-microphone",    N_("Analog Microphone") },
1779         { "analog-input-linein",        N_("Analog Line-In") },
1780         { "analog-input-radio",         N_("Analog Radio") },
1781         { "analog-input-video",         N_("Analog Video") },
1782         { "analog-output",              N_("Analog Output") },
1783         { "analog-output-headphones",   N_("Analog Headphones") },
1784         { "analog-output-lfe-on-mono",  N_("Analog Output (LFE)") },
1785         { "analog-output-mono",         N_("Analog Mono Output") },
1786         { "analog-output-headphones-2", N_("Analog Headphones 2") },
1787         { "analog-output-speaker",      N_("Analog Speaker") }
1788     };
1789
1790     pa_alsa_element *e;
1791
1792     pa_assert(p);
1793
1794     PA_LLIST_FOREACH(e, p->elements)
1795         if (element_verify(e) < 0)
1796             return -1;
1797
1798     if (!p->description)
1799         p->description = pa_xstrdup(lookup_description(p->name,
1800                                                        well_known_descriptions,
1801                                                        PA_ELEMENTSOF(well_known_descriptions)));
1802
1803     if (!p->description)
1804         p->description = pa_xstrdup(p->name);
1805
1806     return 0;
1807 }
1808
1809 pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction) {
1810     pa_alsa_path *p;
1811     char *fn;
1812     int r;
1813     const char *n;
1814
1815     pa_config_item items[] = {
1816         /* [General] */
1817         { "priority",            pa_config_parse_unsigned,          NULL, "General" },
1818         { "description",         pa_config_parse_string,            NULL, "General" },
1819         { "name",                pa_config_parse_string,            NULL, "General" },
1820
1821         /* [Option ...] */
1822         { "priority",            option_parse_priority,             NULL, NULL },
1823         { "name",                option_parse_name,                 NULL, NULL },
1824
1825         /* [Element ...] */
1826         { "switch",              element_parse_switch,              NULL, NULL },
1827         { "volume",              element_parse_volume,              NULL, NULL },
1828         { "enumeration",         element_parse_enumeration,         NULL, NULL },
1829         { "override-map.1",      element_parse_override_map,        NULL, NULL },
1830         { "override-map.2",      element_parse_override_map,        NULL, NULL },
1831         /* ... later on we might add override-map.3 and so on here ... */
1832         { "required",            element_parse_required,            NULL, NULL },
1833         { "required-absent",     element_parse_required,            NULL, NULL },
1834         { "direction",           element_parse_direction,           NULL, NULL },
1835         { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
1836         { NULL, NULL, NULL, NULL }
1837     };
1838
1839     pa_assert(fname);
1840
1841     p = pa_xnew0(pa_alsa_path, 1);
1842     n = pa_path_get_filename(fname);
1843     p->name = pa_xstrndup(n, strcspn(n, "."));
1844     p->direction = direction;
1845
1846     items[0].data = &p->priority;
1847     items[1].data = &p->description;
1848     items[2].data = &p->name;
1849
1850     fn = pa_maybe_prefix_path(fname,
1851 #if defined(__linux__) && !defined(__OPTIMIZE__)
1852                               pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/paths/" :
1853 #endif
1854                               PA_ALSA_PATHS_DIR);
1855
1856     r = pa_config_parse(fn, NULL, items, p);
1857     pa_xfree(fn);
1858
1859     if (r < 0)
1860         goto fail;
1861
1862     if (path_verify(p) < 0)
1863         goto fail;
1864
1865     return p;
1866
1867 fail:
1868     pa_alsa_path_free(p);
1869     return NULL;
1870 }
1871
1872 pa_alsa_path* pa_alsa_path_synthesize(const char*element, pa_alsa_direction_t direction) {
1873     pa_alsa_path *p;
1874     pa_alsa_element *e;
1875
1876     pa_assert(element);
1877
1878     p = pa_xnew0(pa_alsa_path, 1);
1879     p->name = pa_xstrdup(element);
1880     p->direction = direction;
1881
1882     e = pa_xnew0(pa_alsa_element, 1);
1883     e->path = p;
1884     e->alsa_name = pa_xstrdup(element);
1885     e->direction = direction;
1886
1887     e->switch_use = PA_ALSA_SWITCH_MUTE;
1888     e->volume_use = PA_ALSA_VOLUME_MERGE;
1889
1890     PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
1891     return p;
1892 }
1893
1894 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
1895     pa_alsa_option *o, *n;
1896
1897     pa_assert(e);
1898
1899     for (o = e->options; o; o = n) {
1900         n = o->next;
1901
1902         if (o->alsa_idx < 0) {
1903             PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
1904             option_free(o);
1905         }
1906     }
1907
1908     return
1909         e->switch_use != PA_ALSA_SWITCH_IGNORE ||
1910         e->volume_use != PA_ALSA_VOLUME_IGNORE ||
1911         e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
1912 }
1913
1914 static void path_drop_unsupported(pa_alsa_path *p) {
1915     pa_alsa_element *e, *n;
1916
1917     pa_assert(p);
1918
1919     for (e = p->elements; e; e = n) {
1920         n = e->next;
1921
1922         if (!element_drop_unsupported(e)) {
1923             PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
1924             element_free(e);
1925         }
1926     }
1927 }
1928
1929 static void path_make_options_unique(pa_alsa_path *p) {
1930     pa_alsa_element *e;
1931     pa_alsa_option *o, *u;
1932
1933     PA_LLIST_FOREACH(e, p->elements) {
1934         PA_LLIST_FOREACH(o, e->options) {
1935             unsigned i;
1936             char *m;
1937
1938             for (u = o->next; u; u = u->next)
1939                 if (pa_streq(u->name, o->name))
1940                     break;
1941
1942             if (!u)
1943                 continue;
1944
1945             m = pa_xstrdup(o->name);
1946
1947             /* OK, this name is not unique, hence let's rename */
1948             for (i = 1, u = o; u; u = u->next) {
1949                 char *nn, *nd;
1950
1951                 if (!pa_streq(u->name, m))
1952                     continue;
1953
1954                 nn = pa_sprintf_malloc("%s-%u", m, i);
1955                 pa_xfree(u->name);
1956                 u->name = nn;
1957
1958                 nd = pa_sprintf_malloc("%s %u", u->description, i);
1959                 pa_xfree(u->description);
1960                 u->description = nd;
1961
1962                 i++;
1963             }
1964
1965             pa_xfree(m);
1966         }
1967     }
1968 }
1969
1970 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
1971     pa_alsa_option *o;
1972
1973     for (; e; e = e->next)
1974         if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
1975             e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
1976             break;
1977
1978     if (!e)
1979         return FALSE;
1980
1981     for (o = e->options; o; o = o->next) {
1982         pa_alsa_setting *s;
1983
1984         if (template) {
1985             s = pa_xnewdup(pa_alsa_setting, template, 1);
1986             s->options = pa_idxset_copy(template->options);
1987             s->name = pa_sprintf_malloc(_("%s+%s"), template->name, o->name);
1988             s->description =
1989                 (template->description[0] && o->description[0])
1990                 ? pa_sprintf_malloc(_("%s / %s"), template->description, o->description)
1991                 : (template->description[0]
1992                    ? pa_xstrdup(template->description)
1993                    : pa_xstrdup(o->description));
1994
1995             s->priority = PA_MAX(template->priority, o->priority);
1996         } else {
1997             s = pa_xnew0(pa_alsa_setting, 1);
1998             s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1999             s->name = pa_xstrdup(o->name);
2000             s->description = pa_xstrdup(o->description);
2001             s->priority = o->priority;
2002         }
2003
2004         pa_idxset_put(s->options, o, NULL);
2005
2006         if (element_create_settings(e->next, s))
2007             /* This is not a leaf, so let's get rid of it */
2008             setting_free(s);
2009         else {
2010             /* This is a leaf, so let's add it */
2011             PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2012
2013             e->path->last_setting = s;
2014         }
2015     }
2016
2017     return TRUE;
2018 }
2019
2020 static void path_create_settings(pa_alsa_path *p) {
2021     pa_assert(p);
2022
2023     element_create_settings(p->elements, NULL);
2024 }
2025
2026 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) {
2027     pa_alsa_element *e;
2028     double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2029     pa_channel_position_t t;
2030
2031     pa_assert(p);
2032     pa_assert(m);
2033
2034     if (p->probed)
2035         return 0;
2036
2037     pa_zero(min_dB);
2038     pa_zero(max_dB);
2039
2040     pa_log_debug("Probing path '%s'", p->name);
2041
2042     PA_LLIST_FOREACH(e, p->elements) {
2043         if (element_probe(e, m) < 0) {
2044             p->supported = FALSE;
2045             pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2046             return -1;
2047         }
2048
2049         if (ignore_dB)
2050             e->has_dB = FALSE;
2051
2052         if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2053
2054             if (!p->has_volume) {
2055                 p->min_volume = e->min_volume;
2056                 p->max_volume = e->max_volume;
2057             }
2058
2059             if (e->has_dB) {
2060                 if (!p->has_volume) {
2061                     for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2062                         if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2063                             min_dB[t] = e->min_dB;
2064                             max_dB[t] = e->max_dB;
2065                         }
2066
2067                     p->has_dB = TRUE;
2068                 } else {
2069
2070                     if (p->has_dB) {
2071                         for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2072                             if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2073                                 min_dB[t] += e->min_dB;
2074                                 max_dB[t] += e->max_dB;
2075                             }
2076                     } else
2077                         /* Hmm, there's another element before us
2078                          * which cannot do dB volumes, so we we need
2079                          * to 'neutralize' this slider */
2080                         e->volume_use = PA_ALSA_VOLUME_ZERO;
2081                 }
2082             } else if (p->has_volume)
2083                 /* We can't use this volume, so let's ignore it */
2084                 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2085
2086             p->has_volume = TRUE;
2087         }
2088
2089         if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2090             p->has_mute = TRUE;
2091     }
2092
2093     path_drop_unsupported(p);
2094     path_make_options_unique(p);
2095     path_create_settings(p);
2096
2097     p->supported = TRUE;
2098     p->probed = TRUE;
2099
2100     p->min_dB = INFINITY;
2101     p->max_dB = -INFINITY;
2102
2103     for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2104         if (p->min_dB > min_dB[t])
2105             p->min_dB = min_dB[t];
2106
2107         if (p->max_dB < max_dB[t])
2108             p->max_dB = max_dB[t];
2109     }
2110
2111     return 0;
2112 }
2113
2114 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2115     pa_assert(s);
2116
2117     pa_log_debug("Setting %s (%s) priority=%u",
2118                  s->name,
2119                  pa_strnull(s->description),
2120                  s->priority);
2121 }
2122
2123 void pa_alsa_option_dump(pa_alsa_option *o) {
2124     pa_assert(o);
2125
2126     pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2127                  o->alsa_name,
2128                  pa_strnull(o->name),
2129                  pa_strnull(o->description),
2130                  o->alsa_idx,
2131                  o->priority);
2132 }
2133
2134 void pa_alsa_element_dump(pa_alsa_element *e) {
2135     pa_alsa_option *o;
2136     pa_assert(e);
2137
2138     pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, enumeration=%i, required=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s",
2139                  e->alsa_name,
2140                  e->direction,
2141                  e->switch_use,
2142                  e->volume_use,
2143                  e->enumeration_use,
2144                  e->required,
2145                  e->required_absent,
2146                  (long long unsigned) e->merged_mask,
2147                  e->n_channels,
2148                  pa_yes_no(e->override_map));
2149
2150     PA_LLIST_FOREACH(o, e->options)
2151         pa_alsa_option_dump(o);
2152 }
2153
2154 void pa_alsa_path_dump(pa_alsa_path *p) {
2155     pa_alsa_element *e;
2156     pa_alsa_setting *s;
2157     pa_assert(p);
2158
2159     pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2160                  "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2161                  p->name,
2162                  pa_strnull(p->description),
2163                  p->direction,
2164                  p->priority,
2165                  pa_yes_no(p->probed),
2166                  pa_yes_no(p->supported),
2167                  pa_yes_no(p->has_mute),
2168                  pa_yes_no(p->has_volume),
2169                  pa_yes_no(p->has_dB),
2170                  p->min_volume, p->max_volume,
2171                  p->min_dB, p->max_dB);
2172
2173     PA_LLIST_FOREACH(e, p->elements)
2174         pa_alsa_element_dump(e);
2175
2176     PA_LLIST_FOREACH(s, p->settings)
2177         pa_alsa_setting_dump(s);
2178 }
2179
2180 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2181     snd_mixer_selem_id_t *sid;
2182     snd_mixer_elem_t *me;
2183
2184     pa_assert(e);
2185     pa_assert(m);
2186     pa_assert(cb);
2187
2188     SELEM_INIT(sid, e->alsa_name);
2189     if (!(me = snd_mixer_find_selem(m, sid))) {
2190         pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2191         return;
2192     }
2193
2194     snd_mixer_elem_set_callback(me, cb);
2195     snd_mixer_elem_set_callback_private(me, userdata);
2196 }
2197
2198 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2199     pa_alsa_element *e;
2200
2201     pa_assert(p);
2202     pa_assert(m);
2203     pa_assert(cb);
2204
2205     PA_LLIST_FOREACH(e, p->elements)
2206         element_set_callback(e, m, cb, userdata);
2207 }
2208
2209 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2210     pa_alsa_path *p;
2211
2212     pa_assert(ps);
2213     pa_assert(m);
2214     pa_assert(cb);
2215
2216     PA_LLIST_FOREACH(p, ps->paths)
2217         pa_alsa_path_set_callback(p, m, cb, userdata);
2218 }
2219
2220 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction) {
2221     pa_alsa_path_set *ps;
2222     char **pn = NULL, **en = NULL, **ie;
2223
2224     pa_assert(m);
2225     pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2226
2227     if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2228         return NULL;
2229
2230     ps = pa_xnew0(pa_alsa_path_set, 1);
2231     ps->direction = direction;
2232
2233     if (direction == PA_ALSA_DIRECTION_OUTPUT)
2234         pn = m->output_path_names;
2235     else if (direction == PA_ALSA_DIRECTION_INPUT)
2236         pn = m->input_path_names;
2237
2238     if (pn) {
2239         char **in;
2240
2241         for (in = pn; *in; in++) {
2242             pa_alsa_path *p;
2243             pa_bool_t duplicate = FALSE;
2244             char **kn, *fn;
2245
2246             for (kn = pn; kn != in; kn++)
2247                 if (pa_streq(*kn, *in)) {
2248                     duplicate = TRUE;
2249                     break;
2250                 }
2251
2252             if (duplicate)
2253                 continue;
2254
2255             fn = pa_sprintf_malloc("%s.conf", *in);
2256
2257             if ((p = pa_alsa_path_new(fn, direction))) {
2258                 p->path_set = ps;
2259                 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2260                 ps->last_path = p;
2261             }
2262
2263             pa_xfree(fn);
2264         }
2265
2266         return ps;
2267     }
2268
2269     if (direction == PA_ALSA_DIRECTION_OUTPUT)
2270         en = m->output_element;
2271     else if (direction == PA_ALSA_DIRECTION_INPUT)
2272         en = m->input_element;
2273
2274     if (!en) {
2275         pa_alsa_path_set_free(ps);
2276         return NULL;
2277     }
2278
2279     for (ie = en; *ie; ie++) {
2280         char **je;
2281         pa_alsa_path *p;
2282
2283         p = pa_alsa_path_synthesize(*ie, direction);
2284         p->path_set = ps;
2285
2286         /* Mark all other passed elements for require-absent */
2287         for (je = en; *je; je++) {
2288             pa_alsa_element *e;
2289             e = pa_xnew0(pa_alsa_element, 1);
2290             e->path = p;
2291             e->alsa_name = pa_xstrdup(*je);
2292             e->direction = direction;
2293             e->required_absent = PA_ALSA_REQUIRED_ANY;
2294
2295             PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2296             p->last_element = e;
2297         }
2298
2299         PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2300         ps->last_path = p;
2301     }
2302
2303     return ps;
2304 }
2305
2306 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2307     pa_alsa_path *p;
2308     pa_assert(ps);
2309
2310     pa_log_debug("Path Set %p, direction=%i, probed=%s",
2311                  (void*) ps,
2312                  ps->direction,
2313                  pa_yes_no(ps->probed));
2314
2315     PA_LLIST_FOREACH(p, ps->paths)
2316         pa_alsa_path_dump(p);
2317 }
2318
2319 static void path_set_unify(pa_alsa_path_set *ps) {
2320     pa_alsa_path *p;
2321     pa_bool_t has_dB = TRUE, has_volume = TRUE, has_mute = TRUE;
2322     pa_assert(ps);
2323
2324     /* We have issues dealing with paths that vary too wildly. That
2325      * means for now we have to have all paths support volume/mute/dB
2326      * or none. */
2327
2328     PA_LLIST_FOREACH(p, ps->paths) {
2329         pa_assert(p->probed);
2330
2331         if (!p->has_volume)
2332             has_volume = FALSE;
2333         else if (!p->has_dB)
2334             has_dB = FALSE;
2335
2336         if (!p->has_mute)
2337             has_mute = FALSE;
2338     }
2339
2340     if (!has_volume || !has_dB || !has_mute) {
2341
2342         if (!has_volume)
2343             pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2344         else if (!has_dB)
2345             pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2346
2347         if (!has_mute)
2348             pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2349
2350         PA_LLIST_FOREACH(p, ps->paths) {
2351             if (!has_volume)
2352                 p->has_volume = FALSE;
2353             else if (!has_dB)
2354                 p->has_dB = FALSE;
2355
2356             if (!has_mute)
2357                 p->has_mute = FALSE;
2358         }
2359     }
2360 }
2361
2362 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
2363     pa_alsa_path *p, *q;
2364
2365     PA_LLIST_FOREACH(p, ps->paths) {
2366         unsigned i;
2367         char *m;
2368
2369         for (q = p->next; q; q = q->next)
2370             if (pa_streq(q->name, p->name))
2371                 break;
2372
2373         if (!q)
2374             continue;
2375
2376         m = pa_xstrdup(p->name);
2377
2378         /* OK, this name is not unique, hence let's rename */
2379         for (i = 1, q = p; q; q = q->next) {
2380             char *nn, *nd;
2381
2382             if (!pa_streq(q->name, m))
2383                 continue;
2384
2385             nn = pa_sprintf_malloc("%s-%u", m, i);
2386             pa_xfree(q->name);
2387             q->name = nn;
2388
2389             nd = pa_sprintf_malloc("%s %u", q->description, i);
2390             pa_xfree(q->description);
2391             q->description = nd;
2392
2393             i++;
2394         }
2395
2396         pa_xfree(m);
2397     }
2398 }
2399
2400 void pa_alsa_path_set_probe(pa_alsa_path_set *ps, snd_mixer_t *m, pa_bool_t ignore_dB) {
2401     pa_alsa_path *p, *n;
2402
2403     pa_assert(ps);
2404
2405     if (ps->probed)
2406         return;
2407
2408     for (p = ps->paths; p; p = n) {
2409         n = p->next;
2410
2411         if (pa_alsa_path_probe(p, m, ignore_dB) < 0) {
2412             PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
2413             pa_alsa_path_free(p);
2414         }
2415     }
2416
2417     path_set_unify(ps);
2418     path_set_make_paths_unique(ps);
2419     ps->probed = TRUE;
2420 }
2421
2422 static void mapping_free(pa_alsa_mapping *m) {
2423     pa_assert(m);
2424
2425     pa_xfree(m->name);
2426     pa_xfree(m->description);
2427
2428     pa_xstrfreev(m->device_strings);
2429     pa_xstrfreev(m->input_path_names);
2430     pa_xstrfreev(m->output_path_names);
2431     pa_xstrfreev(m->input_element);
2432     pa_xstrfreev(m->output_element);
2433
2434     pa_assert(!m->input_pcm);
2435     pa_assert(!m->output_pcm);
2436
2437     pa_xfree(m);
2438 }
2439
2440 static void profile_free(pa_alsa_profile *p) {
2441     pa_assert(p);
2442
2443     pa_xfree(p->name);
2444     pa_xfree(p->description);
2445
2446     pa_xstrfreev(p->input_mapping_names);
2447     pa_xstrfreev(p->output_mapping_names);
2448
2449     if (p->input_mappings)
2450         pa_idxset_free(p->input_mappings, NULL, NULL);
2451
2452     if (p->output_mappings)
2453         pa_idxset_free(p->output_mappings, NULL, NULL);
2454
2455     pa_xfree(p);
2456 }
2457
2458 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
2459     pa_assert(ps);
2460
2461     if (ps->profiles) {
2462         pa_alsa_profile *p;
2463
2464         while ((p = pa_hashmap_steal_first(ps->profiles)))
2465             profile_free(p);
2466
2467         pa_hashmap_free(ps->profiles, NULL, NULL);
2468     }
2469
2470     if (ps->mappings) {
2471         pa_alsa_mapping *m;
2472
2473         while ((m = pa_hashmap_steal_first(ps->mappings)))
2474             mapping_free(m);
2475
2476         pa_hashmap_free(ps->mappings, NULL, NULL);
2477     }
2478
2479     pa_xfree(ps);
2480 }
2481
2482 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
2483     pa_alsa_mapping *m;
2484
2485     if (!pa_startswith(name, "Mapping "))
2486         return NULL;
2487
2488     name += 8;
2489
2490     if ((m = pa_hashmap_get(ps->mappings, name)))
2491         return m;
2492
2493     m = pa_xnew0(pa_alsa_mapping, 1);
2494     m->profile_set = ps;
2495     m->name = pa_xstrdup(name);
2496     pa_channel_map_init(&m->channel_map);
2497
2498     pa_hashmap_put(ps->mappings, m->name, m);
2499
2500     return m;
2501 }
2502
2503 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
2504     pa_alsa_profile *p;
2505
2506     if (!pa_startswith(name, "Profile "))
2507         return NULL;
2508
2509     name += 8;
2510
2511     if ((p = pa_hashmap_get(ps->profiles, name)))
2512         return p;
2513
2514     p = pa_xnew0(pa_alsa_profile, 1);
2515     p->profile_set = ps;
2516     p->name = pa_xstrdup(name);
2517
2518     pa_hashmap_put(ps->profiles, p->name, p);
2519
2520     return p;
2521 }
2522
2523 static int mapping_parse_device_strings(
2524         const char *filename,
2525         unsigned line,
2526         const char *section,
2527         const char *lvalue,
2528         const char *rvalue,
2529         void *data,
2530         void *userdata) {
2531
2532     pa_alsa_profile_set *ps = userdata;
2533     pa_alsa_mapping *m;
2534
2535     pa_assert(ps);
2536
2537     if (!(m = mapping_get(ps, section))) {
2538         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2539         return -1;
2540     }
2541
2542     pa_xstrfreev(m->device_strings);
2543     if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
2544         pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
2545         return -1;
2546     }
2547
2548     return 0;
2549 }
2550
2551 static int mapping_parse_channel_map(
2552         const char *filename,
2553         unsigned line,
2554         const char *section,
2555         const char *lvalue,
2556         const char *rvalue,
2557         void *data,
2558         void *userdata) {
2559
2560     pa_alsa_profile_set *ps = userdata;
2561     pa_alsa_mapping *m;
2562
2563     pa_assert(ps);
2564
2565     if (!(m = mapping_get(ps, section))) {
2566         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2567         return -1;
2568     }
2569
2570     if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
2571         pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
2572         return -1;
2573     }
2574
2575     return 0;
2576 }
2577
2578 static int mapping_parse_paths(
2579         const char *filename,
2580         unsigned line,
2581         const char *section,
2582         const char *lvalue,
2583         const char *rvalue,
2584         void *data,
2585         void *userdata) {
2586
2587     pa_alsa_profile_set *ps = userdata;
2588     pa_alsa_mapping *m;
2589
2590     pa_assert(ps);
2591
2592     if (!(m = mapping_get(ps, section))) {
2593         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2594         return -1;
2595     }
2596
2597     if (pa_streq(lvalue, "paths-input")) {
2598         pa_xstrfreev(m->input_path_names);
2599         m->input_path_names = pa_split_spaces_strv(rvalue);
2600     } else {
2601         pa_xstrfreev(m->output_path_names);
2602         m->output_path_names = pa_split_spaces_strv(rvalue);
2603     }
2604
2605     return 0;
2606 }
2607
2608 static int mapping_parse_element(
2609         const char *filename,
2610         unsigned line,
2611         const char *section,
2612         const char *lvalue,
2613         const char *rvalue,
2614         void *data,
2615         void *userdata) {
2616
2617     pa_alsa_profile_set *ps = userdata;
2618     pa_alsa_mapping *m;
2619
2620     pa_assert(ps);
2621
2622     if (!(m = mapping_get(ps, section))) {
2623         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2624         return -1;
2625     }
2626
2627     if (pa_streq(lvalue, "element-input")) {
2628         pa_xstrfreev(m->input_element);
2629         m->input_element = pa_split_spaces_strv(rvalue);
2630     } else {
2631         pa_xstrfreev(m->output_element);
2632         m->output_element = pa_split_spaces_strv(rvalue);
2633     }
2634
2635     return 0;
2636 }
2637
2638 static int mapping_parse_direction(
2639         const char *filename,
2640         unsigned line,
2641         const char *section,
2642         const char *lvalue,
2643         const char *rvalue,
2644         void *data,
2645         void *userdata) {
2646
2647     pa_alsa_profile_set *ps = userdata;
2648     pa_alsa_mapping *m;
2649
2650     pa_assert(ps);
2651
2652     if (!(m = mapping_get(ps, section))) {
2653         pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2654         return -1;
2655     }
2656
2657     if (pa_streq(rvalue, "input"))
2658         m->direction = PA_ALSA_DIRECTION_INPUT;
2659     else if (pa_streq(rvalue, "output"))
2660         m->direction = PA_ALSA_DIRECTION_OUTPUT;
2661     else if (pa_streq(rvalue, "any"))
2662         m->direction = PA_ALSA_DIRECTION_ANY;
2663     else {
2664         pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
2665         return -1;
2666     }
2667
2668     return 0;
2669 }
2670
2671 static int mapping_parse_description(
2672         const char *filename,
2673         unsigned line,
2674         const char *section,
2675         const char *lvalue,
2676         const char *rvalue,
2677         void *data,
2678         void *userdata) {
2679
2680     pa_alsa_profile_set *ps = userdata;
2681     pa_alsa_profile *p;
2682     pa_alsa_mapping *m;
2683
2684     pa_assert(ps);
2685
2686     if ((m = mapping_get(ps, section))) {
2687         pa_xstrdup(m->description);
2688         m->description = pa_xstrdup(rvalue);
2689     } else if ((p = profile_get(ps, section))) {
2690         pa_xfree(p->description);
2691         p->description = pa_xstrdup(rvalue);
2692     } else {
2693         pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2694         return -1;
2695     }
2696
2697     return 0;
2698 }
2699
2700 static int mapping_parse_priority(
2701         const char *filename,
2702         unsigned line,
2703         const char *section,
2704         const char *lvalue,
2705         const char *rvalue,
2706         void *data,
2707         void *userdata) {
2708
2709     pa_alsa_profile_set *ps = userdata;
2710     pa_alsa_profile *p;
2711     pa_alsa_mapping *m;
2712     uint32_t prio;
2713
2714     pa_assert(ps);
2715
2716     if (pa_atou(rvalue, &prio) < 0) {
2717         pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
2718         return -1;
2719     }
2720
2721     if ((m = mapping_get(ps, section)))
2722         m->priority = prio;
2723     else if ((p = profile_get(ps, section)))
2724         p->priority = prio;
2725     else {
2726         pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2727         return -1;
2728     }
2729
2730     return 0;
2731 }
2732
2733 static int profile_parse_mappings(
2734         const char *filename,
2735         unsigned line,
2736         const char *section,
2737         const char *lvalue,
2738         const char *rvalue,
2739         void *data,
2740         void *userdata) {
2741
2742     pa_alsa_profile_set *ps = userdata;
2743     pa_alsa_profile *p;
2744
2745     pa_assert(ps);
2746
2747     if (!(p = profile_get(ps, section))) {
2748         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2749         return -1;
2750     }
2751
2752     if (pa_streq(lvalue, "input-mappings")) {
2753         pa_xstrfreev(p->input_mapping_names);
2754         p->input_mapping_names = pa_split_spaces_strv(rvalue);
2755     } else {
2756         pa_xstrfreev(p->output_mapping_names);
2757         p->output_mapping_names = pa_split_spaces_strv(rvalue);
2758     }
2759
2760     return 0;
2761 }
2762
2763 static int profile_parse_skip_probe(
2764         const char *filename,
2765         unsigned line,
2766         const char *section,
2767         const char *lvalue,
2768         const char *rvalue,
2769         void *data,
2770         void *userdata) {
2771
2772     pa_alsa_profile_set *ps = userdata;
2773     pa_alsa_profile *p;
2774     int b;
2775
2776     pa_assert(ps);
2777
2778     if (!(p = profile_get(ps, section))) {
2779         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2780         return -1;
2781     }
2782
2783     if ((b = pa_parse_boolean(rvalue)) < 0) {
2784         pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
2785         return -1;
2786     }
2787
2788     p->supported = b;
2789
2790     return 0;
2791 }
2792
2793 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
2794
2795     static const struct description_map well_known_descriptions[] = {
2796         { "analog-mono",            N_("Analog Mono") },
2797         { "analog-stereo",          N_("Analog Stereo") },
2798         { "analog-surround-21",     N_("Analog Surround 2.1") },
2799         { "analog-surround-30",     N_("Analog Surround 3.0") },
2800         { "analog-surround-31",     N_("Analog Surround 3.1") },
2801         { "analog-surround-40",     N_("Analog Surround 4.0") },
2802         { "analog-surround-41",     N_("Analog Surround 4.1") },
2803         { "analog-surround-50",     N_("Analog Surround 5.0") },
2804         { "analog-surround-51",     N_("Analog Surround 5.1") },
2805         { "analog-surround-61",     N_("Analog Surround 6.0") },
2806         { "analog-surround-61",     N_("Analog Surround 6.1") },
2807         { "analog-surround-70",     N_("Analog Surround 7.0") },
2808         { "analog-surround-71",     N_("Analog Surround 7.1") },
2809         { "iec958-stereo",          N_("Digital Stereo (IEC958)") },
2810         { "iec958-surround-40",     N_("Digital Surround 4.0 (IEC958)") },
2811         { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
2812         { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
2813         { "hdmi-stereo",            N_("Digital Stereo (HDMI)") }
2814     };
2815
2816     pa_assert(m);
2817
2818     if (!pa_channel_map_valid(&m->channel_map)) {
2819         pa_log("Mapping %s is missing channel map.", m->name);
2820         return -1;
2821     }
2822
2823     if (!m->device_strings) {
2824         pa_log("Mapping %s is missing device strings.", m->name);
2825         return -1;
2826     }
2827
2828     if ((m->input_path_names && m->input_element) ||
2829         (m->output_path_names && m->output_element)) {
2830         pa_log("Mapping %s must have either mixer path or mixer elment, not both.", m->name);
2831         return -1;
2832     }
2833
2834     if (!m->description)
2835         m->description = pa_xstrdup(lookup_description(m->name,
2836                                                        well_known_descriptions,
2837                                                        PA_ELEMENTSOF(well_known_descriptions)));
2838
2839     if (!m->description)
2840         m->description = pa_xstrdup(m->name);
2841
2842     if (bonus) {
2843         if (pa_channel_map_equal(&m->channel_map, bonus))
2844             m->priority += 50;
2845         else if (m->channel_map.channels == bonus->channels)
2846             m->priority += 30;
2847     }
2848
2849     return 0;
2850 }
2851
2852 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
2853     char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
2854
2855     pa_assert(m);
2856
2857     pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
2858                  m->name,
2859                  pa_strnull(m->description),
2860                  m->priority,
2861                  pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
2862                  pa_yes_no(m->supported),
2863                  m->direction);
2864 }
2865
2866 static void profile_set_add_auto_pair(
2867         pa_alsa_profile_set *ps,
2868         pa_alsa_mapping *m, /* output */
2869         pa_alsa_mapping *n  /* input */) {
2870
2871     char *name;
2872     pa_alsa_profile *p;
2873
2874     pa_assert(ps);
2875     pa_assert(m || n);
2876
2877     if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
2878         return;
2879
2880     if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
2881         return;
2882
2883     if (m && n)
2884         name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
2885     else if (m)
2886         name = pa_sprintf_malloc("output:%s", m->name);
2887     else
2888         name = pa_sprintf_malloc("input:%s", n->name);
2889
2890     if (pa_hashmap_get(ps->profiles, name)) {
2891         pa_xfree(name);
2892         return;
2893     }
2894
2895     p = pa_xnew0(pa_alsa_profile, 1);
2896     p->profile_set = ps;
2897     p->name = name;
2898
2899     if (m) {
2900         p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2901         pa_idxset_put(p->output_mappings, m, NULL);
2902         p->priority += m->priority * 100;
2903     }
2904
2905     if (n) {
2906         p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2907         pa_idxset_put(p->input_mappings, n, NULL);
2908         p->priority += n->priority;
2909     }
2910
2911     pa_hashmap_put(ps->profiles, p->name, p);
2912 }
2913
2914 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
2915     pa_alsa_mapping *m, *n;
2916     void *m_state, *n_state;
2917
2918     pa_assert(ps);
2919
2920     PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
2921         profile_set_add_auto_pair(ps, m, NULL);
2922
2923         PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
2924             profile_set_add_auto_pair(ps, m, n);
2925     }
2926
2927     PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
2928         profile_set_add_auto_pair(ps, NULL, n);
2929 }
2930
2931 static int profile_verify(pa_alsa_profile *p) {
2932
2933     static const struct description_map well_known_descriptions[] = {
2934         { "output:analog-mono+input:analog-mono",     N_("Analog Mono Duplex") },
2935         { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
2936         { "output:iec958-stereo",                     N_("Digital Stereo Duplex (IEC958)") },
2937         { "off",                                      N_("Off") }
2938     };
2939
2940     pa_assert(p);
2941
2942     /* Replace the output mapping names by the actual mappings */
2943     if (p->output_mapping_names) {
2944         char **name;
2945
2946         pa_assert(!p->output_mappings);
2947         p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2948
2949         for (name = p->output_mapping_names; *name; name++) {
2950             pa_alsa_mapping *m;
2951             char **in;
2952             pa_bool_t duplicate = FALSE;
2953
2954             for (in = name + 1; *in; in++)
2955                 if (pa_streq(*name, *in)) {
2956                     duplicate = TRUE;
2957                     break;
2958                 }
2959
2960             if (duplicate)
2961                 continue;
2962
2963             if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
2964                 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
2965                 return -1;
2966             }
2967
2968             pa_idxset_put(p->output_mappings, m, NULL);
2969
2970             if (p->supported)
2971                 m->supported++;
2972         }
2973
2974         pa_xstrfreev(p->output_mapping_names);
2975         p->output_mapping_names = NULL;
2976     }
2977
2978     /* Replace the input mapping names by the actual mappings */
2979     if (p->input_mapping_names) {
2980         char **name;
2981
2982         pa_assert(!p->input_mappings);
2983         p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2984
2985         for (name = p->input_mapping_names; *name; name++) {
2986             pa_alsa_mapping *m;
2987             char **in;
2988             pa_bool_t duplicate = FALSE;
2989
2990             for (in = name + 1; *in; in++)
2991                 if (pa_streq(*name, *in)) {
2992                     duplicate = TRUE;
2993                     break;
2994                 }
2995
2996             if (duplicate)
2997                 continue;
2998
2999             if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
3000                 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
3001                 return -1;
3002             }
3003
3004             pa_idxset_put(p->input_mappings, m, NULL);
3005
3006             if (p->supported)
3007                 m->supported++;
3008         }
3009
3010         pa_xstrfreev(p->input_mapping_names);
3011         p->input_mapping_names = NULL;
3012     }
3013
3014     if (!p->input_mappings && !p->output_mappings) {
3015         pa_log("Profile '%s' lacks mappings.", p->name);
3016         return -1;
3017     }
3018
3019     if (!p->description)
3020         p->description = pa_xstrdup(lookup_description(p->name,
3021                                                        well_known_descriptions,
3022                                                        PA_ELEMENTSOF(well_known_descriptions)));
3023
3024     if (!p->description) {
3025         pa_strbuf *sb;
3026         uint32_t idx;
3027         pa_alsa_mapping *m;
3028
3029         sb = pa_strbuf_new();
3030
3031         if (p->output_mappings)
3032             PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3033                 if (!pa_strbuf_isempty(sb))
3034                     pa_strbuf_puts(sb, " + ");
3035
3036                 pa_strbuf_printf(sb, "%s Output", m->description);
3037             }
3038
3039         if (p->input_mappings)
3040             PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3041                 if (!pa_strbuf_isempty(sb))
3042                     pa_strbuf_puts(sb, " + ");
3043
3044                 pa_strbuf_printf(sb, "%s Input", m->description);
3045             }
3046
3047         p->description = pa_strbuf_tostring_free(sb);
3048     }
3049
3050     return 0;
3051 }
3052
3053 void pa_alsa_profile_dump(pa_alsa_profile *p) {
3054     uint32_t idx;
3055     pa_alsa_mapping *m;
3056     pa_assert(p);
3057
3058     pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3059                  p->name,
3060                  pa_strnull(p->description),
3061                  p->priority,
3062                  pa_yes_no(p->supported),
3063                  p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
3064                  p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
3065
3066     if (p->input_mappings)
3067         PA_IDXSET_FOREACH(m, p->input_mappings, idx)
3068             pa_log_debug("Input %s", m->name);
3069
3070     if (p->output_mappings)
3071         PA_IDXSET_FOREACH(m, p->output_mappings, idx)
3072             pa_log_debug("Output %s", m->name);
3073 }
3074
3075 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
3076     pa_alsa_profile_set *ps;
3077     pa_alsa_profile *p;
3078     pa_alsa_mapping *m;
3079     char *fn;
3080     int r;
3081     void *state;
3082
3083     static pa_config_item items[] = {
3084         /* [General] */
3085         { "auto-profiles",          pa_config_parse_bool,         NULL, "General" },
3086
3087         /* [Mapping ...] */
3088         { "device-strings",         mapping_parse_device_strings, NULL, NULL },
3089         { "channel-map",            mapping_parse_channel_map,    NULL, NULL },
3090         { "paths-input",            mapping_parse_paths,          NULL, NULL },
3091         { "paths-output",           mapping_parse_paths,          NULL, NULL },
3092         { "element-input",          mapping_parse_element,        NULL, NULL },
3093         { "element-output",         mapping_parse_element,        NULL, NULL },
3094         { "direction",              mapping_parse_direction,      NULL, NULL },
3095
3096         /* Shared by [Mapping ...] and [Profile ...] */
3097         { "description",            mapping_parse_description,    NULL, NULL },
3098         { "priority",               mapping_parse_priority,       NULL, NULL },
3099
3100         /* [Profile ...] */
3101         { "input-mappings",         profile_parse_mappings,       NULL, NULL },
3102         { "output-mappings",        profile_parse_mappings,       NULL, NULL },
3103         { "skip-probe",             profile_parse_skip_probe,     NULL, NULL },
3104         { NULL, NULL, NULL, NULL }
3105     };
3106
3107     ps = pa_xnew0(pa_alsa_profile_set, 1);
3108     ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3109     ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3110
3111     items[0].data = &ps->auto_profiles;
3112
3113     if (!fname)
3114         fname = "default.conf";
3115
3116     fn = pa_maybe_prefix_path(fname,
3117 #if defined(__linux__) && !defined(__OPTIMIZE__)
3118                               pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
3119 #endif
3120                               PA_ALSA_PROFILE_SETS_DIR);
3121
3122     r = pa_config_parse(fn, NULL, items, ps);
3123     pa_xfree(fn);
3124
3125     if (r < 0)
3126         goto fail;
3127
3128     PA_HASHMAP_FOREACH(m, ps->mappings, state)
3129         if (mapping_verify(m, bonus) < 0)
3130             goto fail;
3131
3132     if (ps->auto_profiles)
3133         profile_set_add_auto(ps);
3134
3135     PA_HASHMAP_FOREACH(p, ps->profiles, state)
3136         if (profile_verify(p) < 0)
3137             goto fail;
3138
3139     return ps;
3140
3141 fail:
3142     pa_alsa_profile_set_free(ps);
3143     return NULL;
3144 }
3145
3146 void pa_alsa_profile_set_probe(
3147         pa_alsa_profile_set *ps,
3148         const char *dev_id,
3149         const pa_sample_spec *ss,
3150         unsigned default_n_fragments,
3151         unsigned default_fragment_size_msec) {
3152
3153     void *state;
3154     pa_alsa_profile *p, *last = NULL;
3155     pa_alsa_mapping *m;
3156
3157     pa_assert(ps);
3158     pa_assert(dev_id);
3159     pa_assert(ss);
3160
3161     if (ps->probed)
3162         return;
3163
3164     PA_HASHMAP_FOREACH(p, ps->profiles, state) {
3165         pa_sample_spec try_ss;
3166         pa_channel_map try_map;
3167         snd_pcm_uframes_t try_period_size, try_buffer_size;
3168         uint32_t idx;
3169
3170         /* Is this already marked that it is supported? (i.e. from the config file) */
3171         if (p->supported)
3172             continue;
3173
3174         pa_log_debug("Looking at profile %s", p->name);
3175
3176         /* Close PCMs from the last iteration we don't need anymore */
3177         if (last && last->output_mappings)
3178             PA_IDXSET_FOREACH(m, last->output_mappings, idx) {
3179
3180                 if (!m->output_pcm)
3181                     break;
3182
3183                 if (last->supported)
3184                     m->supported++;
3185
3186                 if (!p->output_mappings || !pa_idxset_get_by_data(p->output_mappings, m, NULL)) {
3187                     snd_pcm_close(m->output_pcm);
3188                     m->output_pcm = NULL;
3189                 }
3190             }
3191
3192         if (last && last->input_mappings)
3193             PA_IDXSET_FOREACH(m, last->input_mappings, idx) {
3194
3195                 if (!m->input_pcm)
3196                     break;
3197
3198                 if (last->supported)
3199                     m->supported++;
3200
3201                 if (!p->input_mappings || !pa_idxset_get_by_data(p->input_mappings, m, NULL)) {
3202                     snd_pcm_close(m->input_pcm);
3203                     m->input_pcm = NULL;
3204                 }
3205             }
3206
3207         p->supported = TRUE;
3208
3209         /* Check if we can open all new ones */
3210         if (p->output_mappings)
3211             PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3212
3213                 if (m->output_pcm)
3214                     continue;
3215
3216                 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
3217                 try_map = m->channel_map;
3218                 try_ss = *ss;
3219                 try_ss.channels = try_map.channels;
3220
3221                 try_period_size =
3222                     pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
3223                     pa_frame_size(&try_ss);
3224                 try_buffer_size = default_n_fragments * try_period_size;
3225
3226                 if (!(m ->output_pcm = pa_alsa_open_by_template(
3227                               m->device_strings,
3228                               dev_id,
3229                               NULL,
3230                               &try_ss, &try_map,
3231                               SND_PCM_STREAM_PLAYBACK,
3232                               &try_period_size, &try_buffer_size, 0, NULL, NULL,
3233                               TRUE))) {
3234                     p->supported = FALSE;
3235                     break;
3236                 }
3237             }
3238
3239         if (p->input_mappings && p->supported)
3240             PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3241
3242                 if (m->input_pcm)
3243                     continue;
3244
3245                 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
3246                 try_map = m->channel_map;
3247                 try_ss = *ss;
3248                 try_ss.channels = try_map.channels;
3249
3250                 try_period_size =
3251                     pa_usec_to_bytes(default_fragment_size_msec*PA_USEC_PER_MSEC, &try_ss) /
3252                     pa_frame_size(&try_ss);
3253                 try_buffer_size = default_n_fragments * try_period_size;
3254
3255                 if (!(m ->input_pcm = pa_alsa_open_by_template(
3256                               m->device_strings,
3257                               dev_id,
3258                               NULL,
3259                               &try_ss, &try_map,
3260                               SND_PCM_STREAM_CAPTURE,
3261                               &try_period_size, &try_buffer_size, 0, NULL, NULL,
3262                               TRUE))) {
3263                     p->supported = FALSE;
3264                     break;
3265                 }
3266             }
3267
3268         last = p;
3269
3270         if (p->supported)
3271             pa_log_debug("Profile %s supported.", p->name);
3272     }
3273
3274     /* Clean up */
3275     if (last) {
3276         uint32_t idx;
3277
3278         if (last->output_mappings)
3279             PA_IDXSET_FOREACH(m, last->output_mappings, idx)
3280                 if (m->output_pcm) {
3281
3282                     if (last->supported)
3283                         m->supported++;
3284
3285                     snd_pcm_close(m->output_pcm);
3286                     m->output_pcm = NULL;
3287                 }
3288
3289         if (last->input_mappings)
3290             PA_IDXSET_FOREACH(m, last->input_mappings, idx)
3291                 if (m->input_pcm) {
3292
3293                     if (last->supported)
3294                         m->supported++;
3295
3296                     snd_pcm_close(m->input_pcm);
3297                     m->input_pcm = NULL;
3298                 }
3299     }
3300
3301     PA_HASHMAP_FOREACH(p, ps->profiles, state)
3302         if (!p->supported) {
3303             pa_hashmap_remove(ps->profiles, p->name);
3304             profile_free(p);
3305         }
3306
3307     PA_HASHMAP_FOREACH(m, ps->mappings, state)
3308         if (m->supported <= 0) {
3309             pa_hashmap_remove(ps->mappings, m->name);
3310             mapping_free(m);
3311         }
3312
3313     ps->probed = TRUE;
3314 }
3315
3316 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
3317     pa_alsa_profile *p;
3318     pa_alsa_mapping *m;
3319     void *state;
3320
3321     pa_assert(ps);
3322
3323     pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u",
3324                  (void*)
3325                  ps,
3326                  pa_yes_no(ps->auto_profiles),
3327                  pa_yes_no(ps->probed),
3328                  pa_hashmap_size(ps->mappings),
3329                  pa_hashmap_size(ps->profiles));
3330
3331     PA_HASHMAP_FOREACH(m, ps->mappings, state)
3332         pa_alsa_mapping_dump(m);
3333
3334     PA_HASHMAP_FOREACH(p, ps->profiles, state)
3335         pa_alsa_profile_dump(p);
3336 }
3337
3338 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps) {
3339     pa_alsa_path *path;
3340
3341     pa_assert(p);
3342     pa_assert(!*p);
3343     pa_assert(ps);
3344
3345     /* if there is no path, we don't want a port list */
3346     if (!ps->paths)
3347         return;
3348
3349     if (!ps->paths->next){
3350         pa_alsa_setting *s;
3351
3352         /* If there is only one path, but no or only one setting, then
3353          * we want a port list either */
3354         if (!ps->paths->settings || !ps->paths->settings->next)
3355             return;
3356
3357         /* Ok, there is only one path, however with multiple settings,
3358          * so let's create a port for each setting */
3359         *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3360
3361         PA_LLIST_FOREACH(s, ps->paths->settings) {
3362             pa_device_port *port;
3363             pa_alsa_port_data *data;
3364
3365             port = pa_device_port_new(s->name, s->description, sizeof(pa_alsa_port_data));
3366             port->priority = s->priority;
3367
3368             data = PA_DEVICE_PORT_DATA(port);
3369             data->path = ps->paths;
3370             data->setting = s;
3371
3372             pa_hashmap_put(*p, port->name, port);
3373         }
3374
3375     } else {
3376
3377         /* We have multiple paths, so let's create a port for each
3378          * one, and each of each settings */
3379         *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3380
3381         PA_LLIST_FOREACH(path, ps->paths) {
3382
3383             if (!path->settings || !path->settings->next) {
3384                 pa_device_port *port;
3385                 pa_alsa_port_data *data;
3386
3387                 /* If there is no or just one setting we only need a
3388                  * single entry */
3389
3390                 port = pa_device_port_new(path->name, path->description, sizeof(pa_alsa_port_data));
3391                 port->priority = path->priority * 100;
3392
3393
3394                 data = PA_DEVICE_PORT_DATA(port);
3395                 data->path = path;
3396                 data->setting = path->settings;
3397
3398                 pa_hashmap_put(*p, port->name, port);
3399             } else {
3400                 pa_alsa_setting *s;
3401
3402                 PA_LLIST_FOREACH(s, path->settings) {
3403                     pa_device_port *port;
3404                     pa_alsa_port_data *data;
3405                     char *n, *d;
3406
3407                     n = pa_sprintf_malloc("%s;%s", path->name, s->name);
3408
3409                     if (s->description[0])
3410                         d = pa_sprintf_malloc(_("%s / %s"), path->description, s->description);
3411                     else
3412                         d = pa_xstrdup(path->description);
3413
3414                     port = pa_device_port_new(n, d, sizeof(pa_alsa_port_data));
3415                     port->priority = path->priority * 100 + s->priority;
3416
3417                     pa_xfree(n);
3418                     pa_xfree(d);
3419
3420                     data = PA_DEVICE_PORT_DATA(port);
3421                     data->path = path;
3422                     data->setting = s;
3423
3424                     pa_hashmap_put(*p, port->name, port);
3425                 }
3426             }
3427         }
3428     }
3429
3430     pa_log_debug("Added %u ports", pa_hashmap_size(*p));
3431 }