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