28022314de08673588ab6fc642a9761ccc8ed835
[platform/upstream/pulseaudio.git] / src / modules / alsa / alsa-mixer.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2004-2009 Lennart Poettering
5   Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7   PulseAudio is free software; you can redistribute it and/or modify
8   it under the terms of the GNU Lesser General Public License as published
9   by the Free Software Foundation; either version 2.1 of the License,
10   or (at your option) any later version.
11
12   PulseAudio is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with PulseAudio; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20   USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <sys/types.h>
28 #include <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",
2907                  (void*) ps,
2908                  ps->direction);
2909
2910     PA_HASHMAP_FOREACH(p, ps->paths, state)
2911         pa_alsa_path_dump(p);
2912 }
2913
2914
2915 static pa_bool_t options_have_option(pa_alsa_option *options, const char *alsa_name) {
2916     pa_alsa_option *o;
2917
2918     pa_assert(options);
2919     pa_assert(alsa_name);
2920
2921     PA_LLIST_FOREACH(o, options) {
2922         if (pa_streq(o->alsa_name, alsa_name))
2923             return TRUE;
2924     }
2925     return FALSE;
2926 }
2927
2928 static pa_bool_t enumeration_is_subset(pa_alsa_option *a_options, pa_alsa_option *b_options) {
2929     pa_alsa_option *oa, *ob;
2930
2931     if (!a_options) return TRUE;
2932     if (!b_options) return FALSE;
2933
2934     /* If there is an option A offers that B does not, then A is not a subset of B. */
2935     PA_LLIST_FOREACH(oa, a_options) {
2936         pa_bool_t found = FALSE;
2937         PA_LLIST_FOREACH(ob, b_options) {
2938             if (pa_streq(oa->alsa_name, ob->alsa_name)) {
2939                 found = TRUE;
2940                 break;
2941             }
2942         }
2943         if (!found)
2944             return FALSE;
2945     }
2946     return TRUE;
2947 }
2948
2949 /**
2950  *  Compares two elements to see if a is a subset of b
2951  */
2952 static pa_bool_t element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_t *m) {
2953     pa_assert(a);
2954     pa_assert(b);
2955     pa_assert(m);
2956
2957     /* General rules:
2958      * Every state is a subset of itself (with caveats for volume_limits and options)
2959      * IGNORE is a subset of every other state */
2960
2961     /* Check the volume_use */
2962     if (a->volume_use != PA_ALSA_VOLUME_IGNORE) {
2963
2964         /* "Constant" is subset of "Constant" only when their constant values are equal */
2965         if (a->volume_use == PA_ALSA_VOLUME_CONSTANT && b->volume_use == PA_ALSA_VOLUME_CONSTANT && a->constant_volume != b->constant_volume)
2966             return FALSE;
2967
2968         /* Different volume uses when b is not "Merge" means we are definitely not a subset */
2969         if (a->volume_use != b->volume_use && b->volume_use != PA_ALSA_VOLUME_MERGE)
2970             return FALSE;
2971
2972         /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
2973          * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
2974          * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
2975         if (b->volume_use == PA_ALSA_VOLUME_MERGE && b->volume_limit >= 0) {
2976             long a_limit;
2977
2978             if (a->volume_use == PA_ALSA_VOLUME_CONSTANT)
2979                 a_limit = a->constant_volume;
2980             else if (a->volume_use == PA_ALSA_VOLUME_ZERO) {
2981                 long dB = 0;
2982
2983                 if (a->db_fix) {
2984                     int rounding = (a->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1);
2985                     a_limit = decibel_fix_get_step(a->db_fix, &dB, rounding);
2986                 } else {
2987                     snd_mixer_selem_id_t *sid;
2988                     snd_mixer_elem_t *me;
2989
2990                     SELEM_INIT(sid, a->alsa_name);
2991                     if (!(me = snd_mixer_find_selem(m, sid))) {
2992                         pa_log_warn("Element %s seems to have disappeared.", a->alsa_name);
2993                         return FALSE;
2994                     }
2995
2996                     if (a->direction == PA_ALSA_DIRECTION_OUTPUT) {
2997                         if (snd_mixer_selem_ask_playback_dB_vol(me, dB, +1, &a_limit) < 0)
2998                             return FALSE;
2999                     } else {
3000                         if (snd_mixer_selem_ask_capture_dB_vol(me, dB, -1, &a_limit) < 0)
3001                             return FALSE;
3002                     }
3003                 }
3004             } else if (a->volume_use == PA_ALSA_VOLUME_OFF)
3005                 a_limit = a->min_volume;
3006             else if (a->volume_use == PA_ALSA_VOLUME_MERGE)
3007                 a_limit = a->volume_limit;
3008             else
3009                 /* This should never be reached */
3010                 pa_assert(FALSE);
3011
3012             if (a_limit > b->volume_limit)
3013                 return FALSE;
3014         }
3015     }
3016
3017     if (a->switch_use != PA_ALSA_SWITCH_IGNORE) {
3018         /* "On" is a subset of "Mute".
3019          * "Off" is a subset of "Mute".
3020          * "On" is a subset of "Select", if there is an "Option:On" in B.
3021          * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3022          * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3023
3024         if (a->switch_use != b->switch_use) {
3025
3026             if (a->switch_use == PA_ALSA_SWITCH_SELECT || a->switch_use == PA_ALSA_SWITCH_MUTE
3027                 || b->switch_use == PA_ALSA_SWITCH_OFF || b->switch_use == PA_ALSA_SWITCH_ON)
3028                 return FALSE;
3029
3030             if (b->switch_use == PA_ALSA_SWITCH_SELECT) {
3031                 if (a->switch_use == PA_ALSA_SWITCH_ON) {
3032                     if (!options_have_option(b->options, "on"))
3033                         return FALSE;
3034                 } else if (a->switch_use == PA_ALSA_SWITCH_OFF) {
3035                     if (!options_have_option(b->options, "off"))
3036                         return FALSE;
3037                 }
3038             }
3039         } else if (a->switch_use == PA_ALSA_SWITCH_SELECT) {
3040             if (!enumeration_is_subset(a->options, b->options))
3041                 return FALSE;
3042         }
3043     }
3044
3045     if (a->enumeration_use != PA_ALSA_ENUMERATION_IGNORE) {
3046         if (b->enumeration_use == PA_ALSA_ENUMERATION_IGNORE)
3047             return FALSE;
3048         if (!enumeration_is_subset(a->options, b->options))
3049             return FALSE;
3050     }
3051
3052     return TRUE;
3053 }
3054
3055 static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
3056     pa_alsa_path *p;
3057     void *state;
3058
3059     pa_assert(ps);
3060     pa_assert(m);
3061
3062     /* If we only have one path, then don't bother */
3063     if (pa_hashmap_size(ps->paths) < 2)
3064         return;
3065
3066     PA_HASHMAP_FOREACH(p, ps->paths, state) {
3067         pa_alsa_path *p2;
3068         void *state2;
3069
3070         PA_HASHMAP_FOREACH(p2, ps->paths, state2) {
3071             pa_alsa_element *ea, *eb;
3072             pa_bool_t is_subset = TRUE;
3073
3074             if (p == p2)
3075                 continue;
3076
3077             /* Compare the elements of each set... */
3078             pa_assert_se(ea = p->elements);
3079             pa_assert_se(eb = p2->elements);
3080
3081             while (is_subset) {
3082                 if (pa_streq(ea->alsa_name, eb->alsa_name)) {
3083                     if (element_is_subset(ea, eb, m)) {
3084                         ea = ea->next;
3085                         eb = eb->next;
3086                         if ((ea && !eb) || (!ea && eb))
3087                             is_subset = FALSE;
3088                         else if (!ea && !eb)
3089                             break;
3090                     } else
3091                         is_subset = FALSE;
3092
3093                 } else
3094                     is_subset = FALSE;
3095             }
3096
3097             if (is_subset) {
3098                 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p->name, p2->name);
3099                 pa_hashmap_remove(ps->paths, p);
3100                 break;
3101             }
3102         }
3103     }
3104 }
3105
3106 static pa_alsa_path* path_set_find_path_by_name(pa_alsa_path_set *ps, const char* name, pa_alsa_path *ignore)
3107 {
3108     pa_alsa_path* p;
3109     void *state;
3110
3111     PA_HASHMAP_FOREACH(p, ps->paths, state)
3112         if (p != ignore && pa_streq(p->name, name))
3113             return p;
3114     return NULL;
3115 }
3116
3117 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
3118     pa_alsa_path *p, *q;
3119     void *state, *state2;
3120
3121     PA_HASHMAP_FOREACH(p, ps->paths, state) {
3122         unsigned i;
3123         char *m;
3124
3125         q = path_set_find_path_by_name(ps, p->name, p);
3126
3127         if (!q)
3128             continue;
3129
3130         m = pa_xstrdup(p->name);
3131
3132         /* OK, this name is not unique, hence let's rename */
3133         i = 1;
3134         PA_HASHMAP_FOREACH(q, ps->paths, state2) {
3135             char *nn, *nd;
3136
3137             if (!pa_streq(q->name, m))
3138                 continue;
3139
3140             nn = pa_sprintf_malloc("%s-%u", m, i);
3141             pa_xfree(q->name);
3142             q->name = nn;
3143
3144             nd = pa_sprintf_malloc("%s %u", q->description, i);
3145             pa_xfree(q->description);
3146             q->description = nd;
3147
3148             i++;
3149         }
3150
3151         pa_xfree(m);
3152     }
3153 }
3154
3155 static void mapping_free(pa_alsa_mapping *m) {
3156     pa_assert(m);
3157
3158     pa_xfree(m->name);
3159     pa_xfree(m->description);
3160
3161     pa_xstrfreev(m->device_strings);
3162     pa_xstrfreev(m->input_path_names);
3163     pa_xstrfreev(m->output_path_names);
3164     pa_xstrfreev(m->input_element);
3165     pa_xstrfreev(m->output_element);
3166     if (m->input_path_set)
3167         pa_alsa_path_set_free(m->input_path_set);
3168     if (m->output_path_set)
3169         pa_alsa_path_set_free(m->output_path_set);
3170
3171     pa_assert(!m->input_pcm);
3172     pa_assert(!m->output_pcm);
3173
3174     pa_xfree(m);
3175 }
3176
3177 static void profile_free(pa_alsa_profile *p) {
3178     pa_assert(p);
3179
3180     pa_xfree(p->name);
3181     pa_xfree(p->description);
3182
3183     pa_xstrfreev(p->input_mapping_names);
3184     pa_xstrfreev(p->output_mapping_names);
3185
3186     if (p->input_mappings)
3187         pa_idxset_free(p->input_mappings, NULL, NULL);
3188
3189     if (p->output_mappings)
3190         pa_idxset_free(p->output_mappings, NULL, NULL);
3191
3192     pa_xfree(p);
3193 }
3194
3195 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
3196     pa_assert(ps);
3197
3198     if (ps->input_paths) {
3199         pa_alsa_path *p;
3200
3201         while ((p = pa_hashmap_steal_first(ps->input_paths)))
3202             pa_alsa_path_free(p);
3203
3204         pa_hashmap_free(ps->input_paths, NULL, NULL);
3205     }
3206
3207     if (ps->output_paths) {
3208         pa_alsa_path *p;
3209
3210         while ((p = pa_hashmap_steal_first(ps->output_paths)))
3211             pa_alsa_path_free(p);
3212
3213         pa_hashmap_free(ps->output_paths, NULL, NULL);
3214     }
3215
3216     if (ps->profiles) {
3217         pa_alsa_profile *p;
3218
3219         while ((p = pa_hashmap_steal_first(ps->profiles)))
3220             profile_free(p);
3221
3222         pa_hashmap_free(ps->profiles, NULL, NULL);
3223     }
3224
3225     if (ps->mappings) {
3226         pa_alsa_mapping *m;
3227
3228         while ((m = pa_hashmap_steal_first(ps->mappings)))
3229             mapping_free(m);
3230
3231         pa_hashmap_free(ps->mappings, NULL, NULL);
3232     }
3233
3234     if (ps->decibel_fixes) {
3235         pa_alsa_decibel_fix *db_fix;
3236
3237         while ((db_fix = pa_hashmap_steal_first(ps->decibel_fixes)))
3238             decibel_fix_free(db_fix);
3239
3240         pa_hashmap_free(ps->decibel_fixes, NULL, NULL);
3241     }
3242
3243     pa_xfree(ps);
3244 }
3245
3246 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
3247     pa_alsa_mapping *m;
3248
3249     if (!pa_startswith(name, "Mapping "))
3250         return NULL;
3251
3252     name += 8;
3253
3254     if ((m = pa_hashmap_get(ps->mappings, name)))
3255         return m;
3256
3257     m = pa_xnew0(pa_alsa_mapping, 1);
3258     m->profile_set = ps;
3259     m->name = pa_xstrdup(name);
3260     pa_channel_map_init(&m->channel_map);
3261
3262     pa_hashmap_put(ps->mappings, m->name, m);
3263
3264     return m;
3265 }
3266
3267 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
3268     pa_alsa_profile *p;
3269
3270     if (!pa_startswith(name, "Profile "))
3271         return NULL;
3272
3273     name += 8;
3274
3275     if ((p = pa_hashmap_get(ps->profiles, name)))
3276         return p;
3277
3278     p = pa_xnew0(pa_alsa_profile, 1);
3279     p->profile_set = ps;
3280     p->name = pa_xstrdup(name);
3281
3282     pa_hashmap_put(ps->profiles, p->name, p);
3283
3284     return p;
3285 }
3286
3287 static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) {
3288     pa_alsa_decibel_fix *db_fix;
3289
3290     if (!pa_startswith(name, "DecibelFix "))
3291         return NULL;
3292
3293     name += 11;
3294
3295     if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name)))
3296         return db_fix;
3297
3298     db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
3299     db_fix->profile_set = ps;
3300     db_fix->name = pa_xstrdup(name);
3301
3302     pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix);
3303
3304     return db_fix;
3305 }
3306
3307 static int mapping_parse_device_strings(
3308         const char *filename,
3309         unsigned line,
3310         const char *section,
3311         const char *lvalue,
3312         const char *rvalue,
3313         void *data,
3314         void *userdata) {
3315
3316     pa_alsa_profile_set *ps = userdata;
3317     pa_alsa_mapping *m;
3318
3319     pa_assert(ps);
3320
3321     if (!(m = mapping_get(ps, section))) {
3322         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3323         return -1;
3324     }
3325
3326     pa_xstrfreev(m->device_strings);
3327     if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
3328         pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
3329         return -1;
3330     }
3331
3332     return 0;
3333 }
3334
3335 static int mapping_parse_channel_map(
3336         const char *filename,
3337         unsigned line,
3338         const char *section,
3339         const char *lvalue,
3340         const char *rvalue,
3341         void *data,
3342         void *userdata) {
3343
3344     pa_alsa_profile_set *ps = userdata;
3345     pa_alsa_mapping *m;
3346
3347     pa_assert(ps);
3348
3349     if (!(m = mapping_get(ps, section))) {
3350         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3351         return -1;
3352     }
3353
3354     if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
3355         pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
3356         return -1;
3357     }
3358
3359     return 0;
3360 }
3361
3362 static int mapping_parse_paths(
3363         const char *filename,
3364         unsigned line,
3365         const char *section,
3366         const char *lvalue,
3367         const char *rvalue,
3368         void *data,
3369         void *userdata) {
3370
3371     pa_alsa_profile_set *ps = userdata;
3372     pa_alsa_mapping *m;
3373
3374     pa_assert(ps);
3375
3376     if (!(m = mapping_get(ps, section))) {
3377         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3378         return -1;
3379     }
3380
3381     if (pa_streq(lvalue, "paths-input")) {
3382         pa_xstrfreev(m->input_path_names);
3383         m->input_path_names = pa_split_spaces_strv(rvalue);
3384     } else {
3385         pa_xstrfreev(m->output_path_names);
3386         m->output_path_names = pa_split_spaces_strv(rvalue);
3387     }
3388
3389     return 0;
3390 }
3391
3392 static int mapping_parse_element(
3393         const char *filename,
3394         unsigned line,
3395         const char *section,
3396         const char *lvalue,
3397         const char *rvalue,
3398         void *data,
3399         void *userdata) {
3400
3401     pa_alsa_profile_set *ps = userdata;
3402     pa_alsa_mapping *m;
3403
3404     pa_assert(ps);
3405
3406     if (!(m = mapping_get(ps, section))) {
3407         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3408         return -1;
3409     }
3410
3411     if (pa_streq(lvalue, "element-input")) {
3412         pa_xstrfreev(m->input_element);
3413         m->input_element = pa_split_spaces_strv(rvalue);
3414     } else {
3415         pa_xstrfreev(m->output_element);
3416         m->output_element = pa_split_spaces_strv(rvalue);
3417     }
3418
3419     return 0;
3420 }
3421
3422 static int mapping_parse_direction(
3423         const char *filename,
3424         unsigned line,
3425         const char *section,
3426         const char *lvalue,
3427         const char *rvalue,
3428         void *data,
3429         void *userdata) {
3430
3431     pa_alsa_profile_set *ps = userdata;
3432     pa_alsa_mapping *m;
3433
3434     pa_assert(ps);
3435
3436     if (!(m = mapping_get(ps, section))) {
3437         pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3438         return -1;
3439     }
3440
3441     if (pa_streq(rvalue, "input"))
3442         m->direction = PA_ALSA_DIRECTION_INPUT;
3443     else if (pa_streq(rvalue, "output"))
3444         m->direction = PA_ALSA_DIRECTION_OUTPUT;
3445     else if (pa_streq(rvalue, "any"))
3446         m->direction = PA_ALSA_DIRECTION_ANY;
3447     else {
3448         pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
3449         return -1;
3450     }
3451
3452     return 0;
3453 }
3454
3455 static int mapping_parse_description(
3456         const char *filename,
3457         unsigned line,
3458         const char *section,
3459         const char *lvalue,
3460         const char *rvalue,
3461         void *data,
3462         void *userdata) {
3463
3464     pa_alsa_profile_set *ps = userdata;
3465     pa_alsa_profile *p;
3466     pa_alsa_mapping *m;
3467
3468     pa_assert(ps);
3469
3470     if ((m = mapping_get(ps, section))) {
3471         pa_xfree(m->description);
3472         m->description = pa_xstrdup(rvalue);
3473     } else if ((p = profile_get(ps, section))) {
3474         pa_xfree(p->description);
3475         p->description = pa_xstrdup(rvalue);
3476     } else {
3477         pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3478         return -1;
3479     }
3480
3481     return 0;
3482 }
3483
3484 static int mapping_parse_priority(
3485         const char *filename,
3486         unsigned line,
3487         const char *section,
3488         const char *lvalue,
3489         const char *rvalue,
3490         void *data,
3491         void *userdata) {
3492
3493     pa_alsa_profile_set *ps = userdata;
3494     pa_alsa_profile *p;
3495     pa_alsa_mapping *m;
3496     uint32_t prio;
3497
3498     pa_assert(ps);
3499
3500     if (pa_atou(rvalue, &prio) < 0) {
3501         pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
3502         return -1;
3503     }
3504
3505     if ((m = mapping_get(ps, section)))
3506         m->priority = prio;
3507     else if ((p = profile_get(ps, section)))
3508         p->priority = prio;
3509     else {
3510         pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3511         return -1;
3512     }
3513
3514     return 0;
3515 }
3516
3517 static int profile_parse_mappings(
3518         const char *filename,
3519         unsigned line,
3520         const char *section,
3521         const char *lvalue,
3522         const char *rvalue,
3523         void *data,
3524         void *userdata) {
3525
3526     pa_alsa_profile_set *ps = userdata;
3527     pa_alsa_profile *p;
3528
3529     pa_assert(ps);
3530
3531     if (!(p = profile_get(ps, section))) {
3532         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3533         return -1;
3534     }
3535
3536     if (pa_streq(lvalue, "input-mappings")) {
3537         pa_xstrfreev(p->input_mapping_names);
3538         p->input_mapping_names = pa_split_spaces_strv(rvalue);
3539     } else {
3540         pa_xstrfreev(p->output_mapping_names);
3541         p->output_mapping_names = pa_split_spaces_strv(rvalue);
3542     }
3543
3544     return 0;
3545 }
3546
3547 static int profile_parse_skip_probe(
3548         const char *filename,
3549         unsigned line,
3550         const char *section,
3551         const char *lvalue,
3552         const char *rvalue,
3553         void *data,
3554         void *userdata) {
3555
3556     pa_alsa_profile_set *ps = userdata;
3557     pa_alsa_profile *p;
3558     int b;
3559
3560     pa_assert(ps);
3561
3562     if (!(p = profile_get(ps, section))) {
3563         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3564         return -1;
3565     }
3566
3567     if ((b = pa_parse_boolean(rvalue)) < 0) {
3568         pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
3569         return -1;
3570     }
3571
3572     p->supported = b;
3573
3574     return 0;
3575 }
3576
3577 static int decibel_fix_parse_db_values(
3578         const char *filename,
3579         unsigned line,
3580         const char *section,
3581         const char *lvalue,
3582         const char *rvalue,
3583         void *data,
3584         void *userdata) {
3585
3586     pa_alsa_profile_set *ps = userdata;
3587     pa_alsa_decibel_fix *db_fix;
3588     char **items;
3589     char *item;
3590     long *db_values;
3591     unsigned n = 8; /* Current size of the db_values table. */
3592     unsigned min_step = 0;
3593     unsigned max_step = 0;
3594     unsigned i = 0; /* Index to the items table. */
3595     unsigned prev_step = 0;
3596     double prev_db = 0;
3597
3598     pa_assert(filename);
3599     pa_assert(section);
3600     pa_assert(lvalue);
3601     pa_assert(rvalue);
3602     pa_assert(ps);
3603
3604     if (!(db_fix = decibel_fix_get(ps, section))) {
3605         pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3606         return -1;
3607     }
3608
3609     if (!(items = pa_split_spaces_strv(rvalue))) {
3610         pa_log("[%s:%u] Value missing", pa_strnull(filename), line);
3611         return -1;
3612     }
3613
3614     db_values = pa_xnew(long, n);
3615
3616     while ((item = items[i++])) {
3617         char *s = item; /* Step value string. */
3618         char *d = item; /* dB value string. */
3619         uint32_t step;
3620         double db;
3621
3622         /* Move d forward until it points to a colon or to the end of the item. */
3623         for (; *d && *d != ':'; ++d);
3624
3625         if (d == s) {
3626             /* item started with colon. */
3627             pa_log("[%s:%u] No step value found in %s", filename, line, item);
3628             goto fail;
3629         }
3630
3631         if (!*d || !*(d + 1)) {
3632             /* No colon found, or it was the last character in item. */
3633             pa_log("[%s:%u] No dB value found in %s", filename, line, item);
3634             goto fail;
3635         }
3636
3637         /* pa_atou() needs a null-terminating string. Let's replace the colon
3638          * with a zero byte. */
3639         *d++ = '\0';
3640
3641         if (pa_atou(s, &step) < 0) {
3642             pa_log("[%s:%u] Invalid step value: %s", filename, line, s);
3643             goto fail;
3644         }
3645
3646         if (pa_atod(d, &db) < 0) {
3647             pa_log("[%s:%u] Invalid dB value: %s", filename, line, d);
3648             goto fail;
3649         }
3650
3651         if (step <= prev_step && i != 1) {
3652             pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename, line, step, prev_step);
3653             goto fail;
3654         }
3655
3656         if (db < prev_db && i != 1) {
3657             pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename, line, db, prev_db);
3658             goto fail;
3659         }
3660
3661         if (i == 1) {
3662             min_step = step;
3663             db_values[0] = (long) (db * 100.0);
3664             prev_step = step;
3665             prev_db = db;
3666         } else {
3667             /* Interpolate linearly. */
3668             double db_increment = (db - prev_db) / (step - prev_step);
3669
3670             for (; prev_step < step; ++prev_step, prev_db += db_increment) {
3671
3672                 /* Reallocate the db_values table if it's about to overflow. */
3673                 if (prev_step + 1 - min_step == n) {
3674                     n *= 2;
3675                     db_values = pa_xrenew(long, db_values, n);
3676                 }
3677
3678                 db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
3679             }
3680         }
3681
3682         max_step = step;
3683     }
3684
3685     db_fix->min_step = min_step;
3686     db_fix->max_step = max_step;
3687     pa_xfree(db_fix->db_values);
3688     db_fix->db_values = db_values;
3689
3690     pa_xstrfreev(items);
3691
3692     return 0;
3693
3694 fail:
3695     pa_xstrfreev(items);
3696     pa_xfree(db_values);
3697
3698     return -1;
3699 }
3700
3701 static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
3702                                 pa_alsa_direction_t direction) {
3703
3704     pa_alsa_path *p;
3705     void *state;
3706     snd_pcm_t *pcm_handle;
3707     pa_alsa_path_set *ps;
3708     snd_mixer_t *mixer_handle;
3709
3710     if (direction == PA_ALSA_DIRECTION_OUTPUT) {
3711         if (m->output_path_set)
3712             return; /* Already probed */
3713         m->output_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3714         pcm_handle = m->output_pcm;
3715     } else {
3716         if (m->input_path_set)
3717             return; /* Already probed */
3718         m->input_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3719         pcm_handle = m->input_pcm;
3720     }
3721
3722     if (!ps)
3723         return; /* No paths */
3724
3725     pa_assert(pcm_handle);
3726
3727     mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL);
3728     if (!mixer_handle) {
3729          /* Cannot open mixer, remove all entries */
3730         while (pa_hashmap_steal_first(ps->paths));
3731         return;
3732     }
3733
3734
3735     PA_HASHMAP_FOREACH(p, ps->paths, state) {
3736         if (pa_alsa_path_probe(p, mixer_handle, m->profile_set->ignore_dB) < 0) {
3737             pa_hashmap_remove(ps->paths, p);
3738         }
3739     }
3740
3741     path_set_condense(ps, mixer_handle);
3742     path_set_make_paths_unique(ps);
3743
3744     if (mixer_handle)
3745         snd_mixer_close(mixer_handle);
3746
3747     pa_log_debug("Available mixer paths (after tidying):");
3748     pa_alsa_path_set_dump(ps);
3749 }
3750
3751 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
3752
3753     static const struct description_map well_known_descriptions[] = {
3754         { "analog-mono",            N_("Analog Mono") },
3755         { "analog-stereo",          N_("Analog Stereo") },
3756         { "analog-surround-21",     N_("Analog Surround 2.1") },
3757         { "analog-surround-30",     N_("Analog Surround 3.0") },
3758         { "analog-surround-31",     N_("Analog Surround 3.1") },
3759         { "analog-surround-40",     N_("Analog Surround 4.0") },
3760         { "analog-surround-41",     N_("Analog Surround 4.1") },
3761         { "analog-surround-50",     N_("Analog Surround 5.0") },
3762         { "analog-surround-51",     N_("Analog Surround 5.1") },
3763         { "analog-surround-61",     N_("Analog Surround 6.0") },
3764         { "analog-surround-61",     N_("Analog Surround 6.1") },
3765         { "analog-surround-70",     N_("Analog Surround 7.0") },
3766         { "analog-surround-71",     N_("Analog Surround 7.1") },
3767         { "iec958-stereo",          N_("Digital Stereo (IEC958)") },
3768         { "iec958-passthrough",     N_("Digital Passthrough  (IEC958)") },
3769         { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3770         { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3771         { "hdmi-stereo",            N_("Digital Stereo (HDMI)") }
3772     };
3773
3774     pa_assert(m);
3775
3776     if (!pa_channel_map_valid(&m->channel_map)) {
3777         pa_log("Mapping %s is missing channel map.", m->name);
3778         return -1;
3779     }
3780
3781     if (!m->device_strings) {
3782         pa_log("Mapping %s is missing device strings.", m->name);
3783         return -1;
3784     }
3785
3786     if ((m->input_path_names && m->input_element) ||
3787         (m->output_path_names && m->output_element)) {
3788         pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
3789         return -1;
3790     }
3791
3792     if (!m->description)
3793         m->description = pa_xstrdup(lookup_description(m->name,
3794                                                        well_known_descriptions,
3795                                                        PA_ELEMENTSOF(well_known_descriptions)));
3796
3797     if (!m->description)
3798         m->description = pa_xstrdup(m->name);
3799
3800     if (bonus) {
3801         if (pa_channel_map_equal(&m->channel_map, bonus))
3802             m->priority += 50;
3803         else if (m->channel_map.channels == bonus->channels)
3804             m->priority += 30;
3805     }
3806
3807     return 0;
3808 }
3809
3810 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
3811     char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
3812
3813     pa_assert(m);
3814
3815     pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3816                  m->name,
3817                  pa_strnull(m->description),
3818                  m->priority,
3819                  pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
3820                  pa_yes_no(m->supported),
3821                  m->direction);
3822 }
3823
3824 static void profile_set_add_auto_pair(
3825         pa_alsa_profile_set *ps,
3826         pa_alsa_mapping *m, /* output */
3827         pa_alsa_mapping *n  /* input */) {
3828
3829     char *name;
3830     pa_alsa_profile *p;
3831
3832     pa_assert(ps);
3833     pa_assert(m || n);
3834
3835     if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
3836         return;
3837
3838     if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
3839         return;
3840
3841     if (m && n)
3842         name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
3843     else if (m)
3844         name = pa_sprintf_malloc("output:%s", m->name);
3845     else
3846         name = pa_sprintf_malloc("input:%s", n->name);
3847
3848     if (pa_hashmap_get(ps->profiles, name)) {
3849         pa_xfree(name);
3850         return;
3851     }
3852
3853     p = pa_xnew0(pa_alsa_profile, 1);
3854     p->profile_set = ps;
3855     p->name = name;
3856
3857     if (m) {
3858         p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3859         pa_idxset_put(p->output_mappings, m, NULL);
3860         p->priority += m->priority * 100;
3861     }
3862
3863     if (n) {
3864         p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3865         pa_idxset_put(p->input_mappings, n, NULL);
3866         p->priority += n->priority;
3867     }
3868
3869     pa_hashmap_put(ps->profiles, p->name, p);
3870 }
3871
3872 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
3873     pa_alsa_mapping *m, *n;
3874     void *m_state, *n_state;
3875
3876     pa_assert(ps);
3877
3878     PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
3879         profile_set_add_auto_pair(ps, m, NULL);
3880
3881         PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3882             profile_set_add_auto_pair(ps, m, n);
3883     }
3884
3885     PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3886         profile_set_add_auto_pair(ps, NULL, n);
3887 }
3888
3889 static int profile_verify(pa_alsa_profile *p) {
3890
3891     static const struct description_map well_known_descriptions[] = {
3892         { "output:analog-mono+input:analog-mono",     N_("Analog Mono Duplex") },
3893         { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3894         { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3895         { "off",                                      N_("Off") }
3896     };
3897
3898     pa_assert(p);
3899
3900     /* Replace the output mapping names by the actual mappings */
3901     if (p->output_mapping_names) {
3902         char **name;
3903
3904         pa_assert(!p->output_mappings);
3905         p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3906
3907         for (name = p->output_mapping_names; *name; name++) {
3908             pa_alsa_mapping *m;
3909             char **in;
3910             pa_bool_t duplicate = FALSE;
3911
3912             for (in = name + 1; *in; in++)
3913                 if (pa_streq(*name, *in)) {
3914                     duplicate = TRUE;
3915                     break;
3916                 }
3917
3918             if (duplicate)
3919                 continue;
3920
3921             if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
3922                 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
3923                 return -1;
3924             }
3925
3926             pa_idxset_put(p->output_mappings, m, NULL);
3927
3928             if (p->supported)
3929                 m->supported++;
3930         }
3931
3932         pa_xstrfreev(p->output_mapping_names);
3933         p->output_mapping_names = NULL;
3934     }
3935
3936     /* Replace the input mapping names by the actual mappings */
3937     if (p->input_mapping_names) {
3938         char **name;
3939
3940         pa_assert(!p->input_mappings);
3941         p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3942
3943         for (name = p->input_mapping_names; *name; name++) {
3944             pa_alsa_mapping *m;
3945             char **in;
3946             pa_bool_t duplicate = FALSE;
3947
3948             for (in = name + 1; *in; in++)
3949                 if (pa_streq(*name, *in)) {
3950                     duplicate = TRUE;
3951                     break;
3952                 }
3953
3954             if (duplicate)
3955                 continue;
3956
3957             if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
3958                 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
3959                 return -1;
3960             }
3961
3962             pa_idxset_put(p->input_mappings, m, NULL);
3963
3964             if (p->supported)
3965                 m->supported++;
3966         }
3967
3968         pa_xstrfreev(p->input_mapping_names);
3969         p->input_mapping_names = NULL;
3970     }
3971
3972     if (!p->input_mappings && !p->output_mappings) {
3973         pa_log("Profile '%s' lacks mappings.", p->name);
3974         return -1;
3975     }
3976
3977     if (!p->description)
3978         p->description = pa_xstrdup(lookup_description(p->name,
3979                                                        well_known_descriptions,
3980                                                        PA_ELEMENTSOF(well_known_descriptions)));
3981
3982     if (!p->description) {
3983         pa_strbuf *sb;
3984         uint32_t idx;
3985         pa_alsa_mapping *m;
3986
3987         sb = pa_strbuf_new();
3988
3989         if (p->output_mappings)
3990             PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3991                 if (!pa_strbuf_isempty(sb))
3992                     pa_strbuf_puts(sb, " + ");
3993
3994                 pa_strbuf_printf(sb, _("%s Output"), m->description);
3995             }
3996
3997         if (p->input_mappings)
3998             PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3999                 if (!pa_strbuf_isempty(sb))
4000                     pa_strbuf_puts(sb, " + ");
4001
4002                 pa_strbuf_printf(sb, _("%s Input"), m->description);
4003             }
4004
4005         p->description = pa_strbuf_tostring_free(sb);
4006     }
4007
4008     return 0;
4009 }
4010
4011 void pa_alsa_profile_dump(pa_alsa_profile *p) {
4012     uint32_t idx;
4013     pa_alsa_mapping *m;
4014     pa_assert(p);
4015
4016     pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4017                  p->name,
4018                  pa_strnull(p->description),
4019                  p->priority,
4020                  pa_yes_no(p->supported),
4021                  p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
4022                  p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
4023
4024     if (p->input_mappings)
4025         PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4026             pa_log_debug("Input %s", m->name);
4027
4028     if (p->output_mappings)
4029         PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4030             pa_log_debug("Output %s", m->name);
4031 }
4032
4033 static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
4034     pa_assert(db_fix);
4035
4036     /* Check that the dB mapping has been configured. Since "db-values" is
4037      * currently the only option in the DecibelFix section, and decibel fix
4038      * objects don't get created if a DecibelFix section is empty, this is
4039      * actually a redundant check. Having this may prevent future bugs,
4040      * however. */
4041     if (!db_fix->db_values) {
4042         pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
4043         return -1;
4044     }
4045
4046     return 0;
4047 }
4048
4049 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
4050     char *db_values = NULL;
4051
4052     pa_assert(db_fix);
4053
4054     if (db_fix->db_values) {
4055         pa_strbuf *buf;
4056         unsigned long i, nsteps;
4057
4058         pa_assert(db_fix->min_step <= db_fix->max_step);
4059         nsteps = db_fix->max_step - db_fix->min_step + 1;
4060
4061         buf = pa_strbuf_new();
4062         for (i = 0; i < nsteps; ++i)
4063             pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
4064
4065         db_values = pa_strbuf_tostring_free(buf);
4066     }
4067
4068     pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4069                  db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
4070
4071     pa_xfree(db_values);
4072 }
4073
4074 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
4075     pa_alsa_profile_set *ps;
4076     pa_alsa_profile *p;
4077     pa_alsa_mapping *m;
4078     pa_alsa_decibel_fix *db_fix;
4079     char *fn;
4080     int r;
4081     void *state;
4082
4083     static pa_config_item items[] = {
4084         /* [General] */
4085         { "auto-profiles",          pa_config_parse_bool,         NULL, "General" },
4086
4087         /* [Mapping ...] */
4088         { "device-strings",         mapping_parse_device_strings, NULL, NULL },
4089         { "channel-map",            mapping_parse_channel_map,    NULL, NULL },
4090         { "paths-input",            mapping_parse_paths,          NULL, NULL },
4091         { "paths-output",           mapping_parse_paths,          NULL, NULL },
4092         { "element-input",          mapping_parse_element,        NULL, NULL },
4093         { "element-output",         mapping_parse_element,        NULL, NULL },
4094         { "direction",              mapping_parse_direction,      NULL, NULL },
4095
4096         /* Shared by [Mapping ...] and [Profile ...] */
4097         { "description",            mapping_parse_description,    NULL, NULL },
4098         { "priority",               mapping_parse_priority,       NULL, NULL },
4099
4100         /* [Profile ...] */
4101         { "input-mappings",         profile_parse_mappings,       NULL, NULL },
4102         { "output-mappings",        profile_parse_mappings,       NULL, NULL },
4103         { "skip-probe",             profile_parse_skip_probe,     NULL, NULL },
4104
4105         /* [DecibelFix ...] */
4106         { "db-values",              decibel_fix_parse_db_values,  NULL, NULL },
4107         { NULL, NULL, NULL, NULL }
4108     };
4109
4110     ps = pa_xnew0(pa_alsa_profile_set, 1);
4111     ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4112     ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4113     ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4114     ps->input_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4115     ps->output_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4116
4117     items[0].data = &ps->auto_profiles;
4118
4119     if (!fname)
4120         fname = "default.conf";
4121
4122     fn = pa_maybe_prefix_path(fname,
4123                               pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
4124                               PA_ALSA_PROFILE_SETS_DIR);
4125
4126     r = pa_config_parse(fn, NULL, items, ps);
4127     pa_xfree(fn);
4128
4129     if (r < 0)
4130         goto fail;
4131
4132     PA_HASHMAP_FOREACH(m, ps->mappings, state)
4133         if (mapping_verify(m, bonus) < 0)
4134             goto fail;
4135
4136     if (ps->auto_profiles)
4137         profile_set_add_auto(ps);
4138
4139     PA_HASHMAP_FOREACH(p, ps->profiles, state)
4140         if (profile_verify(p) < 0)
4141             goto fail;
4142
4143     PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4144         if (decibel_fix_verify(db_fix) < 0)
4145             goto fail;
4146
4147     return ps;
4148
4149 fail:
4150     pa_alsa_profile_set_free(ps);
4151     return NULL;
4152 }
4153
4154 static void profile_finalize_probing(pa_alsa_profile *to_be_finalized, pa_alsa_profile *next) {
4155     pa_alsa_mapping *m;
4156     uint32_t idx;
4157
4158     if (!to_be_finalized)
4159         return;
4160
4161     if (to_be_finalized->output_mappings)
4162         PA_IDXSET_FOREACH(m, to_be_finalized->output_mappings, idx) {
4163
4164             if (!m->output_pcm)
4165                 continue;
4166
4167             if (to_be_finalized->supported)
4168                 m->supported++;
4169
4170             /* If this mapping is also in the next profile, we won't close the
4171              * pcm handle here, because it would get immediately reopened
4172              * anyway. */
4173             if (next && next->output_mappings && pa_idxset_get_by_data(next->output_mappings, m, NULL))
4174                 continue;
4175
4176             snd_pcm_close(m->output_pcm);
4177             m->output_pcm = NULL;
4178         }
4179
4180     if (to_be_finalized->input_mappings)
4181         PA_IDXSET_FOREACH(m, to_be_finalized->input_mappings, idx) {
4182
4183             if (!m->input_pcm)
4184                 continue;
4185
4186             if (to_be_finalized->supported)
4187                 m->supported++;
4188
4189             /* If this mapping is also in the next profile, we won't close the
4190              * pcm handle here, because it would get immediately reopened
4191              * anyway. */
4192             if (next && next->input_mappings && pa_idxset_get_by_data(next->input_mappings, m, NULL))
4193                 continue;
4194
4195             snd_pcm_close(m->input_pcm);
4196             m->input_pcm = NULL;
4197         }
4198 }
4199
4200 static snd_pcm_t* mapping_open_pcm(pa_alsa_mapping *m,
4201                                    const pa_sample_spec *ss,
4202                                    const char *dev_id,
4203                                    int mode,
4204                                    unsigned default_n_fragments,
4205                                    unsigned default_fragment_size_msec) {
4206
4207     pa_sample_spec try_ss = *ss;
4208     pa_channel_map try_map = m->channel_map;
4209     snd_pcm_uframes_t try_period_size, try_buffer_size;
4210
4211     try_ss.channels = try_map.channels;
4212
4213     try_period_size =
4214         pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
4215         pa_frame_size(&try_ss);
4216     try_buffer_size = default_n_fragments * try_period_size;
4217
4218     return pa_alsa_open_by_template(
4219                               m->device_strings, dev_id, NULL, &try_ss,
4220                               &try_map, mode, &try_period_size,
4221                               &try_buffer_size, 0, NULL, NULL, TRUE);
4222 }
4223
4224 void pa_alsa_profile_set_probe(
4225         pa_alsa_profile_set *ps,
4226         const char *dev_id,
4227         const pa_sample_spec *ss,
4228         unsigned default_n_fragments,
4229         unsigned default_fragment_size_msec) {
4230
4231     void *state;
4232     pa_alsa_profile *p, *last = NULL;
4233     pa_alsa_mapping *m;
4234
4235     pa_assert(ps);
4236     pa_assert(dev_id);
4237     pa_assert(ss);
4238
4239     if (ps->probed)
4240         return;
4241
4242     PA_HASHMAP_FOREACH(p, ps->profiles, state) {
4243         uint32_t idx;
4244
4245         /* Skip if this is already marked that it is supported (i.e. from the config file) */
4246         if (!p->supported) {
4247
4248             pa_log_debug("Looking at profile %s", p->name);
4249             profile_finalize_probing(last, p);
4250             p->supported = TRUE;
4251
4252             /* Check if we can open all new ones */
4253             if (p->output_mappings)
4254                 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4255
4256                     if (m->output_pcm)
4257                         continue;
4258
4259                     pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
4260                     if (!(m->output_pcm = mapping_open_pcm(m, ss, dev_id,
4261                                                            SND_PCM_STREAM_PLAYBACK,
4262                                                            default_n_fragments,
4263                                                            default_fragment_size_msec))) {
4264                         p->supported = FALSE;
4265                         break;
4266                     }
4267                 }
4268
4269             if (p->input_mappings && p->supported)
4270                 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4271
4272                     if (m->input_pcm)
4273                         continue;
4274
4275                     pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
4276                     if (!(m->input_pcm = mapping_open_pcm(m, ss, dev_id,
4277                                                           SND_PCM_STREAM_CAPTURE,
4278                                                           default_n_fragments,
4279                                                           default_fragment_size_msec))) {
4280                         p->supported = FALSE;
4281                         break;
4282                     }
4283                 }
4284
4285             last = p;
4286
4287             if (!p->supported)
4288                 continue;
4289         }
4290
4291         pa_log_debug("Profile %s supported.", p->name);
4292
4293         if (p->output_mappings)
4294             PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4295                 if (m->output_pcm)
4296                     mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT);
4297
4298         if (p->input_mappings)
4299             PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4300                 if (m->input_pcm)
4301                     mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT);
4302     }
4303
4304     /* Clean up */
4305     profile_finalize_probing(last, NULL);
4306
4307     PA_HASHMAP_FOREACH(p, ps->profiles, state)
4308         if (!p->supported) {
4309             pa_hashmap_remove(ps->profiles, p->name);
4310             profile_free(p);
4311         }
4312
4313     PA_HASHMAP_FOREACH(m, ps->mappings, state)
4314         if (m->supported <= 0) {
4315             pa_hashmap_remove(ps->mappings, m->name);
4316             mapping_free(m);
4317         }
4318
4319     ps->probed = TRUE;
4320 }
4321
4322 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
4323     pa_alsa_profile *p;
4324     pa_alsa_mapping *m;
4325     pa_alsa_decibel_fix *db_fix;
4326     void *state;
4327
4328     pa_assert(ps);
4329
4330     pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4331                  (void*)
4332                  ps,
4333                  pa_yes_no(ps->auto_profiles),
4334                  pa_yes_no(ps->probed),
4335                  pa_hashmap_size(ps->mappings),
4336                  pa_hashmap_size(ps->profiles),
4337                  pa_hashmap_size(ps->decibel_fixes));
4338
4339     PA_HASHMAP_FOREACH(m, ps->mappings, state)
4340         pa_alsa_mapping_dump(m);
4341
4342     PA_HASHMAP_FOREACH(p, ps->profiles, state)
4343         pa_alsa_profile_dump(p);
4344
4345     PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4346         pa_alsa_decibel_fix_dump(db_fix);
4347 }
4348
4349 static pa_device_port* device_port_alsa_init(pa_hashmap *ports,
4350     const char* name,
4351     const char* description,
4352     pa_alsa_path *path,
4353     pa_alsa_setting *setting,
4354     pa_card_profile *cp,
4355     pa_hashmap *extra,
4356     pa_core *core) {
4357
4358     pa_device_port * p = pa_hashmap_get(ports, name);
4359     if (!p) {
4360         pa_alsa_port_data *data;
4361
4362         p = pa_device_port_new(core, name, description, sizeof(pa_alsa_port_data));
4363         pa_assert(p);
4364         pa_hashmap_put(ports, name, p);
4365         p->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4366
4367         data = PA_DEVICE_PORT_DATA(p);
4368         data->path = path;
4369         data->setting = setting;
4370     }
4371
4372     p->is_input |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_INPUT;
4373     p->is_output |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_OUTPUT;
4374
4375     if (cp)
4376         pa_hashmap_put(p->profiles, cp->name, cp);
4377
4378     if (extra) {
4379         pa_hashmap_put(extra, name, p);
4380         pa_device_port_ref(p);
4381     }
4382
4383     return p;
4384 }
4385
4386 void pa_alsa_path_set_add_ports(
4387         pa_alsa_path_set *ps,
4388         pa_card_profile *cp,
4389         pa_hashmap *ports,
4390         pa_hashmap *extra,
4391         pa_core *core) {
4392
4393     pa_alsa_path *path;
4394     void *state;
4395
4396     pa_assert(ports);
4397
4398     if (!ps)
4399         return;
4400
4401     PA_HASHMAP_FOREACH(path, ps->paths, state) {
4402         if (!path->settings || !path->settings->next) {
4403             /* If there is no or just one setting we only need a
4404              * single entry */
4405             pa_device_port *port = device_port_alsa_init(ports, path->name,
4406                 path->description, path, path->settings, cp, extra, core);
4407             port->priority = path->priority * 100;
4408
4409         } else {
4410             pa_alsa_setting *s;
4411             PA_LLIST_FOREACH(s, path->settings) {
4412                 pa_device_port *port;
4413                 char *n, *d;
4414
4415                 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
4416
4417                 if (s->description[0])
4418                     d = pa_sprintf_malloc(_("%s / %s"), path->description, s->description);
4419                 else
4420                     d = pa_xstrdup(path->description);
4421
4422                 port = device_port_alsa_init(ports, n, d, path, s, cp, extra, core);
4423                 port->priority = path->priority * 100 + s->priority;
4424
4425                 pa_xfree(n);
4426                 pa_xfree(d);
4427             }
4428         }
4429     }
4430 }
4431
4432 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps, pa_card *card) {
4433
4434     pa_assert(p);
4435     pa_assert(!*p);
4436     pa_assert(ps);
4437
4438     if (ps->paths && pa_hashmap_size(ps->paths) > 0) {
4439         pa_assert(card);
4440         *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4441         pa_alsa_path_set_add_ports(ps, NULL, card->ports, *p, card->core);
4442     }
4443
4444     pa_log_debug("Added %u ports", *p ? pa_hashmap_size(*p) : 0);
4445 }