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
27 #include <sys/types.h>
29 #include <asoundlib.h>
31 #ifdef HAVE_VALGRIND_MEMCHECK_H
32 #include <valgrind/memcheck.h>
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 {
57 const char *description;
60 static const char *lookup_description(const char *name, const struct description_map dm[], unsigned n) {
63 for (i = 0; i < n; i++)
64 if (pa_streq(dm[i].name, name))
65 return dm[i].description;
70 struct pa_alsa_fdlist {
73 /* This is a temporary buffer used to avoid lots of mallocs */
74 struct pollfd *work_fds;
79 pa_defer_event *defer;
84 void (*cb)(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;
93 unsigned short revents;
97 pa_assert(fdl->mixer);
99 pa_assert(fdl->work_fds);
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;
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));
129 a->defer_enable(fdl->defer, 1);
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;
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));
151 num_fds = (unsigned) n;
153 if (num_fds != fdl->num_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));
171 if (memcmp(fdl->fds, fdl->work_fds, sizeof(struct pollfd) * num_fds) == 0)
175 for (i = 0; i < fdl->num_fds; i++)
176 a->io_free(fdl->ios[i]);
178 if (num_fds != fdl->num_fds) {
185 fdl->ios = pa_xnew(pa_io_event*, num_fds);
188 temp = fdl->work_fds;
189 fdl->work_fds = fdl->fds;
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),
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);
209 void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {
214 fdl->m->defer_free(fdl->defer);
220 for (i = 0; i < fdl->num_fds; i++)
221 fdl->m->io_free(fdl->ios[i]);
228 pa_xfree(fdl->work_fds);
233 int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m) {
235 pa_assert(mixer_handle);
239 fdl->mixer = mixer_handle;
241 fdl->defer = m->defer_new(m, defer_cb, fdl);
246 static int prepare_mixer(snd_mixer_t *mixer, const char *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));
257 if ((err = snd_mixer_selem_register(mixer, NULL, NULL)) < 0) {
258 pa_log_warn("Unable to register mixer: %s", pa_alsa_strerror(err));
262 if ((err = snd_mixer_load(mixer)) < 0) {
263 pa_log_warn("Unable to load mixer: %s", pa_alsa_strerror(err));
267 pa_log_info("Successfully attached to mixer '%s'", dev);
271 snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device) {
275 snd_pcm_info_t* info;
276 snd_pcm_info_alloca(&info);
280 if ((err = snd_mixer_open(&m, 0)) < 0) {
281 pa_log("Error opening mixer: %s", pa_alsa_strerror(err));
285 /* First, try by name */
286 if ((dev = snd_pcm_name(pcm)))
287 if (prepare_mixer(m, dev) >= 0) {
289 *ctl_device = pa_xstrdup(dev);
294 /* Then, try by card index */
295 if (snd_pcm_info(pcm, info) >= 0) {
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) {
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) {
389 pa_idxset_free(s->options, NULL, NULL);
392 pa_xfree(s->description);
396 static void option_free(pa_alsa_option *o) {
399 pa_xfree(o->alsa_name);
401 pa_xfree(o->description);
405 static void element_free(pa_alsa_element *e) {
409 while ((o = e->options)) {
410 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
414 pa_xfree(e->alsa_name);
418 void pa_alsa_path_free(pa_alsa_path *p) {
424 while ((e = p->elements)) {
425 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
429 while ((s = p->settings)) {
430 PA_LLIST_REMOVE(pa_alsa_setting, p->settings, s);
435 pa_xfree(p->description);
439 void pa_alsa_path_set_free(pa_alsa_path_set *ps) {
443 while ((p = ps->paths)) {
444 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
445 pa_alsa_path_free(p);
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) {
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) \
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); \
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;
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);
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++) {
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);
512 if (snd_mixer_selem_has_capture_channel(me, c))
513 r = snd_mixer_selem_get_capture_dB(me, c, &value);
521 #ifdef HAVE_VALGRIND_MEMCHECK_H
522 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
525 f = from_alsa_dB(value);
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);
536 if (snd_mixer_selem_has_capture_channel(me, c))
537 r = snd_mixer_selem_get_capture_volume(me, c, &value);
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)
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;
563 int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
574 pa_cvolume_reset(v, cm->channels);
576 PA_LLIST_FOREACH(e, p->elements) {
579 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
582 pa_assert(!p->has_dB || e->has_dB);
584 if (element_get_volume(e, m, cm, &ev) < 0)
587 /* If we have no dB information all we can do is take the first element and leave */
593 pa_sw_cvolume_multiply(v, v, &ev);
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;
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);
614 /* We return muted if at least one channel is muted */
616 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
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);
626 if (snd_mixer_selem_has_capture_channel(me, c))
627 r = snd_mixer_selem_get_capture_switch(me, c, &value);
645 int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t *muted) {
655 PA_LLIST_FOREACH(e, p->elements) {
658 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
661 if (element_get_switch(e, m, &b) < 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;
677 snd_mixer_elem_t *me;
678 snd_mixer_selem_channel_id_t c;
679 pa_channel_position_mask_t mask = 0;
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);
694 pa_cvolume_mute(&rv, cm->channels);
696 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
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)
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);
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);
728 #ifdef HAVE_VALGRIND_MEMCHECK_H
729 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
732 f = from_alsa_dB(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);
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);
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)
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;
775 int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
783 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
788 rv = *v; /* Remaining adjustment */
789 pa_cvolume_reset(v, cm->channels); /* Adjustment done */
791 PA_LLIST_FOREACH(e, p->elements) {
794 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
797 pa_assert(!p->has_dB || e->has_dB);
800 if (element_set_volume(e, m, cm, &ev) < 0)
808 pa_sw_cvolume_multiply(v, v, &ev);
809 pa_sw_cvolume_divide(&rv, &rv, &ev);
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;
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);
829 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
830 r = snd_mixer_selem_set_playback_switch_all(me, b);
832 r = snd_mixer_selem_set_capture_switch_all(me, b);
835 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
840 int pa_alsa_path_set_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t muted) {
849 PA_LLIST_FOREACH(e, p->elements) {
851 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
854 if (element_set_switch(e, m, !muted) < 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;
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);
875 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
876 r = snd_mixer_selem_set_playback_volume_all(me, e->min_volume);
878 r = snd_mixer_selem_set_capture_volume_all(me, e->min_volume);
881 pa_log_warn("Faile to set volume to muted of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
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;
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);
901 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
902 r = snd_mixer_selem_set_playback_dB_all(me, 0, +1);
904 r = snd_mixer_selem_set_capture_dB_all(me, 0, +1);
907 pa_log_warn("Faile to set volume to 0dB of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
912 int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
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);
930 case PA_ALSA_SWITCH_ON:
931 r = element_set_switch(e, m, TRUE);
934 case PA_ALSA_SWITCH_IGNORE:
935 case PA_ALSA_SWITCH_SELECT:
943 switch (e->volume_use) {
944 case PA_ALSA_VOLUME_OFF:
945 case PA_ALSA_VOLUME_MERGE:
946 r = element_mute_volume(e, m);
949 case PA_ALSA_VOLUME_ZERO:
950 r = element_zero_volume(e, m);
953 case PA_ALSA_VOLUME_IGNORE:
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;
973 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
975 snd_mixer_selem_has_playback_switch(me) ||
976 (e->direction_try_other && snd_mixer_selem_has_capture_switch(me));
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) {
985 snd_mixer_selem_has_playback_volume(me) ||
986 (e->direction_try_other && snd_mixer_selem_has_capture_volume(me));
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))
1000 if (e->required == PA_ALSA_REQUIRED_ANY && !(has_switch || has_volume || has_enumeration))
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))
1008 if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration))
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;
1021 SELEM_INIT(sid, e->alsa_name);
1023 if (!(me = snd_mixer_find_selem(m, sid))) {
1025 if (e->required != PA_ALSA_REQUIRED_IGNORE)
1028 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1029 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1030 e->enumeration_use = PA_ALSA_VOLUME_IGNORE;
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;
1042 e->switch_use = PA_ALSA_SWITCH_IGNORE;
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;
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;
1067 e->volume_use = PA_ALSA_VOLUME_IGNORE;
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;
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;
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;
1089 e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0;
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));
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);
1106 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1107 r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume);
1109 r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
1112 pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r));
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;
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;
1128 is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
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];
1142 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1144 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1147 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1148 e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
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);
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)
1165 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1166 has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
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;
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)
1186 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
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) {
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));
1200 PA_LLIST_FOREACH(o, e->options) {
1203 for (i = 0; i < n; i++) {
1206 if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
1209 if (!pa_streq(buf, o->alsa_name))
1220 static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, pa_bool_t prefixed) {
1227 if (!pa_startswith(section, "Element "))
1233 /* This is not an element section, but an enum section? */
1234 if (strchr(section, ':'))
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))
1244 e = pa_xnew0(pa_alsa_element, 1);
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);
1252 p->last_element = e;
1256 static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
1262 if (!pa_startswith(section, "Option "))
1267 /* This is not an enum section, but an element section? */
1268 if (!(on = strchr(section, ':')))
1271 en = pa_xstrndup(section, on - section);
1274 if (p->last_option &&
1275 pa_streq(p->last_option->element->alsa_name, en) &&
1276 pa_streq(p->last_option->alsa_name, on)) {
1278 return p->last_option;
1281 pa_assert_se(e = element_get(p, en, FALSE));
1284 PA_LLIST_FOREACH(o, e->options)
1285 if (pa_streq(o->alsa_name, on))
1288 o = pa_xnew0(pa_alsa_option, 1);
1290 o->alsa_name = pa_xstrdup(on);
1293 if (p->last_option && p->last_option->element == e)
1294 PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
1296 PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
1303 static int element_parse_switch(
1304 const char *filename,
1306 const char *section,
1312 pa_alsa_path *p = userdata;
1317 if (!(e = element_get(p, section, TRUE))) {
1318 pa_log("[%s:%u] Switch makes no sense in '%s'", filename, line, section);
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;
1333 pa_log("[%s:%u] Switch invalid of '%s'", filename, line, section);
1340 static int element_parse_volume(
1341 const char *filename,
1343 const char *section,
1349 pa_alsa_path *p = userdata;
1354 if (!(e = element_get(p, section, TRUE))) {
1355 pa_log("[%s:%u] Volume makes no sense in '%s'", filename, line, section);
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;
1368 pa_log("[%s:%u] Volume invalid of '%s'", filename, line, section);
1375 static int element_parse_enumeration(
1376 const char *filename,
1378 const char *section,
1384 pa_alsa_path *p = userdata;
1389 if (!(e = element_get(p, section, TRUE))) {
1390 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename, line, section);
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;
1399 pa_log("[%s:%u] Enumeration invalid of '%s'", filename, line, section);
1406 static int option_parse_priority(
1407 const char *filename,
1409 const char *section,
1415 pa_alsa_path *p = userdata;
1421 if (!(o = option_get(p, section))) {
1422 pa_log("[%s:%u] Priority makes no sense in '%s'", filename, line, section);
1426 if (pa_atou(rvalue, &prio) < 0) {
1427 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
1435 static int option_parse_name(
1436 const char *filename,
1438 const char *section,
1444 pa_alsa_path *p = userdata;
1449 if (!(o = option_get(p, section))) {
1450 pa_log("[%s:%u] Name makes no sense in '%s'", filename, line, section);
1455 o->name = pa_xstrdup(rvalue);
1460 static int element_parse_required(
1461 const char *filename,
1463 const char *section,
1469 pa_alsa_path *p = userdata;
1471 pa_alsa_required_t req;
1475 if (!(e = element_get(p, section, TRUE))) {
1476 pa_log("[%s:%u] Required makes no sense in '%s'", filename, line, section);
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;
1491 pa_log("[%s:%u] Required invalid of '%s'", filename, line, section);
1495 if (pa_streq(lvalue, "required-absent"))
1496 e->required_absent = req;
1503 static int element_parse_direction(
1504 const char *filename,
1506 const char *section,
1512 pa_alsa_path *p = userdata;
1517 if (!(e = element_get(p, section, TRUE))) {
1518 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
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;
1527 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1534 static int element_parse_direction_try_other(
1535 const char *filename,
1537 const char *section,
1543 pa_alsa_path *p = userdata;
1547 if (!(e = element_get(p, section, TRUE))) {
1548 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1552 if ((yes = pa_parse_boolean(rvalue)) < 0) {
1553 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1557 e->direction_try_other = !!yes;
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;
1583 pa_channel_position_t p;
1585 if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
1588 v = PA_CHANNEL_POSITION_MASK(p);
1594 static int element_parse_override_map(
1595 const char *filename,
1597 const char *section,
1603 pa_alsa_path *p = userdata;
1605 const char *state = NULL;
1609 if (!(e = element_get(p, section, TRUE))) {
1610 pa_log("[%s:%u] Override map makes no sense in '%s'", filename, line, section);
1614 while ((n = pa_split(rvalue, ",", &state))) {
1615 pa_channel_position_mask_t m;
1620 if ((m = parse_mask(n)) == 0) {
1621 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename, line, n, section);
1627 if (pa_streq(lvalue, "override-map.1"))
1628 e->masks[i++][0] = m;
1630 e->masks[i++][1] = m;
1632 /* Later on we might add override-map.3 and so on here ... */
1637 e->override_map = TRUE;
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;
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);
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);
1661 r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
1664 pa_log_warn("Faile to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
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));
1676 int pa_alsa_setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
1683 PA_IDXSET_FOREACH(o, s->options, idx)
1684 element_set_option(o->element, m, o->alsa_idx);
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", "" }
1711 pa_log("No name set for option %s", o->alsa_name);
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);
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);
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);
1738 static int element_verify(pa_alsa_element *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);
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);
1754 PA_LLIST_FOREACH(o, e->options)
1755 if (option_verify(o) < 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") }
1778 PA_LLIST_FOREACH(e, p->elements)
1779 if (element_verify(e) < 0)
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);
1793 pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction) {
1799 pa_config_item items[] = {
1801 { "priority", pa_config_parse_unsigned, NULL, "General" },
1802 { "description", pa_config_parse_string, NULL, "General" },
1803 { "name", pa_config_parse_string, NULL, "General" },
1806 { "priority", option_parse_priority, NULL, NULL },
1807 { "name", option_parse_name, NULL, NULL },
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 }
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);
1841 if (path_verify(p) < 0)
1847 pa_alsa_path_free(p);
1851 pa_alsa_path* pa_alsa_path_synthesize(const char*element, pa_alsa_direction_t direction) {
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);
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);
1873 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
1874 pa_alsa_option *o, *n;
1878 for (o = e->options; o; o = n) {
1881 if (o->alsa_idx < 0) {
1882 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
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;
1898 for (e = p->elements; e; e = n) {
1901 if (!element_drop_unsupported(e)) {
1902 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
1908 static void path_make_options_unique(pa_alsa_path *p) {
1910 pa_alsa_option *o, *u;
1912 PA_LLIST_FOREACH(e, p->elements) {
1913 PA_LLIST_FOREACH(o, e->options) {
1917 for (u = o->next; u; u = u->next)
1918 if (pa_streq(u->name, o->name))
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) {
1930 if (!pa_streq(u->name, m))
1933 nn = pa_sprintf_malloc("%s-%u", m, i);
1937 nd = pa_sprintf_malloc("%s %u", u->description, i);
1938 pa_xfree(u->description);
1939 u->description = nd;
1949 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
1952 for (; e; e = e->next)
1953 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
1954 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
1960 for (o = e->options; o; o = o->next) {
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);
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);
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 */
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;
1999 static void path_create_settings(pa_alsa_path *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) {
2007 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2008 pa_channel_position_t t;
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);
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;
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;
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;
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)
2072 path_drop_unsupported(p);
2073 path_make_options_unique(p);
2074 path_create_settings(p);
2076 p->supported = 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];
2093 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2096 pa_log_debug("Setting %s (%s) priority=%u",
2098 pa_strnull(s->description),
2102 void pa_alsa_option_dump(pa_alsa_option *o) {
2105 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2107 pa_strnull(o->name),
2108 pa_strnull(o->description),
2113 void pa_alsa_element_dump(pa_alsa_element *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",
2125 (long long unsigned) e->merged_mask,
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) {
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",
2141 pa_strnull(p->description),
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;
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);
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) {
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) {
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;
2204 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2206 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
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;
2220 for (in = pn; *in; in++) {
2222 pa_bool_t duplicate = FALSE;
2225 for (kn = pn; kn != in; kn++)
2226 if (pa_streq(*kn, *in)) {
2234 fn = pa_sprintf_malloc("%s.conf", *in);
2236 if ((p = pa_alsa_path_new(fn, direction))) {
2238 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
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;
2254 pa_alsa_path_set_free(ps);
2258 for (ie = en; *ie; ie++) {
2262 p = pa_alsa_path_synthesize(*ie, direction);
2265 /* Mark all other passed elements for require-absent */
2266 for (je = en; *je; je++) {
2268 e = pa_xnew0(pa_alsa_element, 1);
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);
2285 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2289 pa_log_debug("Path Set %p, direction=%i, probed=%s",
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) {
2300 pa_bool_t has_dB = TRUE, has_volume = TRUE, has_mute = TRUE;
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
2307 PA_LLIST_FOREACH(p, ps->paths) {
2308 pa_assert(p->probed);
2312 else if (!p->has_dB)
2319 if (!has_volume || !has_dB || !has_mute) {
2322 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2324 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2327 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2329 PA_LLIST_FOREACH(p, ps->paths) {
2331 p->has_volume = FALSE;
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) {
2348 for (q = p->next; q; q = q->next)
2349 if (pa_streq(q->name, p->name))
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) {
2361 if (!pa_streq(q->name, m))
2364 nn = pa_sprintf_malloc("%s-%u", m, i);
2368 nd = pa_sprintf_malloc("%s %u", q->description, i);
2369 pa_xfree(q->description);
2370 q->description = nd;
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;
2387 for (p = ps->paths; p; p = n) {
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);
2397 path_set_make_paths_unique(ps);
2401 static void mapping_free(pa_alsa_mapping *m) {
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);
2419 static void profile_free(pa_alsa_profile *p) {
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);
2437 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
2443 while ((p = pa_hashmap_steal_first(ps->profiles)))
2446 pa_hashmap_free(ps->profiles, NULL, NULL);
2452 while ((m = pa_hashmap_steal_first(ps->mappings)))
2455 pa_hashmap_free(ps->mappings, NULL, NULL);
2461 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
2464 if (!pa_startswith(name, "Mapping "))
2469 if ((m = pa_hashmap_get(ps->mappings, name)))
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);
2482 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
2485 if (!pa_startswith(name, "Profile "))
2490 if ((p = pa_hashmap_get(ps->profiles, name)))
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);
2502 static int mapping_parse_device_strings(
2503 const char *filename,
2505 const char *section,
2511 pa_alsa_profile_set *ps = userdata;
2516 if (!(m = mapping_get(ps, section))) {
2517 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
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);
2530 static int mapping_parse_channel_map(
2531 const char *filename,
2533 const char *section,
2539 pa_alsa_profile_set *ps = userdata;
2544 if (!(m = mapping_get(ps, section))) {
2545 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2549 if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
2550 pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
2557 static int mapping_parse_paths(
2558 const char *filename,
2560 const char *section,
2566 pa_alsa_profile_set *ps = userdata;
2571 if (!(m = mapping_get(ps, section))) {
2572 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2576 if (pa_streq(lvalue, "paths-input")) {
2577 pa_xstrfreev(m->input_path_names);
2578 m->input_path_names = pa_split_spaces_strv(rvalue);
2580 pa_xstrfreev(m->output_path_names);
2581 m->output_path_names = pa_split_spaces_strv(rvalue);
2587 static int mapping_parse_element(
2588 const char *filename,
2590 const char *section,
2596 pa_alsa_profile_set *ps = userdata;
2601 if (!(m = mapping_get(ps, section))) {
2602 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2606 if (pa_streq(lvalue, "element-input")) {
2607 pa_xstrfreev(m->input_element);
2608 m->input_element = pa_split_spaces_strv(rvalue);
2610 pa_xstrfreev(m->output_element);
2611 m->output_element = pa_split_spaces_strv(rvalue);
2617 static int mapping_parse_direction(
2618 const char *filename,
2620 const char *section,
2626 pa_alsa_profile_set *ps = userdata;
2631 if (!(m = mapping_get(ps, section))) {
2632 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
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;
2643 pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
2650 static int mapping_parse_description(
2651 const char *filename,
2653 const char *section,
2659 pa_alsa_profile_set *ps = userdata;
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);
2672 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2679 static int mapping_parse_priority(
2680 const char *filename,
2682 const char *section,
2688 pa_alsa_profile_set *ps = userdata;
2695 if (pa_atou(rvalue, &prio) < 0) {
2696 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
2700 if ((m = mapping_get(ps, section)))
2702 else if ((p = profile_get(ps, section)))
2705 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2712 static int profile_parse_mappings(
2713 const char *filename,
2715 const char *section,
2721 pa_alsa_profile_set *ps = userdata;
2726 if (!(p = profile_get(ps, section))) {
2727 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2731 if (pa_streq(lvalue, "input-mappings")) {
2732 pa_xstrfreev(p->input_mapping_names);
2733 p->input_mapping_names = pa_split_spaces_strv(rvalue);
2735 pa_xstrfreev(p->output_mapping_names);
2736 p->output_mapping_names = pa_split_spaces_strv(rvalue);
2742 static int profile_parse_skip_probe(
2743 const char *filename,
2745 const char *section,
2751 pa_alsa_profile_set *ps = userdata;
2757 if (!(p = profile_get(ps, section))) {
2758 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2762 if ((b = pa_parse_boolean(rvalue)) < 0) {
2763 pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
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)") }
2797 if (!pa_channel_map_valid(&m->channel_map)) {
2798 pa_log("Mapping %s is missing channel map.", m->name);
2802 if (!m->device_strings) {
2803 pa_log("Mapping %s is missing device strings.", m->name);
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);
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);
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;
2831 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
2832 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
2836 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
2838 pa_strnull(m->description),
2840 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
2841 pa_yes_no(m->supported),
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 */) {
2856 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
2859 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
2863 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
2865 name = pa_sprintf_malloc("output:%s", m->name);
2867 name = pa_sprintf_malloc("input:%s", n->name);
2869 if ((p = pa_hashmap_get(ps->profiles, name))) {
2874 p = pa_xnew0(pa_alsa_profile, 1);
2875 p->profile_set = ps;
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;
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;
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") }
2921 /* Replace the output mapping names by the actual mappings */
2922 if (p->output_mapping_names) {
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++) {
2931 pa_bool_t duplicate = FALSE;
2933 for (in = name + 1; *in; in++)
2934 if (pa_streq(*name, *in)) {
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);
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) {
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++) {
2964 pa_bool_t duplicate = FALSE;
2966 for (in = name + 1; *in; in++)
2967 if (pa_streq(*name, *in)) {
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);
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);
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) {
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);
3026 void pa_alsa_profile_dump(pa_alsa_profile *p) {
3031 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3033 pa_strnull(p->description),
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;
3056 static pa_config_item items[] = {
3058 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
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 },
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;
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);
3096 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3097 if (mapping_verify(m, bonus) < 0)
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)
3110 pa_alsa_profile_set_free(ps);
3114 void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, const pa_sample_spec *ss) {
3116 pa_alsa_profile *p, *last = NULL;
3126 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
3127 pa_sample_spec try_ss;
3128 pa_channel_map try_map;
3131 /* Is this already marked that it is supported? (i.e. from the config file) */
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) {
3144 if (last->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) {
3159 if (last->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) {
3177 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
3178 try_map = m->channel_map;
3180 try_ss.channels = try_map.channels;
3182 if (!(m ->output_pcm = pa_alsa_open_by_template(
3187 SND_PCM_STREAM_PLAYBACK,
3188 NULL, NULL, 0, NULL, NULL,
3190 p->supported = FALSE;
3195 if (p->input_mappings && p->supported)
3196 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3201 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
3202 try_map = m->channel_map;
3204 try_ss.channels = try_map.channels;
3206 if (!(m ->input_pcm = pa_alsa_open_by_template(
3211 SND_PCM_STREAM_CAPTURE,
3212 NULL, NULL, 0, NULL, NULL,
3214 p->supported = FALSE;
3222 pa_log_debug("Profile %s supported.", p->name);
3229 if (last->output_mappings)
3230 PA_IDXSET_FOREACH(m, last->output_mappings, idx)
3231 if (m->output_pcm) {
3233 if (last->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)
3244 if (last->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);
3258 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3259 if (m->supported <= 0) {
3260 pa_hashmap_remove(ps->mappings, m->name);
3267 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
3274 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u",
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) {
3296 /* if there is no path, we don't want a port list */
3300 if (!ps->paths->next){
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)
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;
3323 pa_hashmap_put(*p, port->name, port);
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
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);
3347 data->setting = path->settings;
3349 pa_hashmap_put(*p, port->name, port);
3353 PA_LLIST_FOREACH(s, path->settings) {
3354 pa_device_port *port;
3355 pa_alsa_port_data *data;
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);
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;
3371 data = PA_DEVICE_PORT_DATA(port);
3375 pa_hashmap_put(*p, port->name, port);
3381 pa_log_debug("Added %u ports", pa_hashmap_size(*p));