alsa: fix duplicate mapping detection
[pulseaudio-mirror.git] / src / modules / alsa / alsa-mixer.c
blobc5db303ee87014d4dbc30945d719c7c8a4983eae
1 /***
2 This file is part of PulseAudio.
4 Copyright 2004-2009 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
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.
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.
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 ***/
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include <sys/types.h>
28 #include <limits.h>
29 #include <asoundlib.h>
31 #ifdef HAVE_VALGRIND_MEMCHECK_H
32 #include <valgrind/memcheck.h>
33 #endif
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>
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>
52 #include "alsa-mixer.h"
53 #include "alsa-util.h"
55 struct description_map {
56 const char *name;
57 const char *description;
60 static const char *lookup_description(const char *name, const struct description_map dm[], unsigned n) {
61 unsigned i;
63 for (i = 0; i < n; i++)
64 if (pa_streq(dm[i].name, name))
65 return dm[i].description;
67 return NULL;
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;
76 snd_mixer_t *mixer;
78 pa_mainloop_api *m;
79 pa_defer_event *defer;
80 pa_io_event **ios;
82 pa_bool_t polled;
84 void (*cb)(void *userdata);
85 void *userdata;
88 static void io_cb(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
90 struct pa_alsa_fdlist *fdl = userdata;
91 int err;
92 unsigned i;
93 unsigned short revents;
95 pa_assert(a);
96 pa_assert(fdl);
97 pa_assert(fdl->mixer);
98 pa_assert(fdl->fds);
99 pa_assert(fdl->work_fds);
101 if (fdl->polled)
102 return;
104 fdl->polled = TRUE;
106 memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds);
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;
122 pa_assert(i != fdl->num_fds);
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;
129 a->defer_enable(fdl->defer, 1);
131 if (revents)
132 snd_mixer_handle_events(fdl->mixer);
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;
141 pa_assert(a);
142 pa_assert(fdl);
143 pa_assert(fdl->mixer);
145 a->defer_enable(fdl->defer, 0);
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;
151 num_fds = (unsigned) n;
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);
162 memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds);
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;
169 fdl->polled = FALSE;
171 if (memcmp(fdl->fds, fdl->work_fds, sizeof(struct pollfd) * num_fds) == 0)
172 return;
174 if (fdl->ios) {
175 for (i = 0; i < fdl->num_fds; i++)
176 a->io_free(fdl->ios[i]);
178 if (num_fds != fdl->num_fds) {
179 pa_xfree(fdl->ios);
180 fdl->ios = NULL;
184 if (!fdl->ios)
185 fdl->ios = pa_xnew(pa_io_event*, num_fds);
187 /* Swap pointers */
188 temp = fdl->work_fds;
189 fdl->work_fds = fdl->fds;
190 fdl->fds = temp;
192 fdl->num_fds = num_fds;
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);
201 struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) {
202 struct pa_alsa_fdlist *fdl;
204 fdl = pa_xnew0(struct pa_alsa_fdlist, 1);
206 return fdl;
209 void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {
210 pa_assert(fdl);
212 if (fdl->defer) {
213 pa_assert(fdl->m);
214 fdl->m->defer_free(fdl->defer);
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);
225 if (fdl->fds)
226 pa_xfree(fdl->fds);
227 if (fdl->work_fds)
228 pa_xfree(fdl->work_fds);
230 pa_xfree(fdl);
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);
239 fdl->mixer = mixer_handle;
240 fdl->m = m;
241 fdl->defer = m->defer_new(m, defer_cb, fdl);
243 return 0;
246 static int prepare_mixer(snd_mixer_t *mixer, const char *dev) {
247 int err;
249 pa_assert(mixer);
250 pa_assert(dev);
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;
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;
262 if ((err = snd_mixer_load(mixer)) < 0) {
263 pa_log_warn("Unable to load mixer: %s", pa_alsa_strerror(err));
264 return -1;
267 pa_log_info("Successfully attached to mixer '%s'", dev);
268 return 0;
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);
278 pa_assert(pcm);
280 if ((err = snd_mixer_open(&m, 0)) < 0) {
281 pa_log("Error opening mixer: %s", pa_alsa_strerror(err));
282 return NULL;
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);
291 return m;
294 /* Then, try by card index */
295 if (snd_pcm_info(pcm, info) >= 0) {
296 char *md;
297 int card_idx;
299 if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
301 md = pa_sprintf_malloc("hw:%i", card_idx);
303 if (!dev || !pa_streq(dev, md))
304 if (prepare_mixer(m, md) >= 0) {
306 if (ctl_device)
307 *ctl_device = md;
308 else
309 pa_xfree(md);
311 return m;
314 pa_xfree(md);
318 snd_mixer_close(m);
319 return NULL;
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! */
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,
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,
333 [PA_CHANNEL_POSITION_LFE] = SND_MIXER_SCHN_WOOFER,
335 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
336 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
338 [PA_CHANNEL_POSITION_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT,
339 [PA_CHANNEL_POSITION_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT,
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,
374 [PA_CHANNEL_POSITION_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN,
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,
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
385 static void setting_free(pa_alsa_setting *s) {
386 pa_assert(s);
388 if (s->options)
389 pa_idxset_free(s->options, NULL, NULL);
391 pa_xfree(s->name);
392 pa_xfree(s->description);
393 pa_xfree(s);
396 static void option_free(pa_alsa_option *o) {
397 pa_assert(o);
399 pa_xfree(o->alsa_name);
400 pa_xfree(o->name);
401 pa_xfree(o->description);
402 pa_xfree(o);
405 static void element_free(pa_alsa_element *e) {
406 pa_alsa_option *o;
407 pa_assert(e);
409 while ((o = e->options)) {
410 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
411 option_free(o);
414 pa_xfree(e->alsa_name);
415 pa_xfree(e);
418 void pa_alsa_path_free(pa_alsa_path *p) {
419 pa_alsa_element *e;
420 pa_alsa_setting *s;
422 pa_assert(p);
424 while ((e = p->elements)) {
425 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
426 element_free(e);
429 while ((s = p->settings)) {
430 PA_LLIST_REMOVE(pa_alsa_setting, p->settings, s);
431 setting_free(s);
434 pa_xfree(p->name);
435 pa_xfree(p->description);
436 pa_xfree(p);
439 void pa_alsa_path_set_free(pa_alsa_path_set *ps) {
440 pa_alsa_path *p;
441 pa_assert(ps);
443 while ((p = ps->paths)) {
444 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
445 pa_alsa_path_free(p);
448 pa_xfree(ps);
451 static long to_alsa_dB(pa_volume_t v) {
452 return (long) (pa_sw_volume_to_dB(v) * 100.0);
455 static pa_volume_t from_alsa_dB(long v) {
456 return pa_sw_volume_from_dB((double) v / 100.0);
459 static long to_alsa_volume(pa_volume_t v, long min, long max) {
460 long w;
462 w = (long) round(((double) v * (double) (max - min)) / PA_VOLUME_NORM) + min;
463 return PA_CLAMP_UNLIKELY(w, min, max);
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));
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)
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 unsigned k;
484 pa_assert(m);
485 pa_assert(e);
486 pa_assert(cm);
487 pa_assert(v);
489 SELEM_INIT(sid, e->alsa_name);
490 if (!(me = snd_mixer_find_selem(m, sid))) {
491 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
492 return -1;
495 pa_cvolume_mute(v, cm->channels);
497 /* We take the highest volume of all channels that match */
499 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
500 int r;
501 pa_volume_t f;
503 if (e->has_dB) {
504 long value = 0;
506 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
507 if (snd_mixer_selem_has_playback_channel(me, c))
508 r = snd_mixer_selem_get_playback_dB(me, c, &value);
509 else
510 r = -1;
511 } else {
512 if (snd_mixer_selem_has_capture_channel(me, c))
513 r = snd_mixer_selem_get_capture_dB(me, c, &value);
514 else
515 r = -1;
518 if (r < 0)
519 continue;
521 #ifdef HAVE_VALGRIND_MEMCHECK_H
522 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
523 #endif
525 f = from_alsa_dB(value);
527 } else {
528 long value = 0;
530 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
531 if (snd_mixer_selem_has_playback_channel(me, c))
532 r = snd_mixer_selem_get_playback_volume(me, c, &value);
533 else
534 r = -1;
535 } else {
536 if (snd_mixer_selem_has_capture_channel(me, c))
537 r = snd_mixer_selem_get_capture_volume(me, c, &value);
538 else
539 r = -1;
542 if (r < 0)
543 continue;
545 f = from_alsa_volume(value, e->min_volume, e->max_volume);
548 for (k = 0; k < cm->channels; k++)
549 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
550 if (v->values[k] < f)
551 v->values[k] = f;
553 mask |= e->masks[c][e->n_channels-1];
556 for (k = 0; k < cm->channels; k++)
557 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
558 v->values[k] = PA_VOLUME_NORM;
560 return 0;
563 int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
564 pa_alsa_element *e;
566 pa_assert(m);
567 pa_assert(p);
568 pa_assert(cm);
569 pa_assert(v);
571 if (!p->has_volume)
572 return -1;
574 pa_cvolume_reset(v, cm->channels);
576 PA_LLIST_FOREACH(e, p->elements) {
577 pa_cvolume ev;
579 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
580 continue;
582 pa_assert(!p->has_dB || e->has_dB);
584 if (element_get_volume(e, m, cm, &ev) < 0)
585 return -1;
587 /* If we have no dB information all we can do is take the first element and leave */
588 if (!p->has_dB) {
589 *v = ev;
590 return 0;
593 pa_sw_cvolume_multiply(v, v, &ev);
596 return 0;
599 static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t *b) {
600 snd_mixer_selem_id_t *sid;
601 snd_mixer_elem_t *me;
602 snd_mixer_selem_channel_id_t c;
604 pa_assert(m);
605 pa_assert(e);
606 pa_assert(b);
608 SELEM_INIT(sid, e->alsa_name);
609 if (!(me = snd_mixer_find_selem(m, sid))) {
610 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
611 return -1;
614 /* We return muted if at least one channel is muted */
616 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
617 int r;
618 int value = 0;
620 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
621 if (snd_mixer_selem_has_playback_channel(me, c))
622 r = snd_mixer_selem_get_playback_switch(me, c, &value);
623 else
624 r = -1;
625 } else {
626 if (snd_mixer_selem_has_capture_channel(me, c))
627 r = snd_mixer_selem_get_capture_switch(me, c, &value);
628 else
629 r = -1;
632 if (r < 0)
633 continue;
635 if (!value) {
636 *b = FALSE;
637 return 0;
641 *b = TRUE;
642 return 0;
645 int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t *muted) {
646 pa_alsa_element *e;
648 pa_assert(m);
649 pa_assert(p);
650 pa_assert(muted);
652 if (!p->has_mute)
653 return -1;
655 PA_LLIST_FOREACH(e, p->elements) {
656 pa_bool_t b;
658 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
659 continue;
661 if (element_get_switch(e, m, &b) < 0)
662 return -1;
664 if (!b) {
665 *muted = TRUE;
666 return 0;
670 *muted = FALSE;
671 return 0;
674 static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
675 snd_mixer_selem_id_t *sid;
676 pa_cvolume rv;
677 snd_mixer_elem_t *me;
678 snd_mixer_selem_channel_id_t c;
679 pa_channel_position_mask_t mask = 0;
680 unsigned k;
682 pa_assert(m);
683 pa_assert(e);
684 pa_assert(cm);
685 pa_assert(v);
686 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
688 SELEM_INIT(sid, e->alsa_name);
689 if (!(me = snd_mixer_find_selem(m, sid))) {
690 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
691 return -1;
694 pa_cvolume_mute(&rv, cm->channels);
696 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
697 int r;
698 pa_volume_t f = PA_VOLUME_MUTED;
700 for (k = 0; k < cm->channels; k++)
701 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
702 if (v->values[k] > f)
703 f = v->values[k];
705 if (e->has_dB) {
706 long value = to_alsa_dB(f);
708 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
709 /* If we call set_play_volume() without checking first
710 * if the channel is available, ALSA behaves ver
711 * strangely and doesn't fail the call */
712 if (snd_mixer_selem_has_playback_channel(me, c)) {
713 if ((r = snd_mixer_selem_set_playback_dB(me, c, value, +1)) >= 0)
714 r = snd_mixer_selem_get_playback_dB(me, c, &value);
715 } else
716 r = -1;
717 } else {
718 if (snd_mixer_selem_has_capture_channel(me, c)) {
719 if ((r = snd_mixer_selem_set_capture_dB(me, c, value, +1)) >= 0)
720 r = snd_mixer_selem_get_capture_dB(me, c, &value);
721 } else
722 r = -1;
725 if (r < 0)
726 continue;
728 #ifdef HAVE_VALGRIND_MEMCHECK_H
729 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
730 #endif
732 f = from_alsa_dB(value);
734 } else {
735 long value;
737 value = to_alsa_volume(f, e->min_volume, e->max_volume);
739 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
740 if (snd_mixer_selem_has_playback_channel(me, c)) {
741 if ((r = snd_mixer_selem_set_playback_volume(me, c, value)) >= 0)
742 r = snd_mixer_selem_get_playback_volume(me, c, &value);
743 } else
744 r = -1;
745 } else {
746 if (snd_mixer_selem_has_capture_channel(me, c)) {
747 if ((r = snd_mixer_selem_set_capture_volume(me, c, value)) >= 0)
748 r = snd_mixer_selem_get_capture_volume(me, c, &value);
749 } else
750 r = -1;
753 if (r < 0)
754 continue;
756 f = from_alsa_volume(value, e->min_volume, e->max_volume);
759 for (k = 0; k < cm->channels; k++)
760 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
761 if (rv.values[k] < f)
762 rv.values[k] = f;
764 mask |= e->masks[c][e->n_channels-1];
767 for (k = 0; k < cm->channels; k++)
768 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
769 rv.values[k] = PA_VOLUME_NORM;
771 *v = rv;
772 return 0;
775 int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
776 pa_alsa_element *e;
777 pa_cvolume rv;
779 pa_assert(m);
780 pa_assert(p);
781 pa_assert(cm);
782 pa_assert(v);
783 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
785 if (!p->has_volume)
786 return -1;
788 rv = *v; /* Remaining adjustment */
789 pa_cvolume_reset(v, cm->channels); /* Adjustment done */
791 PA_LLIST_FOREACH(e, p->elements) {
792 pa_cvolume ev;
794 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
795 continue;
797 pa_assert(!p->has_dB || e->has_dB);
799 ev = rv;
800 if (element_set_volume(e, m, cm, &ev) < 0)
801 return -1;
803 if (!p->has_dB) {
804 *v = ev;
805 return 0;
808 pa_sw_cvolume_multiply(v, v, &ev);
809 pa_sw_cvolume_divide(&rv, &rv, &ev);
812 return 0;
815 static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t b) {
816 snd_mixer_elem_t *me;
817 snd_mixer_selem_id_t *sid;
818 int r;
820 pa_assert(m);
821 pa_assert(e);
823 SELEM_INIT(sid, e->alsa_name);
824 if (!(me = snd_mixer_find_selem(m, sid))) {
825 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
826 return -1;
829 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
830 r = snd_mixer_selem_set_playback_switch_all(me, b);
831 else
832 r = snd_mixer_selem_set_capture_switch_all(me, b);
834 if (r < 0)
835 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
837 return r;
840 int pa_alsa_path_set_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t muted) {
841 pa_alsa_element *e;
843 pa_assert(m);
844 pa_assert(p);
846 if (!p->has_mute)
847 return -1;
849 PA_LLIST_FOREACH(e, p->elements) {
851 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
852 continue;
854 if (element_set_switch(e, m, !muted) < 0)
855 return -1;
858 return 0;
861 static int element_mute_volume(pa_alsa_element *e, snd_mixer_t *m) {
862 snd_mixer_elem_t *me;
863 snd_mixer_selem_id_t *sid;
864 int r;
866 pa_assert(m);
867 pa_assert(e);
869 SELEM_INIT(sid, e->alsa_name);
870 if (!(me = snd_mixer_find_selem(m, sid))) {
871 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
872 return -1;
875 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
876 r = snd_mixer_selem_set_playback_volume_all(me, e->min_volume);
877 else
878 r = snd_mixer_selem_set_capture_volume_all(me, e->min_volume);
880 if (r < 0)
881 pa_log_warn("Faile to set volume to muted of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
883 return r;
886 /* The volume to 0dB */
887 static int element_zero_volume(pa_alsa_element *e, snd_mixer_t *m) {
888 snd_mixer_elem_t *me;
889 snd_mixer_selem_id_t *sid;
890 int r;
892 pa_assert(m);
893 pa_assert(e);
895 SELEM_INIT(sid, e->alsa_name);
896 if (!(me = snd_mixer_find_selem(m, sid))) {
897 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
898 return -1;
901 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
902 r = snd_mixer_selem_set_playback_dB_all(me, 0, +1);
903 else
904 r = snd_mixer_selem_set_capture_dB_all(me, 0, +1);
906 if (r < 0)
907 pa_log_warn("Faile to set volume to 0dB of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
909 return r;
912 int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
913 pa_alsa_element *e;
914 int r;
916 pa_assert(m);
917 pa_assert(p);
919 pa_log_debug("Activating path %s", p->name);
920 pa_alsa_path_dump(p);
922 PA_LLIST_FOREACH(e, p->elements) {
924 switch (e->switch_use) {
925 case PA_ALSA_SWITCH_MUTE:
926 case PA_ALSA_SWITCH_OFF:
927 r = element_set_switch(e, m, FALSE);
928 break;
930 case PA_ALSA_SWITCH_ON:
931 r = element_set_switch(e, m, TRUE);
932 break;
934 case PA_ALSA_SWITCH_IGNORE:
935 case PA_ALSA_SWITCH_SELECT:
936 r = 0;
937 break;
940 if (r < 0)
941 return -1;
943 switch (e->volume_use) {
944 case PA_ALSA_VOLUME_OFF:
945 case PA_ALSA_VOLUME_MERGE:
946 r = element_mute_volume(e, m);
947 break;
949 case PA_ALSA_VOLUME_ZERO:
950 r = element_zero_volume(e, m);
951 break;
953 case PA_ALSA_VOLUME_IGNORE:
954 r = 0;
955 break;
958 if (r < 0)
959 return -1;
962 return 0;
965 static int check_required(pa_alsa_element *e, snd_mixer_elem_t *me) {
966 pa_bool_t has_switch;
967 pa_bool_t has_enumeration;
968 pa_bool_t has_volume;
970 pa_assert(e);
971 pa_assert(me);
973 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
974 has_switch =
975 snd_mixer_selem_has_playback_switch(me) ||
976 (e->direction_try_other && snd_mixer_selem_has_capture_switch(me));
977 } else {
978 has_switch =
979 snd_mixer_selem_has_capture_switch(me) ||
980 (e->direction_try_other && snd_mixer_selem_has_playback_switch(me));
983 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
984 has_volume =
985 snd_mixer_selem_has_playback_volume(me) ||
986 (e->direction_try_other && snd_mixer_selem_has_capture_volume(me));
987 } else {
988 has_volume =
989 snd_mixer_selem_has_capture_volume(me) ||
990 (e->direction_try_other && snd_mixer_selem_has_playback_volume(me));
993 has_enumeration = snd_mixer_selem_is_enumerated(me);
995 if ((e->required == PA_ALSA_REQUIRED_SWITCH && !has_switch) ||
996 (e->required == PA_ALSA_REQUIRED_VOLUME && !has_volume) ||
997 (e->required == PA_ALSA_REQUIRED_ENUMERATION && !has_enumeration))
998 return -1;
1000 if (e->required == PA_ALSA_REQUIRED_ANY && !(has_switch || has_volume || has_enumeration))
1001 return -1;
1003 if ((e->required_absent == PA_ALSA_REQUIRED_SWITCH && has_switch) ||
1004 (e->required_absent == PA_ALSA_REQUIRED_VOLUME && has_volume) ||
1005 (e->required_absent == PA_ALSA_REQUIRED_ENUMERATION && has_enumeration))
1006 return -1;
1008 if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration))
1009 return -1;
1011 return 0;
1014 static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
1015 snd_mixer_selem_id_t *sid;
1016 snd_mixer_elem_t *me;
1018 pa_assert(m);
1019 pa_assert(e);
1021 SELEM_INIT(sid, e->alsa_name);
1023 if (!(me = snd_mixer_find_selem(m, sid))) {
1025 if (e->required != PA_ALSA_REQUIRED_IGNORE)
1026 return -1;
1028 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1029 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1030 e->enumeration_use = PA_ALSA_VOLUME_IGNORE;
1032 return 0;
1035 if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
1036 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1038 if (!snd_mixer_selem_has_playback_switch(me)) {
1039 if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
1040 e->direction = PA_ALSA_DIRECTION_INPUT;
1041 else
1042 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1045 } else {
1047 if (!snd_mixer_selem_has_capture_switch(me)) {
1048 if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
1049 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1050 else
1051 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1055 if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
1056 e->direction_try_other = FALSE;
1059 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1061 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1063 if (!snd_mixer_selem_has_playback_volume(me)) {
1064 if (e->direction_try_other && snd_mixer_selem_has_capture_volume(me))
1065 e->direction = PA_ALSA_DIRECTION_INPUT;
1066 else
1067 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1070 } else {
1072 if (!snd_mixer_selem_has_capture_volume(me)) {
1073 if (e->direction_try_other && snd_mixer_selem_has_playback_volume(me))
1074 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1075 else
1076 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1080 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1081 long min_dB = 0, max_dB = 0;
1082 int r;
1084 e->direction_try_other = FALSE;
1086 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1087 e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0;
1088 else
1089 e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0;
1091 if (e->has_dB) {
1092 #ifdef HAVE_VALGRIND_MEMCHECK_H
1093 VALGRIND_MAKE_MEM_DEFINED(&min_dB, sizeof(min_dB));
1094 VALGRIND_MAKE_MEM_DEFINED(&max_dB, sizeof(max_dB));
1095 #endif
1097 e->min_dB = ((double) min_dB) / 100.0;
1098 e->max_dB = ((double) max_dB) / 100.0;
1100 if (min_dB >= max_dB) {
1101 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);
1102 e->has_dB = FALSE;
1106 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1107 r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume);
1108 else
1109 r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
1111 if (r < 0) {
1112 pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1113 return -1;
1117 if (e->min_volume >= e->max_volume) {
1118 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);
1119 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1121 } else {
1122 pa_bool_t is_mono;
1123 pa_channel_position_t p;
1125 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1126 is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
1127 else
1128 is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
1130 if (is_mono) {
1131 e->n_channels = 1;
1133 if (!e->override_map) {
1134 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++)
1135 e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
1136 e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
1139 e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
1140 } else {
1141 e->n_channels = 0;
1142 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1144 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1145 continue;
1147 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1148 e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1149 else
1150 e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1153 if (e->n_channels <= 0) {
1154 pa_log_warn("Volume element %s with no channels?", e->alsa_name);
1155 return -1;
1158 if (!e->override_map) {
1159 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1160 pa_bool_t has_channel;
1162 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1163 continue;
1165 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1166 has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1167 else
1168 has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1170 e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
1174 e->merged_mask = 0;
1175 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++)
1176 e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
1183 if (check_required(e, me) < 0)
1184 return -1;
1186 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1187 pa_alsa_option *o;
1189 PA_LLIST_FOREACH(o, e->options)
1190 o->alsa_idx = pa_streq(o->alsa_name, "on") ? 1 : 0;
1191 } else if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1192 int n;
1193 pa_alsa_option *o;
1195 if ((n = snd_mixer_selem_get_enum_items(me)) < 0) {
1196 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n));
1197 return -1;
1200 PA_LLIST_FOREACH(o, e->options) {
1201 int i;
1203 for (i = 0; i < n; i++) {
1204 char buf[128];
1206 if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
1207 continue;
1209 if (!pa_streq(buf, o->alsa_name))
1210 continue;
1212 o->alsa_idx = i;
1217 return 0;
1220 static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, pa_bool_t prefixed) {
1221 pa_alsa_element *e;
1223 pa_assert(p);
1224 pa_assert(section);
1226 if (prefixed) {
1227 if (!pa_startswith(section, "Element "))
1228 return NULL;
1230 section += 8;
1233 /* This is not an element section, but an enum section? */
1234 if (strchr(section, ':'))
1235 return NULL;
1237 if (p->last_element && pa_streq(p->last_element->alsa_name, section))
1238 return p->last_element;
1240 PA_LLIST_FOREACH(e, p->elements)
1241 if (pa_streq(e->alsa_name, section))
1242 goto finish;
1244 e = pa_xnew0(pa_alsa_element, 1);
1245 e->path = p;
1246 e->alsa_name = pa_xstrdup(section);
1247 e->direction = p->direction;
1249 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
1251 finish:
1252 p->last_element = e;
1253 return e;
1256 static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
1257 char *en;
1258 const char *on;
1259 pa_alsa_option *o;
1260 pa_alsa_element *e;
1262 if (!pa_startswith(section, "Option "))
1263 return NULL;
1265 section += 7;
1267 /* This is not an enum section, but an element section? */
1268 if (!(on = strchr(section, ':')))
1269 return NULL;
1271 en = pa_xstrndup(section, on - section);
1272 on++;
1274 if (p->last_option &&
1275 pa_streq(p->last_option->element->alsa_name, en) &&
1276 pa_streq(p->last_option->alsa_name, on)) {
1277 pa_xfree(en);
1278 return p->last_option;
1281 pa_assert_se(e = element_get(p, en, FALSE));
1282 pa_xfree(en);
1284 PA_LLIST_FOREACH(o, e->options)
1285 if (pa_streq(o->alsa_name, on))
1286 goto finish;
1288 o = pa_xnew0(pa_alsa_option, 1);
1289 o->element = e;
1290 o->alsa_name = pa_xstrdup(on);
1291 o->alsa_idx = -1;
1293 if (p->last_option && p->last_option->element == e)
1294 PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
1295 else
1296 PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
1298 finish:
1299 p->last_option = o;
1300 return o;
1303 static int element_parse_switch(
1304 const char *filename,
1305 unsigned line,
1306 const char *section,
1307 const char *lvalue,
1308 const char *rvalue,
1309 void *data,
1310 void *userdata) {
1312 pa_alsa_path *p = userdata;
1313 pa_alsa_element *e;
1315 pa_assert(p);
1317 if (!(e = element_get(p, section, TRUE))) {
1318 pa_log("[%s:%u] Switch makes no sense in '%s'", filename, line, section);
1319 return -1;
1322 if (pa_streq(rvalue, "ignore"))
1323 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1324 else if (pa_streq(rvalue, "mute"))
1325 e->switch_use = PA_ALSA_SWITCH_MUTE;
1326 else if (pa_streq(rvalue, "off"))
1327 e->switch_use = PA_ALSA_SWITCH_OFF;
1328 else if (pa_streq(rvalue, "on"))
1329 e->switch_use = PA_ALSA_SWITCH_ON;
1330 else if (pa_streq(rvalue, "select"))
1331 e->switch_use = PA_ALSA_SWITCH_SELECT;
1332 else {
1333 pa_log("[%s:%u] Switch invalid of '%s'", filename, line, section);
1334 return -1;
1337 return 0;
1340 static int element_parse_volume(
1341 const char *filename,
1342 unsigned line,
1343 const char *section,
1344 const char *lvalue,
1345 const char *rvalue,
1346 void *data,
1347 void *userdata) {
1349 pa_alsa_path *p = userdata;
1350 pa_alsa_element *e;
1352 pa_assert(p);
1354 if (!(e = element_get(p, section, TRUE))) {
1355 pa_log("[%s:%u] Volume makes no sense in '%s'", filename, line, section);
1356 return -1;
1359 if (pa_streq(rvalue, "ignore"))
1360 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1361 else if (pa_streq(rvalue, "merge"))
1362 e->volume_use = PA_ALSA_VOLUME_MERGE;
1363 else if (pa_streq(rvalue, "off"))
1364 e->volume_use = PA_ALSA_VOLUME_OFF;
1365 else if (pa_streq(rvalue, "zero"))
1366 e->volume_use = PA_ALSA_VOLUME_ZERO;
1367 else {
1368 pa_log("[%s:%u] Volume invalid of '%s'", filename, line, section);
1369 return -1;
1372 return 0;
1375 static int element_parse_enumeration(
1376 const char *filename,
1377 unsigned line,
1378 const char *section,
1379 const char *lvalue,
1380 const char *rvalue,
1381 void *data,
1382 void *userdata) {
1384 pa_alsa_path *p = userdata;
1385 pa_alsa_element *e;
1387 pa_assert(p);
1389 if (!(e = element_get(p, section, TRUE))) {
1390 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename, line, section);
1391 return -1;
1394 if (pa_streq(rvalue, "ignore"))
1395 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1396 else if (pa_streq(rvalue, "select"))
1397 e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
1398 else {
1399 pa_log("[%s:%u] Enumeration invalid of '%s'", filename, line, section);
1400 return -1;
1403 return 0;
1406 static int option_parse_priority(
1407 const char *filename,
1408 unsigned line,
1409 const char *section,
1410 const char *lvalue,
1411 const char *rvalue,
1412 void *data,
1413 void *userdata) {
1415 pa_alsa_path *p = userdata;
1416 pa_alsa_option *o;
1417 uint32_t prio;
1419 pa_assert(p);
1421 if (!(o = option_get(p, section))) {
1422 pa_log("[%s:%u] Priority makes no sense in '%s'", filename, line, section);
1423 return -1;
1426 if (pa_atou(rvalue, &prio) < 0) {
1427 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
1428 return -1;
1431 o->priority = prio;
1432 return 0;
1435 static int option_parse_name(
1436 const char *filename,
1437 unsigned line,
1438 const char *section,
1439 const char *lvalue,
1440 const char *rvalue,
1441 void *data,
1442 void *userdata) {
1444 pa_alsa_path *p = userdata;
1445 pa_alsa_option *o;
1447 pa_assert(p);
1449 if (!(o = option_get(p, section))) {
1450 pa_log("[%s:%u] Name makes no sense in '%s'", filename, line, section);
1451 return -1;
1454 pa_xfree(o->name);
1455 o->name = pa_xstrdup(rvalue);
1457 return 0;
1460 static int element_parse_required(
1461 const char *filename,
1462 unsigned line,
1463 const char *section,
1464 const char *lvalue,
1465 const char *rvalue,
1466 void *data,
1467 void *userdata) {
1469 pa_alsa_path *p = userdata;
1470 pa_alsa_element *e;
1471 pa_alsa_required_t req;
1473 pa_assert(p);
1475 if (!(e = element_get(p, section, TRUE))) {
1476 pa_log("[%s:%u] Required makes no sense in '%s'", filename, line, section);
1477 return -1;
1480 if (pa_streq(rvalue, "ignore"))
1481 req = PA_ALSA_REQUIRED_IGNORE;
1482 else if (pa_streq(rvalue, "switch"))
1483 req = PA_ALSA_REQUIRED_SWITCH;
1484 else if (pa_streq(rvalue, "volume"))
1485 req = PA_ALSA_REQUIRED_VOLUME;
1486 else if (pa_streq(rvalue, "enumeration"))
1487 req = PA_ALSA_REQUIRED_ENUMERATION;
1488 else if (pa_streq(rvalue, "any"))
1489 req = PA_ALSA_REQUIRED_ANY;
1490 else {
1491 pa_log("[%s:%u] Required invalid of '%s'", filename, line, section);
1492 return -1;
1495 if (pa_streq(lvalue, "required-absent"))
1496 e->required_absent = req;
1497 else
1498 e->required = req;
1500 return 0;
1503 static int element_parse_direction(
1504 const char *filename,
1505 unsigned line,
1506 const char *section,
1507 const char *lvalue,
1508 const char *rvalue,
1509 void *data,
1510 void *userdata) {
1512 pa_alsa_path *p = userdata;
1513 pa_alsa_element *e;
1515 pa_assert(p);
1517 if (!(e = element_get(p, section, TRUE))) {
1518 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1519 return -1;
1522 if (pa_streq(rvalue, "playback"))
1523 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1524 else if (pa_streq(rvalue, "capture"))
1525 e->direction = PA_ALSA_DIRECTION_INPUT;
1526 else {
1527 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1528 return -1;
1531 return 0;
1534 static int element_parse_direction_try_other(
1535 const char *filename,
1536 unsigned line,
1537 const char *section,
1538 const char *lvalue,
1539 const char *rvalue,
1540 void *data,
1541 void *userdata) {
1543 pa_alsa_path *p = userdata;
1544 pa_alsa_element *e;
1545 int yes;
1547 if (!(e = element_get(p, section, TRUE))) {
1548 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1549 return -1;
1552 if ((yes = pa_parse_boolean(rvalue)) < 0) {
1553 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1554 return -1;
1557 e->direction_try_other = !!yes;
1558 return 0;
1561 static pa_channel_position_mask_t parse_mask(const char *m) {
1562 pa_channel_position_mask_t v;
1564 if (pa_streq(m, "all-left"))
1565 v = PA_CHANNEL_POSITION_MASK_LEFT;
1566 else if (pa_streq(m, "all-right"))
1567 v = PA_CHANNEL_POSITION_MASK_RIGHT;
1568 else if (pa_streq(m, "all-center"))
1569 v = PA_CHANNEL_POSITION_MASK_CENTER;
1570 else if (pa_streq(m, "all-front"))
1571 v = PA_CHANNEL_POSITION_MASK_FRONT;
1572 else if (pa_streq(m, "all-rear"))
1573 v = PA_CHANNEL_POSITION_MASK_REAR;
1574 else if (pa_streq(m, "all-side"))
1575 v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER;
1576 else if (pa_streq(m, "all-top"))
1577 v = PA_CHANNEL_POSITION_MASK_TOP;
1578 else if (pa_streq(m, "all-no-lfe"))
1579 v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE);
1580 else if (pa_streq(m, "all"))
1581 v = PA_CHANNEL_POSITION_MASK_ALL;
1582 else {
1583 pa_channel_position_t p;
1585 if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
1586 return 0;
1588 v = PA_CHANNEL_POSITION_MASK(p);
1591 return v;
1594 static int element_parse_override_map(
1595 const char *filename,
1596 unsigned line,
1597 const char *section,
1598 const char *lvalue,
1599 const char *rvalue,
1600 void *data,
1601 void *userdata) {
1603 pa_alsa_path *p = userdata;
1604 pa_alsa_element *e;
1605 const char *state = NULL;
1606 unsigned i = 0;
1607 char *n;
1609 if (!(e = element_get(p, section, TRUE))) {
1610 pa_log("[%s:%u] Override map makes no sense in '%s'", filename, line, section);
1611 return -1;
1614 while ((n = pa_split(rvalue, ",", &state))) {
1615 pa_channel_position_mask_t m;
1617 if (!*n)
1618 m = 0;
1619 else {
1620 if ((m = parse_mask(n)) == 0) {
1621 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename, line, n, section);
1622 pa_xfree(n);
1623 return -1;
1627 if (pa_streq(lvalue, "override-map.1"))
1628 e->masks[i++][0] = m;
1629 else
1630 e->masks[i++][1] = m;
1632 /* Later on we might add override-map.3 and so on here ... */
1634 pa_xfree(n);
1637 e->override_map = TRUE;
1639 return 0;
1642 static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
1643 snd_mixer_selem_id_t *sid;
1644 snd_mixer_elem_t *me;
1645 int r;
1647 pa_assert(e);
1648 pa_assert(m);
1650 SELEM_INIT(sid, e->alsa_name);
1651 if (!(me = snd_mixer_find_selem(m, sid))) {
1652 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1653 return -1;
1656 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1658 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1659 r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
1660 else
1661 r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
1663 if (r < 0)
1664 pa_log_warn("Faile to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1666 } else {
1667 pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
1669 if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0)
1670 pa_log_warn("Faile to set enumeration of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1673 return r;
1676 int pa_alsa_setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
1677 pa_alsa_option *o;
1678 uint32_t idx;
1680 pa_assert(s);
1681 pa_assert(m);
1683 PA_IDXSET_FOREACH(o, s->options, idx)
1684 element_set_option(o->element, m, o->alsa_idx);
1686 return 0;
1689 static int option_verify(pa_alsa_option *o) {
1690 static const struct description_map well_known_descriptions[] = {
1691 { "input", N_("Input") },
1692 { "input-docking", N_("Docking Station Input") },
1693 { "input-docking-microphone", N_("Docking Station Microphone") },
1694 { "input-linein", N_("Line-In") },
1695 { "input-microphone", N_("Microphone") },
1696 { "input-microphone-external", N_("External Microphone") },
1697 { "input-microphone-internal", N_("Internal Microphone") },
1698 { "input-radio", N_("Radio") },
1699 { "input-video", N_("Video") },
1700 { "input-agc-on", N_("Automatic Gain Control") },
1701 { "input-agc-off", "" },
1702 { "input-boost-on", N_("Boost") },
1703 { "input-boost-off", "" },
1704 { "output-amplifier-on", N_("Amplifier") },
1705 { "output-amplifier-off", "" }
1708 pa_assert(o);
1710 if (!o->name) {
1711 pa_log("No name set for option %s", o->alsa_name);
1712 return -1;
1715 if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
1716 o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
1717 pa_log("Element %s of option %s not set for select.", o->element->alsa_name, o->name);
1718 return -1;
1721 if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
1722 !pa_streq(o->alsa_name, "on") &&
1723 !pa_streq(o->alsa_name, "off")) {
1724 pa_log("Switch %s options need be named off or on ", o->element->alsa_name);
1725 return -1;
1728 if (!o->description)
1729 o->description = pa_xstrdup(lookup_description(o->name,
1730 well_known_descriptions,
1731 PA_ELEMENTSOF(well_known_descriptions)));
1732 if (!o->description)
1733 o->description = pa_xstrdup(o->name);
1735 return 0;
1738 static int element_verify(pa_alsa_element *e) {
1739 pa_alsa_option *o;
1741 pa_assert(e);
1743 if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
1744 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
1745 pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name);
1746 return -1;
1749 if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1750 pa_log("Element %s cannot set select for both switch and enumeration.", e->alsa_name);
1751 return -1;
1754 PA_LLIST_FOREACH(o, e->options)
1755 if (option_verify(o) < 0)
1756 return -1;
1758 return 0;
1761 static int path_verify(pa_alsa_path *p) {
1762 static const struct description_map well_known_descriptions[] = {
1763 { "analog-input", N_("Analog Input") },
1764 { "analog-input-microphone", N_("Analog Microphone") },
1765 { "analog-input-linein", N_("Analog Line-In") },
1766 { "analog-input-radio", N_("Analog Radio") },
1767 { "analog-input-video", N_("Analog Video") },
1768 { "analog-output", N_("Analog Output") },
1769 { "analog-output-headphones", N_("Analog Headphones") },
1770 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
1771 { "analog-output-mono", N_("Analog Mono Output") }
1774 pa_alsa_element *e;
1776 pa_assert(p);
1778 PA_LLIST_FOREACH(e, p->elements)
1779 if (element_verify(e) < 0)
1780 return -1;
1782 if (!p->description)
1783 p->description = pa_xstrdup(lookup_description(p->name,
1784 well_known_descriptions,
1785 PA_ELEMENTSOF(well_known_descriptions)));
1787 if (!p->description)
1788 p->description = pa_xstrdup(p->name);
1790 return 0;
1793 pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction) {
1794 pa_alsa_path *p;
1795 char *fn;
1796 int r;
1797 const char *n;
1799 pa_config_item items[] = {
1800 /* [General] */
1801 { "priority", pa_config_parse_unsigned, NULL, "General" },
1802 { "description", pa_config_parse_string, NULL, "General" },
1803 { "name", pa_config_parse_string, NULL, "General" },
1805 /* [Option ...] */
1806 { "priority", option_parse_priority, NULL, NULL },
1807 { "name", option_parse_name, NULL, NULL },
1809 /* [Element ...] */
1810 { "switch", element_parse_switch, NULL, NULL },
1811 { "volume", element_parse_volume, NULL, NULL },
1812 { "enumeration", element_parse_enumeration, NULL, NULL },
1813 { "override-map.1", element_parse_override_map, NULL, NULL },
1814 { "override-map.2", element_parse_override_map, NULL, NULL },
1815 /* ... later on we might add override-map.3 and so on here ... */
1816 { "required", element_parse_required, NULL, NULL },
1817 { "required-absent", element_parse_required, NULL, NULL },
1818 { "direction", element_parse_direction, NULL, NULL },
1819 { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
1820 { NULL, NULL, NULL, NULL }
1823 pa_assert(fname);
1825 p = pa_xnew0(pa_alsa_path, 1);
1826 n = pa_path_get_filename(fname);
1827 p->name = pa_xstrndup(n, strcspn(n, "."));
1828 p->direction = direction;
1830 items[0].data = &p->priority;
1831 items[1].data = &p->description;
1832 items[2].data = &p->name;
1834 fn = pa_maybe_prefix_path(fname, PA_ALSA_PATHS_DIR);
1835 r = pa_config_parse(fn, NULL, items, p);
1836 pa_xfree(fn);
1838 if (r < 0)
1839 goto fail;
1841 if (path_verify(p) < 0)
1842 goto fail;
1844 return p;
1846 fail:
1847 pa_alsa_path_free(p);
1848 return NULL;
1851 pa_alsa_path* pa_alsa_path_synthesize(const char*element, pa_alsa_direction_t direction) {
1852 pa_alsa_path *p;
1853 pa_alsa_element *e;
1855 pa_assert(element);
1857 p = pa_xnew0(pa_alsa_path, 1);
1858 p->name = pa_xstrdup(element);
1859 p->direction = direction;
1861 e = pa_xnew0(pa_alsa_element, 1);
1862 e->path = p;
1863 e->alsa_name = pa_xstrdup(element);
1864 e->direction = direction;
1866 e->switch_use = PA_ALSA_SWITCH_MUTE;
1867 e->volume_use = PA_ALSA_VOLUME_MERGE;
1869 PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
1870 return p;
1873 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
1874 pa_alsa_option *o, *n;
1876 pa_assert(e);
1878 for (o = e->options; o; o = n) {
1879 n = o->next;
1881 if (o->alsa_idx < 0) {
1882 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
1883 option_free(o);
1887 return
1888 e->switch_use != PA_ALSA_SWITCH_IGNORE ||
1889 e->volume_use != PA_ALSA_VOLUME_IGNORE ||
1890 e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
1893 static void path_drop_unsupported(pa_alsa_path *p) {
1894 pa_alsa_element *e, *n;
1896 pa_assert(p);
1898 for (e = p->elements; e; e = n) {
1899 n = e->next;
1901 if (!element_drop_unsupported(e)) {
1902 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
1903 element_free(e);
1908 static void path_make_options_unique(pa_alsa_path *p) {
1909 pa_alsa_element *e;
1910 pa_alsa_option *o, *u;
1912 PA_LLIST_FOREACH(e, p->elements) {
1913 PA_LLIST_FOREACH(o, e->options) {
1914 unsigned i;
1915 char *m;
1917 for (u = o->next; u; u = u->next)
1918 if (pa_streq(u->name, o->name))
1919 break;
1921 if (!u)
1922 continue;
1924 m = pa_xstrdup(o->name);
1926 /* OK, this name is not unique, hence let's rename */
1927 for (i = 1, u = o; u; u = u->next) {
1928 char *nn, *nd;
1930 if (!pa_streq(u->name, m))
1931 continue;
1933 nn = pa_sprintf_malloc("%s-%u", m, i);
1934 pa_xfree(u->name);
1935 u->name = nn;
1937 nd = pa_sprintf_malloc("%s %u", u->description, i);
1938 pa_xfree(u->description);
1939 u->description = nd;
1941 i++;
1944 pa_xfree(m);
1949 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
1950 pa_alsa_option *o;
1952 for (; e; e = e->next)
1953 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
1954 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
1955 break;
1957 if (!e)
1958 return FALSE;
1960 for (o = e->options; o; o = o->next) {
1961 pa_alsa_setting *s;
1963 if (template) {
1964 s = pa_xnewdup(pa_alsa_setting, template, 1);
1965 s->options = pa_idxset_copy(template->options);
1966 s->name = pa_sprintf_malloc(_("%s+%s"), template->name, o->name);
1967 s->description =
1968 (template->description[0] && o->description[0])
1969 ? pa_sprintf_malloc(_("%s / %s"), template->description, o->description)
1970 : (template->description[0]
1971 ? pa_xstrdup(template->description)
1972 : pa_xstrdup(o->description));
1974 s->priority = PA_MAX(template->priority, o->priority);
1975 } else {
1976 s = pa_xnew0(pa_alsa_setting, 1);
1977 s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1978 s->name = pa_xstrdup(o->name);
1979 s->description = pa_xstrdup(o->description);
1980 s->priority = o->priority;
1983 pa_idxset_put(s->options, o, NULL);
1985 if (element_create_settings(e->next, s))
1986 /* This is not a leaf, so let's get rid of it */
1987 setting_free(s);
1988 else {
1989 /* This is a leaf, so let's add it */
1990 PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
1992 e->path->last_setting = s;
1996 return TRUE;
1999 static void path_create_settings(pa_alsa_path *p) {
2000 pa_assert(p);
2002 element_create_settings(p->elements, NULL);
2005 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) {
2006 pa_alsa_element *e;
2007 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2008 pa_channel_position_t t;
2010 pa_assert(p);
2011 pa_assert(m);
2013 if (p->probed)
2014 return 0;
2016 pa_zero(min_dB);
2017 pa_zero(max_dB);
2019 pa_log_debug("Probing path '%s'", p->name);
2021 PA_LLIST_FOREACH(e, p->elements) {
2022 if (element_probe(e, m) < 0) {
2023 p->supported = FALSE;
2024 pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2025 return -1;
2028 if (ignore_dB)
2029 e->has_dB = FALSE;
2031 if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2033 if (!p->has_volume) {
2034 p->min_volume = e->min_volume;
2035 p->max_volume = e->max_volume;
2038 if (e->has_dB) {
2039 if (!p->has_volume) {
2040 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2041 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2042 min_dB[t] = e->min_dB;
2043 max_dB[t] = e->max_dB;
2046 p->has_dB = TRUE;
2047 } else {
2049 if (p->has_dB) {
2050 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2051 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2052 min_dB[t] += e->min_dB;
2053 max_dB[t] += e->max_dB;
2055 } else
2056 /* Hmm, there's another element before us
2057 * which cannot do dB volumes, so we we need
2058 * to 'neutralize' this slider */
2059 e->volume_use = PA_ALSA_VOLUME_ZERO;
2061 } else if (p->has_volume)
2062 /* We can't use this volume, so let's ignore it */
2063 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2065 p->has_volume = TRUE;
2068 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2069 p->has_mute = TRUE;
2072 path_drop_unsupported(p);
2073 path_make_options_unique(p);
2074 path_create_settings(p);
2076 p->supported = TRUE;
2077 p->probed = TRUE;
2079 p->min_dB = INFINITY;
2080 p->max_dB = -INFINITY;
2082 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2083 if (p->min_dB > min_dB[t])
2084 p->min_dB = min_dB[t];
2086 if (p->max_dB < max_dB[t])
2087 p->max_dB = max_dB[t];
2090 return 0;
2093 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2094 pa_assert(s);
2096 pa_log_debug("Setting %s (%s) priority=%u",
2097 s->name,
2098 pa_strnull(s->description),
2099 s->priority);
2102 void pa_alsa_option_dump(pa_alsa_option *o) {
2103 pa_assert(o);
2105 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2106 o->alsa_name,
2107 pa_strnull(o->name),
2108 pa_strnull(o->description),
2109 o->alsa_idx,
2110 o->priority);
2113 void pa_alsa_element_dump(pa_alsa_element *e) {
2114 pa_alsa_option *o;
2115 pa_assert(e);
2117 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",
2118 e->alsa_name,
2119 e->direction,
2120 e->switch_use,
2121 e->volume_use,
2122 e->enumeration_use,
2123 e->required,
2124 e->required_absent,
2125 (long long unsigned) e->merged_mask,
2126 e->n_channels,
2127 pa_yes_no(e->override_map));
2129 PA_LLIST_FOREACH(o, e->options)
2130 pa_alsa_option_dump(o);
2133 void pa_alsa_path_dump(pa_alsa_path *p) {
2134 pa_alsa_element *e;
2135 pa_alsa_setting *s;
2136 pa_assert(p);
2138 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2139 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2140 p->name,
2141 pa_strnull(p->description),
2142 p->direction,
2143 p->priority,
2144 pa_yes_no(p->probed),
2145 pa_yes_no(p->supported),
2146 pa_yes_no(p->has_mute),
2147 pa_yes_no(p->has_volume),
2148 pa_yes_no(p->has_dB),
2149 p->min_volume, p->max_volume,
2150 p->min_dB, p->max_dB);
2152 PA_LLIST_FOREACH(e, p->elements)
2153 pa_alsa_element_dump(e);
2155 PA_LLIST_FOREACH(s, p->settings)
2156 pa_alsa_setting_dump(s);
2159 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2160 snd_mixer_selem_id_t *sid;
2161 snd_mixer_elem_t *me;
2163 pa_assert(e);
2164 pa_assert(m);
2165 pa_assert(cb);
2167 SELEM_INIT(sid, e->alsa_name);
2168 if (!(me = snd_mixer_find_selem(m, sid))) {
2169 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2170 return;
2173 snd_mixer_elem_set_callback(me, cb);
2174 snd_mixer_elem_set_callback_private(me, userdata);
2177 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2178 pa_alsa_element *e;
2180 pa_assert(p);
2181 pa_assert(m);
2182 pa_assert(cb);
2184 PA_LLIST_FOREACH(e, p->elements)
2185 element_set_callback(e, m, cb, userdata);
2188 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2189 pa_alsa_path *p;
2191 pa_assert(ps);
2192 pa_assert(m);
2193 pa_assert(cb);
2195 PA_LLIST_FOREACH(p, ps->paths)
2196 pa_alsa_path_set_callback(p, m, cb, userdata);
2199 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction) {
2200 pa_alsa_path_set *ps;
2201 char **pn = NULL, **en = NULL, **ie;
2203 pa_assert(m);
2204 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2206 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2207 return NULL;
2209 ps = pa_xnew0(pa_alsa_path_set, 1);
2210 ps->direction = direction;
2212 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2213 pn = m->output_path_names;
2214 else if (direction == PA_ALSA_DIRECTION_INPUT)
2215 pn = m->input_path_names;
2217 if (pn) {
2218 char **in;
2220 for (in = pn; *in; in++) {
2221 pa_alsa_path *p;
2222 pa_bool_t duplicate = FALSE;
2223 char **kn, *fn;
2225 for (kn = pn; kn != in; kn++)
2226 if (pa_streq(*kn, *in)) {
2227 duplicate = TRUE;
2228 break;
2231 if (duplicate)
2232 continue;
2234 fn = pa_sprintf_malloc("%s.conf", *in);
2236 if ((p = pa_alsa_path_new(fn, direction))) {
2237 p->path_set = ps;
2238 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2239 ps->last_path = p;
2242 pa_xfree(fn);
2245 return ps;
2248 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2249 en = m->output_element;
2250 else if (direction == PA_ALSA_DIRECTION_INPUT)
2251 en = m->input_element;
2253 if (!en) {
2254 pa_alsa_path_set_free(ps);
2255 return NULL;
2258 for (ie = en; *ie; ie++) {
2259 char **je;
2260 pa_alsa_path *p;
2262 p = pa_alsa_path_synthesize(*ie, direction);
2263 p->path_set = ps;
2265 /* Mark all other passed elements for require-absent */
2266 for (je = en; *je; je++) {
2267 pa_alsa_element *e;
2268 e = pa_xnew0(pa_alsa_element, 1);
2269 e->path = p;
2270 e->alsa_name = pa_xstrdup(*je);
2271 e->direction = direction;
2272 e->required_absent = PA_ALSA_REQUIRED_ANY;
2274 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2275 p->last_element = e;
2278 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2279 ps->last_path = p;
2282 return ps;
2285 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2286 pa_alsa_path *p;
2287 pa_assert(ps);
2289 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2290 (void*) ps,
2291 ps->direction,
2292 pa_yes_no(ps->probed));
2294 PA_LLIST_FOREACH(p, ps->paths)
2295 pa_alsa_path_dump(p);
2298 static void path_set_unify(pa_alsa_path_set *ps) {
2299 pa_alsa_path *p;
2300 pa_bool_t has_dB = TRUE, has_volume = TRUE, has_mute = TRUE;
2301 pa_assert(ps);
2303 /* We have issues dealing with paths that vary too wildly. That
2304 * means for now we have to have all paths support volume/mute/dB
2305 * or none. */
2307 PA_LLIST_FOREACH(p, ps->paths) {
2308 pa_assert(p->probed);
2310 if (!p->has_volume)
2311 has_volume = FALSE;
2312 else if (!p->has_dB)
2313 has_dB = FALSE;
2315 if (!p->has_mute)
2316 has_mute = FALSE;
2319 if (!has_volume || !has_dB || !has_mute) {
2321 if (!has_volume)
2322 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2323 else if (!has_dB)
2324 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2326 if (!has_mute)
2327 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2329 PA_LLIST_FOREACH(p, ps->paths) {
2330 if (!has_volume)
2331 p->has_volume = FALSE;
2332 else if (!has_dB)
2333 p->has_dB = FALSE;
2335 if (!has_mute)
2336 p->has_mute = FALSE;
2341 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
2342 pa_alsa_path *p, *q;
2344 PA_LLIST_FOREACH(p, ps->paths) {
2345 unsigned i;
2346 char *m;
2348 for (q = p->next; q; q = q->next)
2349 if (pa_streq(q->name, p->name))
2350 break;
2352 if (!q)
2353 continue;
2355 m = pa_xstrdup(p->name);
2357 /* OK, this name is not unique, hence let's rename */
2358 for (i = 1, q = p; q; q = q->next) {
2359 char *nn, *nd;
2361 if (!pa_streq(q->name, m))
2362 continue;
2364 nn = pa_sprintf_malloc("%s-%u", m, i);
2365 pa_xfree(q->name);
2366 q->name = nn;
2368 nd = pa_sprintf_malloc("%s %u", q->description, i);
2369 pa_xfree(q->description);
2370 q->description = nd;
2372 i++;
2375 pa_xfree(m);
2379 void pa_alsa_path_set_probe(pa_alsa_path_set *ps, snd_mixer_t *m, pa_bool_t ignore_dB) {
2380 pa_alsa_path *p, *n;
2382 pa_assert(ps);
2384 if (ps->probed)
2385 return;
2387 for (p = ps->paths; p; p = n) {
2388 n = p->next;
2390 if (pa_alsa_path_probe(p, m, ignore_dB) < 0) {
2391 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
2392 pa_alsa_path_free(p);
2396 path_set_unify(ps);
2397 path_set_make_paths_unique(ps);
2398 ps->probed = TRUE;
2401 static void mapping_free(pa_alsa_mapping *m) {
2402 pa_assert(m);
2404 pa_xfree(m->name);
2405 pa_xfree(m->description);
2407 pa_xstrfreev(m->device_strings);
2408 pa_xstrfreev(m->input_path_names);
2409 pa_xstrfreev(m->output_path_names);
2410 pa_xstrfreev(m->input_element);
2411 pa_xstrfreev(m->output_element);
2413 pa_assert(!m->input_pcm);
2414 pa_assert(!m->output_pcm);
2416 pa_xfree(m);
2419 static void profile_free(pa_alsa_profile *p) {
2420 pa_assert(p);
2422 pa_xfree(p->name);
2423 pa_xfree(p->description);
2425 pa_xstrfreev(p->input_mapping_names);
2426 pa_xstrfreev(p->output_mapping_names);
2428 if (p->input_mappings)
2429 pa_idxset_free(p->input_mappings, NULL, NULL);
2431 if (p->output_mappings)
2432 pa_idxset_free(p->output_mappings, NULL, NULL);
2434 pa_xfree(p);
2437 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
2438 pa_assert(ps);
2440 if (ps->profiles) {
2441 pa_alsa_profile *p;
2443 while ((p = pa_hashmap_steal_first(ps->profiles)))
2444 profile_free(p);
2446 pa_hashmap_free(ps->profiles, NULL, NULL);
2449 if (ps->mappings) {
2450 pa_alsa_mapping *m;
2452 while ((m = pa_hashmap_steal_first(ps->mappings)))
2453 mapping_free(m);
2455 pa_hashmap_free(ps->mappings, NULL, NULL);
2458 pa_xfree(ps);
2461 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
2462 pa_alsa_mapping *m;
2464 if (!pa_startswith(name, "Mapping "))
2465 return NULL;
2467 name += 8;
2469 if ((m = pa_hashmap_get(ps->mappings, name)))
2470 return m;
2472 m = pa_xnew0(pa_alsa_mapping, 1);
2473 m->profile_set = ps;
2474 m->name = pa_xstrdup(name);
2475 pa_channel_map_init(&m->channel_map);
2477 pa_hashmap_put(ps->mappings, m->name, m);
2479 return m;
2482 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
2483 pa_alsa_profile *p;
2485 if (!pa_startswith(name, "Profile "))
2486 return NULL;
2488 name += 8;
2490 if ((p = pa_hashmap_get(ps->profiles, name)))
2491 return p;
2493 p = pa_xnew0(pa_alsa_profile, 1);
2494 p->profile_set = ps;
2495 p->name = pa_xstrdup(name);
2497 pa_hashmap_put(ps->profiles, p->name, p);
2499 return p;
2502 static int mapping_parse_device_strings(
2503 const char *filename,
2504 unsigned line,
2505 const char *section,
2506 const char *lvalue,
2507 const char *rvalue,
2508 void *data,
2509 void *userdata) {
2511 pa_alsa_profile_set *ps = userdata;
2512 pa_alsa_mapping *m;
2514 pa_assert(ps);
2516 if (!(m = mapping_get(ps, section))) {
2517 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2518 return -1;
2521 pa_xstrfreev(m->device_strings);
2522 if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
2523 pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
2524 return -1;
2527 return 0;
2530 static int mapping_parse_channel_map(
2531 const char *filename,
2532 unsigned line,
2533 const char *section,
2534 const char *lvalue,
2535 const char *rvalue,
2536 void *data,
2537 void *userdata) {
2539 pa_alsa_profile_set *ps = userdata;
2540 pa_alsa_mapping *m;
2542 pa_assert(ps);
2544 if (!(m = mapping_get(ps, section))) {
2545 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2546 return -1;
2549 if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
2550 pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
2551 return -1;
2554 return 0;
2557 static int mapping_parse_paths(
2558 const char *filename,
2559 unsigned line,
2560 const char *section,
2561 const char *lvalue,
2562 const char *rvalue,
2563 void *data,
2564 void *userdata) {
2566 pa_alsa_profile_set *ps = userdata;
2567 pa_alsa_mapping *m;
2569 pa_assert(ps);
2571 if (!(m = mapping_get(ps, section))) {
2572 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2573 return -1;
2576 if (pa_streq(lvalue, "paths-input")) {
2577 pa_xstrfreev(m->input_path_names);
2578 m->input_path_names = pa_split_spaces_strv(rvalue);
2579 } else {
2580 pa_xstrfreev(m->output_path_names);
2581 m->output_path_names = pa_split_spaces_strv(rvalue);
2584 return 0;
2587 static int mapping_parse_element(
2588 const char *filename,
2589 unsigned line,
2590 const char *section,
2591 const char *lvalue,
2592 const char *rvalue,
2593 void *data,
2594 void *userdata) {
2596 pa_alsa_profile_set *ps = userdata;
2597 pa_alsa_mapping *m;
2599 pa_assert(ps);
2601 if (!(m = mapping_get(ps, section))) {
2602 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2603 return -1;
2606 if (pa_streq(lvalue, "element-input")) {
2607 pa_xstrfreev(m->input_element);
2608 m->input_element = pa_split_spaces_strv(rvalue);
2609 } else {
2610 pa_xstrfreev(m->output_element);
2611 m->output_element = pa_split_spaces_strv(rvalue);
2614 return 0;
2617 static int mapping_parse_direction(
2618 const char *filename,
2619 unsigned line,
2620 const char *section,
2621 const char *lvalue,
2622 const char *rvalue,
2623 void *data,
2624 void *userdata) {
2626 pa_alsa_profile_set *ps = userdata;
2627 pa_alsa_mapping *m;
2629 pa_assert(ps);
2631 if (!(m = mapping_get(ps, section))) {
2632 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2633 return -1;
2636 if (pa_streq(rvalue, "input"))
2637 m->direction = PA_ALSA_DIRECTION_INPUT;
2638 else if (pa_streq(rvalue, "output"))
2639 m->direction = PA_ALSA_DIRECTION_OUTPUT;
2640 else if (pa_streq(rvalue, "any"))
2641 m->direction = PA_ALSA_DIRECTION_ANY;
2642 else {
2643 pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
2644 return -1;
2647 return 0;
2650 static int mapping_parse_description(
2651 const char *filename,
2652 unsigned line,
2653 const char *section,
2654 const char *lvalue,
2655 const char *rvalue,
2656 void *data,
2657 void *userdata) {
2659 pa_alsa_profile_set *ps = userdata;
2660 pa_alsa_profile *p;
2661 pa_alsa_mapping *m;
2663 pa_assert(ps);
2665 if ((m = mapping_get(ps, section))) {
2666 pa_xstrdup(m->description);
2667 m->description = pa_xstrdup(rvalue);
2668 } else if ((p = profile_get(ps, section))) {
2669 pa_xfree(p->description);
2670 p->description = pa_xstrdup(rvalue);
2671 } else {
2672 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2673 return -1;
2676 return 0;
2679 static int mapping_parse_priority(
2680 const char *filename,
2681 unsigned line,
2682 const char *section,
2683 const char *lvalue,
2684 const char *rvalue,
2685 void *data,
2686 void *userdata) {
2688 pa_alsa_profile_set *ps = userdata;
2689 pa_alsa_profile *p;
2690 pa_alsa_mapping *m;
2691 uint32_t prio;
2693 pa_assert(ps);
2695 if (pa_atou(rvalue, &prio) < 0) {
2696 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
2697 return -1;
2700 if ((m = mapping_get(ps, section)))
2701 m->priority = prio;
2702 else if ((p = profile_get(ps, section)))
2703 p->priority = prio;
2704 else {
2705 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2706 return -1;
2709 return 0;
2712 static int profile_parse_mappings(
2713 const char *filename,
2714 unsigned line,
2715 const char *section,
2716 const char *lvalue,
2717 const char *rvalue,
2718 void *data,
2719 void *userdata) {
2721 pa_alsa_profile_set *ps = userdata;
2722 pa_alsa_profile *p;
2724 pa_assert(ps);
2726 if (!(p = profile_get(ps, section))) {
2727 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2728 return -1;
2731 if (pa_streq(lvalue, "input-mappings")) {
2732 pa_xstrfreev(p->input_mapping_names);
2733 p->input_mapping_names = pa_split_spaces_strv(rvalue);
2734 } else {
2735 pa_xstrfreev(p->output_mapping_names);
2736 p->output_mapping_names = pa_split_spaces_strv(rvalue);
2739 return 0;
2742 static int profile_parse_skip_probe(
2743 const char *filename,
2744 unsigned line,
2745 const char *section,
2746 const char *lvalue,
2747 const char *rvalue,
2748 void *data,
2749 void *userdata) {
2751 pa_alsa_profile_set *ps = userdata;
2752 pa_alsa_profile *p;
2753 int b;
2755 pa_assert(ps);
2757 if (!(p = profile_get(ps, section))) {
2758 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2759 return -1;
2762 if ((b = pa_parse_boolean(rvalue)) < 0) {
2763 pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
2764 return -1;
2767 p->supported = b;
2769 return 0;
2772 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
2774 static const struct description_map well_known_descriptions[] = {
2775 { "analog-mono", N_("Analog Mono") },
2776 { "analog-stereo", N_("Analog Stereo") },
2777 { "analog-surround-21", N_("Analog Surround 2.1") },
2778 { "analog-surround-30", N_("Analog Surround 3.0") },
2779 { "analog-surround-31", N_("Analog Surround 3.1") },
2780 { "analog-surround-40", N_("Analog Surround 4.0") },
2781 { "analog-surround-41", N_("Analog Surround 4.1") },
2782 { "analog-surround-50", N_("Analog Surround 5.0") },
2783 { "analog-surround-51", N_("Analog Surround 5.1") },
2784 { "analog-surround-61", N_("Analog Surround 6.0") },
2785 { "analog-surround-61", N_("Analog Surround 6.1") },
2786 { "analog-surround-70", N_("Analog Surround 7.0") },
2787 { "analog-surround-71", N_("Analog Surround 7.1") },
2788 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
2789 { "iec958-surround-40", N_("Digital Surround 4.0 (IEC958)") },
2790 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
2791 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
2792 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
2795 pa_assert(m);
2797 if (!pa_channel_map_valid(&m->channel_map)) {
2798 pa_log("Mapping %s is missing channel map.", m->name);
2799 return -1;
2802 if (!m->device_strings) {
2803 pa_log("Mapping %s is missing device strings.", m->name);
2804 return -1;
2807 if ((m->input_path_names && m->input_element) ||
2808 (m->output_path_names && m->output_element)) {
2809 pa_log("Mapping %s must have either mixer path or mixer elment, not both.", m->name);
2810 return -1;
2813 if (!m->description)
2814 m->description = pa_xstrdup(lookup_description(m->name,
2815 well_known_descriptions,
2816 PA_ELEMENTSOF(well_known_descriptions)));
2818 if (!m->description)
2819 m->description = pa_xstrdup(m->name);
2821 if (bonus) {
2822 if (pa_channel_map_equal(&m->channel_map, bonus))
2823 m->priority += 5000;
2824 else if (m->channel_map.channels == bonus->channels)
2825 m->priority += 4000;
2828 return 0;
2831 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
2832 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
2834 pa_assert(m);
2836 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
2837 m->name,
2838 pa_strnull(m->description),
2839 m->priority,
2840 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
2841 pa_yes_no(m->supported),
2842 m->direction);
2845 static void profile_set_add_auto_pair(
2846 pa_alsa_profile_set *ps,
2847 pa_alsa_mapping *m, /* output */
2848 pa_alsa_mapping *n /* input */) {
2850 char *name;
2851 pa_alsa_profile *p;
2853 pa_assert(ps);
2854 pa_assert(m || n);
2856 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
2857 return;
2859 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
2860 return;
2862 if (m && n)
2863 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
2864 else if (m)
2865 name = pa_sprintf_malloc("output:%s", m->name);
2866 else
2867 name = pa_sprintf_malloc("input:%s", n->name);
2869 if ((p = pa_hashmap_get(ps->profiles, name))) {
2870 pa_xfree(name);
2871 return;
2874 p = pa_xnew0(pa_alsa_profile, 1);
2875 p->profile_set = ps;
2876 p->name = name;
2878 if (m) {
2879 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2880 pa_idxset_put(p->output_mappings, m, NULL);
2881 p->priority += m->priority * 100;
2884 if (n) {
2885 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2886 pa_idxset_put(p->input_mappings, n, NULL);
2887 p->priority += n->priority;
2890 pa_hashmap_put(ps->profiles, p->name, p);
2893 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
2894 pa_alsa_mapping *m, *n;
2895 void *m_state, *n_state;
2897 pa_assert(ps);
2899 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
2900 profile_set_add_auto_pair(ps, m, NULL);
2902 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
2903 profile_set_add_auto_pair(ps, m, n);
2906 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
2907 profile_set_add_auto_pair(ps, NULL, n);
2910 static int profile_verify(pa_alsa_profile *p) {
2912 static const struct description_map well_known_descriptions[] = {
2913 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
2914 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
2915 { "output:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
2916 { "off", N_("Off") }
2919 pa_assert(p);
2921 /* Replace the output mapping names by the actual mappings */
2922 if (p->output_mapping_names) {
2923 char **name;
2925 pa_assert(!p->output_mappings);
2926 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2928 for (name = p->output_mapping_names; *name; name++) {
2929 pa_alsa_mapping *m;
2930 char **in;
2931 pa_bool_t duplicate = FALSE;
2933 for (in = name + 1; *in; in++)
2934 if (pa_streq(*name, *in)) {
2935 duplicate = TRUE;
2936 break;
2939 if (duplicate)
2940 continue;
2942 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
2943 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
2944 return -1;
2947 pa_idxset_put(p->output_mappings, m, NULL);
2950 pa_xstrfreev(p->output_mapping_names);
2951 p->output_mapping_names = NULL;
2954 /* Replace the input mapping names by the actual mappings */
2955 if (p->input_mapping_names) {
2956 char **name;
2958 pa_assert(!p->input_mappings);
2959 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2961 for (name = p->input_mapping_names; *name; name++) {
2962 pa_alsa_mapping *m;
2963 char **in;
2964 pa_bool_t duplicate = FALSE;
2966 for (in = name + 1; *in; in++)
2967 if (pa_streq(*name, *in)) {
2968 duplicate = TRUE;
2969 break;
2972 if (duplicate)
2973 continue;
2975 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
2976 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
2977 return -1;
2980 pa_idxset_put(p->input_mappings, m, NULL);
2983 pa_xstrfreev(p->input_mapping_names);
2984 p->input_mapping_names = NULL;
2987 if (!p->input_mappings && !p->output_mappings) {
2988 pa_log("Profile '%s' lacks mappings.", p->name);
2989 return -1;
2992 if (!p->description)
2993 p->description = pa_xstrdup(lookup_description(p->name,
2994 well_known_descriptions,
2995 PA_ELEMENTSOF(well_known_descriptions)));
2997 if (!p->description) {
2998 pa_strbuf *sb;
2999 uint32_t idx;
3000 pa_alsa_mapping *m;
3002 sb = pa_strbuf_new();
3004 if (p->output_mappings)
3005 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3006 if (!pa_strbuf_isempty(sb))
3007 pa_strbuf_puts(sb, " + ");
3009 pa_strbuf_printf(sb, "%s Output", m->description);
3012 if (p->input_mappings)
3013 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3014 if (!pa_strbuf_isempty(sb))
3015 pa_strbuf_puts(sb, " + ");
3017 pa_strbuf_printf(sb, "%s Input", m->description);
3020 p->description = pa_strbuf_tostring_free(sb);
3023 return 0;
3026 void pa_alsa_profile_dump(pa_alsa_profile *p) {
3027 uint32_t idx;
3028 pa_alsa_mapping *m;
3029 pa_assert(p);
3031 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3032 p->name,
3033 pa_strnull(p->description),
3034 p->priority,
3035 pa_yes_no(p->supported),
3036 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
3037 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
3039 if (p->input_mappings)
3040 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
3041 pa_log_debug("Input %s", m->name);
3043 if (p->output_mappings)
3044 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
3045 pa_log_debug("Output %s", m->name);
3048 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
3049 pa_alsa_profile_set *ps;
3050 pa_alsa_profile *p;
3051 pa_alsa_mapping *m;
3052 char *fn;
3053 int r;
3054 void *state;
3056 static pa_config_item items[] = {
3057 /* [General] */
3058 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
3060 /* [Mapping ...] */
3061 { "device-strings", mapping_parse_device_strings, NULL, NULL },
3062 { "channel-map", mapping_parse_channel_map, NULL, NULL },
3063 { "paths-input", mapping_parse_paths, NULL, NULL },
3064 { "paths-output", mapping_parse_paths, NULL, NULL },
3065 { "element-input", mapping_parse_element, NULL, NULL },
3066 { "element-output", mapping_parse_element, NULL, NULL },
3067 { "direction", mapping_parse_direction, NULL, NULL },
3069 /* Shared by [Mapping ...] and [Profile ...] */
3070 { "description", mapping_parse_description, NULL, NULL },
3071 { "priority", mapping_parse_priority, NULL, NULL },
3073 /* [Profile ...] */
3074 { "input-mappings", profile_parse_mappings, NULL, NULL },
3075 { "output-mappings", profile_parse_mappings, NULL, NULL },
3076 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
3077 { NULL, NULL, NULL, NULL }
3080 ps = pa_xnew0(pa_alsa_profile_set, 1);
3081 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3082 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3084 items[0].data = &ps->auto_profiles;
3086 if (!fname)
3087 fname = "default.conf";
3089 fn = pa_maybe_prefix_path(fname, PA_ALSA_PROFILE_SETS_DIR);
3090 r = pa_config_parse(fn, NULL, items, ps);
3091 pa_xfree(fn);
3093 if (r < 0)
3094 goto fail;
3096 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3097 if (mapping_verify(m, bonus) < 0)
3098 goto fail;
3100 if (ps->auto_profiles)
3101 profile_set_add_auto(ps);
3103 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3104 if (profile_verify(p) < 0)
3105 goto fail;
3107 return ps;
3109 fail:
3110 pa_alsa_profile_set_free(ps);
3111 return NULL;
3114 void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, const pa_sample_spec *ss) {
3115 void *state;
3116 pa_alsa_profile *p, *last = NULL;
3117 pa_alsa_mapping *m;
3119 pa_assert(ps);
3120 pa_assert(dev_id);
3121 pa_assert(ss);
3123 if (ps->probed)
3124 return;
3126 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
3127 pa_sample_spec try_ss;
3128 pa_channel_map try_map;
3129 uint32_t idx;
3131 /* Is this already marked that it is supported? (i.e. from the config file) */
3132 if (p->supported)
3133 continue;
3135 pa_log_debug("Looking at profile %s", p->name);
3137 /* Close PCMs from the last iteration we don't need anymore */
3138 if (last && last->output_mappings)
3139 PA_IDXSET_FOREACH(m, last->output_mappings, idx) {
3141 if (!m->output_pcm)
3142 break;
3144 if (last->supported)
3145 m->supported++;
3147 if (!p->output_mappings || !pa_idxset_get_by_data(p->output_mappings, m, NULL)) {
3148 snd_pcm_close(m->output_pcm);
3149 m->output_pcm = NULL;
3153 if (last && last->input_mappings)
3154 PA_IDXSET_FOREACH(m, last->input_mappings, idx) {
3156 if (!m->input_pcm)
3157 break;
3159 if (last->supported)
3160 m->supported++;
3162 if (!p->input_mappings || !pa_idxset_get_by_data(p->input_mappings, m, NULL)) {
3163 snd_pcm_close(m->input_pcm);
3164 m->input_pcm = NULL;
3168 p->supported = TRUE;
3170 /* Check if we can open all new ones */
3171 if (p->output_mappings)
3172 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3174 if (m->output_pcm)
3175 continue;
3177 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
3178 try_map = m->channel_map;
3179 try_ss = *ss;
3180 try_ss.channels = try_map.channels;
3182 if (!(m ->output_pcm = pa_alsa_open_by_template(
3183 m->device_strings,
3184 dev_id,
3185 NULL,
3186 &try_ss, &try_map,
3187 SND_PCM_STREAM_PLAYBACK,
3188 NULL, NULL, 0, NULL, NULL,
3189 TRUE))) {
3190 p->supported = FALSE;
3191 break;
3195 if (p->input_mappings && p->supported)
3196 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3198 if (m->input_pcm)
3199 continue;
3201 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
3202 try_map = m->channel_map;
3203 try_ss = *ss;
3204 try_ss.channels = try_map.channels;
3206 if (!(m ->input_pcm = pa_alsa_open_by_template(
3207 m->device_strings,
3208 dev_id,
3209 NULL,
3210 &try_ss, &try_map,
3211 SND_PCM_STREAM_CAPTURE,
3212 NULL, NULL, 0, NULL, NULL,
3213 TRUE))) {
3214 p->supported = FALSE;
3215 break;
3219 last = p;
3221 if (p->supported)
3222 pa_log_debug("Profile %s supported.", p->name);
3225 /* Clean up */
3226 if (last) {
3227 uint32_t idx;
3229 if (last->output_mappings)
3230 PA_IDXSET_FOREACH(m, last->output_mappings, idx)
3231 if (m->output_pcm) {
3233 if (last->supported)
3234 m->supported++;
3236 snd_pcm_close(m->output_pcm);
3237 m->output_pcm = NULL;
3240 if (last->input_mappings)
3241 PA_IDXSET_FOREACH(m, last->input_mappings, idx)
3242 if (m->input_pcm) {
3244 if (last->supported)
3245 m->supported++;
3247 snd_pcm_close(m->input_pcm);
3248 m->input_pcm = NULL;
3252 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3253 if (!p->supported) {
3254 pa_hashmap_remove(ps->profiles, p->name);
3255 profile_free(p);
3258 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3259 if (m->supported <= 0) {
3260 pa_hashmap_remove(ps->mappings, m->name);
3261 mapping_free(m);
3264 ps->probed = TRUE;
3267 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
3268 pa_alsa_profile *p;
3269 pa_alsa_mapping *m;
3270 void *state;
3272 pa_assert(ps);
3274 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u",
3275 (void*)
3277 pa_yes_no(ps->auto_profiles),
3278 pa_yes_no(ps->probed),
3279 pa_hashmap_size(ps->mappings),
3280 pa_hashmap_size(ps->profiles));
3282 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3283 pa_alsa_mapping_dump(m);
3285 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3286 pa_alsa_profile_dump(p);
3289 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps) {
3290 pa_alsa_path *path;
3292 pa_assert(p);
3293 pa_assert(!*p);
3294 pa_assert(ps);
3296 /* if there is no path, we don't want a port list */
3297 if (!ps->paths)
3298 return;
3300 if (!ps->paths->next){
3301 pa_alsa_setting *s;
3303 /* If there is only one path, but no or only one setting, then
3304 * we want a port list either */
3305 if (!ps->paths->settings || !ps->paths->settings->next)
3306 return;
3308 /* Ok, there is only one path, however with multiple settings,
3309 * so let's create a port for each setting */
3310 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3312 PA_LLIST_FOREACH(s, ps->paths->settings) {
3313 pa_device_port *port;
3314 pa_alsa_port_data *data;
3316 port = pa_device_port_new(s->name, s->description, sizeof(pa_alsa_port_data));
3317 port->priority = s->priority;
3319 data = PA_DEVICE_PORT_DATA(port);
3320 data->path = ps->paths;
3321 data->setting = s;
3323 pa_hashmap_put(*p, port->name, port);
3326 } else {
3328 /* We have multiple paths, so let's create a port for each
3329 * one, and each of each settings */
3330 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3332 PA_LLIST_FOREACH(path, ps->paths) {
3334 if (!path->settings || !path->settings->next) {
3335 pa_device_port *port;
3336 pa_alsa_port_data *data;
3338 /* If there is no or just one setting we only need a
3339 * single entry */
3341 port = pa_device_port_new(path->name, path->description, sizeof(pa_alsa_port_data));
3342 port->priority = path->priority * 100;
3345 data = PA_DEVICE_PORT_DATA(port);
3346 data->path = path;
3347 data->setting = path->settings;
3349 pa_hashmap_put(*p, port->name, port);
3350 } else {
3351 pa_alsa_setting *s;
3353 PA_LLIST_FOREACH(s, path->settings) {
3354 pa_device_port *port;
3355 pa_alsa_port_data *data;
3356 char *n, *d;
3358 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
3360 if (s->description[0])
3361 d = pa_sprintf_malloc(_("%s / %s"), path->description, s->description);
3362 else
3363 d = pa_xstrdup(path->description);
3365 port = pa_device_port_new(n, d, sizeof(pa_alsa_port_data));
3366 port->priority = path->priority * 100 + s->priority;
3368 pa_xfree(n);
3369 pa_xfree(d);
3371 data = PA_DEVICE_PORT_DATA(port);
3372 data->path = path;
3373 data->setting = s;
3375 pa_hashmap_put(*p, port->name, port);
3381 pa_log_debug("Added %u ports", pa_hashmap_size(*p));