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 struct pa_alsa_mixer_pdata {
248 pa_rtpoll_item *poll_item;
253 struct pa_alsa_mixer_pdata *pa_alsa_mixer_pdata_new(void) {
254 struct pa_alsa_mixer_pdata *pd;
256 pd = pa_xnew0(struct pa_alsa_mixer_pdata, 1);
261 void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata *pd) {
265 pa_rtpoll_item_free(pd->poll_item);
271 static int rtpoll_work_cb(pa_rtpoll_item *i) {
272 struct pa_alsa_mixer_pdata *pd;
275 unsigned short revents = 0;
278 pd = pa_rtpoll_item_get_userdata(i);
280 pa_assert_fp(i == pd->poll_item);
282 p = pa_rtpoll_item_get_pollfd(i, &n_fds);
284 if ((err = snd_mixer_poll_descriptors_revents(pd->mixer, p, n_fds, &revents)) < 0) {
285 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
286 pa_rtpoll_item_free(i);
291 snd_mixer_handle_events(pd->mixer);
292 pa_rtpoll_item_free(i);
293 pa_alsa_set_mixer_rtpoll(pd, pd->mixer, pd->rtpoll);
299 int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata *pd, snd_mixer_t *mixer, pa_rtpoll *rtp) {
308 if ((n = snd_mixer_poll_descriptors_count(mixer)) < 0) {
309 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
313 i = pa_rtpoll_item_new(rtp, PA_RTPOLL_LATE, (unsigned) n);
315 p = pa_rtpoll_item_get_pollfd(i, NULL);
317 memset(p, 0, sizeof(struct pollfd) * n);
319 if ((err = snd_mixer_poll_descriptors(mixer, p, (unsigned) n)) < 0) {
320 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
321 pa_rtpoll_item_free(i);
329 pa_rtpoll_item_set_userdata(i, pd);
330 pa_rtpoll_item_set_work_callback(i, rtpoll_work_cb);
335 static int prepare_mixer(snd_mixer_t *mixer, const char *dev) {
341 if ((err = snd_mixer_attach(mixer, dev)) < 0) {
342 pa_log_info("Unable to attach to mixer %s: %s", dev, pa_alsa_strerror(err));
346 if ((err = snd_mixer_selem_register(mixer, NULL, NULL)) < 0) {
347 pa_log_warn("Unable to register mixer: %s", pa_alsa_strerror(err));
351 if ((err = snd_mixer_load(mixer)) < 0) {
352 pa_log_warn("Unable to load mixer: %s", pa_alsa_strerror(err));
356 pa_log_info("Successfully attached to mixer '%s'", dev);
360 snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device) {
364 snd_pcm_info_t* info;
365 snd_pcm_info_alloca(&info);
369 if ((err = snd_mixer_open(&m, 0)) < 0) {
370 pa_log("Error opening mixer: %s", pa_alsa_strerror(err));
374 /* First, try by name */
375 if ((dev = snd_pcm_name(pcm)))
376 if (prepare_mixer(m, dev) >= 0) {
378 *ctl_device = pa_xstrdup(dev);
383 /* Then, try by card index */
384 if (snd_pcm_info(pcm, info) >= 0) {
388 if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
390 md = pa_sprintf_malloc("hw:%i", card_idx);
392 if (!dev || !pa_streq(dev, md))
393 if (prepare_mixer(m, md) >= 0) {
411 static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_MAX] = {
412 [PA_CHANNEL_POSITION_MONO] = SND_MIXER_SCHN_MONO, /* The ALSA name is just an alias! */
414 [PA_CHANNEL_POSITION_FRONT_CENTER] = SND_MIXER_SCHN_FRONT_CENTER,
415 [PA_CHANNEL_POSITION_FRONT_LEFT] = SND_MIXER_SCHN_FRONT_LEFT,
416 [PA_CHANNEL_POSITION_FRONT_RIGHT] = SND_MIXER_SCHN_FRONT_RIGHT,
418 [PA_CHANNEL_POSITION_REAR_CENTER] = SND_MIXER_SCHN_REAR_CENTER,
419 [PA_CHANNEL_POSITION_REAR_LEFT] = SND_MIXER_SCHN_REAR_LEFT,
420 [PA_CHANNEL_POSITION_REAR_RIGHT] = SND_MIXER_SCHN_REAR_RIGHT,
422 [PA_CHANNEL_POSITION_LFE] = SND_MIXER_SCHN_WOOFER,
424 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
425 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
427 [PA_CHANNEL_POSITION_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT,
428 [PA_CHANNEL_POSITION_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT,
430 [PA_CHANNEL_POSITION_AUX0] = SND_MIXER_SCHN_UNKNOWN,
431 [PA_CHANNEL_POSITION_AUX1] = SND_MIXER_SCHN_UNKNOWN,
432 [PA_CHANNEL_POSITION_AUX2] = SND_MIXER_SCHN_UNKNOWN,
433 [PA_CHANNEL_POSITION_AUX3] = SND_MIXER_SCHN_UNKNOWN,
434 [PA_CHANNEL_POSITION_AUX4] = SND_MIXER_SCHN_UNKNOWN,
435 [PA_CHANNEL_POSITION_AUX5] = SND_MIXER_SCHN_UNKNOWN,
436 [PA_CHANNEL_POSITION_AUX6] = SND_MIXER_SCHN_UNKNOWN,
437 [PA_CHANNEL_POSITION_AUX7] = SND_MIXER_SCHN_UNKNOWN,
438 [PA_CHANNEL_POSITION_AUX8] = SND_MIXER_SCHN_UNKNOWN,
439 [PA_CHANNEL_POSITION_AUX9] = SND_MIXER_SCHN_UNKNOWN,
440 [PA_CHANNEL_POSITION_AUX10] = SND_MIXER_SCHN_UNKNOWN,
441 [PA_CHANNEL_POSITION_AUX11] = SND_MIXER_SCHN_UNKNOWN,
442 [PA_CHANNEL_POSITION_AUX12] = SND_MIXER_SCHN_UNKNOWN,
443 [PA_CHANNEL_POSITION_AUX13] = SND_MIXER_SCHN_UNKNOWN,
444 [PA_CHANNEL_POSITION_AUX14] = SND_MIXER_SCHN_UNKNOWN,
445 [PA_CHANNEL_POSITION_AUX15] = SND_MIXER_SCHN_UNKNOWN,
446 [PA_CHANNEL_POSITION_AUX16] = SND_MIXER_SCHN_UNKNOWN,
447 [PA_CHANNEL_POSITION_AUX17] = SND_MIXER_SCHN_UNKNOWN,
448 [PA_CHANNEL_POSITION_AUX18] = SND_MIXER_SCHN_UNKNOWN,
449 [PA_CHANNEL_POSITION_AUX19] = SND_MIXER_SCHN_UNKNOWN,
450 [PA_CHANNEL_POSITION_AUX20] = SND_MIXER_SCHN_UNKNOWN,
451 [PA_CHANNEL_POSITION_AUX21] = SND_MIXER_SCHN_UNKNOWN,
452 [PA_CHANNEL_POSITION_AUX22] = SND_MIXER_SCHN_UNKNOWN,
453 [PA_CHANNEL_POSITION_AUX23] = SND_MIXER_SCHN_UNKNOWN,
454 [PA_CHANNEL_POSITION_AUX24] = SND_MIXER_SCHN_UNKNOWN,
455 [PA_CHANNEL_POSITION_AUX25] = SND_MIXER_SCHN_UNKNOWN,
456 [PA_CHANNEL_POSITION_AUX26] = SND_MIXER_SCHN_UNKNOWN,
457 [PA_CHANNEL_POSITION_AUX27] = SND_MIXER_SCHN_UNKNOWN,
458 [PA_CHANNEL_POSITION_AUX28] = SND_MIXER_SCHN_UNKNOWN,
459 [PA_CHANNEL_POSITION_AUX29] = SND_MIXER_SCHN_UNKNOWN,
460 [PA_CHANNEL_POSITION_AUX30] = SND_MIXER_SCHN_UNKNOWN,
461 [PA_CHANNEL_POSITION_AUX31] = SND_MIXER_SCHN_UNKNOWN,
463 [PA_CHANNEL_POSITION_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN,
465 [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SND_MIXER_SCHN_UNKNOWN,
466 [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SND_MIXER_SCHN_UNKNOWN,
467 [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SND_MIXER_SCHN_UNKNOWN,
469 [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SND_MIXER_SCHN_UNKNOWN,
470 [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SND_MIXER_SCHN_UNKNOWN,
471 [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SND_MIXER_SCHN_UNKNOWN
474 static void setting_free(pa_alsa_setting *s) {
478 pa_idxset_free(s->options, NULL, NULL);
481 pa_xfree(s->description);
485 static void option_free(pa_alsa_option *o) {
488 pa_xfree(o->alsa_name);
490 pa_xfree(o->description);
494 static void decibel_fix_free(pa_alsa_decibel_fix *db_fix) {
497 pa_xfree(db_fix->name);
498 pa_xfree(db_fix->db_values);
503 static void element_free(pa_alsa_element *e) {
507 while ((o = e->options)) {
508 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
513 decibel_fix_free(e->db_fix);
515 pa_xfree(e->alsa_name);
519 void pa_alsa_path_free(pa_alsa_path *p) {
525 while ((e = p->elements)) {
526 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
530 while ((s = p->settings)) {
531 PA_LLIST_REMOVE(pa_alsa_setting, p->settings, s);
536 pa_xfree(p->description);
540 void pa_alsa_path_set_free(pa_alsa_path_set *ps) {
544 while ((p = ps->paths)) {
545 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
546 pa_alsa_path_free(p);
552 static long to_alsa_dB(pa_volume_t v) {
553 return (long) (pa_sw_volume_to_dB(v) * 100.0);
556 static pa_volume_t from_alsa_dB(long v) {
557 return pa_sw_volume_from_dB((double) v / 100.0);
560 static long to_alsa_volume(pa_volume_t v, long min, long max) {
563 w = (long) round(((double) v * (double) (max - min)) / PA_VOLUME_NORM) + min;
564 return PA_CLAMP_UNLIKELY(w, min, max);
567 static pa_volume_t from_alsa_volume(long v, long min, long max) {
568 return (pa_volume_t) round(((double) (v - min) * PA_VOLUME_NORM) / (double) (max - min));
571 #define SELEM_INIT(sid, name) \
573 snd_mixer_selem_id_alloca(&(sid)); \
574 snd_mixer_selem_id_set_name((sid), (name)); \
575 snd_mixer_selem_id_set_index((sid), 0); \
578 static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
579 snd_mixer_selem_id_t *sid;
580 snd_mixer_elem_t *me;
581 snd_mixer_selem_channel_id_t c;
582 pa_channel_position_mask_t mask = 0;
590 SELEM_INIT(sid, e->alsa_name);
591 if (!(me = snd_mixer_find_selem(m, sid))) {
592 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
596 pa_cvolume_mute(v, cm->channels);
598 /* We take the highest volume of all channels that match */
600 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
607 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
608 if (snd_mixer_selem_has_playback_channel(me, c)) {
610 if ((r = snd_mixer_selem_get_playback_volume(me, c, &value)) >= 0) {
611 /* If the channel volume is outside the limits set
612 * by the dB fix, we clamp the hw volume to be
613 * within the limits. */
614 if (value < e->db_fix->min_step) {
615 value = e->db_fix->min_step;
616 snd_mixer_selem_set_playback_volume(me, c, value);
617 pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
618 "Volume reset to %0.2f dB.", e->alsa_name, c,
619 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
620 } else if (value > e->db_fix->max_step) {
621 value = e->db_fix->max_step;
622 snd_mixer_selem_set_playback_volume(me, c, value);
623 pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
624 "Volume reset to %0.2f dB.", e->alsa_name, c,
625 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
628 /* Volume step -> dB value conversion. */
629 value = e->db_fix->db_values[value - e->db_fix->min_step];
632 r = snd_mixer_selem_get_playback_dB(me, c, &value);
636 if (snd_mixer_selem_has_capture_channel(me, c)) {
638 if ((r = snd_mixer_selem_get_capture_volume(me, c, &value)) >= 0) {
639 /* If the channel volume is outside the limits set
640 * by the dB fix, we clamp the hw volume to be
641 * within the limits. */
642 if (value < e->db_fix->min_step) {
643 value = e->db_fix->min_step;
644 snd_mixer_selem_set_capture_volume(me, c, value);
645 pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
646 "Volume reset to %0.2f dB.", e->alsa_name, c,
647 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
648 } else if (value > e->db_fix->max_step) {
649 value = e->db_fix->max_step;
650 snd_mixer_selem_set_capture_volume(me, c, value);
651 pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
652 "Volume reset to %0.2f dB.", e->alsa_name, c,
653 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
656 /* Volume step -> dB value conversion. */
657 value = e->db_fix->db_values[value - e->db_fix->min_step];
660 r = snd_mixer_selem_get_capture_dB(me, c, &value);
668 #ifdef HAVE_VALGRIND_MEMCHECK_H
669 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
672 f = from_alsa_dB(value);
677 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
678 if (snd_mixer_selem_has_playback_channel(me, c))
679 r = snd_mixer_selem_get_playback_volume(me, c, &value);
683 if (snd_mixer_selem_has_capture_channel(me, c))
684 r = snd_mixer_selem_get_capture_volume(me, c, &value);
692 f = from_alsa_volume(value, e->min_volume, e->max_volume);
695 for (k = 0; k < cm->channels; k++)
696 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
697 if (v->values[k] < f)
700 mask |= e->masks[c][e->n_channels-1];
703 for (k = 0; k < cm->channels; k++)
704 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
705 v->values[k] = PA_VOLUME_NORM;
710 int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
721 pa_cvolume_reset(v, cm->channels);
723 PA_LLIST_FOREACH(e, p->elements) {
726 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
729 pa_assert(!p->has_dB || e->has_dB);
731 if (element_get_volume(e, m, cm, &ev) < 0)
734 /* If we have no dB information all we can do is take the first element and leave */
740 pa_sw_cvolume_multiply(v, v, &ev);
746 static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t *b) {
747 snd_mixer_selem_id_t *sid;
748 snd_mixer_elem_t *me;
749 snd_mixer_selem_channel_id_t c;
755 SELEM_INIT(sid, e->alsa_name);
756 if (!(me = snd_mixer_find_selem(m, sid))) {
757 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
761 /* We return muted if at least one channel is muted */
763 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
767 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
768 if (snd_mixer_selem_has_playback_channel(me, c))
769 r = snd_mixer_selem_get_playback_switch(me, c, &value);
773 if (snd_mixer_selem_has_capture_channel(me, c))
774 r = snd_mixer_selem_get_capture_switch(me, c, &value);
792 int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t *muted) {
802 PA_LLIST_FOREACH(e, p->elements) {
805 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
808 if (element_get_switch(e, m, &b) < 0)
821 /* Finds the closest item in db_fix->db_values and returns the corresponding
822 * step. *db_value is replaced with the value from the db_values table.
823 * Rounding is done based on the rounding parameter: -1 means rounding down and
824 * +1 means rounding up. */
825 static long decibel_fix_get_step(pa_alsa_decibel_fix *db_fix, long *db_value, int rounding) {
831 pa_assert(rounding != 0);
833 max_i = db_fix->max_step - db_fix->min_step;
836 for (i = 0; i < max_i; i++) {
837 if (db_fix->db_values[i] >= *db_value)
841 for (i = 0; i < max_i; i++) {
842 if (db_fix->db_values[i + 1] > *db_value)
847 *db_value = db_fix->db_values[i];
849 return i + db_fix->min_step;
852 static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, pa_bool_t write_to_hw) {
854 snd_mixer_selem_id_t *sid;
856 snd_mixer_elem_t *me;
857 snd_mixer_selem_channel_id_t c;
858 pa_channel_position_mask_t mask = 0;
865 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
867 SELEM_INIT(sid, e->alsa_name);
868 if (!(me = snd_mixer_find_selem(m, sid))) {
869 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
873 pa_cvolume_mute(&rv, cm->channels);
875 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
877 pa_volume_t f = PA_VOLUME_MUTED;
878 pa_bool_t found = FALSE;
880 for (k = 0; k < cm->channels; k++)
881 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) {
883 if (v->values[k] > f)
888 /* Hmm, so this channel does not exist in the volume
889 * struct, so let's bind it to the overall max of the
891 f = pa_cvolume_max(v);
895 long value = to_alsa_dB(f);
896 int rounding = value > 0 ? -1 : +1;
898 if (e->volume_limit >= 0 && value > (e->max_dB * 100))
899 value = e->max_dB * 100;
901 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
902 /* If we call set_playback_volume() without checking first
903 * if the channel is available, ALSA behaves very
904 * strangely and doesn't fail the call */
905 if (snd_mixer_selem_has_playback_channel(me, c)) {
908 r = snd_mixer_selem_set_playback_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding));
910 decibel_fix_get_step(e->db_fix, &value, rounding);
916 if ((r = snd_mixer_selem_set_playback_dB(me, c, value, rounding)) >= 0)
917 r = snd_mixer_selem_get_playback_dB(me, c, &value);
920 if ((r = snd_mixer_selem_ask_playback_dB_vol(me, value, rounding, &alsa_val)) >= 0)
921 r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value);
927 if (snd_mixer_selem_has_capture_channel(me, c)) {
930 r = snd_mixer_selem_set_capture_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding));
932 decibel_fix_get_step(e->db_fix, &value, rounding);
938 if ((r = snd_mixer_selem_set_capture_dB(me, c, value, rounding)) >= 0)
939 r = snd_mixer_selem_get_capture_dB(me, c, &value);
942 if ((r = snd_mixer_selem_ask_capture_dB_vol(me, value, rounding, &alsa_val)) >= 0)
943 r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value);
953 #ifdef HAVE_VALGRIND_MEMCHECK_H
954 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
957 f = from_alsa_dB(value);
962 value = to_alsa_volume(f, e->min_volume, e->max_volume);
964 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
965 if (snd_mixer_selem_has_playback_channel(me, c)) {
966 if ((r = snd_mixer_selem_set_playback_volume(me, c, value)) >= 0)
967 r = snd_mixer_selem_get_playback_volume(me, c, &value);
971 if (snd_mixer_selem_has_capture_channel(me, c)) {
972 if ((r = snd_mixer_selem_set_capture_volume(me, c, value)) >= 0)
973 r = snd_mixer_selem_get_capture_volume(me, c, &value);
981 f = from_alsa_volume(value, e->min_volume, e->max_volume);
984 for (k = 0; k < cm->channels; k++)
985 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
986 if (rv.values[k] < f)
989 mask |= e->masks[c][e->n_channels-1];
992 for (k = 0; k < cm->channels; k++)
993 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
994 rv.values[k] = PA_VOLUME_NORM;
1000 int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, pa_bool_t write_to_hw) {
1009 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
1014 rv = *v; /* Remaining adjustment */
1015 pa_cvolume_reset(v, cm->channels); /* Adjustment done */
1017 PA_LLIST_FOREACH(e, p->elements) {
1020 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
1023 pa_assert(!p->has_dB || e->has_dB);
1026 if (element_set_volume(e, m, cm, &ev, write_to_hw) < 0)
1034 pa_sw_cvolume_multiply(v, v, &ev);
1035 pa_sw_cvolume_divide(&rv, &rv, &ev);
1041 static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t b) {
1042 snd_mixer_elem_t *me;
1043 snd_mixer_selem_id_t *sid;
1049 SELEM_INIT(sid, e->alsa_name);
1050 if (!(me = snd_mixer_find_selem(m, sid))) {
1051 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1055 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1056 r = snd_mixer_selem_set_playback_switch_all(me, b);
1058 r = snd_mixer_selem_set_capture_switch_all(me, b);
1061 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1066 int pa_alsa_path_set_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t muted) {
1075 PA_LLIST_FOREACH(e, p->elements) {
1077 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
1080 if (element_set_switch(e, m, !muted) < 0)
1087 /* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
1088 * function sets all channels of the volume element to e->min_volume, 0 dB or
1089 * e->constant_volume. */
1090 static int element_set_constant_volume(pa_alsa_element *e, snd_mixer_t *m) {
1091 snd_mixer_elem_t *me = NULL;
1092 snd_mixer_selem_id_t *sid = NULL;
1099 SELEM_INIT(sid, e->alsa_name);
1100 if (!(me = snd_mixer_find_selem(m, sid))) {
1101 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1105 switch (e->volume_use) {
1106 case PA_ALSA_VOLUME_OFF:
1107 volume = e->min_volume;
1110 case PA_ALSA_VOLUME_ZERO:
1114 volume = decibel_fix_get_step(e->db_fix, &dB, +1);
1118 case PA_ALSA_VOLUME_CONSTANT:
1119 volume = e->constant_volume;
1123 pa_assert_not_reached();
1127 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1128 r = snd_mixer_selem_set_playback_volume_all(me, volume);
1130 r = snd_mixer_selem_set_capture_volume_all(me, volume);
1132 pa_assert(e->volume_use == PA_ALSA_VOLUME_ZERO);
1133 pa_assert(!e->db_fix);
1135 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1136 r = snd_mixer_selem_set_playback_dB_all(me, 0, +1);
1138 r = snd_mixer_selem_set_capture_dB_all(me, 0, +1);
1142 pa_log_warn("Failed to set volume of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1147 int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
1154 pa_log_debug("Activating path %s", p->name);
1155 pa_alsa_path_dump(p);
1157 PA_LLIST_FOREACH(e, p->elements) {
1159 switch (e->switch_use) {
1160 case PA_ALSA_SWITCH_OFF:
1161 r = element_set_switch(e, m, FALSE);
1164 case PA_ALSA_SWITCH_ON:
1165 r = element_set_switch(e, m, TRUE);
1168 case PA_ALSA_SWITCH_MUTE:
1169 case PA_ALSA_SWITCH_IGNORE:
1170 case PA_ALSA_SWITCH_SELECT:
1178 switch (e->volume_use) {
1179 case PA_ALSA_VOLUME_OFF:
1180 case PA_ALSA_VOLUME_ZERO:
1181 case PA_ALSA_VOLUME_CONSTANT:
1182 r = element_set_constant_volume(e, m);
1185 case PA_ALSA_VOLUME_MERGE:
1186 case PA_ALSA_VOLUME_IGNORE:
1198 static int check_required(pa_alsa_element *e, snd_mixer_elem_t *me) {
1199 pa_bool_t has_switch;
1200 pa_bool_t has_enumeration;
1201 pa_bool_t has_volume;
1206 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1208 snd_mixer_selem_has_playback_switch(me) ||
1209 (e->direction_try_other && snd_mixer_selem_has_capture_switch(me));
1212 snd_mixer_selem_has_capture_switch(me) ||
1213 (e->direction_try_other && snd_mixer_selem_has_playback_switch(me));
1216 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1218 snd_mixer_selem_has_playback_volume(me) ||
1219 (e->direction_try_other && snd_mixer_selem_has_capture_volume(me));
1222 snd_mixer_selem_has_capture_volume(me) ||
1223 (e->direction_try_other && snd_mixer_selem_has_playback_volume(me));
1226 has_enumeration = snd_mixer_selem_is_enumerated(me);
1228 if ((e->required == PA_ALSA_REQUIRED_SWITCH && !has_switch) ||
1229 (e->required == PA_ALSA_REQUIRED_VOLUME && !has_volume) ||
1230 (e->required == PA_ALSA_REQUIRED_ENUMERATION && !has_enumeration))
1233 if (e->required == PA_ALSA_REQUIRED_ANY && !(has_switch || has_volume || has_enumeration))
1236 if ((e->required_absent == PA_ALSA_REQUIRED_SWITCH && has_switch) ||
1237 (e->required_absent == PA_ALSA_REQUIRED_VOLUME && has_volume) ||
1238 (e->required_absent == PA_ALSA_REQUIRED_ENUMERATION && has_enumeration))
1241 if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration))
1244 if (e->required_any != PA_ALSA_REQUIRED_IGNORE) {
1245 switch (e->required_any) {
1246 case PA_ALSA_REQUIRED_VOLUME:
1247 e->path->req_any_present |= (e->volume_use != PA_ALSA_VOLUME_IGNORE);
1249 case PA_ALSA_REQUIRED_SWITCH:
1250 e->path->req_any_present |= (e->switch_use != PA_ALSA_SWITCH_IGNORE);
1252 case PA_ALSA_REQUIRED_ENUMERATION:
1253 e->path->req_any_present |= (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
1255 case PA_ALSA_REQUIRED_ANY:
1256 e->path->req_any_present |=
1257 (e->volume_use != PA_ALSA_VOLUME_IGNORE) ||
1258 (e->switch_use != PA_ALSA_SWITCH_IGNORE) ||
1259 (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
1262 pa_assert_not_reached();
1266 if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1268 PA_LLIST_FOREACH(o, e->options) {
1269 e->path->req_any_present |= (o->required_any != PA_ALSA_REQUIRED_IGNORE) &&
1271 if (o->required != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx < 0)
1273 if (o->required_absent != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx >= 0)
1281 static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
1282 snd_mixer_selem_id_t *sid;
1283 snd_mixer_elem_t *me;
1289 SELEM_INIT(sid, e->alsa_name);
1291 if (!(me = snd_mixer_find_selem(m, sid))) {
1293 if (e->required != PA_ALSA_REQUIRED_IGNORE)
1296 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1297 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1298 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1303 if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
1304 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1306 if (!snd_mixer_selem_has_playback_switch(me)) {
1307 if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
1308 e->direction = PA_ALSA_DIRECTION_INPUT;
1310 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1315 if (!snd_mixer_selem_has_capture_switch(me)) {
1316 if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
1317 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1319 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1323 if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
1324 e->direction_try_other = FALSE;
1327 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1329 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1331 if (!snd_mixer_selem_has_playback_volume(me)) {
1332 if (e->direction_try_other && snd_mixer_selem_has_capture_volume(me))
1333 e->direction = PA_ALSA_DIRECTION_INPUT;
1335 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1340 if (!snd_mixer_selem_has_capture_volume(me)) {
1341 if (e->direction_try_other && snd_mixer_selem_has_playback_volume(me))
1342 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1344 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1348 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1349 long min_dB = 0, max_dB = 0;
1352 e->direction_try_other = FALSE;
1354 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1355 r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume);
1357 r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
1360 pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1364 if (e->min_volume >= e->max_volume) {
1365 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);
1366 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1368 } else if (e->volume_use == PA_ALSA_VOLUME_CONSTANT &&
1369 (e->min_volume > e->constant_volume || e->max_volume < e->constant_volume)) {
1370 pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
1371 e->constant_volume, e->alsa_name, e->min_volume, e->max_volume);
1372 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1376 pa_channel_position_t p;
1379 ((e->min_volume > e->db_fix->min_step) ||
1380 (e->max_volume < e->db_fix->max_step))) {
1381 pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1382 "real hardware range (%li-%li). Disabling the decibel fix.", e->alsa_name,
1383 e->db_fix->min_step, e->db_fix->max_step,
1384 e->min_volume, e->max_volume);
1386 decibel_fix_free(e->db_fix);
1392 e->min_volume = e->db_fix->min_step;
1393 e->max_volume = e->db_fix->max_step;
1394 min_dB = e->db_fix->db_values[0];
1395 max_dB = e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step];
1396 } else if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1397 e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0;
1399 e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0;
1402 #ifdef HAVE_VALGRIND_MEMCHECK_H
1403 VALGRIND_MAKE_MEM_DEFINED(&min_dB, sizeof(min_dB));
1404 VALGRIND_MAKE_MEM_DEFINED(&max_dB, sizeof(max_dB));
1407 e->min_dB = ((double) min_dB) / 100.0;
1408 e->max_dB = ((double) max_dB) / 100.0;
1410 if (min_dB >= max_dB) {
1411 pa_assert(!e->db_fix);
1412 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);
1417 if (e->volume_limit >= 0) {
1418 if (e->volume_limit <= e->min_volume || e->volume_limit > e->max_volume)
1419 pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1420 "%li-%li. The volume limit is ignored.",
1421 e->alsa_name, e->path->name, e->volume_limit, e->min_volume + 1, e->max_volume);
1424 e->max_volume = e->volume_limit;
1428 e->db_fix->max_step = e->max_volume;
1429 e->max_dB = ((double) e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step]) / 100.0;
1432 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1433 r = snd_mixer_selem_ask_playback_vol_dB(me, e->max_volume, &max_dB);
1435 r = snd_mixer_selem_ask_capture_vol_dB(me, e->max_volume, &max_dB);
1438 pa_log_warn("Failed to get dB value of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1441 e->max_dB = ((double) max_dB) / 100.0;
1447 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1448 is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
1450 is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
1455 if (!e->override_map) {
1456 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1457 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1460 e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
1463 e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
1466 e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
1469 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1471 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1474 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1475 e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1477 e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1480 if (e->n_channels <= 0) {
1481 pa_log_warn("Volume element %s with no channels?", e->alsa_name);
1485 if (e->n_channels > 2) {
1486 /* FIXME: In some places code like this is used:
1488 * e->masks[alsa_channel_ids[p]][e->n_channels-1]
1490 * The definition of e->masks is
1492 * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST][2];
1494 * Since the array size is fixed at 2, we obviously
1495 * don't support elements with more than two
1497 pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e->alsa_name, e->n_channels);
1501 if (!e->override_map) {
1502 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1503 pa_bool_t has_channel;
1505 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1508 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1509 has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1511 has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1513 e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
1518 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1519 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1522 e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
1530 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1533 PA_LLIST_FOREACH(o, e->options)
1534 o->alsa_idx = pa_streq(o->alsa_name, "on") ? 1 : 0;
1535 } else if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1539 if ((n = snd_mixer_selem_get_enum_items(me)) < 0) {
1540 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n));
1544 PA_LLIST_FOREACH(o, e->options) {
1547 for (i = 0; i < n; i++) {
1550 if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
1553 if (!pa_streq(buf, o->alsa_name))
1561 if (check_required(e, me) < 0)
1567 static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, pa_bool_t prefixed) {
1574 if (!pa_startswith(section, "Element "))
1580 /* This is not an element section, but an enum section? */
1581 if (strchr(section, ':'))
1584 if (p->last_element && pa_streq(p->last_element->alsa_name, section))
1585 return p->last_element;
1587 PA_LLIST_FOREACH(e, p->elements)
1588 if (pa_streq(e->alsa_name, section))
1591 e = pa_xnew0(pa_alsa_element, 1);
1593 e->alsa_name = pa_xstrdup(section);
1594 e->direction = p->direction;
1595 e->volume_limit = -1;
1597 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
1600 p->last_element = e;
1604 static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
1610 if (!pa_startswith(section, "Option "))
1615 /* This is not an enum section, but an element section? */
1616 if (!(on = strchr(section, ':')))
1619 en = pa_xstrndup(section, on - section);
1622 if (p->last_option &&
1623 pa_streq(p->last_option->element->alsa_name, en) &&
1624 pa_streq(p->last_option->alsa_name, on)) {
1626 return p->last_option;
1629 pa_assert_se(e = element_get(p, en, FALSE));
1632 PA_LLIST_FOREACH(o, e->options)
1633 if (pa_streq(o->alsa_name, on))
1636 o = pa_xnew0(pa_alsa_option, 1);
1638 o->alsa_name = pa_xstrdup(on);
1641 if (p->last_option && p->last_option->element == e)
1642 PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
1644 PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
1651 static int element_parse_switch(
1652 const char *filename,
1654 const char *section,
1660 pa_alsa_path *p = userdata;
1665 if (!(e = element_get(p, section, TRUE))) {
1666 pa_log("[%s:%u] Switch makes no sense in '%s'", filename, line, section);
1670 if (pa_streq(rvalue, "ignore"))
1671 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1672 else if (pa_streq(rvalue, "mute"))
1673 e->switch_use = PA_ALSA_SWITCH_MUTE;
1674 else if (pa_streq(rvalue, "off"))
1675 e->switch_use = PA_ALSA_SWITCH_OFF;
1676 else if (pa_streq(rvalue, "on"))
1677 e->switch_use = PA_ALSA_SWITCH_ON;
1678 else if (pa_streq(rvalue, "select"))
1679 e->switch_use = PA_ALSA_SWITCH_SELECT;
1681 pa_log("[%s:%u] Switch invalid of '%s'", filename, line, section);
1688 static int element_parse_volume(
1689 const char *filename,
1691 const char *section,
1697 pa_alsa_path *p = userdata;
1702 if (!(e = element_get(p, section, TRUE))) {
1703 pa_log("[%s:%u] Volume makes no sense in '%s'", filename, line, section);
1707 if (pa_streq(rvalue, "ignore"))
1708 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1709 else if (pa_streq(rvalue, "merge"))
1710 e->volume_use = PA_ALSA_VOLUME_MERGE;
1711 else if (pa_streq(rvalue, "off"))
1712 e->volume_use = PA_ALSA_VOLUME_OFF;
1713 else if (pa_streq(rvalue, "zero"))
1714 e->volume_use = PA_ALSA_VOLUME_ZERO;
1718 if (pa_atou(rvalue, &constant) >= 0) {
1719 e->volume_use = PA_ALSA_VOLUME_CONSTANT;
1720 e->constant_volume = constant;
1722 pa_log("[%s:%u] Volume invalid of '%s'", filename, line, section);
1730 static int element_parse_enumeration(
1731 const char *filename,
1733 const char *section,
1739 pa_alsa_path *p = userdata;
1744 if (!(e = element_get(p, section, TRUE))) {
1745 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename, line, section);
1749 if (pa_streq(rvalue, "ignore"))
1750 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1751 else if (pa_streq(rvalue, "select"))
1752 e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
1754 pa_log("[%s:%u] Enumeration invalid of '%s'", filename, line, section);
1761 static int option_parse_priority(
1762 const char *filename,
1764 const char *section,
1770 pa_alsa_path *p = userdata;
1776 if (!(o = option_get(p, section))) {
1777 pa_log("[%s:%u] Priority makes no sense in '%s'", filename, line, section);
1781 if (pa_atou(rvalue, &prio) < 0) {
1782 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
1790 static int option_parse_name(
1791 const char *filename,
1793 const char *section,
1799 pa_alsa_path *p = userdata;
1804 if (!(o = option_get(p, section))) {
1805 pa_log("[%s:%u] Name makes no sense in '%s'", filename, line, section);
1810 o->name = pa_xstrdup(rvalue);
1815 static int element_parse_required(
1816 const char *filename,
1818 const char *section,
1824 pa_alsa_path *p = userdata;
1827 pa_alsa_required_t req;
1831 e = element_get(p, section, TRUE);
1832 o = option_get(p, section);
1834 pa_log("[%s:%u] Required makes no sense in '%s'", filename, line, section);
1838 if (pa_streq(rvalue, "ignore"))
1839 req = PA_ALSA_REQUIRED_IGNORE;
1840 else if (pa_streq(rvalue, "switch") && e)
1841 req = PA_ALSA_REQUIRED_SWITCH;
1842 else if (pa_streq(rvalue, "volume") && e)
1843 req = PA_ALSA_REQUIRED_VOLUME;
1844 else if (pa_streq(rvalue, "enumeration"))
1845 req = PA_ALSA_REQUIRED_ENUMERATION;
1846 else if (pa_streq(rvalue, "any"))
1847 req = PA_ALSA_REQUIRED_ANY;
1849 pa_log("[%s:%u] Required invalid of '%s'", filename, line, section);
1853 if (pa_streq(lvalue, "required-absent")) {
1855 e->required_absent = req;
1857 o->required_absent = req;
1859 else if (pa_streq(lvalue, "required-any")) {
1861 e->required_any = req;
1862 e->path->has_req_any = TRUE;
1865 o->required_any = req;
1866 o->element->path->has_req_any = TRUE;
1879 static int element_parse_direction(
1880 const char *filename,
1882 const char *section,
1888 pa_alsa_path *p = userdata;
1893 if (!(e = element_get(p, section, TRUE))) {
1894 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1898 if (pa_streq(rvalue, "playback"))
1899 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1900 else if (pa_streq(rvalue, "capture"))
1901 e->direction = PA_ALSA_DIRECTION_INPUT;
1903 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1910 static int element_parse_direction_try_other(
1911 const char *filename,
1913 const char *section,
1919 pa_alsa_path *p = userdata;
1923 if (!(e = element_get(p, section, TRUE))) {
1924 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1928 if ((yes = pa_parse_boolean(rvalue)) < 0) {
1929 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1933 e->direction_try_other = !!yes;
1937 static int element_parse_volume_limit(
1938 const char *filename,
1940 const char *section,
1946 pa_alsa_path *p = userdata;
1950 if (!(e = element_get(p, section, TRUE))) {
1951 pa_log("[%s:%u] volume-limit makes no sense in '%s'", filename, line, section);
1955 if (pa_atol(rvalue, &volume_limit) < 0 || volume_limit < 0) {
1956 pa_log("[%s:%u] Invalid value for volume-limit", filename, line);
1960 e->volume_limit = volume_limit;
1964 static pa_channel_position_mask_t parse_mask(const char *m) {
1965 pa_channel_position_mask_t v;
1967 if (pa_streq(m, "all-left"))
1968 v = PA_CHANNEL_POSITION_MASK_LEFT;
1969 else if (pa_streq(m, "all-right"))
1970 v = PA_CHANNEL_POSITION_MASK_RIGHT;
1971 else if (pa_streq(m, "all-center"))
1972 v = PA_CHANNEL_POSITION_MASK_CENTER;
1973 else if (pa_streq(m, "all-front"))
1974 v = PA_CHANNEL_POSITION_MASK_FRONT;
1975 else if (pa_streq(m, "all-rear"))
1976 v = PA_CHANNEL_POSITION_MASK_REAR;
1977 else if (pa_streq(m, "all-side"))
1978 v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER;
1979 else if (pa_streq(m, "all-top"))
1980 v = PA_CHANNEL_POSITION_MASK_TOP;
1981 else if (pa_streq(m, "all-no-lfe"))
1982 v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE);
1983 else if (pa_streq(m, "all"))
1984 v = PA_CHANNEL_POSITION_MASK_ALL;
1986 pa_channel_position_t p;
1988 if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
1991 v = PA_CHANNEL_POSITION_MASK(p);
1997 static int element_parse_override_map(
1998 const char *filename,
2000 const char *section,
2006 pa_alsa_path *p = userdata;
2008 const char *state = NULL;
2012 if (!(e = element_get(p, section, TRUE))) {
2013 pa_log("[%s:%u] Override map makes no sense in '%s'", filename, line, section);
2017 while ((n = pa_split(rvalue, ",", &state))) {
2018 pa_channel_position_mask_t m;
2023 if ((m = parse_mask(n)) == 0) {
2024 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename, line, n, section);
2030 if (pa_streq(lvalue, "override-map.1"))
2031 e->masks[i++][0] = m;
2033 e->masks[i++][1] = m;
2035 /* Later on we might add override-map.3 and so on here ... */
2040 e->override_map = TRUE;
2045 static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
2046 snd_mixer_selem_id_t *sid;
2047 snd_mixer_elem_t *me;
2053 SELEM_INIT(sid, e->alsa_name);
2054 if (!(me = snd_mixer_find_selem(m, sid))) {
2055 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2059 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
2061 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
2062 r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
2064 r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
2067 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
2070 pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
2072 if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0)
2073 pa_log_warn("Failed to set enumeration of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
2079 int pa_alsa_setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
2086 PA_IDXSET_FOREACH(o, s->options, idx)
2087 element_set_option(o->element, m, o->alsa_idx);
2092 static int option_verify(pa_alsa_option *o) {
2093 static const struct description_map well_known_descriptions[] = {
2094 { "input", N_("Input") },
2095 { "input-docking", N_("Docking Station Input") },
2096 { "input-docking-microphone", N_("Docking Station Microphone") },
2097 { "input-docking-linein", N_("Docking Station Line-In") },
2098 { "input-linein", N_("Line-In") },
2099 { "input-microphone", N_("Microphone") },
2100 { "input-microphone-front", N_("Front Microphone") },
2101 { "input-microphone-rear", N_("Rear Microphone") },
2102 { "input-microphone-external", N_("External Microphone") },
2103 { "input-microphone-internal", N_("Internal Microphone") },
2104 { "input-radio", N_("Radio") },
2105 { "input-video", N_("Video") },
2106 { "input-agc-on", N_("Automatic Gain Control") },
2107 { "input-agc-off", N_("No Automatic Gain Control") },
2108 { "input-boost-on", N_("Boost") },
2109 { "input-boost-off", N_("No Boost") },
2110 { "output-amplifier-on", N_("Amplifier") },
2111 { "output-amplifier-off", N_("No Amplifier") },
2112 { "output-bass-boost-on", N_("Bass Boost") },
2113 { "output-bass-boost-off", N_("No Bass Boost") },
2114 { "output-speaker", N_("Speaker") },
2115 { "output-headphones", N_("Headphones") }
2121 pa_log("No name set for option %s", o->alsa_name);
2125 if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
2126 o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
2127 pa_log("Element %s of option %s not set for select.", o->element->alsa_name, o->name);
2131 if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
2132 !pa_streq(o->alsa_name, "on") &&
2133 !pa_streq(o->alsa_name, "off")) {
2134 pa_log("Switch %s options need be named off or on ", o->element->alsa_name);
2138 if (!o->description)
2139 o->description = pa_xstrdup(lookup_description(o->name,
2140 well_known_descriptions,
2141 PA_ELEMENTSOF(well_known_descriptions)));
2142 if (!o->description)
2143 o->description = pa_xstrdup(o->name);
2148 static int element_verify(pa_alsa_element *e) {
2153 // pa_log_debug("Element %s, path %s: r=%d, r-any=%d, r-abs=%d", e->alsa_name, e->path->name, e->required, e->required_any, e->required_absent);
2154 if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
2155 (e->required_any != PA_ALSA_REQUIRED_IGNORE && e->required_any == e->required_absent) ||
2156 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required_any != PA_ALSA_REQUIRED_IGNORE) ||
2157 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
2158 pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name);
2162 if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
2163 pa_log("Element %s cannot set select for both switch and enumeration.", e->alsa_name);
2167 PA_LLIST_FOREACH(o, e->options)
2168 if (option_verify(o) < 0)
2174 static int path_verify(pa_alsa_path *p) {
2175 static const struct description_map well_known_descriptions[] = {
2176 { "analog-input", N_("Analog Input") },
2177 { "analog-input-microphone", N_("Analog Microphone") },
2178 { "analog-input-microphone-front", N_("Front Microphone") },
2179 { "analog-input-microphone-rear", N_("Rear Microphone") },
2180 { "analog-input-microphone-dock", N_("Docking Station Microphone") },
2181 { "analog-input-microphone-internal", N_("Internal Microphone") },
2182 { "analog-input-linein", N_("Analog Line-In") },
2183 { "analog-input-radio", N_("Analog Radio") },
2184 { "analog-input-video", N_("Analog Video") },
2185 { "analog-output", N_("Analog Output") },
2186 { "analog-output-headphones", N_("Analog Headphones") },
2187 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
2188 { "analog-output-mono", N_("Analog Mono Output") },
2189 { "analog-output-speaker", N_("Analog Speakers") },
2190 { "iec958-stereo-output", N_("Digital Output (IEC958)") },
2191 { "iec958-passthrough-output", N_("Digital Passthrough (IEC958)") }
2198 PA_LLIST_FOREACH(e, p->elements)
2199 if (element_verify(e) < 0)
2202 if (!p->description)
2203 p->description = pa_xstrdup(lookup_description(p->name,
2204 well_known_descriptions,
2205 PA_ELEMENTSOF(well_known_descriptions)));
2207 if (!p->description)
2208 p->description = pa_xstrdup(p->name);
2213 pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction) {
2219 pa_config_item items[] = {
2221 { "priority", pa_config_parse_unsigned, NULL, "General" },
2222 { "description", pa_config_parse_string, NULL, "General" },
2223 { "name", pa_config_parse_string, NULL, "General" },
2226 { "priority", option_parse_priority, NULL, NULL },
2227 { "name", option_parse_name, NULL, NULL },
2230 { "switch", element_parse_switch, NULL, NULL },
2231 { "volume", element_parse_volume, NULL, NULL },
2232 { "enumeration", element_parse_enumeration, NULL, NULL },
2233 { "override-map.1", element_parse_override_map, NULL, NULL },
2234 { "override-map.2", element_parse_override_map, NULL, NULL },
2235 /* ... later on we might add override-map.3 and so on here ... */
2236 { "required", element_parse_required, NULL, NULL },
2237 { "required-any", element_parse_required, NULL, NULL },
2238 { "required-absent", element_parse_required, NULL, NULL },
2239 { "direction", element_parse_direction, NULL, NULL },
2240 { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
2241 { "volume-limit", element_parse_volume_limit, NULL, NULL },
2242 { NULL, NULL, NULL, NULL }
2247 p = pa_xnew0(pa_alsa_path, 1);
2248 n = pa_path_get_filename(fname);
2249 p->name = pa_xstrndup(n, strcspn(n, "."));
2250 p->direction = direction;
2252 items[0].data = &p->priority;
2253 items[1].data = &p->description;
2254 items[2].data = &p->name;
2256 fn = pa_maybe_prefix_path(fname,
2257 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/paths/" :
2260 r = pa_config_parse(fn, NULL, items, p);
2266 if (path_verify(p) < 0)
2272 pa_alsa_path_free(p);
2276 pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction) {
2282 p = pa_xnew0(pa_alsa_path, 1);
2283 p->name = pa_xstrdup(element);
2284 p->direction = direction;
2286 e = pa_xnew0(pa_alsa_element, 1);
2288 e->alsa_name = pa_xstrdup(element);
2289 e->direction = direction;
2290 e->volume_limit = -1;
2292 e->switch_use = PA_ALSA_SWITCH_MUTE;
2293 e->volume_use = PA_ALSA_VOLUME_MERGE;
2295 PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
2296 p->last_element = e;
2300 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
2301 pa_alsa_option *o, *n;
2305 for (o = e->options; o; o = n) {
2308 if (o->alsa_idx < 0) {
2309 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
2315 e->switch_use != PA_ALSA_SWITCH_IGNORE ||
2316 e->volume_use != PA_ALSA_VOLUME_IGNORE ||
2317 e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
2320 static void path_drop_unsupported(pa_alsa_path *p) {
2321 pa_alsa_element *e, *n;
2325 for (e = p->elements; e; e = n) {
2328 if (!element_drop_unsupported(e)) {
2329 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
2335 static void path_make_options_unique(pa_alsa_path *p) {
2337 pa_alsa_option *o, *u;
2339 PA_LLIST_FOREACH(e, p->elements) {
2340 PA_LLIST_FOREACH(o, e->options) {
2344 for (u = o->next; u; u = u->next)
2345 if (pa_streq(u->name, o->name))
2351 m = pa_xstrdup(o->name);
2353 /* OK, this name is not unique, hence let's rename */
2354 for (i = 1, u = o; u; u = u->next) {
2357 if (!pa_streq(u->name, m))
2360 nn = pa_sprintf_malloc("%s-%u", m, i);
2364 nd = pa_sprintf_malloc("%s %u", u->description, i);
2365 pa_xfree(u->description);
2366 u->description = nd;
2376 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
2379 for (; e; e = e->next)
2380 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
2381 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
2387 for (o = e->options; o; o = o->next) {
2391 s = pa_xnewdup(pa_alsa_setting, template, 1);
2392 s->options = pa_idxset_copy(template->options);
2393 s->name = pa_sprintf_malloc(_("%s+%s"), template->name, o->name);
2395 (template->description[0] && o->description[0])
2396 ? pa_sprintf_malloc(_("%s / %s"), template->description, o->description)
2397 : (template->description[0]
2398 ? pa_xstrdup(template->description)
2399 : pa_xstrdup(o->description));
2401 s->priority = PA_MAX(template->priority, o->priority);
2403 s = pa_xnew0(pa_alsa_setting, 1);
2404 s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2405 s->name = pa_xstrdup(o->name);
2406 s->description = pa_xstrdup(o->description);
2407 s->priority = o->priority;
2410 pa_idxset_put(s->options, o, NULL);
2412 if (element_create_settings(e->next, s))
2413 /* This is not a leaf, so let's get rid of it */
2416 /* This is a leaf, so let's add it */
2417 PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2419 e->path->last_setting = s;
2426 static void path_create_settings(pa_alsa_path *p) {
2429 element_create_settings(p->elements, NULL);
2432 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) {
2434 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2435 pa_channel_position_t t;
2436 pa_channel_position_mask_t path_volume_channels = 0;
2447 pa_log_debug("Probing path '%s'", p->name);
2449 PA_LLIST_FOREACH(e, p->elements) {
2450 if (element_probe(e, m) < 0) {
2451 p->supported = FALSE;
2452 pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2455 pa_log_debug("Probe of element '%s' succeeded (volume=%d, switch=%d, enumeration=%d).", e->alsa_name, e->volume_use, e->switch_use, e->enumeration_use);
2460 if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2462 if (!p->has_volume) {
2463 p->min_volume = e->min_volume;
2464 p->max_volume = e->max_volume;
2468 if (!p->has_volume) {
2469 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2470 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2471 min_dB[t] = e->min_dB;
2472 max_dB[t] = e->max_dB;
2473 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2480 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2481 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2482 min_dB[t] += e->min_dB;
2483 max_dB[t] += e->max_dB;
2484 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2487 /* Hmm, there's another element before us
2488 * which cannot do dB volumes, so we we need
2489 * to 'neutralize' this slider */
2490 e->volume_use = PA_ALSA_VOLUME_ZERO;
2491 pa_log_info("Zeroing volume of '%s' on path '%s'", e->alsa_name, p->name);
2494 } else if (p->has_volume) {
2495 /* We can't use this volume, so let's ignore it */
2496 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2497 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e->alsa_name, p->name);
2499 p->has_volume = TRUE;
2502 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2506 if (p->has_req_any && !p->req_any_present) {
2507 p->supported = FALSE;
2508 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name);
2512 path_drop_unsupported(p);
2513 path_make_options_unique(p);
2514 path_create_settings(p);
2516 p->supported = TRUE;
2519 p->min_dB = INFINITY;
2520 p->max_dB = -INFINITY;
2522 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2523 if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) {
2524 if (p->min_dB > min_dB[t])
2525 p->min_dB = min_dB[t];
2527 if (p->max_dB < max_dB[t])
2528 p->max_dB = max_dB[t];
2535 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2538 pa_log_debug("Setting %s (%s) priority=%u",
2540 pa_strnull(s->description),
2544 void pa_alsa_option_dump(pa_alsa_option *o) {
2547 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2549 pa_strnull(o->name),
2550 pa_strnull(o->description),
2555 void pa_alsa_element_dump(pa_alsa_element *e) {
2559 pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, volume_limit=%li, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s",
2569 (long long unsigned) e->merged_mask,
2571 pa_yes_no(e->override_map));
2573 PA_LLIST_FOREACH(o, e->options)
2574 pa_alsa_option_dump(o);
2577 void pa_alsa_path_dump(pa_alsa_path *p) {
2582 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2583 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2585 pa_strnull(p->description),
2588 pa_yes_no(p->probed),
2589 pa_yes_no(p->supported),
2590 pa_yes_no(p->has_mute),
2591 pa_yes_no(p->has_volume),
2592 pa_yes_no(p->has_dB),
2593 p->min_volume, p->max_volume,
2594 p->min_dB, p->max_dB);
2596 PA_LLIST_FOREACH(e, p->elements)
2597 pa_alsa_element_dump(e);
2599 PA_LLIST_FOREACH(s, p->settings)
2600 pa_alsa_setting_dump(s);
2603 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2604 snd_mixer_selem_id_t *sid;
2605 snd_mixer_elem_t *me;
2611 SELEM_INIT(sid, e->alsa_name);
2612 if (!(me = snd_mixer_find_selem(m, sid))) {
2613 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2617 snd_mixer_elem_set_callback(me, cb);
2618 snd_mixer_elem_set_callback_private(me, userdata);
2621 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2628 PA_LLIST_FOREACH(e, p->elements)
2629 element_set_callback(e, m, cb, userdata);
2632 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2639 PA_LLIST_FOREACH(p, ps->paths)
2640 pa_alsa_path_set_callback(p, m, cb, userdata);
2643 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction) {
2644 pa_alsa_path_set *ps;
2645 char **pn = NULL, **en = NULL, **ie;
2646 pa_alsa_decibel_fix *db_fix;
2650 pa_assert(m->profile_set);
2651 pa_assert(m->profile_set->decibel_fixes);
2652 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2654 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2657 ps = pa_xnew0(pa_alsa_path_set, 1);
2658 ps->direction = direction;
2660 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2661 pn = m->output_path_names;
2662 else if (direction == PA_ALSA_DIRECTION_INPUT)
2663 pn = m->input_path_names;
2668 for (in = pn; *in; in++) {
2670 pa_bool_t duplicate = FALSE;
2673 for (kn = pn; kn < in; kn++)
2674 if (pa_streq(*kn, *in)) {
2682 fn = pa_sprintf_malloc("%s.conf", *in);
2684 if ((p = pa_alsa_path_new(fn, direction))) {
2686 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2696 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2697 en = m->output_element;
2698 else if (direction == PA_ALSA_DIRECTION_INPUT)
2699 en = m->input_element;
2702 pa_alsa_path_set_free(ps);
2706 for (ie = en; *ie; ie++) {
2710 p = pa_alsa_path_synthesize(*ie, direction);
2713 /* Mark all other passed elements for require-absent */
2714 for (je = en; *je; je++) {
2720 e = pa_xnew0(pa_alsa_element, 1);
2722 e->alsa_name = pa_xstrdup(*je);
2723 e->direction = direction;
2724 e->required_absent = PA_ALSA_REQUIRED_ANY;
2725 e->volume_limit = -1;
2727 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2728 p->last_element = e;
2731 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2736 /* Assign decibel fixes to elements. */
2737 PA_HASHMAP_FOREACH(db_fix, m->profile_set->decibel_fixes, state) {
2740 PA_LLIST_FOREACH(p, ps->paths) {
2743 PA_LLIST_FOREACH(e, p->elements) {
2744 if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_name)) {
2745 /* The profile set that contains the dB fix may be freed
2746 * before the element, so we have to copy the dB fix
2748 e->db_fix = pa_xnewdup(pa_alsa_decibel_fix, db_fix, 1);
2749 e->db_fix->profile_set = NULL;
2750 e->db_fix->name = pa_xstrdup(db_fix->name);
2751 e->db_fix->db_values = pa_xmemdup(db_fix->db_values, (db_fix->max_step - db_fix->min_step + 1) * sizeof(long));
2760 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2764 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2767 pa_yes_no(ps->probed));
2769 PA_LLIST_FOREACH(p, ps->paths)
2770 pa_alsa_path_dump(p);
2773 static void path_set_unify(pa_alsa_path_set *ps) {
2775 pa_bool_t has_dB = TRUE, has_volume = TRUE, has_mute = TRUE;
2778 /* We have issues dealing with paths that vary too wildly. That
2779 * means for now we have to have all paths support volume/mute/dB
2782 PA_LLIST_FOREACH(p, ps->paths) {
2783 pa_assert(p->probed);
2787 else if (!p->has_dB)
2794 if (!has_volume || !has_dB || !has_mute) {
2797 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2799 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2802 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2804 PA_LLIST_FOREACH(p, ps->paths) {
2806 p->has_volume = FALSE;
2811 p->has_mute = FALSE;
2816 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
2817 pa_alsa_path *p, *q;
2819 PA_LLIST_FOREACH(p, ps->paths) {
2823 for (q = p->next; q; q = q->next)
2824 if (pa_streq(q->name, p->name))
2830 m = pa_xstrdup(p->name);
2832 /* OK, this name is not unique, hence let's rename */
2833 for (i = 1, q = p; q; q = q->next) {
2836 if (!pa_streq(q->name, m))
2839 nn = pa_sprintf_malloc("%s-%u", m, i);
2843 nd = pa_sprintf_malloc("%s %u", q->description, i);
2844 pa_xfree(q->description);
2845 q->description = nd;
2854 void pa_alsa_path_set_probe(pa_alsa_path_set *ps, snd_mixer_t *m, pa_bool_t ignore_dB) {
2855 pa_alsa_path *p, *n;
2862 for (p = ps->paths; p; p = n) {
2865 if (pa_alsa_path_probe(p, m, ignore_dB) < 0) {
2866 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
2867 pa_alsa_path_free(p);
2872 path_set_make_paths_unique(ps);
2876 static void mapping_free(pa_alsa_mapping *m) {
2880 pa_xfree(m->description);
2882 pa_xstrfreev(m->device_strings);
2883 pa_xstrfreev(m->input_path_names);
2884 pa_xstrfreev(m->output_path_names);
2885 pa_xstrfreev(m->input_element);
2886 pa_xstrfreev(m->output_element);
2888 pa_assert(!m->input_pcm);
2889 pa_assert(!m->output_pcm);
2894 static void profile_free(pa_alsa_profile *p) {
2898 pa_xfree(p->description);
2900 pa_xstrfreev(p->input_mapping_names);
2901 pa_xstrfreev(p->output_mapping_names);
2903 if (p->input_mappings)
2904 pa_idxset_free(p->input_mappings, NULL, NULL);
2906 if (p->output_mappings)
2907 pa_idxset_free(p->output_mappings, NULL, NULL);
2912 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
2918 while ((p = pa_hashmap_steal_first(ps->profiles)))
2921 pa_hashmap_free(ps->profiles, NULL, NULL);
2927 while ((m = pa_hashmap_steal_first(ps->mappings)))
2930 pa_hashmap_free(ps->mappings, NULL, NULL);
2933 if (ps->decibel_fixes) {
2934 pa_alsa_decibel_fix *db_fix;
2936 while ((db_fix = pa_hashmap_steal_first(ps->decibel_fixes)))
2937 decibel_fix_free(db_fix);
2939 pa_hashmap_free(ps->decibel_fixes, NULL, NULL);
2945 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
2948 if (!pa_startswith(name, "Mapping "))
2953 if ((m = pa_hashmap_get(ps->mappings, name)))
2956 m = pa_xnew0(pa_alsa_mapping, 1);
2957 m->profile_set = ps;
2958 m->name = pa_xstrdup(name);
2959 pa_channel_map_init(&m->channel_map);
2961 pa_hashmap_put(ps->mappings, m->name, m);
2966 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
2969 if (!pa_startswith(name, "Profile "))
2974 if ((p = pa_hashmap_get(ps->profiles, name)))
2977 p = pa_xnew0(pa_alsa_profile, 1);
2978 p->profile_set = ps;
2979 p->name = pa_xstrdup(name);
2981 pa_hashmap_put(ps->profiles, p->name, p);
2986 static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) {
2987 pa_alsa_decibel_fix *db_fix;
2989 if (!pa_startswith(name, "DecibelFix "))
2994 if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name)))
2997 db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
2998 db_fix->profile_set = ps;
2999 db_fix->name = pa_xstrdup(name);
3001 pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix);
3006 static int mapping_parse_device_strings(
3007 const char *filename,
3009 const char *section,
3015 pa_alsa_profile_set *ps = userdata;
3020 if (!(m = mapping_get(ps, section))) {
3021 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3025 pa_xstrfreev(m->device_strings);
3026 if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
3027 pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
3034 static int mapping_parse_channel_map(
3035 const char *filename,
3037 const char *section,
3043 pa_alsa_profile_set *ps = userdata;
3048 if (!(m = mapping_get(ps, section))) {
3049 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3053 if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
3054 pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
3061 static int mapping_parse_paths(
3062 const char *filename,
3064 const char *section,
3070 pa_alsa_profile_set *ps = userdata;
3075 if (!(m = mapping_get(ps, section))) {
3076 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3080 if (pa_streq(lvalue, "paths-input")) {
3081 pa_xstrfreev(m->input_path_names);
3082 m->input_path_names = pa_split_spaces_strv(rvalue);
3084 pa_xstrfreev(m->output_path_names);
3085 m->output_path_names = pa_split_spaces_strv(rvalue);
3091 static int mapping_parse_element(
3092 const char *filename,
3094 const char *section,
3100 pa_alsa_profile_set *ps = userdata;
3105 if (!(m = mapping_get(ps, section))) {
3106 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3110 if (pa_streq(lvalue, "element-input")) {
3111 pa_xstrfreev(m->input_element);
3112 m->input_element = pa_split_spaces_strv(rvalue);
3114 pa_xstrfreev(m->output_element);
3115 m->output_element = pa_split_spaces_strv(rvalue);
3121 static int mapping_parse_direction(
3122 const char *filename,
3124 const char *section,
3130 pa_alsa_profile_set *ps = userdata;
3135 if (!(m = mapping_get(ps, section))) {
3136 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3140 if (pa_streq(rvalue, "input"))
3141 m->direction = PA_ALSA_DIRECTION_INPUT;
3142 else if (pa_streq(rvalue, "output"))
3143 m->direction = PA_ALSA_DIRECTION_OUTPUT;
3144 else if (pa_streq(rvalue, "any"))
3145 m->direction = PA_ALSA_DIRECTION_ANY;
3147 pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
3154 static int mapping_parse_description(
3155 const char *filename,
3157 const char *section,
3163 pa_alsa_profile_set *ps = userdata;
3169 if ((m = mapping_get(ps, section))) {
3170 pa_xfree(m->description);
3171 m->description = pa_xstrdup(rvalue);
3172 } else if ((p = profile_get(ps, section))) {
3173 pa_xfree(p->description);
3174 p->description = pa_xstrdup(rvalue);
3176 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3183 static int mapping_parse_priority(
3184 const char *filename,
3186 const char *section,
3192 pa_alsa_profile_set *ps = userdata;
3199 if (pa_atou(rvalue, &prio) < 0) {
3200 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
3204 if ((m = mapping_get(ps, section)))
3206 else if ((p = profile_get(ps, section)))
3209 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3216 static int profile_parse_mappings(
3217 const char *filename,
3219 const char *section,
3225 pa_alsa_profile_set *ps = userdata;
3230 if (!(p = profile_get(ps, section))) {
3231 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3235 if (pa_streq(lvalue, "input-mappings")) {
3236 pa_xstrfreev(p->input_mapping_names);
3237 p->input_mapping_names = pa_split_spaces_strv(rvalue);
3239 pa_xstrfreev(p->output_mapping_names);
3240 p->output_mapping_names = pa_split_spaces_strv(rvalue);
3246 static int profile_parse_skip_probe(
3247 const char *filename,
3249 const char *section,
3255 pa_alsa_profile_set *ps = userdata;
3261 if (!(p = profile_get(ps, section))) {
3262 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3266 if ((b = pa_parse_boolean(rvalue)) < 0) {
3267 pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
3276 static int decibel_fix_parse_db_values(
3277 const char *filename,
3279 const char *section,
3285 pa_alsa_profile_set *ps = userdata;
3286 pa_alsa_decibel_fix *db_fix;
3290 unsigned n = 8; /* Current size of the db_values table. */
3291 unsigned min_step = 0;
3292 unsigned max_step = 0;
3293 unsigned i = 0; /* Index to the items table. */
3294 unsigned prev_step = 0;
3297 pa_assert(filename);
3303 if (!(db_fix = decibel_fix_get(ps, section))) {
3304 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3308 if (!(items = pa_split_spaces_strv(rvalue))) {
3309 pa_log("[%s:%u] Value missing", pa_strnull(filename), line);
3313 db_values = pa_xnew(long, n);
3315 while ((item = items[i++])) {
3316 char *s = item; /* Step value string. */
3317 char *d = item; /* dB value string. */
3321 /* Move d forward until it points to a colon or to the end of the item. */
3322 for (; *d && *d != ':'; ++d);
3325 /* item started with colon. */
3326 pa_log("[%s:%u] No step value found in %s", filename, line, item);
3330 if (!*d || !*(d + 1)) {
3331 /* No colon found, or it was the last character in item. */
3332 pa_log("[%s:%u] No dB value found in %s", filename, line, item);
3336 /* pa_atou() needs a null-terminating string. Let's replace the colon
3337 * with a zero byte. */
3340 if (pa_atou(s, &step) < 0) {
3341 pa_log("[%s:%u] Invalid step value: %s", filename, line, s);
3345 if (pa_atod(d, &db) < 0) {
3346 pa_log("[%s:%u] Invalid dB value: %s", filename, line, d);
3350 if (step <= prev_step && i != 1) {
3351 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename, line, step, prev_step);
3355 if (db < prev_db && i != 1) {
3356 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename, line, db, prev_db);
3362 db_values[0] = (long) (db * 100.0);
3366 /* Interpolate linearly. */
3367 double db_increment = (db - prev_db) / (step - prev_step);
3369 for (; prev_step < step; ++prev_step, prev_db += db_increment) {
3371 /* Reallocate the db_values table if it's about to overflow. */
3372 if (prev_step + 1 - min_step == n) {
3374 db_values = pa_xrenew(long, db_values, n);
3377 db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
3384 db_fix->min_step = min_step;
3385 db_fix->max_step = max_step;
3386 pa_xfree(db_fix->db_values);
3387 db_fix->db_values = db_values;
3389 pa_xstrfreev(items);
3394 pa_xstrfreev(items);
3395 pa_xfree(db_values);
3400 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
3402 static const struct description_map well_known_descriptions[] = {
3403 { "analog-mono", N_("Analog Mono") },
3404 { "analog-stereo", N_("Analog Stereo") },
3405 { "analog-surround-21", N_("Analog Surround 2.1") },
3406 { "analog-surround-30", N_("Analog Surround 3.0") },
3407 { "analog-surround-31", N_("Analog Surround 3.1") },
3408 { "analog-surround-40", N_("Analog Surround 4.0") },
3409 { "analog-surround-41", N_("Analog Surround 4.1") },
3410 { "analog-surround-50", N_("Analog Surround 5.0") },
3411 { "analog-surround-51", N_("Analog Surround 5.1") },
3412 { "analog-surround-61", N_("Analog Surround 6.0") },
3413 { "analog-surround-61", N_("Analog Surround 6.1") },
3414 { "analog-surround-70", N_("Analog Surround 7.0") },
3415 { "analog-surround-71", N_("Analog Surround 7.1") },
3416 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3417 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3418 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3419 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3420 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3425 if (!pa_channel_map_valid(&m->channel_map)) {
3426 pa_log("Mapping %s is missing channel map.", m->name);
3430 if (!m->device_strings) {
3431 pa_log("Mapping %s is missing device strings.", m->name);
3435 if ((m->input_path_names && m->input_element) ||
3436 (m->output_path_names && m->output_element)) {
3437 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
3441 if (!m->description)
3442 m->description = pa_xstrdup(lookup_description(m->name,
3443 well_known_descriptions,
3444 PA_ELEMENTSOF(well_known_descriptions)));
3446 if (!m->description)
3447 m->description = pa_xstrdup(m->name);
3450 if (pa_channel_map_equal(&m->channel_map, bonus))
3452 else if (m->channel_map.channels == bonus->channels)
3459 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
3460 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
3464 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3466 pa_strnull(m->description),
3468 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
3469 pa_yes_no(m->supported),
3473 static void profile_set_add_auto_pair(
3474 pa_alsa_profile_set *ps,
3475 pa_alsa_mapping *m, /* output */
3476 pa_alsa_mapping *n /* input */) {
3484 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
3487 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
3491 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
3493 name = pa_sprintf_malloc("output:%s", m->name);
3495 name = pa_sprintf_malloc("input:%s", n->name);
3497 if (pa_hashmap_get(ps->profiles, name)) {
3502 p = pa_xnew0(pa_alsa_profile, 1);
3503 p->profile_set = ps;
3507 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3508 pa_idxset_put(p->output_mappings, m, NULL);
3509 p->priority += m->priority * 100;
3513 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3514 pa_idxset_put(p->input_mappings, n, NULL);
3515 p->priority += n->priority;
3518 pa_hashmap_put(ps->profiles, p->name, p);
3521 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
3522 pa_alsa_mapping *m, *n;
3523 void *m_state, *n_state;
3527 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
3528 profile_set_add_auto_pair(ps, m, NULL);
3530 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3531 profile_set_add_auto_pair(ps, m, n);
3534 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3535 profile_set_add_auto_pair(ps, NULL, n);
3538 static int profile_verify(pa_alsa_profile *p) {
3540 static const struct description_map well_known_descriptions[] = {
3541 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3542 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3543 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3544 { "off", N_("Off") }
3549 /* Replace the output mapping names by the actual mappings */
3550 if (p->output_mapping_names) {
3553 pa_assert(!p->output_mappings);
3554 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3556 for (name = p->output_mapping_names; *name; name++) {
3559 pa_bool_t duplicate = FALSE;
3561 for (in = name + 1; *in; in++)
3562 if (pa_streq(*name, *in)) {
3570 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
3571 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
3575 pa_idxset_put(p->output_mappings, m, NULL);
3581 pa_xstrfreev(p->output_mapping_names);
3582 p->output_mapping_names = NULL;
3585 /* Replace the input mapping names by the actual mappings */
3586 if (p->input_mapping_names) {
3589 pa_assert(!p->input_mappings);
3590 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3592 for (name = p->input_mapping_names; *name; name++) {
3595 pa_bool_t duplicate = FALSE;
3597 for (in = name + 1; *in; in++)
3598 if (pa_streq(*name, *in)) {
3606 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
3607 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
3611 pa_idxset_put(p->input_mappings, m, NULL);
3617 pa_xstrfreev(p->input_mapping_names);
3618 p->input_mapping_names = NULL;
3621 if (!p->input_mappings && !p->output_mappings) {
3622 pa_log("Profile '%s' lacks mappings.", p->name);
3626 if (!p->description)
3627 p->description = pa_xstrdup(lookup_description(p->name,
3628 well_known_descriptions,
3629 PA_ELEMENTSOF(well_known_descriptions)));
3631 if (!p->description) {
3636 sb = pa_strbuf_new();
3638 if (p->output_mappings)
3639 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3640 if (!pa_strbuf_isempty(sb))
3641 pa_strbuf_puts(sb, " + ");
3643 pa_strbuf_printf(sb, _("%s Output"), m->description);
3646 if (p->input_mappings)
3647 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3648 if (!pa_strbuf_isempty(sb))
3649 pa_strbuf_puts(sb, " + ");
3651 pa_strbuf_printf(sb, _("%s Input"), m->description);
3654 p->description = pa_strbuf_tostring_free(sb);
3660 void pa_alsa_profile_dump(pa_alsa_profile *p) {
3665 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3667 pa_strnull(p->description),
3669 pa_yes_no(p->supported),
3670 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
3671 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
3673 if (p->input_mappings)
3674 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
3675 pa_log_debug("Input %s", m->name);
3677 if (p->output_mappings)
3678 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
3679 pa_log_debug("Output %s", m->name);
3682 static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
3685 /* Check that the dB mapping has been configured. Since "db-values" is
3686 * currently the only option in the DecibelFix section, and decibel fix
3687 * objects don't get created if a DecibelFix section is empty, this is
3688 * actually a redundant check. Having this may prevent future bugs,
3690 if (!db_fix->db_values) {
3691 pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
3698 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
3699 char *db_values = NULL;
3703 if (db_fix->db_values) {
3705 unsigned long i, nsteps;
3707 pa_assert(db_fix->min_step <= db_fix->max_step);
3708 nsteps = db_fix->max_step - db_fix->min_step + 1;
3710 buf = pa_strbuf_new();
3711 for (i = 0; i < nsteps; ++i)
3712 pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
3714 db_values = pa_strbuf_tostring_free(buf);
3717 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
3718 db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
3720 pa_xfree(db_values);
3723 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
3724 pa_alsa_profile_set *ps;
3727 pa_alsa_decibel_fix *db_fix;
3732 static pa_config_item items[] = {
3734 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
3737 { "device-strings", mapping_parse_device_strings, NULL, NULL },
3738 { "channel-map", mapping_parse_channel_map, NULL, NULL },
3739 { "paths-input", mapping_parse_paths, NULL, NULL },
3740 { "paths-output", mapping_parse_paths, NULL, NULL },
3741 { "element-input", mapping_parse_element, NULL, NULL },
3742 { "element-output", mapping_parse_element, NULL, NULL },
3743 { "direction", mapping_parse_direction, NULL, NULL },
3745 /* Shared by [Mapping ...] and [Profile ...] */
3746 { "description", mapping_parse_description, NULL, NULL },
3747 { "priority", mapping_parse_priority, NULL, NULL },
3750 { "input-mappings", profile_parse_mappings, NULL, NULL },
3751 { "output-mappings", profile_parse_mappings, NULL, NULL },
3752 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
3754 /* [DecibelFix ...] */
3755 { "db-values", decibel_fix_parse_db_values, NULL, NULL },
3756 { NULL, NULL, NULL, NULL }
3759 ps = pa_xnew0(pa_alsa_profile_set, 1);
3760 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3761 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3762 ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3764 items[0].data = &ps->auto_profiles;
3767 fname = "default.conf";
3769 fn = pa_maybe_prefix_path(fname,
3770 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
3771 PA_ALSA_PROFILE_SETS_DIR);
3773 r = pa_config_parse(fn, NULL, items, ps);
3779 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3780 if (mapping_verify(m, bonus) < 0)
3783 if (ps->auto_profiles)
3784 profile_set_add_auto(ps);
3786 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3787 if (profile_verify(p) < 0)
3790 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
3791 if (decibel_fix_verify(db_fix) < 0)
3797 pa_alsa_profile_set_free(ps);
3801 void pa_alsa_profile_set_probe(
3802 pa_alsa_profile_set *ps,
3804 const pa_sample_spec *ss,
3805 unsigned default_n_fragments,
3806 unsigned default_fragment_size_msec) {
3809 pa_alsa_profile *p, *last = NULL;
3819 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
3820 pa_sample_spec try_ss;
3821 pa_channel_map try_map;
3822 snd_pcm_uframes_t try_period_size, try_buffer_size;
3825 /* Is this already marked that it is supported? (i.e. from the config file) */
3829 pa_log_debug("Looking at profile %s", p->name);
3831 /* Close PCMs from the last iteration we don't need anymore */
3832 if (last && last->output_mappings)
3833 PA_IDXSET_FOREACH(m, last->output_mappings, idx) {
3838 if (last->supported)
3841 if (!p->output_mappings || !pa_idxset_get_by_data(p->output_mappings, m, NULL)) {
3842 snd_pcm_close(m->output_pcm);
3843 m->output_pcm = NULL;
3847 if (last && last->input_mappings)
3848 PA_IDXSET_FOREACH(m, last->input_mappings, idx) {
3853 if (last->supported)
3856 if (!p->input_mappings || !pa_idxset_get_by_data(p->input_mappings, m, NULL)) {
3857 snd_pcm_close(m->input_pcm);
3858 m->input_pcm = NULL;
3862 p->supported = TRUE;
3864 /* Check if we can open all new ones */
3865 if (p->output_mappings)
3866 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3871 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
3872 try_map = m->channel_map;
3874 try_ss.channels = try_map.channels;
3877 pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
3878 pa_frame_size(&try_ss);
3879 try_buffer_size = default_n_fragments * try_period_size;
3881 if (!(m ->output_pcm = pa_alsa_open_by_template(
3886 SND_PCM_STREAM_PLAYBACK,
3887 &try_period_size, &try_buffer_size, 0, NULL, NULL,
3889 p->supported = FALSE;
3894 if (p->input_mappings && p->supported)
3895 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3900 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
3901 try_map = m->channel_map;
3903 try_ss.channels = try_map.channels;
3906 pa_usec_to_bytes(default_fragment_size_msec*PA_USEC_PER_MSEC, &try_ss) /
3907 pa_frame_size(&try_ss);
3908 try_buffer_size = default_n_fragments * try_period_size;
3910 if (!(m ->input_pcm = pa_alsa_open_by_template(
3915 SND_PCM_STREAM_CAPTURE,
3916 &try_period_size, &try_buffer_size, 0, NULL, NULL,
3918 p->supported = FALSE;
3926 pa_log_debug("Profile %s supported.", p->name);
3933 if (last->output_mappings)
3934 PA_IDXSET_FOREACH(m, last->output_mappings, idx)
3935 if (m->output_pcm) {
3937 if (last->supported)
3940 snd_pcm_close(m->output_pcm);
3941 m->output_pcm = NULL;
3944 if (last->input_mappings)
3945 PA_IDXSET_FOREACH(m, last->input_mappings, idx)
3948 if (last->supported)
3951 snd_pcm_close(m->input_pcm);
3952 m->input_pcm = NULL;
3956 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3957 if (!p->supported) {
3958 pa_hashmap_remove(ps->profiles, p->name);
3962 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3963 if (m->supported <= 0) {
3964 pa_hashmap_remove(ps->mappings, m->name);
3971 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
3974 pa_alsa_decibel_fix *db_fix;
3979 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
3982 pa_yes_no(ps->auto_profiles),
3983 pa_yes_no(ps->probed),
3984 pa_hashmap_size(ps->mappings),
3985 pa_hashmap_size(ps->profiles),
3986 pa_hashmap_size(ps->decibel_fixes));
3988 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3989 pa_alsa_mapping_dump(m);
3991 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3992 pa_alsa_profile_dump(p);
3994 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
3995 pa_alsa_decibel_fix_dump(db_fix);
3998 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps) {
4005 /* if there is no path, we don't want a port list */
4009 if (!ps->paths->next){
4012 /* If there is only one path, but no or only one setting, then
4013 * we want a port list either */
4014 if (!ps->paths->settings || !ps->paths->settings->next)
4017 /* Ok, there is only one path, however with multiple settings,
4018 * so let's create a port for each setting */
4019 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4021 PA_LLIST_FOREACH(s, ps->paths->settings) {
4022 pa_device_port *port;
4023 pa_alsa_port_data *data;
4025 port = pa_device_port_new(s->name, s->description, sizeof(pa_alsa_port_data));
4026 port->priority = s->priority;
4028 data = PA_DEVICE_PORT_DATA(port);
4029 data->path = ps->paths;
4032 pa_hashmap_put(*p, port->name, port);
4037 /* We have multiple paths, so let's create a port for each
4038 * one, and each of each settings */
4039 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4041 PA_LLIST_FOREACH(path, ps->paths) {
4043 if (!path->settings || !path->settings->next) {
4044 pa_device_port *port;
4045 pa_alsa_port_data *data;
4047 /* If there is no or just one setting we only need a
4050 port = pa_device_port_new(path->name, path->description, sizeof(pa_alsa_port_data));
4051 port->priority = path->priority * 100;
4054 data = PA_DEVICE_PORT_DATA(port);
4056 data->setting = path->settings;
4058 pa_hashmap_put(*p, port->name, port);
4062 PA_LLIST_FOREACH(s, path->settings) {
4063 pa_device_port *port;
4064 pa_alsa_port_data *data;
4067 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
4069 if (s->description[0])
4070 d = pa_sprintf_malloc(_("%s / %s"), path->description, s->description);
4072 d = pa_xstrdup(path->description);
4074 port = pa_device_port_new(n, d, sizeof(pa_alsa_port_data));
4075 port->priority = path->priority * 100 + s->priority;
4080 data = PA_DEVICE_PORT_DATA(port);
4084 pa_hashmap_put(*p, port->name, port);
4090 pa_log_debug("Added %u ports", pa_hashmap_size(*p));