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