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