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