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