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