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>
28 #include <asoundlib.h>
31 #ifdef HAVE_VALGRIND_MEMCHECK_H
32 #include <valgrind/memcheck.h>
35 #include <pulse/mainloop-api.h>
36 #include <pulse/sample.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/volume.h>
40 #include <pulse/xmalloc.h>
41 #include <pulse/utf8.h>
43 #include <pulsecore/i18n.h>
44 #include <pulsecore/log.h>
45 #include <pulsecore/macro.h>
46 #include <pulsecore/core-util.h>
47 #include <pulsecore/conf-parser.h>
48 #include <pulsecore/strbuf.h>
50 #include "alsa-mixer.h"
51 #include "alsa-util.h"
53 struct description_map {
55 const char *description;
58 static const char *lookup_description(const char *name, const struct description_map dm[], unsigned n) {
61 for (i = 0; i < n; i++)
62 if (pa_streq(dm[i].name, name))
63 return _(dm[i].description);
68 struct pa_alsa_fdlist {
71 /* This is a temporary buffer used to avoid lots of mallocs */
72 struct pollfd *work_fds;
77 pa_defer_event *defer;
82 void (*cb)(void *userdata);
86 static void io_cb(pa_mainloop_api *a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
88 struct pa_alsa_fdlist *fdl = userdata;
91 unsigned short revents;
95 pa_assert(fdl->mixer);
97 pa_assert(fdl->work_fds);
104 memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds);
106 for (i = 0; i < fdl->num_fds; i++) {
107 if (e == fdl->ios[i]) {
108 if (events & PA_IO_EVENT_INPUT)
109 fdl->work_fds[i].revents |= POLLIN;
110 if (events & PA_IO_EVENT_OUTPUT)
111 fdl->work_fds[i].revents |= POLLOUT;
112 if (events & PA_IO_EVENT_ERROR)
113 fdl->work_fds[i].revents |= POLLERR;
114 if (events & PA_IO_EVENT_HANGUP)
115 fdl->work_fds[i].revents |= POLLHUP;
120 pa_assert(i != fdl->num_fds);
122 if ((err = snd_mixer_poll_descriptors_revents(fdl->mixer, fdl->work_fds, fdl->num_fds, &revents)) < 0) {
123 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
127 a->defer_enable(fdl->defer, 1);
130 snd_mixer_handle_events(fdl->mixer);
133 static void defer_cb(pa_mainloop_api *a, pa_defer_event *e, void *userdata) {
134 struct pa_alsa_fdlist *fdl = userdata;
141 pa_assert(fdl->mixer);
143 a->defer_enable(fdl->defer, 0);
145 if ((n = snd_mixer_poll_descriptors_count(fdl->mixer)) < 0) {
146 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
149 num_fds = (unsigned) n;
151 if (num_fds != fdl->num_fds) {
155 pa_xfree(fdl->work_fds);
156 fdl->fds = pa_xnew0(struct pollfd, num_fds);
157 fdl->work_fds = pa_xnew(struct pollfd, num_fds);
160 memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds);
162 if ((err = snd_mixer_poll_descriptors(fdl->mixer, fdl->work_fds, num_fds)) < 0) {
163 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
169 if (memcmp(fdl->fds, fdl->work_fds, sizeof(struct pollfd) * num_fds) == 0)
173 for (i = 0; i < fdl->num_fds; i++)
174 a->io_free(fdl->ios[i]);
176 if (num_fds != fdl->num_fds) {
183 fdl->ios = pa_xnew(pa_io_event*, num_fds);
186 temp = fdl->work_fds;
187 fdl->work_fds = fdl->fds;
190 fdl->num_fds = num_fds;
192 for (i = 0;i < num_fds;i++)
193 fdl->ios[i] = a->io_new(a, fdl->fds[i].fd,
194 ((fdl->fds[i].events & POLLIN) ? PA_IO_EVENT_INPUT : 0) |
195 ((fdl->fds[i].events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0),
199 struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) {
200 struct pa_alsa_fdlist *fdl;
202 fdl = pa_xnew0(struct pa_alsa_fdlist, 1);
207 void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {
212 fdl->m->defer_free(fdl->defer);
218 for (i = 0; i < fdl->num_fds; i++)
219 fdl->m->io_free(fdl->ios[i]);
226 pa_xfree(fdl->work_fds);
231 int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api *m) {
233 pa_assert(mixer_handle);
237 fdl->mixer = mixer_handle;
239 fdl->defer = m->defer_new(m, defer_cb, fdl);
244 struct pa_alsa_mixer_pdata {
246 pa_rtpoll_item *poll_item;
251 struct pa_alsa_mixer_pdata *pa_alsa_mixer_pdata_new(void) {
252 struct pa_alsa_mixer_pdata *pd;
254 pd = pa_xnew0(struct pa_alsa_mixer_pdata, 1);
259 void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata *pd) {
263 pa_rtpoll_item_free(pd->poll_item);
269 static int rtpoll_work_cb(pa_rtpoll_item *i) {
270 struct pa_alsa_mixer_pdata *pd;
273 unsigned short revents = 0;
276 pd = pa_rtpoll_item_get_userdata(i);
278 pa_assert_fp(i == pd->poll_item);
280 p = pa_rtpoll_item_get_pollfd(i, &n_fds);
282 if ((err = snd_mixer_poll_descriptors_revents(pd->mixer, p, n_fds, &revents)) < 0) {
283 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
289 err = snd_mixer_handle_events(pd->mixer);
291 if (PA_UNLIKELY(err == -ENODEV)) {
292 /* The card has been disconnected, stop polling */
295 /* Success, or at least an error we're likely to recover from */
296 pa_rtpoll_item_free(i);
297 pa_alsa_set_mixer_rtpoll(pd, pd->mixer, pd->rtpoll);
304 pa_rtpoll_item_free(i);
306 pd->poll_item = NULL;
313 int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata *pd, snd_mixer_t *mixer, pa_rtpoll *rtp) {
322 if ((n = snd_mixer_poll_descriptors_count(mixer)) < 0) {
323 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
327 i = pa_rtpoll_item_new(rtp, PA_RTPOLL_LATE, (unsigned) n);
329 p = pa_rtpoll_item_get_pollfd(i, NULL);
331 memset(p, 0, sizeof(struct pollfd) * n);
333 if ((err = snd_mixer_poll_descriptors(mixer, p, (unsigned) n)) < 0) {
334 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
335 pa_rtpoll_item_free(i);
343 pa_rtpoll_item_set_userdata(i, pd);
344 pa_rtpoll_item_set_work_callback(i, rtpoll_work_cb);
349 static int prepare_mixer(snd_mixer_t *mixer, const char *dev) {
355 if ((err = snd_mixer_attach(mixer, dev)) < 0) {
356 pa_log_info("Unable to attach to mixer %s: %s", dev, pa_alsa_strerror(err));
360 if ((err = snd_mixer_selem_register(mixer, NULL, NULL)) < 0) {
361 pa_log_warn("Unable to register mixer: %s", pa_alsa_strerror(err));
365 if ((err = snd_mixer_load(mixer)) < 0) {
366 pa_log_warn("Unable to load mixer: %s", pa_alsa_strerror(err));
370 pa_log_info("Successfully attached to mixer '%s'", dev);
374 snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device) {
378 snd_pcm_info_t* info;
379 snd_pcm_info_alloca(&info);
383 if ((err = snd_mixer_open(&m, 0)) < 0) {
384 pa_log("Error opening mixer: %s", pa_alsa_strerror(err));
388 /* First, try by name */
389 if ((dev = snd_pcm_name(pcm)))
390 if (prepare_mixer(m, dev) >= 0) {
392 *ctl_device = pa_xstrdup(dev);
397 /* Then, try by card index */
398 if (snd_pcm_info(pcm, info) >= 0) {
402 if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
404 md = pa_sprintf_malloc("hw:%i", card_idx);
406 if (!dev || !pa_streq(dev, md))
407 if (prepare_mixer(m, md) >= 0) {
425 static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_MAX] = {
426 [PA_CHANNEL_POSITION_MONO] = SND_MIXER_SCHN_MONO, /* The ALSA name is just an alias! */
428 [PA_CHANNEL_POSITION_FRONT_CENTER] = SND_MIXER_SCHN_FRONT_CENTER,
429 [PA_CHANNEL_POSITION_FRONT_LEFT] = SND_MIXER_SCHN_FRONT_LEFT,
430 [PA_CHANNEL_POSITION_FRONT_RIGHT] = SND_MIXER_SCHN_FRONT_RIGHT,
432 [PA_CHANNEL_POSITION_REAR_CENTER] = SND_MIXER_SCHN_REAR_CENTER,
433 [PA_CHANNEL_POSITION_REAR_LEFT] = SND_MIXER_SCHN_REAR_LEFT,
434 [PA_CHANNEL_POSITION_REAR_RIGHT] = SND_MIXER_SCHN_REAR_RIGHT,
436 [PA_CHANNEL_POSITION_LFE] = SND_MIXER_SCHN_WOOFER,
438 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
439 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
441 [PA_CHANNEL_POSITION_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT,
442 [PA_CHANNEL_POSITION_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT,
444 [PA_CHANNEL_POSITION_AUX0] = SND_MIXER_SCHN_UNKNOWN,
445 [PA_CHANNEL_POSITION_AUX1] = SND_MIXER_SCHN_UNKNOWN,
446 [PA_CHANNEL_POSITION_AUX2] = SND_MIXER_SCHN_UNKNOWN,
447 [PA_CHANNEL_POSITION_AUX3] = SND_MIXER_SCHN_UNKNOWN,
448 [PA_CHANNEL_POSITION_AUX4] = SND_MIXER_SCHN_UNKNOWN,
449 [PA_CHANNEL_POSITION_AUX5] = SND_MIXER_SCHN_UNKNOWN,
450 [PA_CHANNEL_POSITION_AUX6] = SND_MIXER_SCHN_UNKNOWN,
451 [PA_CHANNEL_POSITION_AUX7] = SND_MIXER_SCHN_UNKNOWN,
452 [PA_CHANNEL_POSITION_AUX8] = SND_MIXER_SCHN_UNKNOWN,
453 [PA_CHANNEL_POSITION_AUX9] = SND_MIXER_SCHN_UNKNOWN,
454 [PA_CHANNEL_POSITION_AUX10] = SND_MIXER_SCHN_UNKNOWN,
455 [PA_CHANNEL_POSITION_AUX11] = SND_MIXER_SCHN_UNKNOWN,
456 [PA_CHANNEL_POSITION_AUX12] = SND_MIXER_SCHN_UNKNOWN,
457 [PA_CHANNEL_POSITION_AUX13] = SND_MIXER_SCHN_UNKNOWN,
458 [PA_CHANNEL_POSITION_AUX14] = SND_MIXER_SCHN_UNKNOWN,
459 [PA_CHANNEL_POSITION_AUX15] = SND_MIXER_SCHN_UNKNOWN,
460 [PA_CHANNEL_POSITION_AUX16] = SND_MIXER_SCHN_UNKNOWN,
461 [PA_CHANNEL_POSITION_AUX17] = SND_MIXER_SCHN_UNKNOWN,
462 [PA_CHANNEL_POSITION_AUX18] = SND_MIXER_SCHN_UNKNOWN,
463 [PA_CHANNEL_POSITION_AUX19] = SND_MIXER_SCHN_UNKNOWN,
464 [PA_CHANNEL_POSITION_AUX20] = SND_MIXER_SCHN_UNKNOWN,
465 [PA_CHANNEL_POSITION_AUX21] = SND_MIXER_SCHN_UNKNOWN,
466 [PA_CHANNEL_POSITION_AUX22] = SND_MIXER_SCHN_UNKNOWN,
467 [PA_CHANNEL_POSITION_AUX23] = SND_MIXER_SCHN_UNKNOWN,
468 [PA_CHANNEL_POSITION_AUX24] = SND_MIXER_SCHN_UNKNOWN,
469 [PA_CHANNEL_POSITION_AUX25] = SND_MIXER_SCHN_UNKNOWN,
470 [PA_CHANNEL_POSITION_AUX26] = SND_MIXER_SCHN_UNKNOWN,
471 [PA_CHANNEL_POSITION_AUX27] = SND_MIXER_SCHN_UNKNOWN,
472 [PA_CHANNEL_POSITION_AUX28] = SND_MIXER_SCHN_UNKNOWN,
473 [PA_CHANNEL_POSITION_AUX29] = SND_MIXER_SCHN_UNKNOWN,
474 [PA_CHANNEL_POSITION_AUX30] = SND_MIXER_SCHN_UNKNOWN,
475 [PA_CHANNEL_POSITION_AUX31] = SND_MIXER_SCHN_UNKNOWN,
477 [PA_CHANNEL_POSITION_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN,
479 [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SND_MIXER_SCHN_UNKNOWN,
480 [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SND_MIXER_SCHN_UNKNOWN,
481 [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SND_MIXER_SCHN_UNKNOWN,
483 [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SND_MIXER_SCHN_UNKNOWN,
484 [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SND_MIXER_SCHN_UNKNOWN,
485 [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SND_MIXER_SCHN_UNKNOWN
488 static void setting_free(pa_alsa_setting *s) {
492 pa_idxset_free(s->options, NULL, NULL);
495 pa_xfree(s->description);
499 static void option_free(pa_alsa_option *o) {
502 pa_xfree(o->alsa_name);
504 pa_xfree(o->description);
508 static void decibel_fix_free(pa_alsa_decibel_fix *db_fix) {
511 pa_xfree(db_fix->name);
512 pa_xfree(db_fix->db_values);
517 static void element_free(pa_alsa_element *e) {
521 while ((o = e->options)) {
522 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
527 decibel_fix_free(e->db_fix);
529 pa_xfree(e->alsa_name);
533 void pa_alsa_path_free(pa_alsa_path *p) {
539 while ((e = p->elements)) {
540 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
544 while ((s = p->settings)) {
545 PA_LLIST_REMOVE(pa_alsa_setting, p->settings, s);
550 pa_xfree(p->description);
554 void pa_alsa_path_set_free(pa_alsa_path_set *ps) {
558 while ((p = ps->paths)) {
559 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
560 pa_alsa_path_free(p);
566 static long to_alsa_dB(pa_volume_t v) {
567 return (long) (pa_sw_volume_to_dB(v) * 100.0);
570 static pa_volume_t from_alsa_dB(long v) {
571 return pa_sw_volume_from_dB((double) v / 100.0);
574 static long to_alsa_volume(pa_volume_t v, long min, long max) {
577 w = (long) round(((double) v * (double) (max - min)) / PA_VOLUME_NORM) + min;
578 return PA_CLAMP_UNLIKELY(w, min, max);
581 static pa_volume_t from_alsa_volume(long v, long min, long max) {
582 return (pa_volume_t) round(((double) (v - min) * PA_VOLUME_NORM) / (double) (max - min));
585 #define SELEM_INIT(sid, name) \
587 snd_mixer_selem_id_alloca(&(sid)); \
588 snd_mixer_selem_id_set_name((sid), (name)); \
589 snd_mixer_selem_id_set_index((sid), 0); \
592 static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
593 snd_mixer_selem_id_t *sid;
594 snd_mixer_elem_t *me;
595 snd_mixer_selem_channel_id_t c;
596 pa_channel_position_mask_t mask = 0;
604 SELEM_INIT(sid, e->alsa_name);
605 if (!(me = snd_mixer_find_selem(m, sid))) {
606 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
610 pa_cvolume_mute(v, cm->channels);
612 /* We take the highest volume of all channels that match */
614 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
621 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
622 if (snd_mixer_selem_has_playback_channel(me, c)) {
624 if ((r = snd_mixer_selem_get_playback_volume(me, c, &value)) >= 0) {
625 /* If the channel volume is outside the limits set
626 * by the dB fix, we clamp the hw volume to be
627 * within the limits. */
628 if (value < e->db_fix->min_step) {
629 value = e->db_fix->min_step;
630 snd_mixer_selem_set_playback_volume(me, c, value);
631 pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
632 "Volume reset to %0.2f dB.", e->alsa_name, c,
633 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
634 } else if (value > e->db_fix->max_step) {
635 value = e->db_fix->max_step;
636 snd_mixer_selem_set_playback_volume(me, c, value);
637 pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
638 "Volume reset to %0.2f dB.", e->alsa_name, c,
639 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
642 /* Volume step -> dB value conversion. */
643 value = e->db_fix->db_values[value - e->db_fix->min_step];
646 r = snd_mixer_selem_get_playback_dB(me, c, &value);
650 if (snd_mixer_selem_has_capture_channel(me, c)) {
652 if ((r = snd_mixer_selem_get_capture_volume(me, c, &value)) >= 0) {
653 /* If the channel volume is outside the limits set
654 * by the dB fix, we clamp the hw volume to be
655 * within the limits. */
656 if (value < e->db_fix->min_step) {
657 value = e->db_fix->min_step;
658 snd_mixer_selem_set_capture_volume(me, c, value);
659 pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
660 "Volume reset to %0.2f dB.", e->alsa_name, c,
661 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
662 } else if (value > e->db_fix->max_step) {
663 value = e->db_fix->max_step;
664 snd_mixer_selem_set_capture_volume(me, c, value);
665 pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
666 "Volume reset to %0.2f dB.", e->alsa_name, c,
667 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
670 /* Volume step -> dB value conversion. */
671 value = e->db_fix->db_values[value - e->db_fix->min_step];
674 r = snd_mixer_selem_get_capture_dB(me, c, &value);
682 #ifdef HAVE_VALGRIND_MEMCHECK_H
683 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
686 f = from_alsa_dB(value);
691 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
692 if (snd_mixer_selem_has_playback_channel(me, c))
693 r = snd_mixer_selem_get_playback_volume(me, c, &value);
697 if (snd_mixer_selem_has_capture_channel(me, c))
698 r = snd_mixer_selem_get_capture_volume(me, c, &value);
706 f = from_alsa_volume(value, e->min_volume, e->max_volume);
709 for (k = 0; k < cm->channels; k++)
710 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
711 if (v->values[k] < f)
714 mask |= e->masks[c][e->n_channels-1];
717 for (k = 0; k < cm->channels; k++)
718 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
719 v->values[k] = PA_VOLUME_NORM;
724 int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
735 pa_cvolume_reset(v, cm->channels);
737 PA_LLIST_FOREACH(e, p->elements) {
740 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
743 pa_assert(!p->has_dB || e->has_dB);
745 if (element_get_volume(e, m, cm, &ev) < 0)
748 /* If we have no dB information all we can do is take the first element and leave */
754 pa_sw_cvolume_multiply(v, v, &ev);
760 static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t *b) {
761 snd_mixer_selem_id_t *sid;
762 snd_mixer_elem_t *me;
763 snd_mixer_selem_channel_id_t c;
769 SELEM_INIT(sid, e->alsa_name);
770 if (!(me = snd_mixer_find_selem(m, sid))) {
771 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
775 /* We return muted if at least one channel is muted */
777 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
781 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
782 if (snd_mixer_selem_has_playback_channel(me, c))
783 r = snd_mixer_selem_get_playback_switch(me, c, &value);
787 if (snd_mixer_selem_has_capture_channel(me, c))
788 r = snd_mixer_selem_get_capture_switch(me, c, &value);
806 int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t *muted) {
816 PA_LLIST_FOREACH(e, p->elements) {
819 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
822 if (element_get_switch(e, m, &b) < 0)
835 /* Finds the closest item in db_fix->db_values and returns the corresponding
836 * step. *db_value is replaced with the value from the db_values table.
837 * Rounding is done based on the rounding parameter: -1 means rounding down and
838 * +1 means rounding up. */
839 static long decibel_fix_get_step(pa_alsa_decibel_fix *db_fix, long *db_value, int rounding) {
845 pa_assert(rounding != 0);
847 max_i = db_fix->max_step - db_fix->min_step;
850 for (i = 0; i < max_i; i++) {
851 if (db_fix->db_values[i] >= *db_value)
855 for (i = 0; i < max_i; i++) {
856 if (db_fix->db_values[i + 1] > *db_value)
861 *db_value = db_fix->db_values[i];
863 return i + db_fix->min_step;
866 /* Alsa lib documentation says for snd_mixer_selem_set_playback_dB() direction argument,
867 * that "-1 = accurate or first below, 0 = accurate, 1 = accurate or first above".
868 * But even with accurate nearest dB volume step is not selected, so that is why we need
869 * this function. Returns 0 and nearest selectable volume in *value_dB on success or
870 * negative error code if fails. */
871 static int element_get_nearest_alsa_dB(snd_mixer_elem_t *me, snd_mixer_selem_channel_id_t c, pa_alsa_direction_t d, long *value_dB) {
881 if (d == PA_ALSA_DIRECTION_OUTPUT) {
882 if ((r = snd_mixer_selem_ask_playback_dB_vol(me, *value_dB, +1, &alsa_val)) >= 0)
883 r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value_high);
888 if (value_high == *value_dB)
891 if ((r = snd_mixer_selem_ask_playback_dB_vol(me, *value_dB, -1, &alsa_val)) >= 0)
892 r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value_low);
894 if ((r = snd_mixer_selem_ask_capture_dB_vol(me, *value_dB, +1, &alsa_val)) >= 0)
895 r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value_high);
900 if (value_high == *value_dB)
903 if ((r = snd_mixer_selem_ask_capture_dB_vol(me, *value_dB, -1, &alsa_val)) >= 0)
904 r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value_low);
910 if (labs(value_high - *value_dB) < labs(value_low - *value_dB))
911 *value_dB = value_high;
913 *value_dB = value_low;
918 static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, pa_bool_t deferred_volume, pa_bool_t write_to_hw) {
920 snd_mixer_selem_id_t *sid;
922 snd_mixer_elem_t *me;
923 snd_mixer_selem_channel_id_t c;
924 pa_channel_position_mask_t mask = 0;
931 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
933 SELEM_INIT(sid, e->alsa_name);
934 if (!(me = snd_mixer_find_selem(m, sid))) {
935 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
939 pa_cvolume_mute(&rv, cm->channels);
941 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
943 pa_volume_t f = PA_VOLUME_MUTED;
944 pa_bool_t found = FALSE;
946 for (k = 0; k < cm->channels; k++)
947 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) {
949 if (v->values[k] > f)
954 /* Hmm, so this channel does not exist in the volume
955 * struct, so let's bind it to the overall max of the
957 f = pa_cvolume_max(v);
961 long value = to_alsa_dB(f);
964 if (e->volume_limit >= 0 && value > (e->max_dB * 100))
965 value = e->max_dB * 100;
967 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
968 /* If we call set_playback_volume() without checking first
969 * if the channel is available, ALSA behaves very
970 * strangely and doesn't fail the call */
971 if (snd_mixer_selem_has_playback_channel(me, c)) {
975 r = snd_mixer_selem_set_playback_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding));
977 decibel_fix_get_step(e->db_fix, &value, rounding);
983 if (deferred_volume) {
984 if ((r = element_get_nearest_alsa_dB(me, c, PA_ALSA_DIRECTION_OUTPUT, &value)) >= 0)
985 r = snd_mixer_selem_set_playback_dB(me, c, value, 0);
987 if ((r = snd_mixer_selem_set_playback_dB(me, c, value, rounding)) >= 0)
988 r = snd_mixer_selem_get_playback_dB(me, c, &value);
992 if ((r = snd_mixer_selem_ask_playback_dB_vol(me, value, rounding, &alsa_val)) >= 0)
993 r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value);
999 if (snd_mixer_selem_has_capture_channel(me, c)) {
1003 r = snd_mixer_selem_set_capture_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding));
1005 decibel_fix_get_step(e->db_fix, &value, rounding);
1011 if (deferred_volume) {
1012 if ((r = element_get_nearest_alsa_dB(me, c, PA_ALSA_DIRECTION_INPUT, &value)) >= 0)
1013 r = snd_mixer_selem_set_capture_dB(me, c, value, 0);
1015 if ((r = snd_mixer_selem_set_capture_dB(me, c, value, rounding)) >= 0)
1016 r = snd_mixer_selem_get_capture_dB(me, c, &value);
1020 if ((r = snd_mixer_selem_ask_capture_dB_vol(me, value, rounding, &alsa_val)) >= 0)
1021 r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value);
1031 #ifdef HAVE_VALGRIND_MEMCHECK_H
1032 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
1035 f = from_alsa_dB(value);
1040 value = to_alsa_volume(f, e->min_volume, e->max_volume);
1042 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1043 if (snd_mixer_selem_has_playback_channel(me, c)) {
1044 if ((r = snd_mixer_selem_set_playback_volume(me, c, value)) >= 0)
1045 r = snd_mixer_selem_get_playback_volume(me, c, &value);
1049 if (snd_mixer_selem_has_capture_channel(me, c)) {
1050 if ((r = snd_mixer_selem_set_capture_volume(me, c, value)) >= 0)
1051 r = snd_mixer_selem_get_capture_volume(me, c, &value);
1059 f = from_alsa_volume(value, e->min_volume, e->max_volume);
1062 for (k = 0; k < cm->channels; k++)
1063 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
1064 if (rv.values[k] < f)
1067 mask |= e->masks[c][e->n_channels-1];
1070 for (k = 0; k < cm->channels; k++)
1071 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
1072 rv.values[k] = PA_VOLUME_NORM;
1078 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 deferred_volume, pa_bool_t write_to_hw) {
1087 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
1092 rv = *v; /* Remaining adjustment */
1093 pa_cvolume_reset(v, cm->channels); /* Adjustment done */
1095 PA_LLIST_FOREACH(e, p->elements) {
1098 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
1101 pa_assert(!p->has_dB || e->has_dB);
1104 if (element_set_volume(e, m, cm, &ev, deferred_volume, write_to_hw) < 0)
1112 pa_sw_cvolume_multiply(v, v, &ev);
1113 pa_sw_cvolume_divide(&rv, &rv, &ev);
1119 static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t b) {
1120 snd_mixer_elem_t *me;
1121 snd_mixer_selem_id_t *sid;
1127 SELEM_INIT(sid, e->alsa_name);
1128 if (!(me = snd_mixer_find_selem(m, sid))) {
1129 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1133 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1134 r = snd_mixer_selem_set_playback_switch_all(me, b);
1136 r = snd_mixer_selem_set_capture_switch_all(me, b);
1139 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1144 int pa_alsa_path_set_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t muted) {
1153 PA_LLIST_FOREACH(e, p->elements) {
1155 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
1158 if (element_set_switch(e, m, !muted) < 0)
1165 /* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
1166 * function sets all channels of the volume element to e->min_volume, 0 dB or
1167 * e->constant_volume. */
1168 static int element_set_constant_volume(pa_alsa_element *e, snd_mixer_t *m) {
1169 snd_mixer_elem_t *me = NULL;
1170 snd_mixer_selem_id_t *sid = NULL;
1173 pa_bool_t volume_set = FALSE;
1178 SELEM_INIT(sid, e->alsa_name);
1179 if (!(me = snd_mixer_find_selem(m, sid))) {
1180 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1184 switch (e->volume_use) {
1185 case PA_ALSA_VOLUME_OFF:
1186 volume = e->min_volume;
1190 case PA_ALSA_VOLUME_ZERO:
1194 volume = decibel_fix_get_step(e->db_fix, &dB, (e->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1));
1199 case PA_ALSA_VOLUME_CONSTANT:
1200 volume = e->constant_volume;
1205 pa_assert_not_reached();
1209 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1210 r = snd_mixer_selem_set_playback_volume_all(me, volume);
1212 r = snd_mixer_selem_set_capture_volume_all(me, volume);
1214 pa_assert(e->volume_use == PA_ALSA_VOLUME_ZERO);
1215 pa_assert(!e->db_fix);
1217 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1218 r = snd_mixer_selem_set_playback_dB_all(me, 0, +1);
1220 r = snd_mixer_selem_set_capture_dB_all(me, 0, -1);
1224 pa_log_warn("Failed to set volume of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1229 int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
1236 pa_log_debug("Activating path %s", p->name);
1237 pa_alsa_path_dump(p);
1239 PA_LLIST_FOREACH(e, p->elements) {
1241 switch (e->switch_use) {
1242 case PA_ALSA_SWITCH_OFF:
1243 r = element_set_switch(e, m, FALSE);
1246 case PA_ALSA_SWITCH_ON:
1247 r = element_set_switch(e, m, TRUE);
1250 case PA_ALSA_SWITCH_MUTE:
1251 case PA_ALSA_SWITCH_IGNORE:
1252 case PA_ALSA_SWITCH_SELECT:
1260 switch (e->volume_use) {
1261 case PA_ALSA_VOLUME_OFF:
1262 case PA_ALSA_VOLUME_ZERO:
1263 case PA_ALSA_VOLUME_CONSTANT:
1264 r = element_set_constant_volume(e, m);
1267 case PA_ALSA_VOLUME_MERGE:
1268 case PA_ALSA_VOLUME_IGNORE:
1280 static int check_required(pa_alsa_element *e, snd_mixer_elem_t *me) {
1281 pa_bool_t has_switch;
1282 pa_bool_t has_enumeration;
1283 pa_bool_t has_volume;
1288 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1290 snd_mixer_selem_has_playback_switch(me) ||
1291 (e->direction_try_other && snd_mixer_selem_has_capture_switch(me));
1294 snd_mixer_selem_has_capture_switch(me) ||
1295 (e->direction_try_other && snd_mixer_selem_has_playback_switch(me));
1298 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1300 snd_mixer_selem_has_playback_volume(me) ||
1301 (e->direction_try_other && snd_mixer_selem_has_capture_volume(me));
1304 snd_mixer_selem_has_capture_volume(me) ||
1305 (e->direction_try_other && snd_mixer_selem_has_playback_volume(me));
1308 has_enumeration = snd_mixer_selem_is_enumerated(me);
1310 if ((e->required == PA_ALSA_REQUIRED_SWITCH && !has_switch) ||
1311 (e->required == PA_ALSA_REQUIRED_VOLUME && !has_volume) ||
1312 (e->required == PA_ALSA_REQUIRED_ENUMERATION && !has_enumeration))
1315 if (e->required == PA_ALSA_REQUIRED_ANY && !(has_switch || has_volume || has_enumeration))
1318 if ((e->required_absent == PA_ALSA_REQUIRED_SWITCH && has_switch) ||
1319 (e->required_absent == PA_ALSA_REQUIRED_VOLUME && has_volume) ||
1320 (e->required_absent == PA_ALSA_REQUIRED_ENUMERATION && has_enumeration))
1323 if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration))
1326 if (e->required_any != PA_ALSA_REQUIRED_IGNORE) {
1327 switch (e->required_any) {
1328 case PA_ALSA_REQUIRED_VOLUME:
1329 e->path->req_any_present |= (e->volume_use != PA_ALSA_VOLUME_IGNORE);
1331 case PA_ALSA_REQUIRED_SWITCH:
1332 e->path->req_any_present |= (e->switch_use != PA_ALSA_SWITCH_IGNORE);
1334 case PA_ALSA_REQUIRED_ENUMERATION:
1335 e->path->req_any_present |= (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
1337 case PA_ALSA_REQUIRED_ANY:
1338 e->path->req_any_present |=
1339 (e->volume_use != PA_ALSA_VOLUME_IGNORE) ||
1340 (e->switch_use != PA_ALSA_SWITCH_IGNORE) ||
1341 (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
1344 pa_assert_not_reached();
1348 if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1350 PA_LLIST_FOREACH(o, e->options) {
1351 e->path->req_any_present |= (o->required_any != PA_ALSA_REQUIRED_IGNORE) &&
1353 if (o->required != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx < 0)
1355 if (o->required_absent != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx >= 0)
1363 static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
1364 snd_mixer_selem_id_t *sid;
1365 snd_mixer_elem_t *me;
1371 SELEM_INIT(sid, e->alsa_name);
1373 if (!(me = snd_mixer_find_selem(m, sid))) {
1375 if (e->required != PA_ALSA_REQUIRED_IGNORE)
1378 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1379 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1380 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1385 if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
1386 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1388 if (!snd_mixer_selem_has_playback_switch(me)) {
1389 if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
1390 e->direction = PA_ALSA_DIRECTION_INPUT;
1392 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1397 if (!snd_mixer_selem_has_capture_switch(me)) {
1398 if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
1399 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1401 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1405 if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
1406 e->direction_try_other = FALSE;
1409 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1411 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1413 if (!snd_mixer_selem_has_playback_volume(me)) {
1414 if (e->direction_try_other && snd_mixer_selem_has_capture_volume(me))
1415 e->direction = PA_ALSA_DIRECTION_INPUT;
1417 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1422 if (!snd_mixer_selem_has_capture_volume(me)) {
1423 if (e->direction_try_other && snd_mixer_selem_has_playback_volume(me))
1424 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1426 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1430 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1431 long min_dB = 0, max_dB = 0;
1434 e->direction_try_other = FALSE;
1436 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1437 r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume);
1439 r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
1442 pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1446 if (e->min_volume >= e->max_volume) {
1447 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);
1448 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1450 } else if (e->volume_use == PA_ALSA_VOLUME_CONSTANT &&
1451 (e->min_volume > e->constant_volume || e->max_volume < e->constant_volume)) {
1452 pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
1453 e->constant_volume, e->alsa_name, e->min_volume, e->max_volume);
1454 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1458 pa_channel_position_t p;
1461 ((e->min_volume > e->db_fix->min_step) ||
1462 (e->max_volume < e->db_fix->max_step))) {
1463 pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1464 "real hardware range (%li-%li). Disabling the decibel fix.", e->alsa_name,
1465 e->db_fix->min_step, e->db_fix->max_step,
1466 e->min_volume, e->max_volume);
1468 decibel_fix_free(e->db_fix);
1474 e->min_volume = e->db_fix->min_step;
1475 e->max_volume = e->db_fix->max_step;
1476 min_dB = e->db_fix->db_values[0];
1477 max_dB = e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step];
1478 } else if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1479 e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0;
1481 e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0;
1483 /* Check that the kernel driver returns consistent limits with
1484 * both _get_*_dB_range() and _ask_*_vol_dB(). */
1485 if (e->has_dB && !e->db_fix) {
1486 long min_dB_checked = 0;
1487 long max_dB_checked = 0;
1489 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1490 r = snd_mixer_selem_ask_playback_vol_dB(me, e->min_volume, &min_dB_checked);
1492 r = snd_mixer_selem_ask_capture_vol_dB(me, e->min_volume, &min_dB_checked);
1495 pa_log_warn("Failed to query the dB value for %s at volume level %li", e->alsa_name, e->min_volume);
1499 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1500 r = snd_mixer_selem_ask_playback_vol_dB(me, e->max_volume, &max_dB_checked);
1502 r = snd_mixer_selem_ask_capture_vol_dB(me, e->max_volume, &max_dB_checked);
1505 pa_log_warn("Failed to query the dB value for %s at volume level %li", e->alsa_name, e->max_volume);
1509 if (min_dB != min_dB_checked || max_dB != max_dB_checked) {
1510 pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
1511 "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
1512 "%0.2f dB at level %li.",
1514 min_dB / 100.0, max_dB / 100.0,
1515 min_dB_checked / 100.0, e->min_volume, max_dB_checked / 100.0, e->max_volume);
1521 #ifdef HAVE_VALGRIND_MEMCHECK_H
1522 VALGRIND_MAKE_MEM_DEFINED(&min_dB, sizeof(min_dB));
1523 VALGRIND_MAKE_MEM_DEFINED(&max_dB, sizeof(max_dB));
1526 e->min_dB = ((double) min_dB) / 100.0;
1527 e->max_dB = ((double) max_dB) / 100.0;
1529 if (min_dB >= max_dB) {
1530 pa_assert(!e->db_fix);
1531 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);
1536 if (e->volume_limit >= 0) {
1537 if (e->volume_limit <= e->min_volume || e->volume_limit > e->max_volume)
1538 pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1539 "%li-%li. The volume limit is ignored.",
1540 e->alsa_name, e->path->name, e->volume_limit, e->min_volume + 1, e->max_volume);
1543 e->max_volume = e->volume_limit;
1547 e->db_fix->max_step = e->max_volume;
1548 e->max_dB = ((double) e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step]) / 100.0;
1551 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1552 r = snd_mixer_selem_ask_playback_vol_dB(me, e->max_volume, &max_dB);
1554 r = snd_mixer_selem_ask_capture_vol_dB(me, e->max_volume, &max_dB);
1557 pa_log_warn("Failed to get dB value of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1560 e->max_dB = ((double) max_dB) / 100.0;
1566 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1567 is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
1569 is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
1574 if (!e->override_map) {
1575 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1576 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1579 e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
1582 e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
1585 e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
1588 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1590 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1593 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1594 e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1596 e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1599 if (e->n_channels <= 0) {
1600 pa_log_warn("Volume element %s with no channels?", e->alsa_name);
1604 if (e->n_channels > 2) {
1605 /* FIXME: In some places code like this is used:
1607 * e->masks[alsa_channel_ids[p]][e->n_channels-1]
1609 * The definition of e->masks is
1611 * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST][2];
1613 * Since the array size is fixed at 2, we obviously
1614 * don't support elements with more than two
1616 pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e->alsa_name, e->n_channels);
1620 if (!e->override_map) {
1621 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1622 pa_bool_t has_channel;
1624 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1627 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1628 has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1630 has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1632 e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
1637 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1638 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1641 e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
1649 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1652 PA_LLIST_FOREACH(o, e->options)
1653 o->alsa_idx = pa_streq(o->alsa_name, "on") ? 1 : 0;
1654 } else if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1658 if ((n = snd_mixer_selem_get_enum_items(me)) < 0) {
1659 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n));
1663 PA_LLIST_FOREACH(o, e->options) {
1666 for (i = 0; i < n; i++) {
1669 if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
1672 if (!pa_streq(buf, o->alsa_name))
1680 if (check_required(e, me) < 0)
1686 static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, pa_bool_t prefixed) {
1693 if (!pa_startswith(section, "Element "))
1699 /* This is not an element section, but an enum section? */
1700 if (strchr(section, ':'))
1703 if (p->last_element && pa_streq(p->last_element->alsa_name, section))
1704 return p->last_element;
1706 PA_LLIST_FOREACH(e, p->elements)
1707 if (pa_streq(e->alsa_name, section))
1710 e = pa_xnew0(pa_alsa_element, 1);
1712 e->alsa_name = pa_xstrdup(section);
1713 e->direction = p->direction;
1714 e->volume_limit = -1;
1716 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
1719 p->last_element = e;
1723 static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
1729 if (!pa_startswith(section, "Option "))
1734 /* This is not an enum section, but an element section? */
1735 if (!(on = strchr(section, ':')))
1738 en = pa_xstrndup(section, on - section);
1741 if (p->last_option &&
1742 pa_streq(p->last_option->element->alsa_name, en) &&
1743 pa_streq(p->last_option->alsa_name, on)) {
1745 return p->last_option;
1748 pa_assert_se(e = element_get(p, en, FALSE));
1751 PA_LLIST_FOREACH(o, e->options)
1752 if (pa_streq(o->alsa_name, on))
1755 o = pa_xnew0(pa_alsa_option, 1);
1757 o->alsa_name = pa_xstrdup(on);
1760 if (p->last_option && p->last_option->element == e)
1761 PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
1763 PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
1770 static int element_parse_switch(
1771 const char *filename,
1773 const char *section,
1779 pa_alsa_path *p = userdata;
1784 if (!(e = element_get(p, section, TRUE))) {
1785 pa_log("[%s:%u] Switch makes no sense in '%s'", filename, line, section);
1789 if (pa_streq(rvalue, "ignore"))
1790 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1791 else if (pa_streq(rvalue, "mute"))
1792 e->switch_use = PA_ALSA_SWITCH_MUTE;
1793 else if (pa_streq(rvalue, "off"))
1794 e->switch_use = PA_ALSA_SWITCH_OFF;
1795 else if (pa_streq(rvalue, "on"))
1796 e->switch_use = PA_ALSA_SWITCH_ON;
1797 else if (pa_streq(rvalue, "select"))
1798 e->switch_use = PA_ALSA_SWITCH_SELECT;
1800 pa_log("[%s:%u] Switch invalid of '%s'", filename, line, section);
1807 static int element_parse_volume(
1808 const char *filename,
1810 const char *section,
1816 pa_alsa_path *p = userdata;
1821 if (!(e = element_get(p, section, TRUE))) {
1822 pa_log("[%s:%u] Volume makes no sense in '%s'", filename, line, section);
1826 if (pa_streq(rvalue, "ignore"))
1827 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1828 else if (pa_streq(rvalue, "merge"))
1829 e->volume_use = PA_ALSA_VOLUME_MERGE;
1830 else if (pa_streq(rvalue, "off"))
1831 e->volume_use = PA_ALSA_VOLUME_OFF;
1832 else if (pa_streq(rvalue, "zero"))
1833 e->volume_use = PA_ALSA_VOLUME_ZERO;
1837 if (pa_atou(rvalue, &constant) >= 0) {
1838 e->volume_use = PA_ALSA_VOLUME_CONSTANT;
1839 e->constant_volume = constant;
1841 pa_log("[%s:%u] Volume invalid of '%s'", filename, line, section);
1849 static int element_parse_enumeration(
1850 const char *filename,
1852 const char *section,
1858 pa_alsa_path *p = userdata;
1863 if (!(e = element_get(p, section, TRUE))) {
1864 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename, line, section);
1868 if (pa_streq(rvalue, "ignore"))
1869 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1870 else if (pa_streq(rvalue, "select"))
1871 e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
1873 pa_log("[%s:%u] Enumeration invalid of '%s'", filename, line, section);
1880 static int option_parse_priority(
1881 const char *filename,
1883 const char *section,
1889 pa_alsa_path *p = userdata;
1895 if (!(o = option_get(p, section))) {
1896 pa_log("[%s:%u] Priority makes no sense in '%s'", filename, line, section);
1900 if (pa_atou(rvalue, &prio) < 0) {
1901 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
1909 static int option_parse_name(
1910 const char *filename,
1912 const char *section,
1918 pa_alsa_path *p = userdata;
1923 if (!(o = option_get(p, section))) {
1924 pa_log("[%s:%u] Name makes no sense in '%s'", filename, line, section);
1929 o->name = pa_xstrdup(rvalue);
1934 static int element_parse_required(
1935 const char *filename,
1937 const char *section,
1943 pa_alsa_path *p = userdata;
1946 pa_alsa_required_t req;
1950 e = element_get(p, section, TRUE);
1951 o = option_get(p, section);
1953 pa_log("[%s:%u] Required makes no sense in '%s'", filename, line, section);
1957 if (pa_streq(rvalue, "ignore"))
1958 req = PA_ALSA_REQUIRED_IGNORE;
1959 else if (pa_streq(rvalue, "switch") && e)
1960 req = PA_ALSA_REQUIRED_SWITCH;
1961 else if (pa_streq(rvalue, "volume") && e)
1962 req = PA_ALSA_REQUIRED_VOLUME;
1963 else if (pa_streq(rvalue, "enumeration"))
1964 req = PA_ALSA_REQUIRED_ENUMERATION;
1965 else if (pa_streq(rvalue, "any"))
1966 req = PA_ALSA_REQUIRED_ANY;
1968 pa_log("[%s:%u] Required invalid of '%s'", filename, line, section);
1972 if (pa_streq(lvalue, "required-absent")) {
1974 e->required_absent = req;
1976 o->required_absent = req;
1978 else if (pa_streq(lvalue, "required-any")) {
1980 e->required_any = req;
1981 e->path->has_req_any = TRUE;
1984 o->required_any = req;
1985 o->element->path->has_req_any = TRUE;
1998 static int element_parse_direction(
1999 const char *filename,
2001 const char *section,
2007 pa_alsa_path *p = userdata;
2012 if (!(e = element_get(p, section, TRUE))) {
2013 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
2017 if (pa_streq(rvalue, "playback"))
2018 e->direction = PA_ALSA_DIRECTION_OUTPUT;
2019 else if (pa_streq(rvalue, "capture"))
2020 e->direction = PA_ALSA_DIRECTION_INPUT;
2022 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
2029 static int element_parse_direction_try_other(
2030 const char *filename,
2032 const char *section,
2038 pa_alsa_path *p = userdata;
2042 if (!(e = element_get(p, section, TRUE))) {
2043 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
2047 if ((yes = pa_parse_boolean(rvalue)) < 0) {
2048 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
2052 e->direction_try_other = !!yes;
2056 static int element_parse_volume_limit(
2057 const char *filename,
2059 const char *section,
2065 pa_alsa_path *p = userdata;
2069 if (!(e = element_get(p, section, TRUE))) {
2070 pa_log("[%s:%u] volume-limit makes no sense in '%s'", filename, line, section);
2074 if (pa_atol(rvalue, &volume_limit) < 0 || volume_limit < 0) {
2075 pa_log("[%s:%u] Invalid value for volume-limit", filename, line);
2079 e->volume_limit = volume_limit;
2083 static pa_channel_position_mask_t parse_mask(const char *m) {
2084 pa_channel_position_mask_t v;
2086 if (pa_streq(m, "all-left"))
2087 v = PA_CHANNEL_POSITION_MASK_LEFT;
2088 else if (pa_streq(m, "all-right"))
2089 v = PA_CHANNEL_POSITION_MASK_RIGHT;
2090 else if (pa_streq(m, "all-center"))
2091 v = PA_CHANNEL_POSITION_MASK_CENTER;
2092 else if (pa_streq(m, "all-front"))
2093 v = PA_CHANNEL_POSITION_MASK_FRONT;
2094 else if (pa_streq(m, "all-rear"))
2095 v = PA_CHANNEL_POSITION_MASK_REAR;
2096 else if (pa_streq(m, "all-side"))
2097 v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER;
2098 else if (pa_streq(m, "all-top"))
2099 v = PA_CHANNEL_POSITION_MASK_TOP;
2100 else if (pa_streq(m, "all-no-lfe"))
2101 v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE);
2102 else if (pa_streq(m, "all"))
2103 v = PA_CHANNEL_POSITION_MASK_ALL;
2105 pa_channel_position_t p;
2107 if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
2110 v = PA_CHANNEL_POSITION_MASK(p);
2116 static int element_parse_override_map(
2117 const char *filename,
2119 const char *section,
2125 pa_alsa_path *p = userdata;
2127 const char *state = NULL;
2131 if (!(e = element_get(p, section, TRUE))) {
2132 pa_log("[%s:%u] Override map makes no sense in '%s'", filename, line, section);
2136 while ((n = pa_split(rvalue, ",", &state))) {
2137 pa_channel_position_mask_t m;
2142 if ((m = parse_mask(n)) == 0) {
2143 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename, line, n, section);
2149 if (pa_streq(lvalue, "override-map.1"))
2150 e->masks[i++][0] = m;
2152 e->masks[i++][1] = m;
2154 /* Later on we might add override-map.3 and so on here ... */
2159 e->override_map = TRUE;
2164 static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
2165 snd_mixer_selem_id_t *sid;
2166 snd_mixer_elem_t *me;
2172 SELEM_INIT(sid, e->alsa_name);
2173 if (!(me = snd_mixer_find_selem(m, sid))) {
2174 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2178 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
2180 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
2181 r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
2183 r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
2186 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
2189 pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
2191 if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0)
2192 pa_log_warn("Failed to set enumeration of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
2198 int pa_alsa_setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
2205 PA_IDXSET_FOREACH(o, s->options, idx)
2206 element_set_option(o->element, m, o->alsa_idx);
2211 static int option_verify(pa_alsa_option *o) {
2212 static const struct description_map well_known_descriptions[] = {
2213 { "input", N_("Input") },
2214 { "input-docking", N_("Docking Station Input") },
2215 { "input-docking-microphone", N_("Docking Station Microphone") },
2216 { "input-docking-linein", N_("Docking Station Line-In") },
2217 { "input-linein", N_("Line-In") },
2218 { "input-microphone", N_("Microphone") },
2219 { "input-microphone-front", N_("Front Microphone") },
2220 { "input-microphone-rear", N_("Rear Microphone") },
2221 { "input-microphone-external", N_("External Microphone") },
2222 { "input-microphone-internal", N_("Internal Microphone") },
2223 { "input-radio", N_("Radio") },
2224 { "input-video", N_("Video") },
2225 { "input-agc-on", N_("Automatic Gain Control") },
2226 { "input-agc-off", N_("No Automatic Gain Control") },
2227 { "input-boost-on", N_("Boost") },
2228 { "input-boost-off", N_("No Boost") },
2229 { "output-amplifier-on", N_("Amplifier") },
2230 { "output-amplifier-off", N_("No Amplifier") },
2231 { "output-bass-boost-on", N_("Bass Boost") },
2232 { "output-bass-boost-off", N_("No Bass Boost") },
2233 { "output-speaker", N_("Speaker") },
2234 { "output-headphones", N_("Headphones") }
2240 pa_log("No name set for option %s", o->alsa_name);
2244 if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
2245 o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
2246 pa_log("Element %s of option %s not set for select.", o->element->alsa_name, o->name);
2250 if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
2251 !pa_streq(o->alsa_name, "on") &&
2252 !pa_streq(o->alsa_name, "off")) {
2253 pa_log("Switch %s options need be named off or on ", o->element->alsa_name);
2257 if (!o->description)
2258 o->description = pa_xstrdup(lookup_description(o->name,
2259 well_known_descriptions,
2260 PA_ELEMENTSOF(well_known_descriptions)));
2261 if (!o->description)
2262 o->description = pa_xstrdup(o->name);
2267 static int element_verify(pa_alsa_element *e) {
2272 // 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);
2273 if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
2274 (e->required_any != PA_ALSA_REQUIRED_IGNORE && e->required_any == e->required_absent) ||
2275 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required_any != PA_ALSA_REQUIRED_IGNORE) ||
2276 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
2277 pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name);
2281 if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
2282 pa_log("Element %s cannot set select for both switch and enumeration.", e->alsa_name);
2286 PA_LLIST_FOREACH(o, e->options)
2287 if (option_verify(o) < 0)
2293 static int path_verify(pa_alsa_path *p) {
2294 static const struct description_map well_known_descriptions[] = {
2295 { "analog-input", N_("Analog Input") },
2296 { "analog-input-microphone", N_("Analog Microphone") },
2297 { "analog-input-microphone-front", N_("Front Microphone") },
2298 { "analog-input-microphone-rear", N_("Rear Microphone") },
2299 { "analog-input-microphone-dock", N_("Docking Station Microphone") },
2300 { "analog-input-microphone-internal", N_("Internal Microphone") },
2301 { "analog-input-linein", N_("Analog Line-In") },
2302 { "analog-input-radio", N_("Analog Radio") },
2303 { "analog-input-video", N_("Analog Video") },
2304 { "analog-output", N_("Analog Output") },
2305 { "analog-output-headphones", N_("Analog Headphones") },
2306 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
2307 { "analog-output-mono", N_("Analog Mono Output") },
2308 { "analog-output-speaker", N_("Analog Speakers") },
2309 { "iec958-stereo-output", N_("Digital Output (IEC958)") },
2310 { "iec958-passthrough-output", N_("Digital Passthrough (IEC958)") }
2317 PA_LLIST_FOREACH(e, p->elements)
2318 if (element_verify(e) < 0)
2321 if (!p->description)
2322 p->description = pa_xstrdup(lookup_description(p->name,
2323 well_known_descriptions,
2324 PA_ELEMENTSOF(well_known_descriptions)));
2326 if (!p->description)
2327 p->description = pa_xstrdup(p->name);
2332 pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction) {
2338 pa_config_item items[] = {
2340 { "priority", pa_config_parse_unsigned, NULL, "General" },
2341 { "description", pa_config_parse_string, NULL, "General" },
2342 { "name", pa_config_parse_string, NULL, "General" },
2345 { "priority", option_parse_priority, NULL, NULL },
2346 { "name", option_parse_name, NULL, NULL },
2349 { "switch", element_parse_switch, NULL, NULL },
2350 { "volume", element_parse_volume, NULL, NULL },
2351 { "enumeration", element_parse_enumeration, NULL, NULL },
2352 { "override-map.1", element_parse_override_map, NULL, NULL },
2353 { "override-map.2", element_parse_override_map, NULL, NULL },
2354 /* ... later on we might add override-map.3 and so on here ... */
2355 { "required", element_parse_required, NULL, NULL },
2356 { "required-any", element_parse_required, NULL, NULL },
2357 { "required-absent", element_parse_required, NULL, NULL },
2358 { "direction", element_parse_direction, NULL, NULL },
2359 { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
2360 { "volume-limit", element_parse_volume_limit, NULL, NULL },
2361 { NULL, NULL, NULL, NULL }
2366 p = pa_xnew0(pa_alsa_path, 1);
2367 n = pa_path_get_filename(fname);
2368 p->name = pa_xstrndup(n, strcspn(n, "."));
2369 p->direction = direction;
2371 items[0].data = &p->priority;
2372 items[1].data = &p->description;
2373 items[2].data = &p->name;
2375 fn = pa_maybe_prefix_path(fname,
2376 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/paths/" :
2379 r = pa_config_parse(fn, NULL, items, p);
2385 if (path_verify(p) < 0)
2391 pa_alsa_path_free(p);
2395 pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction) {
2401 p = pa_xnew0(pa_alsa_path, 1);
2402 p->name = pa_xstrdup(element);
2403 p->direction = direction;
2405 e = pa_xnew0(pa_alsa_element, 1);
2407 e->alsa_name = pa_xstrdup(element);
2408 e->direction = direction;
2409 e->volume_limit = -1;
2411 e->switch_use = PA_ALSA_SWITCH_MUTE;
2412 e->volume_use = PA_ALSA_VOLUME_MERGE;
2414 PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
2415 p->last_element = e;
2419 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
2420 pa_alsa_option *o, *n;
2424 for (o = e->options; o; o = n) {
2427 if (o->alsa_idx < 0) {
2428 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
2434 e->switch_use != PA_ALSA_SWITCH_IGNORE ||
2435 e->volume_use != PA_ALSA_VOLUME_IGNORE ||
2436 e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
2439 static void path_drop_unsupported(pa_alsa_path *p) {
2440 pa_alsa_element *e, *n;
2444 for (e = p->elements; e; e = n) {
2447 if (!element_drop_unsupported(e)) {
2448 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
2454 static void path_make_options_unique(pa_alsa_path *p) {
2456 pa_alsa_option *o, *u;
2458 PA_LLIST_FOREACH(e, p->elements) {
2459 PA_LLIST_FOREACH(o, e->options) {
2463 for (u = o->next; u; u = u->next)
2464 if (pa_streq(u->name, o->name))
2470 m = pa_xstrdup(o->name);
2472 /* OK, this name is not unique, hence let's rename */
2473 for (i = 1, u = o; u; u = u->next) {
2476 if (!pa_streq(u->name, m))
2479 nn = pa_sprintf_malloc("%s-%u", m, i);
2483 nd = pa_sprintf_malloc("%s %u", u->description, i);
2484 pa_xfree(u->description);
2485 u->description = nd;
2495 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
2498 for (; e; e = e->next)
2499 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
2500 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
2506 for (o = e->options; o; o = o->next) {
2510 s = pa_xnewdup(pa_alsa_setting, template, 1);
2511 s->options = pa_idxset_copy(template->options);
2512 s->name = pa_sprintf_malloc(_("%s+%s"), template->name, o->name);
2514 (template->description[0] && o->description[0])
2515 ? pa_sprintf_malloc(_("%s / %s"), template->description, o->description)
2516 : (template->description[0]
2517 ? pa_xstrdup(template->description)
2518 : pa_xstrdup(o->description));
2520 s->priority = PA_MAX(template->priority, o->priority);
2522 s = pa_xnew0(pa_alsa_setting, 1);
2523 s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2524 s->name = pa_xstrdup(o->name);
2525 s->description = pa_xstrdup(o->description);
2526 s->priority = o->priority;
2529 pa_idxset_put(s->options, o, NULL);
2531 if (element_create_settings(e->next, s))
2532 /* This is not a leaf, so let's get rid of it */
2535 /* This is a leaf, so let's add it */
2536 PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2538 e->path->last_setting = s;
2545 static void path_create_settings(pa_alsa_path *p) {
2548 element_create_settings(p->elements, NULL);
2551 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) {
2553 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2554 pa_channel_position_t t;
2555 pa_channel_position_mask_t path_volume_channels = 0;
2566 pa_log_debug("Probing path '%s'", p->name);
2568 PA_LLIST_FOREACH(e, p->elements) {
2569 if (element_probe(e, m) < 0) {
2570 p->supported = FALSE;
2571 pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2574 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);
2579 if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2581 if (!p->has_volume) {
2582 p->min_volume = e->min_volume;
2583 p->max_volume = e->max_volume;
2587 if (!p->has_volume) {
2588 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2589 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2590 min_dB[t] = e->min_dB;
2591 max_dB[t] = e->max_dB;
2592 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2599 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2600 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2601 min_dB[t] += e->min_dB;
2602 max_dB[t] += e->max_dB;
2603 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2606 /* Hmm, there's another element before us
2607 * which cannot do dB volumes, so we we need
2608 * to 'neutralize' this slider */
2609 e->volume_use = PA_ALSA_VOLUME_ZERO;
2610 pa_log_info("Zeroing volume of '%s' on path '%s'", e->alsa_name, p->name);
2613 } else if (p->has_volume) {
2614 /* We can't use this volume, so let's ignore it */
2615 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2616 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e->alsa_name, p->name);
2618 p->has_volume = TRUE;
2621 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2625 if (p->has_req_any && !p->req_any_present) {
2626 p->supported = FALSE;
2627 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name);
2631 path_drop_unsupported(p);
2632 path_make_options_unique(p);
2633 path_create_settings(p);
2635 p->supported = TRUE;
2638 p->min_dB = INFINITY;
2639 p->max_dB = -INFINITY;
2641 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2642 if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) {
2643 if (p->min_dB > min_dB[t])
2644 p->min_dB = min_dB[t];
2646 if (p->max_dB < max_dB[t])
2647 p->max_dB = max_dB[t];
2654 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2657 pa_log_debug("Setting %s (%s) priority=%u",
2659 pa_strnull(s->description),
2663 void pa_alsa_option_dump(pa_alsa_option *o) {
2666 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2668 pa_strnull(o->name),
2669 pa_strnull(o->description),
2674 void pa_alsa_element_dump(pa_alsa_element *e) {
2678 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",
2688 (long long unsigned) e->merged_mask,
2690 pa_yes_no(e->override_map));
2692 PA_LLIST_FOREACH(o, e->options)
2693 pa_alsa_option_dump(o);
2696 void pa_alsa_path_dump(pa_alsa_path *p) {
2701 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2702 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2704 pa_strnull(p->description),
2707 pa_yes_no(p->probed),
2708 pa_yes_no(p->supported),
2709 pa_yes_no(p->has_mute),
2710 pa_yes_no(p->has_volume),
2711 pa_yes_no(p->has_dB),
2712 p->min_volume, p->max_volume,
2713 p->min_dB, p->max_dB);
2715 PA_LLIST_FOREACH(e, p->elements)
2716 pa_alsa_element_dump(e);
2718 PA_LLIST_FOREACH(s, p->settings)
2719 pa_alsa_setting_dump(s);
2722 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2723 snd_mixer_selem_id_t *sid;
2724 snd_mixer_elem_t *me;
2730 SELEM_INIT(sid, e->alsa_name);
2731 if (!(me = snd_mixer_find_selem(m, sid))) {
2732 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2736 snd_mixer_elem_set_callback(me, cb);
2737 snd_mixer_elem_set_callback_private(me, userdata);
2740 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2747 PA_LLIST_FOREACH(e, p->elements)
2748 element_set_callback(e, m, cb, userdata);
2751 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2758 PA_LLIST_FOREACH(p, ps->paths)
2759 pa_alsa_path_set_callback(p, m, cb, userdata);
2762 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction) {
2763 pa_alsa_path_set *ps;
2764 char **pn = NULL, **en = NULL, **ie;
2765 pa_alsa_decibel_fix *db_fix;
2769 pa_assert(m->profile_set);
2770 pa_assert(m->profile_set->decibel_fixes);
2771 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2773 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2776 ps = pa_xnew0(pa_alsa_path_set, 1);
2777 ps->direction = direction;
2779 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2780 pn = m->output_path_names;
2781 else if (direction == PA_ALSA_DIRECTION_INPUT)
2782 pn = m->input_path_names;
2787 for (in = pn; *in; in++) {
2789 pa_bool_t duplicate = FALSE;
2792 for (kn = pn; kn < in; kn++)
2793 if (pa_streq(*kn, *in)) {
2801 fn = pa_sprintf_malloc("%s.conf", *in);
2803 if ((p = pa_alsa_path_new(fn, direction))) {
2805 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2815 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2816 en = m->output_element;
2817 else if (direction == PA_ALSA_DIRECTION_INPUT)
2818 en = m->input_element;
2821 pa_alsa_path_set_free(ps);
2825 for (ie = en; *ie; ie++) {
2829 p = pa_alsa_path_synthesize(*ie, direction);
2832 /* Mark all other passed elements for require-absent */
2833 for (je = en; *je; je++) {
2839 e = pa_xnew0(pa_alsa_element, 1);
2841 e->alsa_name = pa_xstrdup(*je);
2842 e->direction = direction;
2843 e->required_absent = PA_ALSA_REQUIRED_ANY;
2844 e->volume_limit = -1;
2846 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2847 p->last_element = e;
2850 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2855 /* Assign decibel fixes to elements. */
2856 PA_HASHMAP_FOREACH(db_fix, m->profile_set->decibel_fixes, state) {
2859 PA_LLIST_FOREACH(p, ps->paths) {
2862 PA_LLIST_FOREACH(e, p->elements) {
2863 if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_name)) {
2864 /* The profile set that contains the dB fix may be freed
2865 * before the element, so we have to copy the dB fix
2867 e->db_fix = pa_xnewdup(pa_alsa_decibel_fix, db_fix, 1);
2868 e->db_fix->profile_set = NULL;
2869 e->db_fix->name = pa_xstrdup(db_fix->name);
2870 e->db_fix->db_values = pa_xmemdup(db_fix->db_values, (db_fix->max_step - db_fix->min_step + 1) * sizeof(long));
2879 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2883 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2886 pa_yes_no(ps->probed));
2888 PA_LLIST_FOREACH(p, ps->paths)
2889 pa_alsa_path_dump(p);
2893 static pa_bool_t options_have_option(pa_alsa_option *options, const char *alsa_name) {
2897 pa_assert(alsa_name);
2899 PA_LLIST_FOREACH(o, options) {
2900 if (pa_streq(o->alsa_name, alsa_name))
2906 static pa_bool_t enumeration_is_subset(pa_alsa_option *a_options, pa_alsa_option *b_options) {
2907 pa_alsa_option *oa, *ob;
2909 if (!a_options) return TRUE;
2910 if (!b_options) return FALSE;
2912 /* If there is an option A offers that B does not, then A is not a subset of B. */
2913 PA_LLIST_FOREACH(oa, a_options) {
2914 pa_bool_t found = FALSE;
2915 PA_LLIST_FOREACH(ob, b_options) {
2916 if (pa_streq(oa->alsa_name, ob->alsa_name)) {
2928 * Compares two elements to see if a is a subset of b
2930 static pa_bool_t element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_t *m) {
2936 * Every state is a subset of itself (with caveats for volume_limits and options)
2937 * IGNORE is a subset of every other state */
2939 /* Check the volume_use */
2940 if (a->volume_use != PA_ALSA_VOLUME_IGNORE) {
2942 /* "Constant" is subset of "Constant" only when their constant values are equal */
2943 if (a->volume_use == PA_ALSA_VOLUME_CONSTANT && b->volume_use == PA_ALSA_VOLUME_CONSTANT && a->constant_volume != b->constant_volume)
2946 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
2947 if (a->volume_use != b->volume_use && b->volume_use != PA_ALSA_VOLUME_MERGE)
2950 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
2951 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
2952 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
2953 if (b->volume_use == PA_ALSA_VOLUME_MERGE && b->volume_limit >= 0) {
2956 if (a->volume_use == PA_ALSA_VOLUME_CONSTANT)
2957 a_limit = a->constant_volume;
2958 else if (a->volume_use == PA_ALSA_VOLUME_ZERO) {
2962 int rounding = (a->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1);
2963 a_limit = decibel_fix_get_step(a->db_fix, &dB, rounding);
2965 snd_mixer_selem_id_t *sid;
2966 snd_mixer_elem_t *me;
2968 SELEM_INIT(sid, a->alsa_name);
2969 if (!(me = snd_mixer_find_selem(m, sid))) {
2970 pa_log_warn("Element %s seems to have disappeared.", a->alsa_name);
2974 if (a->direction == PA_ALSA_DIRECTION_OUTPUT) {
2975 if (snd_mixer_selem_ask_playback_dB_vol(me, dB, +1, &a_limit) < 0)
2978 if (snd_mixer_selem_ask_capture_dB_vol(me, dB, -1, &a_limit) < 0)
2982 } else if (a->volume_use == PA_ALSA_VOLUME_OFF)
2983 a_limit = a->min_volume;
2984 else if (a->volume_use == PA_ALSA_VOLUME_MERGE)
2985 a_limit = a->volume_limit;
2987 /* This should never be reached */
2990 if (a_limit > b->volume_limit)
2995 if (a->switch_use != PA_ALSA_SWITCH_IGNORE) {
2996 /* "On" is a subset of "Mute".
2997 * "Off" is a subset of "Mute".
2998 * "On" is a subset of "Select", if there is an "Option:On" in B.
2999 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3000 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3002 if (a->switch_use != b->switch_use) {
3004 if (a->switch_use == PA_ALSA_SWITCH_SELECT || a->switch_use == PA_ALSA_SWITCH_MUTE
3005 || b->switch_use == PA_ALSA_SWITCH_OFF || b->switch_use == PA_ALSA_SWITCH_ON)
3008 if (b->switch_use == PA_ALSA_SWITCH_SELECT) {
3009 if (a->switch_use == PA_ALSA_SWITCH_ON) {
3010 if (!options_have_option(b->options, "on"))
3012 } else if (a->switch_use == PA_ALSA_SWITCH_OFF) {
3013 if (!options_have_option(b->options, "off"))
3017 } else if (a->switch_use == PA_ALSA_SWITCH_SELECT) {
3018 if (!enumeration_is_subset(a->options, b->options))
3023 if (a->enumeration_use != PA_ALSA_ENUMERATION_IGNORE) {
3024 if (b->enumeration_use == PA_ALSA_ENUMERATION_IGNORE)
3026 if (!enumeration_is_subset(a->options, b->options))
3033 static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
3034 pa_alsa_path *p, *np;
3039 /* If we only have one path, then don't bother */
3040 if (!ps->paths || !ps->paths->next)
3043 for (p = ps->paths; p; p = np) {
3047 PA_LLIST_FOREACH(p2, ps->paths) {
3048 pa_alsa_element *ea, *eb;
3049 pa_bool_t is_subset = TRUE;
3054 /* Compare the elements of each set... */
3055 pa_assert_se(ea = p->elements);
3056 pa_assert_se(eb = p2->elements);
3059 if (pa_streq(ea->alsa_name, eb->alsa_name)) {
3060 if (element_is_subset(ea, eb, m)) {
3063 if ((ea && !eb) || (!ea && eb))
3065 else if (!ea && !eb)
3075 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p->name, p2->name);
3076 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
3077 pa_alsa_path_free(p);
3084 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
3085 pa_alsa_path *p, *q;
3087 PA_LLIST_FOREACH(p, ps->paths) {
3091 for (q = p->next; q; q = q->next)
3092 if (pa_streq(q->name, p->name))
3098 m = pa_xstrdup(p->name);
3100 /* OK, this name is not unique, hence let's rename */
3101 for (i = 1, q = p; q; q = q->next) {
3104 if (!pa_streq(q->name, m))
3107 nn = pa_sprintf_malloc("%s-%u", m, i);
3111 nd = pa_sprintf_malloc("%s %u", q->description, i);
3112 pa_xfree(q->description);
3113 q->description = nd;
3122 void pa_alsa_path_set_probe(pa_alsa_path_set *ps, snd_mixer_t *m, pa_bool_t ignore_dB) {
3123 pa_alsa_path *p, *n;
3130 for (p = ps->paths; p; p = n) {
3133 if (pa_alsa_path_probe(p, m, ignore_dB) < 0) {
3134 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
3135 pa_alsa_path_free(p);
3139 pa_log_debug("Found mixer paths (before tidying):");
3140 pa_alsa_path_set_dump(ps);
3142 path_set_condense(ps, m);
3143 path_set_make_paths_unique(ps);
3146 pa_log_debug("Available mixer paths (after tidying):");
3147 pa_alsa_path_set_dump(ps);
3150 static void mapping_free(pa_alsa_mapping *m) {
3154 pa_xfree(m->description);
3156 pa_xstrfreev(m->device_strings);
3157 pa_xstrfreev(m->input_path_names);
3158 pa_xstrfreev(m->output_path_names);
3159 pa_xstrfreev(m->input_element);
3160 pa_xstrfreev(m->output_element);
3162 pa_assert(!m->input_pcm);
3163 pa_assert(!m->output_pcm);
3168 static void profile_free(pa_alsa_profile *p) {
3172 pa_xfree(p->description);
3174 pa_xstrfreev(p->input_mapping_names);
3175 pa_xstrfreev(p->output_mapping_names);
3177 if (p->input_mappings)
3178 pa_idxset_free(p->input_mappings, NULL, NULL);
3180 if (p->output_mappings)
3181 pa_idxset_free(p->output_mappings, NULL, NULL);
3186 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
3192 while ((p = pa_hashmap_steal_first(ps->profiles)))
3195 pa_hashmap_free(ps->profiles, NULL, NULL);
3201 while ((m = pa_hashmap_steal_first(ps->mappings)))
3204 pa_hashmap_free(ps->mappings, NULL, NULL);
3207 if (ps->decibel_fixes) {
3208 pa_alsa_decibel_fix *db_fix;
3210 while ((db_fix = pa_hashmap_steal_first(ps->decibel_fixes)))
3211 decibel_fix_free(db_fix);
3213 pa_hashmap_free(ps->decibel_fixes, NULL, NULL);
3219 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
3222 if (!pa_startswith(name, "Mapping "))
3227 if ((m = pa_hashmap_get(ps->mappings, name)))
3230 m = pa_xnew0(pa_alsa_mapping, 1);
3231 m->profile_set = ps;
3232 m->name = pa_xstrdup(name);
3233 pa_channel_map_init(&m->channel_map);
3235 pa_hashmap_put(ps->mappings, m->name, m);
3240 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
3243 if (!pa_startswith(name, "Profile "))
3248 if ((p = pa_hashmap_get(ps->profiles, name)))
3251 p = pa_xnew0(pa_alsa_profile, 1);
3252 p->profile_set = ps;
3253 p->name = pa_xstrdup(name);
3255 pa_hashmap_put(ps->profiles, p->name, p);
3260 static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) {
3261 pa_alsa_decibel_fix *db_fix;
3263 if (!pa_startswith(name, "DecibelFix "))
3268 if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name)))
3271 db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
3272 db_fix->profile_set = ps;
3273 db_fix->name = pa_xstrdup(name);
3275 pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix);
3280 static int mapping_parse_device_strings(
3281 const char *filename,
3283 const char *section,
3289 pa_alsa_profile_set *ps = userdata;
3294 if (!(m = mapping_get(ps, section))) {
3295 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3299 pa_xstrfreev(m->device_strings);
3300 if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
3301 pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
3308 static int mapping_parse_channel_map(
3309 const char *filename,
3311 const char *section,
3317 pa_alsa_profile_set *ps = userdata;
3322 if (!(m = mapping_get(ps, section))) {
3323 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3327 if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
3328 pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
3335 static int mapping_parse_paths(
3336 const char *filename,
3338 const char *section,
3344 pa_alsa_profile_set *ps = userdata;
3349 if (!(m = mapping_get(ps, section))) {
3350 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3354 if (pa_streq(lvalue, "paths-input")) {
3355 pa_xstrfreev(m->input_path_names);
3356 m->input_path_names = pa_split_spaces_strv(rvalue);
3358 pa_xstrfreev(m->output_path_names);
3359 m->output_path_names = pa_split_spaces_strv(rvalue);
3365 static int mapping_parse_element(
3366 const char *filename,
3368 const char *section,
3374 pa_alsa_profile_set *ps = userdata;
3379 if (!(m = mapping_get(ps, section))) {
3380 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3384 if (pa_streq(lvalue, "element-input")) {
3385 pa_xstrfreev(m->input_element);
3386 m->input_element = pa_split_spaces_strv(rvalue);
3388 pa_xstrfreev(m->output_element);
3389 m->output_element = pa_split_spaces_strv(rvalue);
3395 static int mapping_parse_direction(
3396 const char *filename,
3398 const char *section,
3404 pa_alsa_profile_set *ps = userdata;
3409 if (!(m = mapping_get(ps, section))) {
3410 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3414 if (pa_streq(rvalue, "input"))
3415 m->direction = PA_ALSA_DIRECTION_INPUT;
3416 else if (pa_streq(rvalue, "output"))
3417 m->direction = PA_ALSA_DIRECTION_OUTPUT;
3418 else if (pa_streq(rvalue, "any"))
3419 m->direction = PA_ALSA_DIRECTION_ANY;
3421 pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
3428 static int mapping_parse_description(
3429 const char *filename,
3431 const char *section,
3437 pa_alsa_profile_set *ps = userdata;
3443 if ((m = mapping_get(ps, section))) {
3444 pa_xfree(m->description);
3445 m->description = pa_xstrdup(rvalue);
3446 } else if ((p = profile_get(ps, section))) {
3447 pa_xfree(p->description);
3448 p->description = pa_xstrdup(rvalue);
3450 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3457 static int mapping_parse_priority(
3458 const char *filename,
3460 const char *section,
3466 pa_alsa_profile_set *ps = userdata;
3473 if (pa_atou(rvalue, &prio) < 0) {
3474 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
3478 if ((m = mapping_get(ps, section)))
3480 else if ((p = profile_get(ps, section)))
3483 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3490 static int profile_parse_mappings(
3491 const char *filename,
3493 const char *section,
3499 pa_alsa_profile_set *ps = userdata;
3504 if (!(p = profile_get(ps, section))) {
3505 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3509 if (pa_streq(lvalue, "input-mappings")) {
3510 pa_xstrfreev(p->input_mapping_names);
3511 p->input_mapping_names = pa_split_spaces_strv(rvalue);
3513 pa_xstrfreev(p->output_mapping_names);
3514 p->output_mapping_names = pa_split_spaces_strv(rvalue);
3520 static int profile_parse_skip_probe(
3521 const char *filename,
3523 const char *section,
3529 pa_alsa_profile_set *ps = userdata;
3535 if (!(p = profile_get(ps, section))) {
3536 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3540 if ((b = pa_parse_boolean(rvalue)) < 0) {
3541 pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
3550 static int decibel_fix_parse_db_values(
3551 const char *filename,
3553 const char *section,
3559 pa_alsa_profile_set *ps = userdata;
3560 pa_alsa_decibel_fix *db_fix;
3564 unsigned n = 8; /* Current size of the db_values table. */
3565 unsigned min_step = 0;
3566 unsigned max_step = 0;
3567 unsigned i = 0; /* Index to the items table. */
3568 unsigned prev_step = 0;
3571 pa_assert(filename);
3577 if (!(db_fix = decibel_fix_get(ps, section))) {
3578 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3582 if (!(items = pa_split_spaces_strv(rvalue))) {
3583 pa_log("[%s:%u] Value missing", pa_strnull(filename), line);
3587 db_values = pa_xnew(long, n);
3589 while ((item = items[i++])) {
3590 char *s = item; /* Step value string. */
3591 char *d = item; /* dB value string. */
3595 /* Move d forward until it points to a colon or to the end of the item. */
3596 for (; *d && *d != ':'; ++d);
3599 /* item started with colon. */
3600 pa_log("[%s:%u] No step value found in %s", filename, line, item);
3604 if (!*d || !*(d + 1)) {
3605 /* No colon found, or it was the last character in item. */
3606 pa_log("[%s:%u] No dB value found in %s", filename, line, item);
3610 /* pa_atou() needs a null-terminating string. Let's replace the colon
3611 * with a zero byte. */
3614 if (pa_atou(s, &step) < 0) {
3615 pa_log("[%s:%u] Invalid step value: %s", filename, line, s);
3619 if (pa_atod(d, &db) < 0) {
3620 pa_log("[%s:%u] Invalid dB value: %s", filename, line, d);
3624 if (step <= prev_step && i != 1) {
3625 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename, line, step, prev_step);
3629 if (db < prev_db && i != 1) {
3630 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename, line, db, prev_db);
3636 db_values[0] = (long) (db * 100.0);
3640 /* Interpolate linearly. */
3641 double db_increment = (db - prev_db) / (step - prev_step);
3643 for (; prev_step < step; ++prev_step, prev_db += db_increment) {
3645 /* Reallocate the db_values table if it's about to overflow. */
3646 if (prev_step + 1 - min_step == n) {
3648 db_values = pa_xrenew(long, db_values, n);
3651 db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
3658 db_fix->min_step = min_step;
3659 db_fix->max_step = max_step;
3660 pa_xfree(db_fix->db_values);
3661 db_fix->db_values = db_values;
3663 pa_xstrfreev(items);
3668 pa_xstrfreev(items);
3669 pa_xfree(db_values);
3674 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
3676 static const struct description_map well_known_descriptions[] = {
3677 { "analog-mono", N_("Analog Mono") },
3678 { "analog-stereo", N_("Analog Stereo") },
3679 { "analog-surround-21", N_("Analog Surround 2.1") },
3680 { "analog-surround-30", N_("Analog Surround 3.0") },
3681 { "analog-surround-31", N_("Analog Surround 3.1") },
3682 { "analog-surround-40", N_("Analog Surround 4.0") },
3683 { "analog-surround-41", N_("Analog Surround 4.1") },
3684 { "analog-surround-50", N_("Analog Surround 5.0") },
3685 { "analog-surround-51", N_("Analog Surround 5.1") },
3686 { "analog-surround-61", N_("Analog Surround 6.0") },
3687 { "analog-surround-61", N_("Analog Surround 6.1") },
3688 { "analog-surround-70", N_("Analog Surround 7.0") },
3689 { "analog-surround-71", N_("Analog Surround 7.1") },
3690 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3691 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3692 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3693 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3694 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3699 if (!pa_channel_map_valid(&m->channel_map)) {
3700 pa_log("Mapping %s is missing channel map.", m->name);
3704 if (!m->device_strings) {
3705 pa_log("Mapping %s is missing device strings.", m->name);
3709 if ((m->input_path_names && m->input_element) ||
3710 (m->output_path_names && m->output_element)) {
3711 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
3715 if (!m->description)
3716 m->description = pa_xstrdup(lookup_description(m->name,
3717 well_known_descriptions,
3718 PA_ELEMENTSOF(well_known_descriptions)));
3720 if (!m->description)
3721 m->description = pa_xstrdup(m->name);
3724 if (pa_channel_map_equal(&m->channel_map, bonus))
3726 else if (m->channel_map.channels == bonus->channels)
3733 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
3734 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
3738 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3740 pa_strnull(m->description),
3742 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
3743 pa_yes_no(m->supported),
3747 static void profile_set_add_auto_pair(
3748 pa_alsa_profile_set *ps,
3749 pa_alsa_mapping *m, /* output */
3750 pa_alsa_mapping *n /* input */) {
3758 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
3761 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
3765 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
3767 name = pa_sprintf_malloc("output:%s", m->name);
3769 name = pa_sprintf_malloc("input:%s", n->name);
3771 if (pa_hashmap_get(ps->profiles, name)) {
3776 p = pa_xnew0(pa_alsa_profile, 1);
3777 p->profile_set = ps;
3781 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3782 pa_idxset_put(p->output_mappings, m, NULL);
3783 p->priority += m->priority * 100;
3787 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3788 pa_idxset_put(p->input_mappings, n, NULL);
3789 p->priority += n->priority;
3792 pa_hashmap_put(ps->profiles, p->name, p);
3795 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
3796 pa_alsa_mapping *m, *n;
3797 void *m_state, *n_state;
3801 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
3802 profile_set_add_auto_pair(ps, m, NULL);
3804 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3805 profile_set_add_auto_pair(ps, m, n);
3808 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3809 profile_set_add_auto_pair(ps, NULL, n);
3812 static int profile_verify(pa_alsa_profile *p) {
3814 static const struct description_map well_known_descriptions[] = {
3815 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3816 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3817 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3818 { "off", N_("Off") }
3823 /* Replace the output mapping names by the actual mappings */
3824 if (p->output_mapping_names) {
3827 pa_assert(!p->output_mappings);
3828 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3830 for (name = p->output_mapping_names; *name; name++) {
3833 pa_bool_t duplicate = FALSE;
3835 for (in = name + 1; *in; in++)
3836 if (pa_streq(*name, *in)) {
3844 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
3845 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
3849 pa_idxset_put(p->output_mappings, m, NULL);
3855 pa_xstrfreev(p->output_mapping_names);
3856 p->output_mapping_names = NULL;
3859 /* Replace the input mapping names by the actual mappings */
3860 if (p->input_mapping_names) {
3863 pa_assert(!p->input_mappings);
3864 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3866 for (name = p->input_mapping_names; *name; name++) {
3869 pa_bool_t duplicate = FALSE;
3871 for (in = name + 1; *in; in++)
3872 if (pa_streq(*name, *in)) {
3880 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
3881 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
3885 pa_idxset_put(p->input_mappings, m, NULL);
3891 pa_xstrfreev(p->input_mapping_names);
3892 p->input_mapping_names = NULL;
3895 if (!p->input_mappings && !p->output_mappings) {
3896 pa_log("Profile '%s' lacks mappings.", p->name);
3900 if (!p->description)
3901 p->description = pa_xstrdup(lookup_description(p->name,
3902 well_known_descriptions,
3903 PA_ELEMENTSOF(well_known_descriptions)));
3905 if (!p->description) {
3910 sb = pa_strbuf_new();
3912 if (p->output_mappings)
3913 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3914 if (!pa_strbuf_isempty(sb))
3915 pa_strbuf_puts(sb, " + ");
3917 pa_strbuf_printf(sb, _("%s Output"), m->description);
3920 if (p->input_mappings)
3921 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3922 if (!pa_strbuf_isempty(sb))
3923 pa_strbuf_puts(sb, " + ");
3925 pa_strbuf_printf(sb, _("%s Input"), m->description);
3928 p->description = pa_strbuf_tostring_free(sb);
3934 void pa_alsa_profile_dump(pa_alsa_profile *p) {
3939 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3941 pa_strnull(p->description),
3943 pa_yes_no(p->supported),
3944 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
3945 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
3947 if (p->input_mappings)
3948 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
3949 pa_log_debug("Input %s", m->name);
3951 if (p->output_mappings)
3952 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
3953 pa_log_debug("Output %s", m->name);
3956 static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
3959 /* Check that the dB mapping has been configured. Since "db-values" is
3960 * currently the only option in the DecibelFix section, and decibel fix
3961 * objects don't get created if a DecibelFix section is empty, this is
3962 * actually a redundant check. Having this may prevent future bugs,
3964 if (!db_fix->db_values) {
3965 pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
3972 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
3973 char *db_values = NULL;
3977 if (db_fix->db_values) {
3979 unsigned long i, nsteps;
3981 pa_assert(db_fix->min_step <= db_fix->max_step);
3982 nsteps = db_fix->max_step - db_fix->min_step + 1;
3984 buf = pa_strbuf_new();
3985 for (i = 0; i < nsteps; ++i)
3986 pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
3988 db_values = pa_strbuf_tostring_free(buf);
3991 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
3992 db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
3994 pa_xfree(db_values);
3997 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
3998 pa_alsa_profile_set *ps;
4001 pa_alsa_decibel_fix *db_fix;
4006 static pa_config_item items[] = {
4008 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
4011 { "device-strings", mapping_parse_device_strings, NULL, NULL },
4012 { "channel-map", mapping_parse_channel_map, NULL, NULL },
4013 { "paths-input", mapping_parse_paths, NULL, NULL },
4014 { "paths-output", mapping_parse_paths, NULL, NULL },
4015 { "element-input", mapping_parse_element, NULL, NULL },
4016 { "element-output", mapping_parse_element, NULL, NULL },
4017 { "direction", mapping_parse_direction, NULL, NULL },
4019 /* Shared by [Mapping ...] and [Profile ...] */
4020 { "description", mapping_parse_description, NULL, NULL },
4021 { "priority", mapping_parse_priority, NULL, NULL },
4024 { "input-mappings", profile_parse_mappings, NULL, NULL },
4025 { "output-mappings", profile_parse_mappings, NULL, NULL },
4026 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
4028 /* [DecibelFix ...] */
4029 { "db-values", decibel_fix_parse_db_values, NULL, NULL },
4030 { NULL, NULL, NULL, NULL }
4033 ps = pa_xnew0(pa_alsa_profile_set, 1);
4034 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4035 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4036 ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4038 items[0].data = &ps->auto_profiles;
4041 fname = "default.conf";
4043 fn = pa_maybe_prefix_path(fname,
4044 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
4045 PA_ALSA_PROFILE_SETS_DIR);
4047 r = pa_config_parse(fn, NULL, items, ps);
4053 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4054 if (mapping_verify(m, bonus) < 0)
4057 if (ps->auto_profiles)
4058 profile_set_add_auto(ps);
4060 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4061 if (profile_verify(p) < 0)
4064 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4065 if (decibel_fix_verify(db_fix) < 0)
4071 pa_alsa_profile_set_free(ps);
4075 void pa_alsa_profile_set_probe(
4076 pa_alsa_profile_set *ps,
4078 const pa_sample_spec *ss,
4079 unsigned default_n_fragments,
4080 unsigned default_fragment_size_msec) {
4083 pa_alsa_profile *p, *last = NULL;
4093 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
4094 pa_sample_spec try_ss;
4095 pa_channel_map try_map;
4096 snd_pcm_uframes_t try_period_size, try_buffer_size;
4099 /* Is this already marked that it is supported? (i.e. from the config file) */
4103 pa_log_debug("Looking at profile %s", p->name);
4105 /* Close PCMs from the last iteration we don't need anymore */
4106 if (last && last->output_mappings)
4107 PA_IDXSET_FOREACH(m, last->output_mappings, idx) {
4112 if (last->supported)
4115 if (!p->output_mappings || !pa_idxset_get_by_data(p->output_mappings, m, NULL)) {
4116 snd_pcm_close(m->output_pcm);
4117 m->output_pcm = NULL;
4121 if (last && last->input_mappings)
4122 PA_IDXSET_FOREACH(m, last->input_mappings, idx) {
4127 if (last->supported)
4130 if (!p->input_mappings || !pa_idxset_get_by_data(p->input_mappings, m, NULL)) {
4131 snd_pcm_close(m->input_pcm);
4132 m->input_pcm = NULL;
4136 p->supported = TRUE;
4138 /* Check if we can open all new ones */
4139 if (p->output_mappings)
4140 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4145 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
4146 try_map = m->channel_map;
4148 try_ss.channels = try_map.channels;
4151 pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
4152 pa_frame_size(&try_ss);
4153 try_buffer_size = default_n_fragments * try_period_size;
4155 if (!(m ->output_pcm = pa_alsa_open_by_template(
4160 SND_PCM_STREAM_PLAYBACK,
4161 &try_period_size, &try_buffer_size, 0, NULL, NULL,
4163 p->supported = FALSE;
4168 if (p->input_mappings && p->supported)
4169 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4174 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
4175 try_map = m->channel_map;
4177 try_ss.channels = try_map.channels;
4180 pa_usec_to_bytes(default_fragment_size_msec*PA_USEC_PER_MSEC, &try_ss) /
4181 pa_frame_size(&try_ss);
4182 try_buffer_size = default_n_fragments * try_period_size;
4184 if (!(m ->input_pcm = pa_alsa_open_by_template(
4189 SND_PCM_STREAM_CAPTURE,
4190 &try_period_size, &try_buffer_size, 0, NULL, NULL,
4192 p->supported = FALSE;
4200 pa_log_debug("Profile %s supported.", p->name);
4207 if (last->output_mappings)
4208 PA_IDXSET_FOREACH(m, last->output_mappings, idx)
4209 if (m->output_pcm) {
4211 if (last->supported)
4214 snd_pcm_close(m->output_pcm);
4215 m->output_pcm = NULL;
4218 if (last->input_mappings)
4219 PA_IDXSET_FOREACH(m, last->input_mappings, idx)
4222 if (last->supported)
4225 snd_pcm_close(m->input_pcm);
4226 m->input_pcm = NULL;
4230 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4231 if (!p->supported) {
4232 pa_hashmap_remove(ps->profiles, p->name);
4236 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4237 if (m->supported <= 0) {
4238 pa_hashmap_remove(ps->mappings, m->name);
4245 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
4248 pa_alsa_decibel_fix *db_fix;
4253 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4256 pa_yes_no(ps->auto_profiles),
4257 pa_yes_no(ps->probed),
4258 pa_hashmap_size(ps->mappings),
4259 pa_hashmap_size(ps->profiles),
4260 pa_hashmap_size(ps->decibel_fixes));
4262 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4263 pa_alsa_mapping_dump(m);
4265 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4266 pa_alsa_profile_dump(p);
4268 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4269 pa_alsa_decibel_fix_dump(db_fix);
4272 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps) {
4279 /* if there is no path, we don't want a port list */
4283 if (!ps->paths->next){
4286 /* If there is only one path, but no or only one setting, then
4287 * we want a port list either */
4288 if (!ps->paths->settings || !ps->paths->settings->next)
4291 /* Ok, there is only one path, however with multiple settings,
4292 * so let's create a port for each setting */
4293 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4295 PA_LLIST_FOREACH(s, ps->paths->settings) {
4296 pa_device_port *port;
4297 pa_alsa_port_data *data;
4299 port = pa_device_port_new(s->name, s->description, sizeof(pa_alsa_port_data));
4300 port->priority = s->priority;
4302 data = PA_DEVICE_PORT_DATA(port);
4303 data->path = ps->paths;
4306 pa_hashmap_put(*p, port->name, port);
4311 /* We have multiple paths, so let's create a port for each
4312 * one, and each of each settings */
4313 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4315 PA_LLIST_FOREACH(path, ps->paths) {
4317 if (!path->settings || !path->settings->next) {
4318 pa_device_port *port;
4319 pa_alsa_port_data *data;
4321 /* If there is no or just one setting we only need a
4324 port = pa_device_port_new(path->name, path->description, sizeof(pa_alsa_port_data));
4325 port->priority = path->priority * 100;
4328 data = PA_DEVICE_PORT_DATA(port);
4330 data->setting = path->settings;
4332 pa_hashmap_put(*p, port->name, port);
4336 PA_LLIST_FOREACH(s, path->settings) {
4337 pa_device_port *port;
4338 pa_alsa_port_data *data;
4341 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
4343 if (s->description[0])
4344 d = pa_sprintf_malloc(_("%s / %s"), path->description, s->description);
4346 d = pa_xstrdup(path->description);
4348 port = pa_device_port_new(n, d, sizeof(pa_alsa_port_data));
4349 port->priority = path->priority * 100 + s->priority;
4354 data = PA_DEVICE_PORT_DATA(port);
4358 pa_hashmap_put(*p, port->name, port);
4364 pa_log_debug("Added %u ports", pa_hashmap_size(*p));