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 static int setting_select(pa_alsa_setting *s, snd_mixer_t *m);
55 struct description_map {
57 const char *description;
60 static const char *lookup_description(const char *key, const struct description_map dm[], unsigned n) {
66 for (i = 0; i < n; i++)
67 if (pa_streq(dm[i].key, key))
68 return _(dm[i].description);
73 struct pa_alsa_fdlist {
76 /* This is a temporary buffer used to avoid lots of mallocs */
77 struct pollfd *work_fds;
83 pa_defer_event *defer;
88 void (*cb)(void *userdata);
92 static void io_cb(pa_mainloop_api *a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
94 struct pa_alsa_fdlist *fdl = userdata;
97 unsigned short revents;
101 pa_assert(fdl->mixer || fdl->hctl);
103 pa_assert(fdl->work_fds);
110 memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds);
112 for (i = 0; i < fdl->num_fds; i++) {
113 if (e == fdl->ios[i]) {
114 if (events & PA_IO_EVENT_INPUT)
115 fdl->work_fds[i].revents |= POLLIN;
116 if (events & PA_IO_EVENT_OUTPUT)
117 fdl->work_fds[i].revents |= POLLOUT;
118 if (events & PA_IO_EVENT_ERROR)
119 fdl->work_fds[i].revents |= POLLERR;
120 if (events & PA_IO_EVENT_HANGUP)
121 fdl->work_fds[i].revents |= POLLHUP;
126 pa_assert(i != fdl->num_fds);
129 err = snd_hctl_poll_descriptors_revents(fdl->hctl, fdl->work_fds, fdl->num_fds, &revents);
131 err = snd_mixer_poll_descriptors_revents(fdl->mixer, fdl->work_fds, fdl->num_fds, &revents);
134 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
138 a->defer_enable(fdl->defer, 1);
142 snd_hctl_handle_events(fdl->hctl);
144 snd_mixer_handle_events(fdl->mixer);
148 static void defer_cb(pa_mainloop_api *a, pa_defer_event *e, void *userdata) {
149 struct pa_alsa_fdlist *fdl = userdata;
156 pa_assert(fdl->mixer || fdl->hctl);
158 a->defer_enable(fdl->defer, 0);
161 n = snd_hctl_poll_descriptors_count(fdl->hctl);
163 n = snd_mixer_poll_descriptors_count(fdl->mixer);
166 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
169 num_fds = (unsigned) n;
171 if (num_fds != fdl->num_fds) {
175 pa_xfree(fdl->work_fds);
176 fdl->fds = pa_xnew0(struct pollfd, num_fds);
177 fdl->work_fds = pa_xnew(struct pollfd, num_fds);
180 memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds);
183 err = snd_hctl_poll_descriptors(fdl->hctl, fdl->work_fds, num_fds);
185 err = snd_mixer_poll_descriptors(fdl->mixer, fdl->work_fds, num_fds);
188 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
194 if (memcmp(fdl->fds, fdl->work_fds, sizeof(struct pollfd) * num_fds) == 0)
198 for (i = 0; i < fdl->num_fds; i++)
199 a->io_free(fdl->ios[i]);
201 if (num_fds != fdl->num_fds) {
208 fdl->ios = pa_xnew(pa_io_event*, num_fds);
211 temp = fdl->work_fds;
212 fdl->work_fds = fdl->fds;
215 fdl->num_fds = num_fds;
217 for (i = 0;i < num_fds;i++)
218 fdl->ios[i] = a->io_new(a, fdl->fds[i].fd,
219 ((fdl->fds[i].events & POLLIN) ? PA_IO_EVENT_INPUT : 0) |
220 ((fdl->fds[i].events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0),
224 struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) {
225 struct pa_alsa_fdlist *fdl;
227 fdl = pa_xnew0(struct pa_alsa_fdlist, 1);
232 void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {
237 fdl->m->defer_free(fdl->defer);
243 for (i = 0; i < fdl->num_fds; i++)
244 fdl->m->io_free(fdl->ios[i]);
251 pa_xfree(fdl->work_fds);
256 /* We can listen to either a snd_hctl_t or a snd_mixer_t, but not both */
257 int pa_alsa_fdlist_set_handle(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, snd_hctl_t *hctl_handle, pa_mainloop_api *m) {
259 pa_assert(hctl_handle || mixer_handle);
260 pa_assert(!(hctl_handle && mixer_handle));
264 fdl->hctl = hctl_handle;
265 fdl->mixer = mixer_handle;
267 fdl->defer = m->defer_new(m, defer_cb, fdl);
272 struct pa_alsa_mixer_pdata {
274 pa_rtpoll_item *poll_item;
279 struct pa_alsa_mixer_pdata *pa_alsa_mixer_pdata_new(void) {
280 struct pa_alsa_mixer_pdata *pd;
282 pd = pa_xnew0(struct pa_alsa_mixer_pdata, 1);
287 void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata *pd) {
291 pa_rtpoll_item_free(pd->poll_item);
297 static int rtpoll_work_cb(pa_rtpoll_item *i) {
298 struct pa_alsa_mixer_pdata *pd;
301 unsigned short revents = 0;
304 pd = pa_rtpoll_item_get_userdata(i);
306 pa_assert_fp(i == pd->poll_item);
308 p = pa_rtpoll_item_get_pollfd(i, &n_fds);
310 if ((err = snd_mixer_poll_descriptors_revents(pd->mixer, p, n_fds, &revents)) < 0) {
311 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
317 if (revents & (POLLNVAL | POLLERR)) {
318 pa_log_debug("Device disconnected, stopping poll on mixer");
320 } else if (revents & POLLERR) {
321 /* This shouldn't happen. */
322 pa_log_error("Got a POLLERR (revents = %04x), stopping poll on mixer", revents);
326 err = snd_mixer_handle_events(pd->mixer);
328 if (PA_LIKELY(err >= 0)) {
329 pa_rtpoll_item_free(i);
330 pa_alsa_set_mixer_rtpoll(pd, pd->mixer, pd->rtpoll);
332 pa_log_error("Error handling mixer event: %s", pa_alsa_strerror(err));
341 pa_rtpoll_item_free(i);
343 pd->poll_item = NULL;
350 int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata *pd, snd_mixer_t *mixer, pa_rtpoll *rtp) {
359 if ((n = snd_mixer_poll_descriptors_count(mixer)) < 0) {
360 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
364 i = pa_rtpoll_item_new(rtp, PA_RTPOLL_LATE, (unsigned) n);
366 p = pa_rtpoll_item_get_pollfd(i, NULL);
368 memset(p, 0, sizeof(struct pollfd) * n);
370 if ((err = snd_mixer_poll_descriptors(mixer, p, (unsigned) n)) < 0) {
371 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
372 pa_rtpoll_item_free(i);
380 pa_rtpoll_item_set_userdata(i, pd);
381 pa_rtpoll_item_set_work_callback(i, rtpoll_work_cb);
388 static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_MAX] = {
389 [PA_CHANNEL_POSITION_MONO] = SND_MIXER_SCHN_MONO, /* The ALSA name is just an alias! */
391 [PA_CHANNEL_POSITION_FRONT_CENTER] = SND_MIXER_SCHN_FRONT_CENTER,
392 [PA_CHANNEL_POSITION_FRONT_LEFT] = SND_MIXER_SCHN_FRONT_LEFT,
393 [PA_CHANNEL_POSITION_FRONT_RIGHT] = SND_MIXER_SCHN_FRONT_RIGHT,
395 [PA_CHANNEL_POSITION_REAR_CENTER] = SND_MIXER_SCHN_REAR_CENTER,
396 [PA_CHANNEL_POSITION_REAR_LEFT] = SND_MIXER_SCHN_REAR_LEFT,
397 [PA_CHANNEL_POSITION_REAR_RIGHT] = SND_MIXER_SCHN_REAR_RIGHT,
399 [PA_CHANNEL_POSITION_LFE] = SND_MIXER_SCHN_WOOFER,
401 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
402 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
404 [PA_CHANNEL_POSITION_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT,
405 [PA_CHANNEL_POSITION_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT,
407 [PA_CHANNEL_POSITION_AUX0] = SND_MIXER_SCHN_UNKNOWN,
408 [PA_CHANNEL_POSITION_AUX1] = SND_MIXER_SCHN_UNKNOWN,
409 [PA_CHANNEL_POSITION_AUX2] = SND_MIXER_SCHN_UNKNOWN,
410 [PA_CHANNEL_POSITION_AUX3] = SND_MIXER_SCHN_UNKNOWN,
411 [PA_CHANNEL_POSITION_AUX4] = SND_MIXER_SCHN_UNKNOWN,
412 [PA_CHANNEL_POSITION_AUX5] = SND_MIXER_SCHN_UNKNOWN,
413 [PA_CHANNEL_POSITION_AUX6] = SND_MIXER_SCHN_UNKNOWN,
414 [PA_CHANNEL_POSITION_AUX7] = SND_MIXER_SCHN_UNKNOWN,
415 [PA_CHANNEL_POSITION_AUX8] = SND_MIXER_SCHN_UNKNOWN,
416 [PA_CHANNEL_POSITION_AUX9] = SND_MIXER_SCHN_UNKNOWN,
417 [PA_CHANNEL_POSITION_AUX10] = SND_MIXER_SCHN_UNKNOWN,
418 [PA_CHANNEL_POSITION_AUX11] = SND_MIXER_SCHN_UNKNOWN,
419 [PA_CHANNEL_POSITION_AUX12] = SND_MIXER_SCHN_UNKNOWN,
420 [PA_CHANNEL_POSITION_AUX13] = SND_MIXER_SCHN_UNKNOWN,
421 [PA_CHANNEL_POSITION_AUX14] = SND_MIXER_SCHN_UNKNOWN,
422 [PA_CHANNEL_POSITION_AUX15] = SND_MIXER_SCHN_UNKNOWN,
423 [PA_CHANNEL_POSITION_AUX16] = SND_MIXER_SCHN_UNKNOWN,
424 [PA_CHANNEL_POSITION_AUX17] = SND_MIXER_SCHN_UNKNOWN,
425 [PA_CHANNEL_POSITION_AUX18] = SND_MIXER_SCHN_UNKNOWN,
426 [PA_CHANNEL_POSITION_AUX19] = SND_MIXER_SCHN_UNKNOWN,
427 [PA_CHANNEL_POSITION_AUX20] = SND_MIXER_SCHN_UNKNOWN,
428 [PA_CHANNEL_POSITION_AUX21] = SND_MIXER_SCHN_UNKNOWN,
429 [PA_CHANNEL_POSITION_AUX22] = SND_MIXER_SCHN_UNKNOWN,
430 [PA_CHANNEL_POSITION_AUX23] = SND_MIXER_SCHN_UNKNOWN,
431 [PA_CHANNEL_POSITION_AUX24] = SND_MIXER_SCHN_UNKNOWN,
432 [PA_CHANNEL_POSITION_AUX25] = SND_MIXER_SCHN_UNKNOWN,
433 [PA_CHANNEL_POSITION_AUX26] = SND_MIXER_SCHN_UNKNOWN,
434 [PA_CHANNEL_POSITION_AUX27] = SND_MIXER_SCHN_UNKNOWN,
435 [PA_CHANNEL_POSITION_AUX28] = SND_MIXER_SCHN_UNKNOWN,
436 [PA_CHANNEL_POSITION_AUX29] = SND_MIXER_SCHN_UNKNOWN,
437 [PA_CHANNEL_POSITION_AUX30] = SND_MIXER_SCHN_UNKNOWN,
438 [PA_CHANNEL_POSITION_AUX31] = SND_MIXER_SCHN_UNKNOWN,
440 [PA_CHANNEL_POSITION_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN,
442 [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SND_MIXER_SCHN_UNKNOWN,
443 [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SND_MIXER_SCHN_UNKNOWN,
444 [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SND_MIXER_SCHN_UNKNOWN,
446 [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SND_MIXER_SCHN_UNKNOWN,
447 [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SND_MIXER_SCHN_UNKNOWN,
448 [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SND_MIXER_SCHN_UNKNOWN
451 static void setting_free(pa_alsa_setting *s) {
455 pa_idxset_free(s->options, NULL);
458 pa_xfree(s->description);
462 static void option_free(pa_alsa_option *o) {
465 pa_xfree(o->alsa_name);
467 pa_xfree(o->description);
471 static void decibel_fix_free(pa_alsa_decibel_fix *db_fix) {
474 pa_xfree(db_fix->name);
475 pa_xfree(db_fix->db_values);
480 static void jack_free(pa_alsa_jack *j) {
483 pa_xfree(j->alsa_name);
488 static void element_free(pa_alsa_element *e) {
492 while ((o = e->options)) {
493 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
498 decibel_fix_free(e->db_fix);
500 pa_xfree(e->alsa_name);
504 void pa_alsa_path_free(pa_alsa_path *p) {
511 while ((j = p->jacks)) {
512 PA_LLIST_REMOVE(pa_alsa_jack, p->jacks, j);
516 while ((e = p->elements)) {
517 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
521 while ((s = p->settings)) {
522 PA_LLIST_REMOVE(pa_alsa_setting, p->settings, s);
526 pa_proplist_free(p->proplist);
528 pa_xfree(p->description);
532 void pa_alsa_path_set_free(pa_alsa_path_set *ps) {
536 pa_hashmap_free(ps->paths, NULL);
541 static long to_alsa_dB(pa_volume_t v) {
542 return (long) (pa_sw_volume_to_dB(v) * 100.0);
545 static pa_volume_t from_alsa_dB(long v) {
546 return pa_sw_volume_from_dB((double) v / 100.0);
549 static long to_alsa_volume(pa_volume_t v, long min, long max) {
552 w = (long) round(((double) v * (double) (max - min)) / PA_VOLUME_NORM) + min;
553 return PA_CLAMP_UNLIKELY(w, min, max);
556 static pa_volume_t from_alsa_volume(long v, long min, long max) {
557 return (pa_volume_t) round(((double) (v - min) * PA_VOLUME_NORM) / (double) (max - min));
560 #define SELEM_INIT(sid, name) \
562 snd_mixer_selem_id_alloca(&(sid)); \
563 snd_mixer_selem_id_set_name((sid), (name)); \
564 snd_mixer_selem_id_set_index((sid), 0); \
567 static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
568 snd_mixer_selem_id_t *sid;
569 snd_mixer_elem_t *me;
570 snd_mixer_selem_channel_id_t c;
571 pa_channel_position_mask_t mask = 0;
579 SELEM_INIT(sid, e->alsa_name);
580 if (!(me = snd_mixer_find_selem(m, sid))) {
581 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
585 pa_cvolume_mute(v, cm->channels);
587 /* We take the highest volume of all channels that match */
589 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
596 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
597 if (snd_mixer_selem_has_playback_channel(me, c)) {
599 if ((r = snd_mixer_selem_get_playback_volume(me, c, &value)) >= 0) {
600 /* If the channel volume is outside the limits set
601 * by the dB fix, we clamp the hw volume to be
602 * within the limits. */
603 if (value < e->db_fix->min_step) {
604 value = e->db_fix->min_step;
605 snd_mixer_selem_set_playback_volume(me, c, value);
606 pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
607 "Volume reset to %0.2f dB.", e->alsa_name, c,
608 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
609 } else if (value > e->db_fix->max_step) {
610 value = e->db_fix->max_step;
611 snd_mixer_selem_set_playback_volume(me, c, value);
612 pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
613 "Volume reset to %0.2f dB.", e->alsa_name, c,
614 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
617 /* Volume step -> dB value conversion. */
618 value = e->db_fix->db_values[value - e->db_fix->min_step];
621 r = snd_mixer_selem_get_playback_dB(me, c, &value);
625 if (snd_mixer_selem_has_capture_channel(me, c)) {
627 if ((r = snd_mixer_selem_get_capture_volume(me, c, &value)) >= 0) {
628 /* If the channel volume is outside the limits set
629 * by the dB fix, we clamp the hw volume to be
630 * within the limits. */
631 if (value < e->db_fix->min_step) {
632 value = e->db_fix->min_step;
633 snd_mixer_selem_set_capture_volume(me, c, value);
634 pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
635 "Volume reset to %0.2f dB.", e->alsa_name, c,
636 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
637 } else if (value > e->db_fix->max_step) {
638 value = e->db_fix->max_step;
639 snd_mixer_selem_set_capture_volume(me, c, value);
640 pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
641 "Volume reset to %0.2f dB.", e->alsa_name, c,
642 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
645 /* Volume step -> dB value conversion. */
646 value = e->db_fix->db_values[value - e->db_fix->min_step];
649 r = snd_mixer_selem_get_capture_dB(me, c, &value);
657 #ifdef HAVE_VALGRIND_MEMCHECK_H
658 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
661 f = from_alsa_dB(value);
666 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
667 if (snd_mixer_selem_has_playback_channel(me, c))
668 r = snd_mixer_selem_get_playback_volume(me, c, &value);
672 if (snd_mixer_selem_has_capture_channel(me, c))
673 r = snd_mixer_selem_get_capture_volume(me, c, &value);
681 f = from_alsa_volume(value, e->min_volume, e->max_volume);
684 for (k = 0; k < cm->channels; k++)
685 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
686 if (v->values[k] < f)
689 mask |= e->masks[c][e->n_channels-1];
692 for (k = 0; k < cm->channels; k++)
693 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
694 v->values[k] = PA_VOLUME_NORM;
699 int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
710 pa_cvolume_reset(v, cm->channels);
712 PA_LLIST_FOREACH(e, p->elements) {
715 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
718 pa_assert(!p->has_dB || e->has_dB);
720 if (element_get_volume(e, m, cm, &ev) < 0)
723 /* If we have no dB information all we can do is take the first element and leave */
729 pa_sw_cvolume_multiply(v, v, &ev);
735 static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t *b) {
736 snd_mixer_selem_id_t *sid;
737 snd_mixer_elem_t *me;
738 snd_mixer_selem_channel_id_t c;
744 SELEM_INIT(sid, e->alsa_name);
745 if (!(me = snd_mixer_find_selem(m, sid))) {
746 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
750 /* We return muted if at least one channel is muted */
752 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
756 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
757 if (snd_mixer_selem_has_playback_channel(me, c))
758 r = snd_mixer_selem_get_playback_switch(me, c, &value);
762 if (snd_mixer_selem_has_capture_channel(me, c))
763 r = snd_mixer_selem_get_capture_switch(me, c, &value);
781 int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t *muted) {
791 PA_LLIST_FOREACH(e, p->elements) {
794 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
797 if (element_get_switch(e, m, &b) < 0)
810 /* Finds the closest item in db_fix->db_values and returns the corresponding
811 * step. *db_value is replaced with the value from the db_values table.
812 * Rounding is done based on the rounding parameter: -1 means rounding down and
813 * +1 means rounding up. */
814 static long decibel_fix_get_step(pa_alsa_decibel_fix *db_fix, long *db_value, int rounding) {
820 pa_assert(rounding != 0);
822 max_i = db_fix->max_step - db_fix->min_step;
825 for (i = 0; i < max_i; i++) {
826 if (db_fix->db_values[i] >= *db_value)
830 for (i = 0; i < max_i; i++) {
831 if (db_fix->db_values[i + 1] > *db_value)
836 *db_value = db_fix->db_values[i];
838 return i + db_fix->min_step;
841 /* Alsa lib documentation says for snd_mixer_selem_set_playback_dB() direction argument,
842 * that "-1 = accurate or first below, 0 = accurate, 1 = accurate or first above".
843 * But even with accurate nearest dB volume step is not selected, so that is why we need
844 * this function. Returns 0 and nearest selectable volume in *value_dB on success or
845 * negative error code if fails. */
846 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) {
856 if (d == PA_ALSA_DIRECTION_OUTPUT) {
857 if ((r = snd_mixer_selem_ask_playback_dB_vol(me, *value_dB, +1, &alsa_val)) >= 0)
858 r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value_high);
863 if (value_high == *value_dB)
866 if ((r = snd_mixer_selem_ask_playback_dB_vol(me, *value_dB, -1, &alsa_val)) >= 0)
867 r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value_low);
869 if ((r = snd_mixer_selem_ask_capture_dB_vol(me, *value_dB, +1, &alsa_val)) >= 0)
870 r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value_high);
875 if (value_high == *value_dB)
878 if ((r = snd_mixer_selem_ask_capture_dB_vol(me, *value_dB, -1, &alsa_val)) >= 0)
879 r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value_low);
885 if (labs(value_high - *value_dB) < labs(value_low - *value_dB))
886 *value_dB = value_high;
888 *value_dB = value_low;
893 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) {
895 snd_mixer_selem_id_t *sid;
897 snd_mixer_elem_t *me;
898 snd_mixer_selem_channel_id_t c;
899 pa_channel_position_mask_t mask = 0;
906 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
908 SELEM_INIT(sid, e->alsa_name);
909 if (!(me = snd_mixer_find_selem(m, sid))) {
910 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
914 pa_cvolume_mute(&rv, cm->channels);
916 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
918 pa_volume_t f = PA_VOLUME_MUTED;
919 pa_bool_t found = FALSE;
921 for (k = 0; k < cm->channels; k++)
922 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) {
924 if (v->values[k] > f)
929 /* Hmm, so this channel does not exist in the volume
930 * struct, so let's bind it to the overall max of the
932 f = pa_cvolume_max(v);
936 long value = to_alsa_dB(f);
939 if (e->volume_limit >= 0 && value > (e->max_dB * 100))
940 value = e->max_dB * 100;
942 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
943 /* If we call set_playback_volume() without checking first
944 * if the channel is available, ALSA behaves very
945 * strangely and doesn't fail the call */
946 if (snd_mixer_selem_has_playback_channel(me, c)) {
950 r = snd_mixer_selem_set_playback_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding));
952 decibel_fix_get_step(e->db_fix, &value, rounding);
958 if (deferred_volume) {
959 if ((r = element_get_nearest_alsa_dB(me, c, PA_ALSA_DIRECTION_OUTPUT, &value)) >= 0)
960 r = snd_mixer_selem_set_playback_dB(me, c, value, 0);
962 if ((r = snd_mixer_selem_set_playback_dB(me, c, value, rounding)) >= 0)
963 r = snd_mixer_selem_get_playback_dB(me, c, &value);
967 if ((r = snd_mixer_selem_ask_playback_dB_vol(me, value, rounding, &alsa_val)) >= 0)
968 r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value);
974 if (snd_mixer_selem_has_capture_channel(me, c)) {
978 r = snd_mixer_selem_set_capture_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding));
980 decibel_fix_get_step(e->db_fix, &value, rounding);
986 if (deferred_volume) {
987 if ((r = element_get_nearest_alsa_dB(me, c, PA_ALSA_DIRECTION_INPUT, &value)) >= 0)
988 r = snd_mixer_selem_set_capture_dB(me, c, value, 0);
990 if ((r = snd_mixer_selem_set_capture_dB(me, c, value, rounding)) >= 0)
991 r = snd_mixer_selem_get_capture_dB(me, c, &value);
995 if ((r = snd_mixer_selem_ask_capture_dB_vol(me, value, rounding, &alsa_val)) >= 0)
996 r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value);
1006 #ifdef HAVE_VALGRIND_MEMCHECK_H
1007 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
1010 f = from_alsa_dB(value);
1015 value = to_alsa_volume(f, e->min_volume, e->max_volume);
1017 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1018 if (snd_mixer_selem_has_playback_channel(me, c)) {
1019 if ((r = snd_mixer_selem_set_playback_volume(me, c, value)) >= 0)
1020 r = snd_mixer_selem_get_playback_volume(me, c, &value);
1024 if (snd_mixer_selem_has_capture_channel(me, c)) {
1025 if ((r = snd_mixer_selem_set_capture_volume(me, c, value)) >= 0)
1026 r = snd_mixer_selem_get_capture_volume(me, c, &value);
1034 f = from_alsa_volume(value, e->min_volume, e->max_volume);
1037 for (k = 0; k < cm->channels; k++)
1038 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
1039 if (rv.values[k] < f)
1042 mask |= e->masks[c][e->n_channels-1];
1045 for (k = 0; k < cm->channels; k++)
1046 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
1047 rv.values[k] = PA_VOLUME_NORM;
1053 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) {
1062 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
1067 rv = *v; /* Remaining adjustment */
1068 pa_cvolume_reset(v, cm->channels); /* Adjustment done */
1070 PA_LLIST_FOREACH(e, p->elements) {
1073 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
1076 pa_assert(!p->has_dB || e->has_dB);
1079 if (element_set_volume(e, m, cm, &ev, deferred_volume, write_to_hw) < 0)
1087 pa_sw_cvolume_multiply(v, v, &ev);
1088 pa_sw_cvolume_divide(&rv, &rv, &ev);
1094 static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t b) {
1095 snd_mixer_elem_t *me;
1096 snd_mixer_selem_id_t *sid;
1102 SELEM_INIT(sid, e->alsa_name);
1103 if (!(me = snd_mixer_find_selem(m, sid))) {
1104 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1108 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1109 r = snd_mixer_selem_set_playback_switch_all(me, b);
1111 r = snd_mixer_selem_set_capture_switch_all(me, b);
1114 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1119 int pa_alsa_path_set_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t muted) {
1128 PA_LLIST_FOREACH(e, p->elements) {
1130 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
1133 if (element_set_switch(e, m, !muted) < 0)
1140 /* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
1141 * function sets all channels of the volume element to e->min_volume, 0 dB or
1142 * e->constant_volume. */
1143 static int element_set_constant_volume(pa_alsa_element *e, snd_mixer_t *m) {
1144 snd_mixer_elem_t *me = NULL;
1145 snd_mixer_selem_id_t *sid = NULL;
1148 pa_bool_t volume_set = FALSE;
1153 SELEM_INIT(sid, e->alsa_name);
1154 if (!(me = snd_mixer_find_selem(m, sid))) {
1155 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1159 switch (e->volume_use) {
1160 case PA_ALSA_VOLUME_OFF:
1161 volume = e->min_volume;
1165 case PA_ALSA_VOLUME_ZERO:
1169 volume = decibel_fix_get_step(e->db_fix, &dB, (e->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1));
1174 case PA_ALSA_VOLUME_CONSTANT:
1175 volume = e->constant_volume;
1180 pa_assert_not_reached();
1184 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1185 r = snd_mixer_selem_set_playback_volume_all(me, volume);
1187 r = snd_mixer_selem_set_capture_volume_all(me, volume);
1189 pa_assert(e->volume_use == PA_ALSA_VOLUME_ZERO);
1190 pa_assert(!e->db_fix);
1192 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1193 r = snd_mixer_selem_set_playback_dB_all(me, 0, +1);
1195 r = snd_mixer_selem_set_capture_dB_all(me, 0, -1);
1199 pa_log_warn("Failed to set volume of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1204 int pa_alsa_path_select(pa_alsa_path *p, pa_alsa_setting *s, snd_mixer_t *m, bool device_is_muted) {
1211 pa_log_debug("Activating path %s", p->name);
1212 pa_alsa_path_dump(p);
1214 /* First turn on hw mute if available, to avoid noise
1215 * when setting the mixer controls. */
1216 if (p->mute_during_activation) {
1217 PA_LLIST_FOREACH(e, p->elements) {
1218 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
1219 /* If the muting fails here, that's not a critical problem for
1220 * selecting a path, so we ignore the return value.
1221 * element_set_switch() will print a warning anyway, so this
1222 * won't be a silent failure either. */
1223 (void) element_set_switch(e, m, FALSE);
1227 PA_LLIST_FOREACH(e, p->elements) {
1229 switch (e->switch_use) {
1230 case PA_ALSA_SWITCH_OFF:
1231 r = element_set_switch(e, m, FALSE);
1234 case PA_ALSA_SWITCH_ON:
1235 r = element_set_switch(e, m, TRUE);
1238 case PA_ALSA_SWITCH_MUTE:
1239 case PA_ALSA_SWITCH_IGNORE:
1240 case PA_ALSA_SWITCH_SELECT:
1248 switch (e->volume_use) {
1249 case PA_ALSA_VOLUME_OFF:
1250 case PA_ALSA_VOLUME_ZERO:
1251 case PA_ALSA_VOLUME_CONSTANT:
1252 r = element_set_constant_volume(e, m);
1255 case PA_ALSA_VOLUME_MERGE:
1256 case PA_ALSA_VOLUME_IGNORE:
1266 setting_select(s, m);
1268 /* Finally restore hw mute to the device mute status. */
1269 if (p->mute_during_activation) {
1270 PA_LLIST_FOREACH(e, p->elements) {
1271 if (e->switch_use == PA_ALSA_SWITCH_MUTE) {
1272 if (element_set_switch(e, m, !device_is_muted) < 0)
1281 static int check_required(pa_alsa_element *e, snd_mixer_elem_t *me) {
1282 pa_bool_t has_switch;
1283 pa_bool_t has_enumeration;
1284 pa_bool_t has_volume;
1289 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1291 snd_mixer_selem_has_playback_switch(me) ||
1292 (e->direction_try_other && snd_mixer_selem_has_capture_switch(me));
1295 snd_mixer_selem_has_capture_switch(me) ||
1296 (e->direction_try_other && snd_mixer_selem_has_playback_switch(me));
1299 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1301 snd_mixer_selem_has_playback_volume(me) ||
1302 (e->direction_try_other && snd_mixer_selem_has_capture_volume(me));
1305 snd_mixer_selem_has_capture_volume(me) ||
1306 (e->direction_try_other && snd_mixer_selem_has_playback_volume(me));
1309 has_enumeration = snd_mixer_selem_is_enumerated(me);
1311 if ((e->required == PA_ALSA_REQUIRED_SWITCH && !has_switch) ||
1312 (e->required == PA_ALSA_REQUIRED_VOLUME && !has_volume) ||
1313 (e->required == PA_ALSA_REQUIRED_ENUMERATION && !has_enumeration))
1316 if (e->required == PA_ALSA_REQUIRED_ANY && !(has_switch || has_volume || has_enumeration))
1319 if ((e->required_absent == PA_ALSA_REQUIRED_SWITCH && has_switch) ||
1320 (e->required_absent == PA_ALSA_REQUIRED_VOLUME && has_volume) ||
1321 (e->required_absent == PA_ALSA_REQUIRED_ENUMERATION && has_enumeration))
1324 if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration))
1327 if (e->required_any != PA_ALSA_REQUIRED_IGNORE) {
1328 switch (e->required_any) {
1329 case PA_ALSA_REQUIRED_VOLUME:
1330 e->path->req_any_present |= (e->volume_use != PA_ALSA_VOLUME_IGNORE);
1332 case PA_ALSA_REQUIRED_SWITCH:
1333 e->path->req_any_present |= (e->switch_use != PA_ALSA_SWITCH_IGNORE);
1335 case PA_ALSA_REQUIRED_ENUMERATION:
1336 e->path->req_any_present |= (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
1338 case PA_ALSA_REQUIRED_ANY:
1339 e->path->req_any_present |=
1340 (e->volume_use != PA_ALSA_VOLUME_IGNORE) ||
1341 (e->switch_use != PA_ALSA_SWITCH_IGNORE) ||
1342 (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
1345 pa_assert_not_reached();
1349 if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1351 PA_LLIST_FOREACH(o, e->options) {
1352 e->path->req_any_present |= (o->required_any != PA_ALSA_REQUIRED_IGNORE) &&
1354 if (o->required != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx < 0)
1356 if (o->required_absent != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx >= 0)
1364 static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
1365 snd_mixer_selem_id_t *sid;
1366 snd_mixer_elem_t *me;
1372 SELEM_INIT(sid, e->alsa_name);
1374 if (!(me = snd_mixer_find_selem(m, sid))) {
1376 if (e->required != PA_ALSA_REQUIRED_IGNORE)
1379 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1380 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1381 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1386 if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
1387 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1389 if (!snd_mixer_selem_has_playback_switch(me)) {
1390 if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
1391 e->direction = PA_ALSA_DIRECTION_INPUT;
1393 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1398 if (!snd_mixer_selem_has_capture_switch(me)) {
1399 if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
1400 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1402 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1406 if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
1407 e->direction_try_other = FALSE;
1410 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1412 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1414 if (!snd_mixer_selem_has_playback_volume(me)) {
1415 if (e->direction_try_other && snd_mixer_selem_has_capture_volume(me))
1416 e->direction = PA_ALSA_DIRECTION_INPUT;
1418 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1423 if (!snd_mixer_selem_has_capture_volume(me)) {
1424 if (e->direction_try_other && snd_mixer_selem_has_playback_volume(me))
1425 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1427 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1431 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1432 long min_dB = 0, max_dB = 0;
1435 e->direction_try_other = FALSE;
1437 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1438 r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume);
1440 r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
1443 pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1447 if (e->min_volume >= e->max_volume) {
1448 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);
1449 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1451 } else if (e->volume_use == PA_ALSA_VOLUME_CONSTANT &&
1452 (e->min_volume > e->constant_volume || e->max_volume < e->constant_volume)) {
1453 pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
1454 e->constant_volume, e->alsa_name, e->min_volume, e->max_volume);
1455 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1459 pa_channel_position_t p;
1462 ((e->min_volume > e->db_fix->min_step) ||
1463 (e->max_volume < e->db_fix->max_step))) {
1464 pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1465 "real hardware range (%li-%li). Disabling the decibel fix.", e->alsa_name,
1466 e->db_fix->min_step, e->db_fix->max_step,
1467 e->min_volume, e->max_volume);
1469 decibel_fix_free(e->db_fix);
1475 e->min_volume = e->db_fix->min_step;
1476 e->max_volume = e->db_fix->max_step;
1477 min_dB = e->db_fix->db_values[0];
1478 max_dB = e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step];
1479 } else if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1480 e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0;
1482 e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0;
1484 /* Check that the kernel driver returns consistent limits with
1485 * both _get_*_dB_range() and _ask_*_vol_dB(). */
1486 if (e->has_dB && !e->db_fix) {
1487 long min_dB_checked = 0;
1488 long max_dB_checked = 0;
1490 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1491 r = snd_mixer_selem_ask_playback_vol_dB(me, e->min_volume, &min_dB_checked);
1493 r = snd_mixer_selem_ask_capture_vol_dB(me, e->min_volume, &min_dB_checked);
1496 pa_log_warn("Failed to query the dB value for %s at volume level %li", e->alsa_name, e->min_volume);
1500 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1501 r = snd_mixer_selem_ask_playback_vol_dB(me, e->max_volume, &max_dB_checked);
1503 r = snd_mixer_selem_ask_capture_vol_dB(me, e->max_volume, &max_dB_checked);
1506 pa_log_warn("Failed to query the dB value for %s at volume level %li", e->alsa_name, e->max_volume);
1510 if (min_dB != min_dB_checked || max_dB != max_dB_checked) {
1511 pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
1512 "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
1513 "%0.2f dB at level %li.",
1515 min_dB / 100.0, max_dB / 100.0,
1516 min_dB_checked / 100.0, e->min_volume, max_dB_checked / 100.0, e->max_volume);
1522 #ifdef HAVE_VALGRIND_MEMCHECK_H
1523 VALGRIND_MAKE_MEM_DEFINED(&min_dB, sizeof(min_dB));
1524 VALGRIND_MAKE_MEM_DEFINED(&max_dB, sizeof(max_dB));
1527 e->min_dB = ((double) min_dB) / 100.0;
1528 e->max_dB = ((double) max_dB) / 100.0;
1530 if (min_dB >= max_dB) {
1531 pa_assert(!e->db_fix);
1532 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);
1537 if (e->volume_limit >= 0) {
1538 if (e->volume_limit <= e->min_volume || e->volume_limit > e->max_volume)
1539 pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1540 "%li-%li. The volume limit is ignored.",
1541 e->alsa_name, e->path->name, e->volume_limit, e->min_volume + 1, e->max_volume);
1544 e->max_volume = e->volume_limit;
1548 e->db_fix->max_step = e->max_volume;
1549 e->max_dB = ((double) e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step]) / 100.0;
1552 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1553 r = snd_mixer_selem_ask_playback_vol_dB(me, e->max_volume, &max_dB);
1555 r = snd_mixer_selem_ask_capture_vol_dB(me, e->max_volume, &max_dB);
1558 pa_log_warn("Failed to get dB value of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1561 e->max_dB = ((double) max_dB) / 100.0;
1567 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1568 is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
1570 is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
1575 if (!e->override_map) {
1576 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1577 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1580 e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
1583 e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
1586 e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
1589 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1591 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1594 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1595 e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1597 e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1600 if (e->n_channels <= 0) {
1601 pa_log_warn("Volume element %s with no channels?", e->alsa_name);
1605 if (e->n_channels > 2) {
1606 /* FIXME: In some places code like this is used:
1608 * e->masks[alsa_channel_ids[p]][e->n_channels-1]
1610 * The definition of e->masks is
1612 * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][2];
1614 * Since the array size is fixed at 2, we obviously
1615 * don't support elements with more than two
1617 pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e->alsa_name, e->n_channels);
1621 if (!e->override_map) {
1622 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1623 pa_bool_t has_channel;
1625 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1628 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1629 has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1631 has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1633 e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
1638 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1639 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1642 e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
1650 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1653 PA_LLIST_FOREACH(o, e->options)
1654 o->alsa_idx = pa_streq(o->alsa_name, "on") ? 1 : 0;
1655 } else if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1659 if ((n = snd_mixer_selem_get_enum_items(me)) < 0) {
1660 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n));
1664 PA_LLIST_FOREACH(o, e->options) {
1667 for (i = 0; i < n; i++) {
1670 if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
1673 if (!pa_streq(buf, o->alsa_name))
1681 if (check_required(e, me) < 0)
1687 static int jack_probe(pa_alsa_jack *j, snd_hctl_t *h) {
1692 j->has_control = pa_alsa_find_jack(h, j->alsa_name) != NULL;
1694 if (j->has_control) {
1695 if (j->required_absent != PA_ALSA_REQUIRED_IGNORE)
1697 if (j->required_any != PA_ALSA_REQUIRED_IGNORE)
1698 j->path->req_any_present = TRUE;
1700 if (j->required != PA_ALSA_REQUIRED_IGNORE)
1707 static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, pa_bool_t prefixed) {
1714 if (!pa_startswith(section, "Element "))
1720 /* This is not an element section, but an enum section? */
1721 if (strchr(section, ':'))
1724 if (p->last_element && pa_streq(p->last_element->alsa_name, section))
1725 return p->last_element;
1727 PA_LLIST_FOREACH(e, p->elements)
1728 if (pa_streq(e->alsa_name, section))
1731 e = pa_xnew0(pa_alsa_element, 1);
1733 e->alsa_name = pa_xstrdup(section);
1734 e->direction = p->direction;
1735 e->volume_limit = -1;
1737 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
1740 p->last_element = e;
1744 static pa_alsa_jack* jack_get(pa_alsa_path *p, const char *section) {
1747 if (!pa_startswith(section, "Jack "))
1751 if (p->last_jack && pa_streq(p->last_jack->name, section))
1752 return p->last_jack;
1754 PA_LLIST_FOREACH(j, p->jacks)
1755 if (pa_streq(j->name, section))
1758 j = pa_xnew0(pa_alsa_jack, 1);
1759 j->state_unplugged = PA_AVAILABLE_NO;
1760 j->state_plugged = PA_AVAILABLE_YES;
1762 j->name = pa_xstrdup(section);
1763 j->alsa_name = pa_sprintf_malloc("%s Jack", section);
1764 PA_LLIST_INSERT_AFTER(pa_alsa_jack, p->jacks, p->last_jack, j);
1772 static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
1778 if (!pa_startswith(section, "Option "))
1783 /* This is not an enum section, but an element section? */
1784 if (!(on = strchr(section, ':')))
1787 en = pa_xstrndup(section, on - section);
1790 if (p->last_option &&
1791 pa_streq(p->last_option->element->alsa_name, en) &&
1792 pa_streq(p->last_option->alsa_name, on)) {
1794 return p->last_option;
1797 pa_assert_se(e = element_get(p, en, FALSE));
1800 PA_LLIST_FOREACH(o, e->options)
1801 if (pa_streq(o->alsa_name, on))
1804 o = pa_xnew0(pa_alsa_option, 1);
1806 o->alsa_name = pa_xstrdup(on);
1809 if (p->last_option && p->last_option->element == e)
1810 PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
1812 PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
1819 static int element_parse_switch(pa_config_parser_state *state) {
1825 p = state->userdata;
1827 if (!(e = element_get(p, state->section, TRUE))) {
1828 pa_log("[%s:%u] Switch makes no sense in '%s'", state->filename, state->lineno, state->section);
1832 if (pa_streq(state->rvalue, "ignore"))
1833 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1834 else if (pa_streq(state->rvalue, "mute"))
1835 e->switch_use = PA_ALSA_SWITCH_MUTE;
1836 else if (pa_streq(state->rvalue, "off"))
1837 e->switch_use = PA_ALSA_SWITCH_OFF;
1838 else if (pa_streq(state->rvalue, "on"))
1839 e->switch_use = PA_ALSA_SWITCH_ON;
1840 else if (pa_streq(state->rvalue, "select"))
1841 e->switch_use = PA_ALSA_SWITCH_SELECT;
1843 pa_log("[%s:%u] Switch invalid of '%s'", state->filename, state->lineno, state->section);
1850 static int element_parse_volume(pa_config_parser_state *state) {
1856 p = state->userdata;
1858 if (!(e = element_get(p, state->section, TRUE))) {
1859 pa_log("[%s:%u] Volume makes no sense in '%s'", state->filename, state->lineno, state->section);
1863 if (pa_streq(state->rvalue, "ignore"))
1864 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1865 else if (pa_streq(state->rvalue, "merge"))
1866 e->volume_use = PA_ALSA_VOLUME_MERGE;
1867 else if (pa_streq(state->rvalue, "off"))
1868 e->volume_use = PA_ALSA_VOLUME_OFF;
1869 else if (pa_streq(state->rvalue, "zero"))
1870 e->volume_use = PA_ALSA_VOLUME_ZERO;
1874 if (pa_atou(state->rvalue, &constant) >= 0) {
1875 e->volume_use = PA_ALSA_VOLUME_CONSTANT;
1876 e->constant_volume = constant;
1878 pa_log("[%s:%u] Volume invalid of '%s'", state->filename, state->lineno, state->section);
1886 static int element_parse_enumeration(pa_config_parser_state *state) {
1892 p = state->userdata;
1894 if (!(e = element_get(p, state->section, TRUE))) {
1895 pa_log("[%s:%u] Enumeration makes no sense in '%s'", state->filename, state->lineno, state->section);
1899 if (pa_streq(state->rvalue, "ignore"))
1900 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1901 else if (pa_streq(state->rvalue, "select"))
1902 e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
1904 pa_log("[%s:%u] Enumeration invalid of '%s'", state->filename, state->lineno, state->section);
1911 static int option_parse_priority(pa_config_parser_state *state) {
1918 p = state->userdata;
1920 if (!(o = option_get(p, state->section))) {
1921 pa_log("[%s:%u] Priority makes no sense in '%s'", state->filename, state->lineno, state->section);
1925 if (pa_atou(state->rvalue, &prio) < 0) {
1926 pa_log("[%s:%u] Priority invalid of '%s'", state->filename, state->lineno, state->section);
1934 static int option_parse_name(pa_config_parser_state *state) {
1940 p = state->userdata;
1942 if (!(o = option_get(p, state->section))) {
1943 pa_log("[%s:%u] Name makes no sense in '%s'", state->filename, state->lineno, state->section);
1948 o->name = pa_xstrdup(state->rvalue);
1953 static int element_parse_required(pa_config_parser_state *state) {
1958 pa_alsa_required_t req;
1962 p = state->userdata;
1964 e = element_get(p, state->section, TRUE);
1965 o = option_get(p, state->section);
1966 j = jack_get(p, state->section);
1967 if (!e && !o && !j) {
1968 pa_log("[%s:%u] Required makes no sense in '%s'", state->filename, state->lineno, state->section);
1972 if (pa_streq(state->rvalue, "ignore"))
1973 req = PA_ALSA_REQUIRED_IGNORE;
1974 else if (pa_streq(state->rvalue, "switch") && e)
1975 req = PA_ALSA_REQUIRED_SWITCH;
1976 else if (pa_streq(state->rvalue, "volume") && e)
1977 req = PA_ALSA_REQUIRED_VOLUME;
1978 else if (pa_streq(state->rvalue, "enumeration"))
1979 req = PA_ALSA_REQUIRED_ENUMERATION;
1980 else if (pa_streq(state->rvalue, "any"))
1981 req = PA_ALSA_REQUIRED_ANY;
1983 pa_log("[%s:%u] Required invalid of '%s'", state->filename, state->lineno, state->section);
1987 if (pa_streq(state->lvalue, "required-absent")) {
1989 e->required_absent = req;
1991 o->required_absent = req;
1993 j->required_absent = req;
1995 else if (pa_streq(state->lvalue, "required-any")) {
1997 e->required_any = req;
1998 e->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
2001 o->required_any = req;
2002 o->element->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
2005 j->required_any = req;
2006 j->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
2022 static int element_parse_direction(pa_config_parser_state *state) {
2028 p = state->userdata;
2030 if (!(e = element_get(p, state->section, TRUE))) {
2031 pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section);
2035 if (pa_streq(state->rvalue, "playback"))
2036 e->direction = PA_ALSA_DIRECTION_OUTPUT;
2037 else if (pa_streq(state->rvalue, "capture"))
2038 e->direction = PA_ALSA_DIRECTION_INPUT;
2040 pa_log("[%s:%u] Direction invalid of '%s'", state->filename, state->lineno, state->section);
2047 static int element_parse_direction_try_other(pa_config_parser_state *state) {
2054 p = state->userdata;
2056 if (!(e = element_get(p, state->section, TRUE))) {
2057 pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section);
2061 if ((yes = pa_parse_boolean(state->rvalue)) < 0) {
2062 pa_log("[%s:%u] Direction invalid of '%s'", state->filename, state->lineno, state->section);
2066 e->direction_try_other = !!yes;
2070 static int element_parse_volume_limit(pa_config_parser_state *state) {
2077 p = state->userdata;
2079 if (!(e = element_get(p, state->section, TRUE))) {
2080 pa_log("[%s:%u] volume-limit makes no sense in '%s'", state->filename, state->lineno, state->section);
2084 if (pa_atol(state->rvalue, &volume_limit) < 0 || volume_limit < 0) {
2085 pa_log("[%s:%u] Invalid value for volume-limit", state->filename, state->lineno);
2089 e->volume_limit = volume_limit;
2093 static pa_channel_position_mask_t parse_mask(const char *m) {
2094 pa_channel_position_mask_t v;
2096 if (pa_streq(m, "all-left"))
2097 v = PA_CHANNEL_POSITION_MASK_LEFT;
2098 else if (pa_streq(m, "all-right"))
2099 v = PA_CHANNEL_POSITION_MASK_RIGHT;
2100 else if (pa_streq(m, "all-center"))
2101 v = PA_CHANNEL_POSITION_MASK_CENTER;
2102 else if (pa_streq(m, "all-front"))
2103 v = PA_CHANNEL_POSITION_MASK_FRONT;
2104 else if (pa_streq(m, "all-rear"))
2105 v = PA_CHANNEL_POSITION_MASK_REAR;
2106 else if (pa_streq(m, "all-side"))
2107 v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER;
2108 else if (pa_streq(m, "all-top"))
2109 v = PA_CHANNEL_POSITION_MASK_TOP;
2110 else if (pa_streq(m, "all-no-lfe"))
2111 v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE);
2112 else if (pa_streq(m, "all"))
2113 v = PA_CHANNEL_POSITION_MASK_ALL;
2115 pa_channel_position_t p;
2117 if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
2120 v = PA_CHANNEL_POSITION_MASK(p);
2126 static int element_parse_override_map(pa_config_parser_state *state) {
2129 const char *split_state = NULL;
2135 p = state->userdata;
2137 if (!(e = element_get(p, state->section, TRUE))) {
2138 pa_log("[%s:%u] Override map makes no sense in '%s'", state->filename, state->lineno, state->section);
2142 while ((n = pa_split(state->rvalue, ",", &split_state))) {
2143 pa_channel_position_mask_t m;
2148 if ((m = parse_mask(n)) == 0) {
2149 pa_log("[%s:%u] Override map '%s' invalid in '%s'", state->filename, state->lineno, n, state->section);
2155 if (pa_streq(state->lvalue, "override-map.1"))
2156 e->masks[i++][0] = m;
2158 e->masks[i++][1] = m;
2160 /* Later on we might add override-map.3 and so on here ... */
2165 e->override_map = TRUE;
2170 static int jack_parse_state(pa_config_parser_state *state) {
2177 p = state->userdata;
2179 if (!(j = jack_get(p, state->section))) {
2180 pa_log("[%s:%u] state makes no sense in '%s'", state->filename, state->lineno, state->section);
2184 if (pa_streq(state->rvalue, "yes"))
2185 pa = PA_AVAILABLE_YES;
2186 else if (pa_streq(state->rvalue, "no"))
2187 pa = PA_AVAILABLE_NO;
2188 else if (pa_streq(state->rvalue, "unknown"))
2189 pa = PA_AVAILABLE_UNKNOWN;
2191 pa_log("[%s:%u] state must be 'yes', 'no' or 'unknown' in '%s'", state->filename, state->lineno, state->section);
2195 if (pa_streq(state->lvalue, "state.unplugged"))
2196 j->state_unplugged = pa;
2198 j->state_plugged = pa;
2199 pa_assert(pa_streq(state->lvalue, "state.plugged"));
2205 static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
2206 snd_mixer_selem_id_t *sid;
2207 snd_mixer_elem_t *me;
2213 SELEM_INIT(sid, e->alsa_name);
2214 if (!(me = snd_mixer_find_selem(m, sid))) {
2215 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2219 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
2221 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
2222 r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
2224 r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
2227 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
2230 pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
2232 if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0)
2233 pa_log_warn("Failed to set enumeration of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
2239 static int setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
2246 PA_IDXSET_FOREACH(o, s->options, idx)
2247 element_set_option(o->element, m, o->alsa_idx);
2252 static int option_verify(pa_alsa_option *o) {
2253 static const struct description_map well_known_descriptions[] = {
2254 { "input", N_("Input") },
2255 { "input-docking", N_("Docking Station Input") },
2256 { "input-docking-microphone", N_("Docking Station Microphone") },
2257 { "input-docking-linein", N_("Docking Station Line In") },
2258 { "input-linein", N_("Line In") },
2259 { "input-microphone", N_("Microphone") },
2260 { "input-microphone-front", N_("Front Microphone") },
2261 { "input-microphone-rear", N_("Rear Microphone") },
2262 { "input-microphone-external", N_("External Microphone") },
2263 { "input-microphone-internal", N_("Internal Microphone") },
2264 { "input-radio", N_("Radio") },
2265 { "input-video", N_("Video") },
2266 { "input-agc-on", N_("Automatic Gain Control") },
2267 { "input-agc-off", N_("No Automatic Gain Control") },
2268 { "input-boost-on", N_("Boost") },
2269 { "input-boost-off", N_("No Boost") },
2270 { "output-amplifier-on", N_("Amplifier") },
2271 { "output-amplifier-off", N_("No Amplifier") },
2272 { "output-bass-boost-on", N_("Bass Boost") },
2273 { "output-bass-boost-off", N_("No Bass Boost") },
2274 { "output-speaker", N_("Speaker") },
2275 { "output-headphones", N_("Headphones") }
2281 pa_log("No name set for option %s", o->alsa_name);
2285 if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
2286 o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
2287 pa_log("Element %s of option %s not set for select.", o->element->alsa_name, o->name);
2291 if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
2292 !pa_streq(o->alsa_name, "on") &&
2293 !pa_streq(o->alsa_name, "off")) {
2294 pa_log("Switch %s options need be named off or on ", o->element->alsa_name);
2298 if (!o->description)
2299 o->description = pa_xstrdup(lookup_description(o->name,
2300 well_known_descriptions,
2301 PA_ELEMENTSOF(well_known_descriptions)));
2302 if (!o->description)
2303 o->description = pa_xstrdup(o->name);
2308 static int element_verify(pa_alsa_element *e) {
2313 // 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);
2314 if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
2315 (e->required_any != PA_ALSA_REQUIRED_IGNORE && e->required_any == e->required_absent) ||
2316 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required_any != PA_ALSA_REQUIRED_IGNORE) ||
2317 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
2318 pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name);
2322 if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
2323 pa_log("Element %s cannot set select for both switch and enumeration.", e->alsa_name);
2327 PA_LLIST_FOREACH(o, e->options)
2328 if (option_verify(o) < 0)
2334 static int path_verify(pa_alsa_path *p) {
2335 static const struct description_map well_known_descriptions[] = {
2336 { "analog-input", N_("Analog Input") },
2337 { "analog-input-microphone", N_("Microphone") },
2338 { "analog-input-microphone-front", N_("Front Microphone") },
2339 { "analog-input-microphone-rear", N_("Rear Microphone") },
2340 { "analog-input-microphone-dock", N_("Dock Microphone") },
2341 { "analog-input-microphone-internal", N_("Internal Microphone") },
2342 { "analog-input-microphone-headset", N_("Headset Microphone") },
2343 { "analog-input-linein", N_("Line In") },
2344 { "analog-input-radio", N_("Radio") },
2345 { "analog-input-video", N_("Video") },
2346 { "analog-output", N_("Analog Output") },
2347 { "analog-output-headphones", N_("Headphones") },
2348 { "analog-output-lfe-on-mono", N_("LFE on Separate Mono Output") },
2349 { "analog-output-lineout", N_("Line Out") },
2350 { "analog-output-mono", N_("Analog Mono Output") },
2351 { "analog-output-speaker", N_("Speakers") },
2352 { "hdmi-output", N_("HDMI / DisplayPort") },
2353 { "iec958-stereo-output", N_("Digital Output (S/PDIF)") },
2354 { "iec958-stereo-input", N_("Digital Input (S/PDIF)") },
2355 { "iec958-passthrough-output", N_("Digital Passthrough (S/PDIF)") }
2362 PA_LLIST_FOREACH(e, p->elements)
2363 if (element_verify(e) < 0)
2366 if (!p->description)
2367 p->description = pa_xstrdup(lookup_description(p->description_key ? p->description_key : p->name,
2368 well_known_descriptions,
2369 PA_ELEMENTSOF(well_known_descriptions)));
2371 if (!p->description) {
2372 if (p->description_key)
2373 pa_log_warn("Path %s: Unrecognized description key: %s", p->name, p->description_key);
2375 p->description = pa_xstrdup(p->name);
2381 static const char *get_default_paths_dir(void) {
2382 if (pa_run_from_build_tree())
2383 return PA_SRCDIR "/modules/alsa/mixer/paths/";
2385 return PA_ALSA_PATHS_DIR;
2388 pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa_direction_t direction) {
2393 bool mute_during_activation = false;
2395 pa_config_item items[] = {
2397 { "priority", pa_config_parse_unsigned, NULL, "General" },
2398 { "description-key", pa_config_parse_string, NULL, "General" },
2399 { "description", pa_config_parse_string, NULL, "General" },
2400 { "mute-during-activation", pa_config_parse_bool, NULL, "General" },
2401 { "eld-device", pa_config_parse_int, NULL, "General" },
2404 { "priority", option_parse_priority, NULL, NULL },
2405 { "name", option_parse_name, NULL, NULL },
2408 { "state.plugged", jack_parse_state, NULL, NULL },
2409 { "state.unplugged", jack_parse_state, NULL, NULL },
2412 { "switch", element_parse_switch, NULL, NULL },
2413 { "volume", element_parse_volume, NULL, NULL },
2414 { "enumeration", element_parse_enumeration, NULL, NULL },
2415 { "override-map.1", element_parse_override_map, NULL, NULL },
2416 { "override-map.2", element_parse_override_map, NULL, NULL },
2417 /* ... later on we might add override-map.3 and so on here ... */
2418 { "required", element_parse_required, NULL, NULL },
2419 { "required-any", element_parse_required, NULL, NULL },
2420 { "required-absent", element_parse_required, NULL, NULL },
2421 { "direction", element_parse_direction, NULL, NULL },
2422 { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
2423 { "volume-limit", element_parse_volume_limit, NULL, NULL },
2424 { NULL, NULL, NULL, NULL }
2429 p = pa_xnew0(pa_alsa_path, 1);
2430 n = pa_path_get_filename(fname);
2431 p->name = pa_xstrndup(n, strcspn(n, "."));
2432 p->proplist = pa_proplist_new();
2433 p->direction = direction;
2436 items[0].data = &p->priority;
2437 items[1].data = &p->description_key;
2438 items[2].data = &p->description;
2439 items[3].data = &mute_during_activation;
2440 items[4].data = &p->eld_device;
2443 paths_dir = get_default_paths_dir();
2445 fn = pa_maybe_prefix_path(fname, paths_dir);
2447 r = pa_config_parse(fn, NULL, items, p->proplist, p);
2453 p->mute_during_activation = mute_during_activation;
2455 if (path_verify(p) < 0)
2461 pa_alsa_path_free(p);
2465 pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction) {
2471 p = pa_xnew0(pa_alsa_path, 1);
2472 p->name = pa_xstrdup(element);
2473 p->direction = direction;
2475 e = pa_xnew0(pa_alsa_element, 1);
2477 e->alsa_name = pa_xstrdup(element);
2478 e->direction = direction;
2479 e->volume_limit = -1;
2481 e->switch_use = PA_ALSA_SWITCH_MUTE;
2482 e->volume_use = PA_ALSA_VOLUME_MERGE;
2484 PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
2485 p->last_element = e;
2489 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
2490 pa_alsa_option *o, *n;
2494 for (o = e->options; o; o = n) {
2497 if (o->alsa_idx < 0) {
2498 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
2504 e->switch_use != PA_ALSA_SWITCH_IGNORE ||
2505 e->volume_use != PA_ALSA_VOLUME_IGNORE ||
2506 e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
2509 static void path_drop_unsupported(pa_alsa_path *p) {
2510 pa_alsa_element *e, *n;
2514 for (e = p->elements; e; e = n) {
2517 if (!element_drop_unsupported(e)) {
2518 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
2524 static void path_make_options_unique(pa_alsa_path *p) {
2526 pa_alsa_option *o, *u;
2528 PA_LLIST_FOREACH(e, p->elements) {
2529 PA_LLIST_FOREACH(o, e->options) {
2533 for (u = o->next; u; u = u->next)
2534 if (pa_streq(u->name, o->name))
2540 m = pa_xstrdup(o->name);
2542 /* OK, this name is not unique, hence let's rename */
2543 for (i = 1, u = o; u; u = u->next) {
2546 if (!pa_streq(u->name, m))
2549 nn = pa_sprintf_malloc("%s-%u", m, i);
2553 nd = pa_sprintf_malloc("%s %u", u->description, i);
2554 pa_xfree(u->description);
2555 u->description = nd;
2565 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
2568 for (; e; e = e->next)
2569 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
2570 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
2576 for (o = e->options; o; o = o->next) {
2580 s = pa_xnewdup(pa_alsa_setting, template, 1);
2581 s->options = pa_idxset_copy(template->options);
2582 s->name = pa_sprintf_malloc("%s+%s", template->name, o->name);
2584 (template->description[0] && o->description[0])
2585 ? pa_sprintf_malloc("%s / %s", template->description, o->description)
2586 : (template->description[0]
2587 ? pa_xstrdup(template->description)
2588 : pa_xstrdup(o->description));
2590 s->priority = PA_MAX(template->priority, o->priority);
2592 s = pa_xnew0(pa_alsa_setting, 1);
2593 s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2594 s->name = pa_xstrdup(o->name);
2595 s->description = pa_xstrdup(o->description);
2596 s->priority = o->priority;
2599 pa_idxset_put(s->options, o, NULL);
2601 if (element_create_settings(e->next, s))
2602 /* This is not a leaf, so let's get rid of it */
2605 /* This is a leaf, so let's add it */
2606 PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2608 e->path->last_setting = s;
2615 static void path_create_settings(pa_alsa_path *p) {
2618 element_create_settings(p->elements, NULL);
2621 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, snd_hctl_t *hctl, pa_bool_t ignore_dB) {
2624 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2625 pa_channel_position_t t;
2626 pa_channel_position_mask_t path_volume_channels = 0;
2632 return p->supported ? 0 : -1;
2638 pa_log_debug("Probing path '%s'", p->name);
2640 PA_LLIST_FOREACH(j, p->jacks) {
2641 if (jack_probe(j, hctl) < 0) {
2642 p->supported = FALSE;
2643 pa_log_debug("Probe of jack '%s' failed.", j->alsa_name);
2646 pa_log_debug("Probe of jack '%s' succeeded (%s)", j->alsa_name, j->has_control ? "found!" : "not found");
2649 PA_LLIST_FOREACH(e, p->elements) {
2650 if (element_probe(e, m) < 0) {
2651 p->supported = FALSE;
2652 pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2655 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);
2660 if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2662 if (!p->has_volume) {
2663 p->min_volume = e->min_volume;
2664 p->max_volume = e->max_volume;
2668 if (!p->has_volume) {
2669 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2670 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2671 min_dB[t] = e->min_dB;
2672 max_dB[t] = e->max_dB;
2673 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2680 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2681 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2682 min_dB[t] += e->min_dB;
2683 max_dB[t] += e->max_dB;
2684 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2687 /* Hmm, there's another element before us
2688 * which cannot do dB volumes, so we we need
2689 * to 'neutralize' this slider */
2690 e->volume_use = PA_ALSA_VOLUME_ZERO;
2691 pa_log_info("Zeroing volume of '%s' on path '%s'", e->alsa_name, p->name);
2694 } else if (p->has_volume) {
2695 /* We can't use this volume, so let's ignore it */
2696 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2697 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e->alsa_name, p->name);
2699 p->has_volume = TRUE;
2702 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2706 if (p->has_req_any && !p->req_any_present) {
2707 p->supported = FALSE;
2708 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name);
2712 path_drop_unsupported(p);
2713 path_make_options_unique(p);
2714 path_create_settings(p);
2716 p->supported = TRUE;
2718 p->min_dB = INFINITY;
2719 p->max_dB = -INFINITY;
2721 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2722 if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) {
2723 if (p->min_dB > min_dB[t])
2724 p->min_dB = min_dB[t];
2726 if (p->max_dB < max_dB[t])
2727 p->max_dB = max_dB[t];
2734 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2737 pa_log_debug("Setting %s (%s) priority=%u",
2739 pa_strnull(s->description),
2743 void pa_alsa_jack_dump(pa_alsa_jack *j) {
2746 pa_log_debug("Jack %s, alsa_name='%s', detection %s", j->name, j->alsa_name, j->has_control ? "possible" : "unavailable");
2749 void pa_alsa_option_dump(pa_alsa_option *o) {
2752 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2754 pa_strnull(o->name),
2755 pa_strnull(o->description),
2760 void pa_alsa_element_dump(pa_alsa_element *e) {
2764 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",
2774 (long long unsigned) e->merged_mask,
2776 pa_yes_no(e->override_map));
2778 PA_LLIST_FOREACH(o, e->options)
2779 pa_alsa_option_dump(o);
2782 void pa_alsa_path_dump(pa_alsa_path *p) {
2788 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2789 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2791 pa_strnull(p->description),
2794 pa_yes_no(p->probed),
2795 pa_yes_no(p->supported),
2796 pa_yes_no(p->has_mute),
2797 pa_yes_no(p->has_volume),
2798 pa_yes_no(p->has_dB),
2799 p->min_volume, p->max_volume,
2800 p->min_dB, p->max_dB);
2802 PA_LLIST_FOREACH(e, p->elements)
2803 pa_alsa_element_dump(e);
2805 PA_LLIST_FOREACH(j, p->jacks)
2806 pa_alsa_jack_dump(j);
2808 PA_LLIST_FOREACH(s, p->settings)
2809 pa_alsa_setting_dump(s);
2812 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2813 snd_mixer_selem_id_t *sid;
2814 snd_mixer_elem_t *me;
2820 SELEM_INIT(sid, e->alsa_name);
2821 if (!(me = snd_mixer_find_selem(m, sid))) {
2822 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2826 snd_mixer_elem_set_callback(me, cb);
2827 snd_mixer_elem_set_callback_private(me, userdata);
2830 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2837 PA_LLIST_FOREACH(e, p->elements)
2838 element_set_callback(e, m, cb, userdata);
2841 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2849 PA_HASHMAP_FOREACH(p, ps->paths, state)
2850 pa_alsa_path_set_callback(p, m, cb, userdata);
2853 static pa_alsa_path *profile_set_get_path(pa_alsa_profile_set *ps, const char *path_name) {
2857 pa_assert(path_name);
2859 if ((path = pa_hashmap_get(ps->output_paths, path_name)))
2862 return pa_hashmap_get(ps->input_paths, path_name);
2865 static void profile_set_add_path(pa_alsa_profile_set *ps, pa_alsa_path *path) {
2869 switch (path->direction) {
2870 case PA_ALSA_DIRECTION_OUTPUT:
2871 pa_assert_se(pa_hashmap_put(ps->output_paths, path->name, path) >= 0);
2874 case PA_ALSA_DIRECTION_INPUT:
2875 pa_assert_se(pa_hashmap_put(ps->input_paths, path->name, path) >= 0);
2879 pa_assert_not_reached();
2883 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction, const char *paths_dir) {
2884 pa_alsa_path_set *ps;
2885 char **pn = NULL, **en = NULL, **ie;
2886 pa_alsa_decibel_fix *db_fix;
2887 void *state, *state2;
2890 pa_assert(m->profile_set);
2891 pa_assert(m->profile_set->decibel_fixes);
2892 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2894 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2897 ps = pa_xnew0(pa_alsa_path_set, 1);
2898 ps->direction = direction;
2899 ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2901 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2902 pn = m->output_path_names;
2904 pn = m->input_path_names;
2909 for (in = pn; *in; in++) {
2910 pa_alsa_path *p = NULL;
2911 pa_bool_t duplicate = FALSE;
2914 for (kn = pn; kn < in; kn++)
2915 if (pa_streq(*kn, *in)) {
2923 p = profile_set_get_path(m->profile_set, *in);
2925 if (p && p->direction != direction) {
2926 pa_log("Configuration error: Path %s is used both as an input and as an output path.", p->name);
2931 char *fn = pa_sprintf_malloc("%s.conf", *in);
2932 p = pa_alsa_path_new(paths_dir, fn, direction);
2935 profile_set_add_path(m->profile_set, p);
2939 pa_hashmap_put(ps->paths, p, p);
2946 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2947 en = m->output_element;
2949 en = m->input_element;
2954 for (ie = en; *ie; ie++) {
2958 p = pa_alsa_path_synthesize(*ie, direction);
2960 /* Mark all other passed elements for require-absent */
2961 for (je = en; *je; je++) {
2967 e = pa_xnew0(pa_alsa_element, 1);
2969 e->alsa_name = pa_xstrdup(*je);
2970 e->direction = direction;
2971 e->required_absent = PA_ALSA_REQUIRED_ANY;
2972 e->volume_limit = -1;
2974 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2975 p->last_element = e;
2978 pa_hashmap_put(ps->paths, *ie, p);
2982 /* Assign decibel fixes to elements. */
2983 PA_HASHMAP_FOREACH(db_fix, m->profile_set->decibel_fixes, state) {
2986 PA_HASHMAP_FOREACH(p, ps->paths, state2) {
2989 PA_LLIST_FOREACH(e, p->elements) {
2990 if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_name)) {
2991 /* The profile set that contains the dB fix may be freed
2992 * before the element, so we have to copy the dB fix
2994 e->db_fix = pa_xnewdup(pa_alsa_decibel_fix, db_fix, 1);
2995 e->db_fix->profile_set = NULL;
2996 e->db_fix->name = pa_xstrdup(db_fix->name);
2997 e->db_fix->db_values = pa_xmemdup(db_fix->db_values, (db_fix->max_step - db_fix->min_step + 1) * sizeof(long));
3007 pa_alsa_path_set_free(ps);
3012 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
3017 pa_log_debug("Path Set %p, direction=%i",
3021 PA_HASHMAP_FOREACH(p, ps->paths, state)
3022 pa_alsa_path_dump(p);
3026 static pa_bool_t options_have_option(pa_alsa_option *options, const char *alsa_name) {
3030 pa_assert(alsa_name);
3032 PA_LLIST_FOREACH(o, options) {
3033 if (pa_streq(o->alsa_name, alsa_name))
3039 static pa_bool_t enumeration_is_subset(pa_alsa_option *a_options, pa_alsa_option *b_options) {
3040 pa_alsa_option *oa, *ob;
3042 if (!a_options) return TRUE;
3043 if (!b_options) return FALSE;
3045 /* If there is an option A offers that B does not, then A is not a subset of B. */
3046 PA_LLIST_FOREACH(oa, a_options) {
3047 pa_bool_t found = FALSE;
3048 PA_LLIST_FOREACH(ob, b_options) {
3049 if (pa_streq(oa->alsa_name, ob->alsa_name)) {
3061 * Compares two elements to see if a is a subset of b
3063 static pa_bool_t element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_t *m) {
3069 * Every state is a subset of itself (with caveats for volume_limits and options)
3070 * IGNORE is a subset of every other state */
3072 /* Check the volume_use */
3073 if (a->volume_use != PA_ALSA_VOLUME_IGNORE) {
3075 /* "Constant" is subset of "Constant" only when their constant values are equal */
3076 if (a->volume_use == PA_ALSA_VOLUME_CONSTANT && b->volume_use == PA_ALSA_VOLUME_CONSTANT && a->constant_volume != b->constant_volume)
3079 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
3080 if (a->volume_use != b->volume_use && b->volume_use != PA_ALSA_VOLUME_MERGE)
3083 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
3084 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
3085 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
3086 if (b->volume_use == PA_ALSA_VOLUME_MERGE && b->volume_limit >= 0) {
3089 if (a->volume_use == PA_ALSA_VOLUME_CONSTANT)
3090 a_limit = a->constant_volume;
3091 else if (a->volume_use == PA_ALSA_VOLUME_ZERO) {
3095 int rounding = (a->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1);
3096 a_limit = decibel_fix_get_step(a->db_fix, &dB, rounding);
3098 snd_mixer_selem_id_t *sid;
3099 snd_mixer_elem_t *me;
3101 SELEM_INIT(sid, a->alsa_name);
3102 if (!(me = snd_mixer_find_selem(m, sid))) {
3103 pa_log_warn("Element %s seems to have disappeared.", a->alsa_name);
3107 if (a->direction == PA_ALSA_DIRECTION_OUTPUT) {
3108 if (snd_mixer_selem_ask_playback_dB_vol(me, dB, +1, &a_limit) < 0)
3111 if (snd_mixer_selem_ask_capture_dB_vol(me, dB, -1, &a_limit) < 0)
3115 } else if (a->volume_use == PA_ALSA_VOLUME_OFF)
3116 a_limit = a->min_volume;
3117 else if (a->volume_use == PA_ALSA_VOLUME_MERGE)
3118 a_limit = a->volume_limit;
3120 /* This should never be reached */
3123 if (a_limit > b->volume_limit)
3127 if (a->volume_use == PA_ALSA_VOLUME_MERGE) {
3129 /* If override-maps are different, they're not subsets */
3130 if (a->n_channels != b->n_channels)
3132 for (s = 0; s <= SND_MIXER_SCHN_LAST; s++)
3133 if (a->masks[s][a->n_channels-1] != b->masks[s][b->n_channels-1]) {
3134 pa_log_debug("Element %s is not a subset - mask a: 0x%" PRIx64 ", mask b: 0x%" PRIx64 ", at channel %d",
3135 a->alsa_name, a->masks[s][a->n_channels-1], b->masks[s][b->n_channels-1], s);
3141 if (a->switch_use != PA_ALSA_SWITCH_IGNORE) {
3142 /* "On" is a subset of "Mute".
3143 * "Off" is a subset of "Mute".
3144 * "On" is a subset of "Select", if there is an "Option:On" in B.
3145 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3146 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3148 if (a->switch_use != b->switch_use) {
3150 if (a->switch_use == PA_ALSA_SWITCH_SELECT || a->switch_use == PA_ALSA_SWITCH_MUTE
3151 || b->switch_use == PA_ALSA_SWITCH_OFF || b->switch_use == PA_ALSA_SWITCH_ON)
3154 if (b->switch_use == PA_ALSA_SWITCH_SELECT) {
3155 if (a->switch_use == PA_ALSA_SWITCH_ON) {
3156 if (!options_have_option(b->options, "on"))
3158 } else if (a->switch_use == PA_ALSA_SWITCH_OFF) {
3159 if (!options_have_option(b->options, "off"))
3163 } else if (a->switch_use == PA_ALSA_SWITCH_SELECT) {
3164 if (!enumeration_is_subset(a->options, b->options))
3169 if (a->enumeration_use != PA_ALSA_ENUMERATION_IGNORE) {
3170 if (b->enumeration_use == PA_ALSA_ENUMERATION_IGNORE)
3172 if (!enumeration_is_subset(a->options, b->options))
3179 static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
3186 /* If we only have one path, then don't bother */
3187 if (pa_hashmap_size(ps->paths) < 2)
3190 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3194 PA_HASHMAP_FOREACH(p2, ps->paths, state2) {
3195 pa_alsa_element *ea, *eb;
3196 pa_alsa_jack *ja, *jb;
3197 bool is_subset = true;
3202 /* If a has a jack that b does not have, a is not a subset */
3203 PA_LLIST_FOREACH(ja, p->jacks) {
3204 bool exists = false;
3206 if (!ja->has_control)
3209 PA_LLIST_FOREACH(jb, p2->jacks) {
3210 if (jb->has_control && pa_streq(jb->alsa_name, ja->alsa_name) &&
3211 (ja->state_plugged == jb->state_plugged) &&
3212 (ja->state_unplugged == jb->state_unplugged)) {
3224 /* Compare the elements of each set... */
3231 else if ((ea && !eb) || (!ea && eb))
3233 else if (pa_streq(ea->alsa_name, eb->alsa_name)) {
3234 if (element_is_subset(ea, eb, m)) {
3244 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p->name, p2->name);
3245 pa_hashmap_remove(ps->paths, p);
3252 static pa_alsa_path* path_set_find_path_by_description(pa_alsa_path_set *ps, const char* description, pa_alsa_path *ignore) {
3256 PA_HASHMAP_FOREACH(p, ps->paths, state)
3257 if (p != ignore && pa_streq(p->description, description))
3263 static void path_set_make_path_descriptions_unique(pa_alsa_path_set *ps) {
3264 pa_alsa_path *p, *q;
3265 void *state, *state2;
3267 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3269 char *old_description;
3271 q = path_set_find_path_by_description(ps, p->description, p);
3276 old_description = pa_xstrdup(p->description);
3278 /* OK, this description is not unique, hence let's rename */
3280 PA_HASHMAP_FOREACH(q, ps->paths, state2) {
3281 char *new_description;
3283 if (!pa_streq(q->description, old_description))
3286 new_description = pa_sprintf_malloc("%s %u", q->description, i);
3287 pa_xfree(q->description);
3288 q->description = new_description;
3293 pa_xfree(old_description);
3297 static void mapping_free(pa_alsa_mapping *m) {
3301 pa_xfree(m->description);
3303 pa_proplist_free(m->proplist);
3305 pa_xstrfreev(m->device_strings);
3306 pa_xstrfreev(m->input_path_names);
3307 pa_xstrfreev(m->output_path_names);
3308 pa_xstrfreev(m->input_element);
3309 pa_xstrfreev(m->output_element);
3310 if (m->input_path_set)
3311 pa_alsa_path_set_free(m->input_path_set);
3312 if (m->output_path_set)
3313 pa_alsa_path_set_free(m->output_path_set);
3315 pa_assert(!m->input_pcm);
3316 pa_assert(!m->output_pcm);
3318 pa_alsa_ucm_mapping_context_free(&m->ucm_context);
3323 static void profile_free(pa_alsa_profile *p) {
3327 pa_xfree(p->description);
3329 pa_xstrfreev(p->input_mapping_names);
3330 pa_xstrfreev(p->output_mapping_names);
3332 if (p->input_mappings)
3333 pa_idxset_free(p->input_mappings, NULL);
3335 if (p->output_mappings)
3336 pa_idxset_free(p->output_mappings, NULL);
3341 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
3344 if (ps->input_paths)
3345 pa_hashmap_free(ps->input_paths, (pa_free_cb_t) pa_alsa_path_free);
3347 if (ps->output_paths)
3348 pa_hashmap_free(ps->output_paths, (pa_free_cb_t) pa_alsa_path_free);
3351 pa_hashmap_free(ps->profiles, (pa_free_cb_t) profile_free);
3354 pa_hashmap_free(ps->mappings, (pa_free_cb_t) mapping_free);
3356 if (ps->decibel_fixes)
3357 pa_hashmap_free(ps->decibel_fixes, (pa_free_cb_t) decibel_fix_free);
3362 pa_alsa_mapping *pa_alsa_mapping_get(pa_alsa_profile_set *ps, const char *name) {
3365 if (!pa_startswith(name, "Mapping "))
3370 if ((m = pa_hashmap_get(ps->mappings, name)))
3373 m = pa_xnew0(pa_alsa_mapping, 1);
3374 m->profile_set = ps;
3375 m->name = pa_xstrdup(name);
3376 pa_channel_map_init(&m->channel_map);
3377 m->proplist = pa_proplist_new();
3379 pa_hashmap_put(ps->mappings, m->name, m);
3384 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
3387 if (!pa_startswith(name, "Profile "))
3392 if ((p = pa_hashmap_get(ps->profiles, name)))
3395 p = pa_xnew0(pa_alsa_profile, 1);
3396 p->profile_set = ps;
3397 p->name = pa_xstrdup(name);
3399 pa_hashmap_put(ps->profiles, p->name, p);
3404 static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) {
3405 pa_alsa_decibel_fix *db_fix;
3407 if (!pa_startswith(name, "DecibelFix "))
3412 if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name)))
3415 db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
3416 db_fix->profile_set = ps;
3417 db_fix->name = pa_xstrdup(name);
3419 pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix);
3424 static int mapping_parse_device_strings(pa_config_parser_state *state) {
3425 pa_alsa_profile_set *ps;
3430 ps = state->userdata;
3432 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3433 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3437 pa_xstrfreev(m->device_strings);
3438 if (!(m->device_strings = pa_split_spaces_strv(state->rvalue))) {
3439 pa_log("[%s:%u] Device string list empty of '%s'", state->filename, state->lineno, state->section);
3446 static int mapping_parse_channel_map(pa_config_parser_state *state) {
3447 pa_alsa_profile_set *ps;
3452 ps = state->userdata;
3454 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3455 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3459 if (!(pa_channel_map_parse(&m->channel_map, state->rvalue))) {
3460 pa_log("[%s:%u] Channel map invalid of '%s'", state->filename, state->lineno, state->section);
3467 static int mapping_parse_paths(pa_config_parser_state *state) {
3468 pa_alsa_profile_set *ps;
3473 ps = state->userdata;
3475 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3476 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3480 if (pa_streq(state->lvalue, "paths-input")) {
3481 pa_xstrfreev(m->input_path_names);
3482 m->input_path_names = pa_split_spaces_strv(state->rvalue);
3484 pa_xstrfreev(m->output_path_names);
3485 m->output_path_names = pa_split_spaces_strv(state->rvalue);
3491 static int mapping_parse_element(pa_config_parser_state *state) {
3492 pa_alsa_profile_set *ps;
3497 ps = state->userdata;
3499 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3500 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3504 if (pa_streq(state->lvalue, "element-input")) {
3505 pa_xstrfreev(m->input_element);
3506 m->input_element = pa_split_spaces_strv(state->rvalue);
3508 pa_xstrfreev(m->output_element);
3509 m->output_element = pa_split_spaces_strv(state->rvalue);
3515 static int mapping_parse_direction(pa_config_parser_state *state) {
3516 pa_alsa_profile_set *ps;
3521 ps = state->userdata;
3523 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3524 pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
3528 if (pa_streq(state->rvalue, "input"))
3529 m->direction = PA_ALSA_DIRECTION_INPUT;
3530 else if (pa_streq(state->rvalue, "output"))
3531 m->direction = PA_ALSA_DIRECTION_OUTPUT;
3532 else if (pa_streq(state->rvalue, "any"))
3533 m->direction = PA_ALSA_DIRECTION_ANY;
3535 pa_log("[%s:%u] Direction %s invalid.", state->filename, state->lineno, state->rvalue);
3542 static int mapping_parse_description(pa_config_parser_state *state) {
3543 pa_alsa_profile_set *ps;
3549 ps = state->userdata;
3551 if ((m = pa_alsa_mapping_get(ps, state->section))) {
3552 pa_xfree(m->description);
3553 m->description = pa_xstrdup(state->rvalue);
3554 } else if ((p = profile_get(ps, state->section))) {
3555 pa_xfree(p->description);
3556 p->description = pa_xstrdup(state->rvalue);
3558 pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
3565 static int mapping_parse_priority(pa_config_parser_state *state) {
3566 pa_alsa_profile_set *ps;
3573 ps = state->userdata;
3575 if (pa_atou(state->rvalue, &prio) < 0) {
3576 pa_log("[%s:%u] Priority invalid of '%s'", state->filename, state->lineno, state->section);
3580 if ((m = pa_alsa_mapping_get(ps, state->section)))
3582 else if ((p = profile_get(ps, state->section)))
3585 pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
3592 static int profile_parse_mappings(pa_config_parser_state *state) {
3593 pa_alsa_profile_set *ps;
3598 ps = state->userdata;
3600 if (!(p = profile_get(ps, state->section))) {
3601 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3605 if (pa_streq(state->lvalue, "input-mappings")) {
3606 pa_xstrfreev(p->input_mapping_names);
3607 p->input_mapping_names = pa_split_spaces_strv(state->rvalue);
3609 pa_xstrfreev(p->output_mapping_names);
3610 p->output_mapping_names = pa_split_spaces_strv(state->rvalue);
3616 static int profile_parse_skip_probe(pa_config_parser_state *state) {
3617 pa_alsa_profile_set *ps;
3623 ps = state->userdata;
3625 if (!(p = profile_get(ps, state->section))) {
3626 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3630 if ((b = pa_parse_boolean(state->rvalue)) < 0) {
3631 pa_log("[%s:%u] Skip probe invalid of '%s'", state->filename, state->lineno, state->section);
3640 static int decibel_fix_parse_db_values(pa_config_parser_state *state) {
3641 pa_alsa_profile_set *ps;
3642 pa_alsa_decibel_fix *db_fix;
3646 unsigned n = 8; /* Current size of the db_values table. */
3647 unsigned min_step = 0;
3648 unsigned max_step = 0;
3649 unsigned i = 0; /* Index to the items table. */
3650 unsigned prev_step = 0;
3655 ps = state->userdata;
3657 if (!(db_fix = decibel_fix_get(ps, state->section))) {
3658 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3662 if (!(items = pa_split_spaces_strv(state->rvalue))) {
3663 pa_log("[%s:%u] Value missing", state->filename, state->lineno);
3667 db_values = pa_xnew(long, n);
3669 while ((item = items[i++])) {
3670 char *s = item; /* Step value string. */
3671 char *d = item; /* dB value string. */
3675 /* Move d forward until it points to a colon or to the end of the item. */
3676 for (; *d && *d != ':'; ++d);
3679 /* item started with colon. */
3680 pa_log("[%s:%u] No step value found in %s", state->filename, state->lineno, item);
3684 if (!*d || !*(d + 1)) {
3685 /* No colon found, or it was the last character in item. */
3686 pa_log("[%s:%u] No dB value found in %s", state->filename, state->lineno, item);
3690 /* pa_atou() needs a null-terminating string. Let's replace the colon
3691 * with a zero byte. */
3694 if (pa_atou(s, &step) < 0) {
3695 pa_log("[%s:%u] Invalid step value: %s", state->filename, state->lineno, s);
3699 if (pa_atod(d, &db) < 0) {
3700 pa_log("[%s:%u] Invalid dB value: %s", state->filename, state->lineno, d);
3704 if (step <= prev_step && i != 1) {
3705 pa_log("[%s:%u] Step value %u not greater than the previous value %u", state->filename, state->lineno, step, prev_step);
3709 if (db < prev_db && i != 1) {
3710 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", state->filename, state->lineno, db, prev_db);
3716 db_values[0] = (long) (db * 100.0);
3720 /* Interpolate linearly. */
3721 double db_increment = (db - prev_db) / (step - prev_step);
3723 for (; prev_step < step; ++prev_step, prev_db += db_increment) {
3725 /* Reallocate the db_values table if it's about to overflow. */
3726 if (prev_step + 1 - min_step == n) {
3728 db_values = pa_xrenew(long, db_values, n);
3731 db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
3738 db_fix->min_step = min_step;
3739 db_fix->max_step = max_step;
3740 pa_xfree(db_fix->db_values);
3741 db_fix->db_values = db_values;
3743 pa_xstrfreev(items);
3748 pa_xstrfreev(items);
3749 pa_xfree(db_values);
3754 static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
3755 pa_alsa_direction_t direction) {
3759 snd_pcm_t *pcm_handle;
3760 pa_alsa_path_set *ps;
3761 snd_mixer_t *mixer_handle;
3762 snd_hctl_t *hctl_handle;
3764 if (direction == PA_ALSA_DIRECTION_OUTPUT) {
3765 if (m->output_path_set)
3766 return; /* Already probed */
3767 m->output_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3768 pcm_handle = m->output_pcm;
3770 if (m->input_path_set)
3771 return; /* Already probed */
3772 m->input_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3773 pcm_handle = m->input_pcm;
3777 return; /* No paths */
3779 pa_assert(pcm_handle);
3781 mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL, &hctl_handle);
3782 if (!mixer_handle || !hctl_handle) {
3783 /* Cannot open mixer, remove all entries */
3784 pa_hashmap_remove_all(ps->paths, NULL);
3789 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3790 if (pa_alsa_path_probe(p, mixer_handle, hctl_handle, m->profile_set->ignore_dB) < 0) {
3791 pa_hashmap_remove(ps->paths, p);
3795 path_set_condense(ps, mixer_handle);
3796 path_set_make_path_descriptions_unique(ps);
3799 snd_mixer_close(mixer_handle);
3801 pa_log_debug("Available mixer paths (after tidying):");
3802 pa_alsa_path_set_dump(ps);
3805 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
3807 static const struct description_map well_known_descriptions[] = {
3808 { "analog-mono", N_("Analog Mono") },
3809 { "analog-stereo", N_("Analog Stereo") },
3810 { "analog-surround-21", N_("Analog Surround 2.1") },
3811 { "analog-surround-30", N_("Analog Surround 3.0") },
3812 { "analog-surround-31", N_("Analog Surround 3.1") },
3813 { "analog-surround-40", N_("Analog Surround 4.0") },
3814 { "analog-surround-41", N_("Analog Surround 4.1") },
3815 { "analog-surround-50", N_("Analog Surround 5.0") },
3816 { "analog-surround-51", N_("Analog Surround 5.1") },
3817 { "analog-surround-61", N_("Analog Surround 6.0") },
3818 { "analog-surround-61", N_("Analog Surround 6.1") },
3819 { "analog-surround-70", N_("Analog Surround 7.0") },
3820 { "analog-surround-71", N_("Analog Surround 7.1") },
3821 { "analog-4-channel-input", N_("Analog 4-channel Input") },
3822 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3823 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3824 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3825 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3826 { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
3827 { "hdmi-stereo", N_("Digital Stereo (HDMI)") },
3828 { "hdmi-surround-51", N_("Digital Surround 5.1 (HDMI)") }
3833 if (!pa_channel_map_valid(&m->channel_map)) {
3834 pa_log("Mapping %s is missing channel map.", m->name);
3838 if (!m->device_strings) {
3839 pa_log("Mapping %s is missing device strings.", m->name);
3843 if ((m->input_path_names && m->input_element) ||
3844 (m->output_path_names && m->output_element)) {
3845 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
3849 if (!m->description)
3850 m->description = pa_xstrdup(lookup_description(m->name,
3851 well_known_descriptions,
3852 PA_ELEMENTSOF(well_known_descriptions)));
3854 if (!m->description)
3855 m->description = pa_xstrdup(m->name);
3858 if (pa_channel_map_equal(&m->channel_map, bonus))
3860 else if (m->channel_map.channels == bonus->channels)
3867 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
3868 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
3872 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3874 pa_strnull(m->description),
3876 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
3877 pa_yes_no(m->supported),
3881 static void profile_set_add_auto_pair(
3882 pa_alsa_profile_set *ps,
3883 pa_alsa_mapping *m, /* output */
3884 pa_alsa_mapping *n /* input */) {
3892 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
3895 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
3899 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
3901 name = pa_sprintf_malloc("output:%s", m->name);
3903 name = pa_sprintf_malloc("input:%s", n->name);
3905 if (pa_hashmap_get(ps->profiles, name)) {
3910 p = pa_xnew0(pa_alsa_profile, 1);
3911 p->profile_set = ps;
3915 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3916 pa_idxset_put(p->output_mappings, m, NULL);
3917 p->priority += m->priority * 100;
3921 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3922 pa_idxset_put(p->input_mappings, n, NULL);
3923 p->priority += n->priority;
3926 pa_hashmap_put(ps->profiles, p->name, p);
3929 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
3930 pa_alsa_mapping *m, *n;
3931 void *m_state, *n_state;
3935 /* The order is important here:
3936 1) try single inputs and outputs before trying their
3937 combination, because if the half-duplex test failed, we don't have
3939 2) try the output right before the input combinations with
3940 that output, because then the output_pcm is not closed between tests.
3942 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3943 profile_set_add_auto_pair(ps, NULL, n);
3945 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
3946 profile_set_add_auto_pair(ps, m, NULL);
3948 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3949 profile_set_add_auto_pair(ps, m, n);
3954 static int profile_verify(pa_alsa_profile *p) {
3956 static const struct description_map well_known_descriptions[] = {
3957 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3958 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3959 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3960 { "off", N_("Off") }
3965 /* Replace the output mapping names by the actual mappings */
3966 if (p->output_mapping_names) {
3969 pa_assert(!p->output_mappings);
3970 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3972 for (name = p->output_mapping_names; *name; name++) {
3975 pa_bool_t duplicate = FALSE;
3977 for (in = name + 1; *in; in++)
3978 if (pa_streq(*name, *in)) {
3986 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
3987 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
3991 pa_idxset_put(p->output_mappings, m, NULL);
3997 pa_xstrfreev(p->output_mapping_names);
3998 p->output_mapping_names = NULL;
4001 /* Replace the input mapping names by the actual mappings */
4002 if (p->input_mapping_names) {
4005 pa_assert(!p->input_mappings);
4006 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4008 for (name = p->input_mapping_names; *name; name++) {
4011 pa_bool_t duplicate = FALSE;
4013 for (in = name + 1; *in; in++)
4014 if (pa_streq(*name, *in)) {
4022 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
4023 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
4027 pa_idxset_put(p->input_mappings, m, NULL);
4033 pa_xstrfreev(p->input_mapping_names);
4034 p->input_mapping_names = NULL;
4037 if (!p->input_mappings && !p->output_mappings) {
4038 pa_log("Profile '%s' lacks mappings.", p->name);
4042 if (!p->description)
4043 p->description = pa_xstrdup(lookup_description(p->name,
4044 well_known_descriptions,
4045 PA_ELEMENTSOF(well_known_descriptions)));
4047 if (!p->description) {
4052 sb = pa_strbuf_new();
4054 if (p->output_mappings)
4055 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4056 if (!pa_strbuf_isempty(sb))
4057 pa_strbuf_puts(sb, " + ");
4059 pa_strbuf_printf(sb, _("%s Output"), m->description);
4062 if (p->input_mappings)
4063 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4064 if (!pa_strbuf_isempty(sb))
4065 pa_strbuf_puts(sb, " + ");
4067 pa_strbuf_printf(sb, _("%s Input"), m->description);
4070 p->description = pa_strbuf_tostring_free(sb);
4076 void pa_alsa_profile_dump(pa_alsa_profile *p) {
4081 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4083 pa_strnull(p->description),
4085 pa_yes_no(p->supported),
4086 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
4087 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
4089 if (p->input_mappings)
4090 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4091 pa_log_debug("Input %s", m->name);
4093 if (p->output_mappings)
4094 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4095 pa_log_debug("Output %s", m->name);
4098 static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
4101 /* Check that the dB mapping has been configured. Since "db-values" is
4102 * currently the only option in the DecibelFix section, and decibel fix
4103 * objects don't get created if a DecibelFix section is empty, this is
4104 * actually a redundant check. Having this may prevent future bugs,
4106 if (!db_fix->db_values) {
4107 pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
4114 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
4115 char *db_values = NULL;
4119 if (db_fix->db_values) {
4121 unsigned long i, nsteps;
4123 pa_assert(db_fix->min_step <= db_fix->max_step);
4124 nsteps = db_fix->max_step - db_fix->min_step + 1;
4126 buf = pa_strbuf_new();
4127 for (i = 0; i < nsteps; ++i)
4128 pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
4130 db_values = pa_strbuf_tostring_free(buf);
4133 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4134 db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
4136 pa_xfree(db_values);
4139 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
4140 pa_alsa_profile_set *ps;
4143 pa_alsa_decibel_fix *db_fix;
4148 static pa_config_item items[] = {
4150 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
4153 { "device-strings", mapping_parse_device_strings, NULL, NULL },
4154 { "channel-map", mapping_parse_channel_map, NULL, NULL },
4155 { "paths-input", mapping_parse_paths, NULL, NULL },
4156 { "paths-output", mapping_parse_paths, NULL, NULL },
4157 { "element-input", mapping_parse_element, NULL, NULL },
4158 { "element-output", mapping_parse_element, NULL, NULL },
4159 { "direction", mapping_parse_direction, NULL, NULL },
4161 /* Shared by [Mapping ...] and [Profile ...] */
4162 { "description", mapping_parse_description, NULL, NULL },
4163 { "priority", mapping_parse_priority, NULL, NULL },
4166 { "input-mappings", profile_parse_mappings, NULL, NULL },
4167 { "output-mappings", profile_parse_mappings, NULL, NULL },
4168 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
4170 /* [DecibelFix ...] */
4171 { "db-values", decibel_fix_parse_db_values, NULL, NULL },
4172 { NULL, NULL, NULL, NULL }
4175 ps = pa_xnew0(pa_alsa_profile_set, 1);
4176 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4177 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4178 ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4179 ps->input_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4180 ps->output_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4182 items[0].data = &ps->auto_profiles;
4185 fname = "default.conf";
4187 fn = pa_maybe_prefix_path(fname,
4188 pa_run_from_build_tree() ? PA_SRCDIR "/modules/alsa/mixer/profile-sets/" :
4189 PA_ALSA_PROFILE_SETS_DIR);
4191 r = pa_config_parse(fn, NULL, items, NULL, ps);
4197 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4198 if (mapping_verify(m, bonus) < 0)
4201 if (ps->auto_profiles)
4202 profile_set_add_auto(ps);
4204 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4205 if (profile_verify(p) < 0)
4208 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4209 if (decibel_fix_verify(db_fix) < 0)
4215 pa_alsa_profile_set_free(ps);
4219 static void profile_finalize_probing(pa_alsa_profile *to_be_finalized, pa_alsa_profile *next) {
4223 if (!to_be_finalized)
4226 if (to_be_finalized->output_mappings)
4227 PA_IDXSET_FOREACH(m, to_be_finalized->output_mappings, idx) {
4232 if (to_be_finalized->supported)
4235 /* If this mapping is also in the next profile, we won't close the
4236 * pcm handle here, because it would get immediately reopened
4238 if (next && next->output_mappings && pa_idxset_get_by_data(next->output_mappings, m, NULL))
4241 snd_pcm_close(m->output_pcm);
4242 m->output_pcm = NULL;
4245 if (to_be_finalized->input_mappings)
4246 PA_IDXSET_FOREACH(m, to_be_finalized->input_mappings, idx) {
4251 if (to_be_finalized->supported)
4254 /* If this mapping is also in the next profile, we won't close the
4255 * pcm handle here, because it would get immediately reopened
4257 if (next && next->input_mappings && pa_idxset_get_by_data(next->input_mappings, m, NULL))
4260 snd_pcm_close(m->input_pcm);
4261 m->input_pcm = NULL;
4265 static snd_pcm_t* mapping_open_pcm(pa_alsa_mapping *m,
4266 const pa_sample_spec *ss,
4269 unsigned default_n_fragments,
4270 unsigned default_fragment_size_msec) {
4272 pa_sample_spec try_ss = *ss;
4273 pa_channel_map try_map = m->channel_map;
4274 snd_pcm_uframes_t try_period_size, try_buffer_size;
4276 try_ss.channels = try_map.channels;
4279 pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
4280 pa_frame_size(&try_ss);
4281 try_buffer_size = default_n_fragments * try_period_size;
4283 return pa_alsa_open_by_template(
4284 m->device_strings, dev_id, NULL, &try_ss,
4285 &try_map, mode, &try_period_size,
4286 &try_buffer_size, 0, NULL, NULL, TRUE);
4289 static void paths_drop_unsupported(pa_hashmap* h) {
4296 p = pa_hashmap_iterate(h, &state, &key);
4298 if (p->supported <= 0) {
4299 pa_hashmap_remove(h, key);
4300 pa_alsa_path_free(p);
4302 p = pa_hashmap_iterate(h, &state, &key);
4306 void pa_alsa_profile_set_probe(
4307 pa_alsa_profile_set *ps,
4309 const pa_sample_spec *ss,
4310 unsigned default_n_fragments,
4311 unsigned default_fragment_size_msec) {
4314 pa_alsa_profile *p, *last = NULL;
4316 pa_hashmap *broken_inputs, *broken_outputs;
4325 broken_inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4326 broken_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4328 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
4331 /* Skip if this is already marked that it is supported (i.e. from the config file) */
4332 if (!p->supported) {
4334 profile_finalize_probing(last, p);
4335 p->supported = TRUE;
4337 if (p->output_mappings) {
4338 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4339 if (pa_hashmap_get(broken_outputs, m) == m) {
4340 pa_log_debug("Skipping profile %s - will not be able to open output:%s", p->name, m->name);
4341 p->supported = FALSE;
4347 if (p->input_mappings && p->supported) {
4348 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4349 if (pa_hashmap_get(broken_inputs, m) == m) {
4350 pa_log_debug("Skipping profile %s - will not be able to open input:%s", p->name, m->name);
4351 p->supported = FALSE;
4358 pa_log_debug("Looking at profile %s", p->name);
4360 /* Check if we can open all new ones */
4361 if (p->output_mappings && p->supported)
4362 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4367 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
4368 if (!(m->output_pcm = mapping_open_pcm(m, ss, dev_id,
4369 SND_PCM_STREAM_PLAYBACK,
4370 default_n_fragments,
4371 default_fragment_size_msec))) {
4372 p->supported = FALSE;
4373 if (pa_idxset_size(p->output_mappings) == 1 &&
4374 ((!p->input_mappings) || pa_idxset_size(p->input_mappings) == 0)) {
4375 pa_log_debug("Caching failure to open output:%s", m->name);
4376 pa_hashmap_put(broken_outputs, m, m);
4382 if (p->input_mappings && p->supported)
4383 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4388 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
4389 if (!(m->input_pcm = mapping_open_pcm(m, ss, dev_id,
4390 SND_PCM_STREAM_CAPTURE,
4391 default_n_fragments,
4392 default_fragment_size_msec))) {
4393 p->supported = FALSE;
4394 if (pa_idxset_size(p->input_mappings) == 1 &&
4395 ((!p->output_mappings) || pa_idxset_size(p->output_mappings) == 0)) {
4396 pa_log_debug("Caching failure to open input:%s", m->name);
4397 pa_hashmap_put(broken_inputs, m, m);
4409 pa_log_debug("Profile %s supported.", p->name);
4411 if (p->output_mappings)
4412 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4414 mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT);
4416 if (p->input_mappings)
4417 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4419 mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT);
4423 profile_finalize_probing(last, NULL);
4425 pa_alsa_profile_set_drop_unsupported(ps);
4427 paths_drop_unsupported(ps->input_paths);
4428 paths_drop_unsupported(ps->output_paths);
4429 pa_hashmap_free(broken_inputs, NULL);
4430 pa_hashmap_free(broken_outputs, NULL);
4435 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
4438 pa_alsa_decibel_fix *db_fix;
4443 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4446 pa_yes_no(ps->auto_profiles),
4447 pa_yes_no(ps->probed),
4448 pa_hashmap_size(ps->mappings),
4449 pa_hashmap_size(ps->profiles),
4450 pa_hashmap_size(ps->decibel_fixes));
4452 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4453 pa_alsa_mapping_dump(m);
4455 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4456 pa_alsa_profile_dump(p);
4458 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4459 pa_alsa_decibel_fix_dump(db_fix);
4462 void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set *ps) {
4467 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
4468 if (!p->supported) {
4469 pa_hashmap_remove(ps->profiles, p->name);
4474 PA_HASHMAP_FOREACH(m, ps->mappings, state) {
4475 if (m->supported <= 0) {
4476 pa_hashmap_remove(ps->mappings, m->name);
4482 static pa_device_port* device_port_alsa_init(pa_hashmap *ports, /* card ports */
4484 const char* description,
4486 pa_alsa_setting *setting,
4487 pa_card_profile *cp,
4488 pa_hashmap *extra, /* sink/source ports */
4495 p = pa_hashmap_get(ports, name);
4498 pa_alsa_port_data *data;
4499 pa_device_port_new_data port_data;
4501 pa_device_port_new_data_init(&port_data);
4502 pa_device_port_new_data_set_name(&port_data, name);
4503 pa_device_port_new_data_set_description(&port_data, description);
4504 pa_device_port_new_data_set_direction(&port_data, path->direction == PA_ALSA_DIRECTION_OUTPUT ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT);
4506 p = pa_device_port_new(core, &port_data, sizeof(pa_alsa_port_data));
4507 pa_device_port_new_data_done(&port_data);
4509 pa_hashmap_put(ports, p->name, p);
4510 pa_proplist_update(p->proplist, PA_UPDATE_REPLACE, path->proplist);
4512 data = PA_DEVICE_PORT_DATA(p);
4514 data->setting = setting;
4519 pa_hashmap_put(p->profiles, cp->name, cp);
4522 pa_hashmap_put(extra, p->name, p);
4523 pa_device_port_ref(p);
4529 void pa_alsa_path_set_add_ports(
4530 pa_alsa_path_set *ps,
4531 pa_card_profile *cp,
4532 pa_hashmap *ports, /* card ports */
4533 pa_hashmap *extra, /* sink/source ports */
4544 PA_HASHMAP_FOREACH(path, ps->paths, state) {
4545 if (!path->settings || !path->settings->next) {
4546 /* If there is no or just one setting we only need a
4548 pa_device_port *port = device_port_alsa_init(ports, path->name,
4549 path->description, path, path->settings, cp, extra, core);
4550 port->priority = path->priority * 100;
4554 PA_LLIST_FOREACH(s, path->settings) {
4555 pa_device_port *port;
4558 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
4560 if (s->description[0])
4561 d = pa_sprintf_malloc("%s / %s", path->description, s->description);
4563 d = pa_xstrdup(path->description);
4565 port = device_port_alsa_init(ports, n, d, path, s, cp, extra, core);
4566 port->priority = path->priority * 100 + s->priority;
4575 void pa_alsa_add_ports(void *sink_or_source_new_data, pa_alsa_path_set *ps, pa_card *card) {
4578 pa_assert(sink_or_source_new_data);
4581 if (ps->direction == PA_ALSA_DIRECTION_OUTPUT)
4582 ports = ((pa_sink_new_data *) sink_or_source_new_data)->ports;
4584 ports = ((pa_source_new_data *) sink_or_source_new_data)->ports;
4586 if (ps->paths && pa_hashmap_size(ps->paths) > 0) {
4588 pa_alsa_path_set_add_ports(ps, NULL, card->ports, ports, card->core);
4591 pa_log_debug("Added %u ports", pa_hashmap_size(ports));