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