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