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;
278 struct pa_alsa_mixer_pdata *pa_alsa_mixer_pdata_new(void) {
279 struct pa_alsa_mixer_pdata *pd;
281 pd = pa_xnew0(struct pa_alsa_mixer_pdata, 1);
286 void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata *pd) {
290 pa_rtpoll_item_free(pd->poll_item);
296 static int rtpoll_work_cb(pa_rtpoll_item *i) {
297 struct pa_alsa_mixer_pdata *pd;
300 unsigned short revents = 0;
303 pd = pa_rtpoll_item_get_userdata(i);
305 pa_assert_fp(i == pd->poll_item);
307 p = pa_rtpoll_item_get_pollfd(i, &n_fds);
309 if ((err = snd_mixer_poll_descriptors_revents(pd->mixer, p, n_fds, &revents)) < 0) {
310 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
316 if (revents & (POLLNVAL | POLLERR)) {
317 pa_log_debug("Device disconnected, stopping poll on mixer");
319 } else if (revents & POLLERR) {
320 /* This shouldn't happen. */
321 pa_log_error("Got a POLLERR (revents = %04x), stopping poll on mixer", revents);
325 err = snd_mixer_handle_events(pd->mixer);
327 if (PA_LIKELY(err >= 0)) {
328 pa_rtpoll_item_free(i);
329 pa_alsa_set_mixer_rtpoll(pd, pd->mixer, pd->rtpoll);
331 pa_log_error("Error handling mixer event: %s", pa_alsa_strerror(err));
340 pa_rtpoll_item_free(i);
342 pd->poll_item = NULL;
349 int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata *pd, snd_mixer_t *mixer, pa_rtpoll *rtp) {
358 if ((n = snd_mixer_poll_descriptors_count(mixer)) < 0) {
359 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
363 i = pa_rtpoll_item_new(rtp, PA_RTPOLL_LATE, (unsigned) n);
365 p = pa_rtpoll_item_get_pollfd(i, NULL);
367 memset(p, 0, sizeof(struct pollfd) * n);
369 if ((err = snd_mixer_poll_descriptors(mixer, p, (unsigned) n)) < 0) {
370 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
371 pa_rtpoll_item_free(i);
379 pa_rtpoll_item_set_userdata(i, pd);
380 pa_rtpoll_item_set_work_callback(i, rtpoll_work_cb);
385 static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_MAX] = {
386 [PA_CHANNEL_POSITION_MONO] = SND_MIXER_SCHN_MONO, /* The ALSA name is just an alias! */
388 [PA_CHANNEL_POSITION_FRONT_CENTER] = SND_MIXER_SCHN_FRONT_CENTER,
389 [PA_CHANNEL_POSITION_FRONT_LEFT] = SND_MIXER_SCHN_FRONT_LEFT,
390 [PA_CHANNEL_POSITION_FRONT_RIGHT] = SND_MIXER_SCHN_FRONT_RIGHT,
392 [PA_CHANNEL_POSITION_REAR_CENTER] = SND_MIXER_SCHN_REAR_CENTER,
393 [PA_CHANNEL_POSITION_REAR_LEFT] = SND_MIXER_SCHN_REAR_LEFT,
394 [PA_CHANNEL_POSITION_REAR_RIGHT] = SND_MIXER_SCHN_REAR_RIGHT,
396 [PA_CHANNEL_POSITION_LFE] = SND_MIXER_SCHN_WOOFER,
398 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
399 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
401 [PA_CHANNEL_POSITION_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT,
402 [PA_CHANNEL_POSITION_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT,
404 [PA_CHANNEL_POSITION_AUX0] = SND_MIXER_SCHN_UNKNOWN,
405 [PA_CHANNEL_POSITION_AUX1] = SND_MIXER_SCHN_UNKNOWN,
406 [PA_CHANNEL_POSITION_AUX2] = SND_MIXER_SCHN_UNKNOWN,
407 [PA_CHANNEL_POSITION_AUX3] = SND_MIXER_SCHN_UNKNOWN,
408 [PA_CHANNEL_POSITION_AUX4] = SND_MIXER_SCHN_UNKNOWN,
409 [PA_CHANNEL_POSITION_AUX5] = SND_MIXER_SCHN_UNKNOWN,
410 [PA_CHANNEL_POSITION_AUX6] = SND_MIXER_SCHN_UNKNOWN,
411 [PA_CHANNEL_POSITION_AUX7] = SND_MIXER_SCHN_UNKNOWN,
412 [PA_CHANNEL_POSITION_AUX8] = SND_MIXER_SCHN_UNKNOWN,
413 [PA_CHANNEL_POSITION_AUX9] = SND_MIXER_SCHN_UNKNOWN,
414 [PA_CHANNEL_POSITION_AUX10] = SND_MIXER_SCHN_UNKNOWN,
415 [PA_CHANNEL_POSITION_AUX11] = SND_MIXER_SCHN_UNKNOWN,
416 [PA_CHANNEL_POSITION_AUX12] = SND_MIXER_SCHN_UNKNOWN,
417 [PA_CHANNEL_POSITION_AUX13] = SND_MIXER_SCHN_UNKNOWN,
418 [PA_CHANNEL_POSITION_AUX14] = SND_MIXER_SCHN_UNKNOWN,
419 [PA_CHANNEL_POSITION_AUX15] = SND_MIXER_SCHN_UNKNOWN,
420 [PA_CHANNEL_POSITION_AUX16] = SND_MIXER_SCHN_UNKNOWN,
421 [PA_CHANNEL_POSITION_AUX17] = SND_MIXER_SCHN_UNKNOWN,
422 [PA_CHANNEL_POSITION_AUX18] = SND_MIXER_SCHN_UNKNOWN,
423 [PA_CHANNEL_POSITION_AUX19] = SND_MIXER_SCHN_UNKNOWN,
424 [PA_CHANNEL_POSITION_AUX20] = SND_MIXER_SCHN_UNKNOWN,
425 [PA_CHANNEL_POSITION_AUX21] = SND_MIXER_SCHN_UNKNOWN,
426 [PA_CHANNEL_POSITION_AUX22] = SND_MIXER_SCHN_UNKNOWN,
427 [PA_CHANNEL_POSITION_AUX23] = SND_MIXER_SCHN_UNKNOWN,
428 [PA_CHANNEL_POSITION_AUX24] = SND_MIXER_SCHN_UNKNOWN,
429 [PA_CHANNEL_POSITION_AUX25] = SND_MIXER_SCHN_UNKNOWN,
430 [PA_CHANNEL_POSITION_AUX26] = SND_MIXER_SCHN_UNKNOWN,
431 [PA_CHANNEL_POSITION_AUX27] = SND_MIXER_SCHN_UNKNOWN,
432 [PA_CHANNEL_POSITION_AUX28] = SND_MIXER_SCHN_UNKNOWN,
433 [PA_CHANNEL_POSITION_AUX29] = SND_MIXER_SCHN_UNKNOWN,
434 [PA_CHANNEL_POSITION_AUX30] = SND_MIXER_SCHN_UNKNOWN,
435 [PA_CHANNEL_POSITION_AUX31] = SND_MIXER_SCHN_UNKNOWN,
437 [PA_CHANNEL_POSITION_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN,
439 [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SND_MIXER_SCHN_UNKNOWN,
440 [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SND_MIXER_SCHN_UNKNOWN,
441 [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SND_MIXER_SCHN_UNKNOWN,
443 [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SND_MIXER_SCHN_UNKNOWN,
444 [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SND_MIXER_SCHN_UNKNOWN,
445 [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SND_MIXER_SCHN_UNKNOWN
448 static void setting_free(pa_alsa_setting *s) {
452 pa_idxset_free(s->options, NULL);
455 pa_xfree(s->description);
459 static void option_free(pa_alsa_option *o) {
462 pa_xfree(o->alsa_name);
464 pa_xfree(o->description);
468 static void decibel_fix_free(pa_alsa_decibel_fix *db_fix) {
471 pa_xfree(db_fix->name);
472 pa_xfree(db_fix->db_values);
477 static void jack_free(pa_alsa_jack *j) {
480 pa_xfree(j->alsa_name);
485 static void element_free(pa_alsa_element *e) {
489 while ((o = e->options)) {
490 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
495 decibel_fix_free(e->db_fix);
497 pa_xfree(e->alsa_name);
501 void pa_alsa_path_free(pa_alsa_path *p) {
508 while ((j = p->jacks)) {
509 PA_LLIST_REMOVE(pa_alsa_jack, p->jacks, j);
513 while ((e = p->elements)) {
514 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
518 while ((s = p->settings)) {
519 PA_LLIST_REMOVE(pa_alsa_setting, p->settings, s);
523 pa_proplist_free(p->proplist);
525 pa_xfree(p->description);
529 void pa_alsa_path_set_free(pa_alsa_path_set *ps) {
533 pa_hashmap_free(ps->paths, NULL);
538 static long to_alsa_dB(pa_volume_t v) {
539 return (long) (pa_sw_volume_to_dB(v) * 100.0);
542 static pa_volume_t from_alsa_dB(long v) {
543 return pa_sw_volume_from_dB((double) v / 100.0);
546 static long to_alsa_volume(pa_volume_t v, long min, long max) {
549 w = (long) round(((double) v * (double) (max - min)) / PA_VOLUME_NORM) + min;
550 return PA_CLAMP_UNLIKELY(w, min, max);
553 static pa_volume_t from_alsa_volume(long v, long min, long max) {
554 return (pa_volume_t) round(((double) (v - min) * PA_VOLUME_NORM) / (double) (max - min));
557 #define SELEM_INIT(sid, name) \
559 snd_mixer_selem_id_alloca(&(sid)); \
560 snd_mixer_selem_id_set_name((sid), (name)); \
561 snd_mixer_selem_id_set_index((sid), 0); \
564 static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
565 snd_mixer_selem_id_t *sid;
566 snd_mixer_elem_t *me;
567 snd_mixer_selem_channel_id_t c;
568 pa_channel_position_mask_t mask = 0;
576 SELEM_INIT(sid, e->alsa_name);
577 if (!(me = snd_mixer_find_selem(m, sid))) {
578 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
582 pa_cvolume_mute(v, cm->channels);
584 /* We take the highest volume of all channels that match */
586 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
593 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
594 if (snd_mixer_selem_has_playback_channel(me, c)) {
596 if ((r = snd_mixer_selem_get_playback_volume(me, c, &value)) >= 0) {
597 /* If the channel volume is outside the limits set
598 * by the dB fix, we clamp the hw volume to be
599 * within the limits. */
600 if (value < e->db_fix->min_step) {
601 value = e->db_fix->min_step;
602 snd_mixer_selem_set_playback_volume(me, c, value);
603 pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
604 "Volume reset to %0.2f dB.", e->alsa_name, c,
605 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
606 } else if (value > e->db_fix->max_step) {
607 value = e->db_fix->max_step;
608 snd_mixer_selem_set_playback_volume(me, c, value);
609 pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
610 "Volume reset to %0.2f dB.", e->alsa_name, c,
611 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
614 /* Volume step -> dB value conversion. */
615 value = e->db_fix->db_values[value - e->db_fix->min_step];
618 r = snd_mixer_selem_get_playback_dB(me, c, &value);
622 if (snd_mixer_selem_has_capture_channel(me, c)) {
624 if ((r = snd_mixer_selem_get_capture_volume(me, c, &value)) >= 0) {
625 /* If the channel volume is outside the limits set
626 * by the dB fix, we clamp the hw volume to be
627 * within the limits. */
628 if (value < e->db_fix->min_step) {
629 value = e->db_fix->min_step;
630 snd_mixer_selem_set_capture_volume(me, c, value);
631 pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
632 "Volume reset to %0.2f dB.", e->alsa_name, c,
633 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
634 } else if (value > e->db_fix->max_step) {
635 value = e->db_fix->max_step;
636 snd_mixer_selem_set_capture_volume(me, c, value);
637 pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
638 "Volume reset to %0.2f dB.", e->alsa_name, c,
639 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
642 /* Volume step -> dB value conversion. */
643 value = e->db_fix->db_values[value - e->db_fix->min_step];
646 r = snd_mixer_selem_get_capture_dB(me, c, &value);
654 #ifdef HAVE_VALGRIND_MEMCHECK_H
655 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
658 f = from_alsa_dB(value);
663 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
664 if (snd_mixer_selem_has_playback_channel(me, c))
665 r = snd_mixer_selem_get_playback_volume(me, c, &value);
669 if (snd_mixer_selem_has_capture_channel(me, c))
670 r = snd_mixer_selem_get_capture_volume(me, c, &value);
678 f = from_alsa_volume(value, e->min_volume, e->max_volume);
681 for (k = 0; k < cm->channels; k++)
682 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
683 if (v->values[k] < f)
686 mask |= e->masks[c][e->n_channels-1];
689 for (k = 0; k < cm->channels; k++)
690 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
691 v->values[k] = PA_VOLUME_NORM;
696 int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
707 pa_cvolume_reset(v, cm->channels);
709 PA_LLIST_FOREACH(e, p->elements) {
712 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
715 pa_assert(!p->has_dB || e->has_dB);
717 if (element_get_volume(e, m, cm, &ev) < 0)
720 /* If we have no dB information all we can do is take the first element and leave */
726 pa_sw_cvolume_multiply(v, v, &ev);
732 static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t *b) {
733 snd_mixer_selem_id_t *sid;
734 snd_mixer_elem_t *me;
735 snd_mixer_selem_channel_id_t c;
741 SELEM_INIT(sid, e->alsa_name);
742 if (!(me = snd_mixer_find_selem(m, sid))) {
743 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
747 /* We return muted if at least one channel is muted */
749 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
753 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
754 if (snd_mixer_selem_has_playback_channel(me, c))
755 r = snd_mixer_selem_get_playback_switch(me, c, &value);
759 if (snd_mixer_selem_has_capture_channel(me, c))
760 r = snd_mixer_selem_get_capture_switch(me, c, &value);
778 int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t *muted) {
788 PA_LLIST_FOREACH(e, p->elements) {
791 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
794 if (element_get_switch(e, m, &b) < 0)
807 /* Finds the closest item in db_fix->db_values and returns the corresponding
808 * step. *db_value is replaced with the value from the db_values table.
809 * Rounding is done based on the rounding parameter: -1 means rounding down and
810 * +1 means rounding up. */
811 static long decibel_fix_get_step(pa_alsa_decibel_fix *db_fix, long *db_value, int rounding) {
817 pa_assert(rounding != 0);
819 max_i = db_fix->max_step - db_fix->min_step;
822 for (i = 0; i < max_i; i++) {
823 if (db_fix->db_values[i] >= *db_value)
827 for (i = 0; i < max_i; i++) {
828 if (db_fix->db_values[i + 1] > *db_value)
833 *db_value = db_fix->db_values[i];
835 return i + db_fix->min_step;
838 /* Alsa lib documentation says for snd_mixer_selem_set_playback_dB() direction argument,
839 * that "-1 = accurate or first below, 0 = accurate, 1 = accurate or first above".
840 * But even with accurate nearest dB volume step is not selected, so that is why we need
841 * this function. Returns 0 and nearest selectable volume in *value_dB on success or
842 * negative error code if fails. */
843 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) {
853 if (d == PA_ALSA_DIRECTION_OUTPUT) {
854 if ((r = snd_mixer_selem_ask_playback_dB_vol(me, *value_dB, +1, &alsa_val)) >= 0)
855 r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value_high);
860 if (value_high == *value_dB)
863 if ((r = snd_mixer_selem_ask_playback_dB_vol(me, *value_dB, -1, &alsa_val)) >= 0)
864 r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value_low);
866 if ((r = snd_mixer_selem_ask_capture_dB_vol(me, *value_dB, +1, &alsa_val)) >= 0)
867 r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value_high);
872 if (value_high == *value_dB)
875 if ((r = snd_mixer_selem_ask_capture_dB_vol(me, *value_dB, -1, &alsa_val)) >= 0)
876 r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value_low);
882 if (labs(value_high - *value_dB) < labs(value_low - *value_dB))
883 *value_dB = value_high;
885 *value_dB = value_low;
890 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) {
892 snd_mixer_selem_id_t *sid;
894 snd_mixer_elem_t *me;
895 snd_mixer_selem_channel_id_t c;
896 pa_channel_position_mask_t mask = 0;
903 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
905 SELEM_INIT(sid, e->alsa_name);
906 if (!(me = snd_mixer_find_selem(m, sid))) {
907 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
911 pa_cvolume_mute(&rv, cm->channels);
913 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
915 pa_volume_t f = PA_VOLUME_MUTED;
916 pa_bool_t found = FALSE;
918 for (k = 0; k < cm->channels; k++)
919 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) {
921 if (v->values[k] > f)
926 /* Hmm, so this channel does not exist in the volume
927 * struct, so let's bind it to the overall max of the
929 f = pa_cvolume_max(v);
933 long value = to_alsa_dB(f);
936 if (e->volume_limit >= 0 && value > (e->max_dB * 100))
937 value = e->max_dB * 100;
939 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
940 /* If we call set_playback_volume() without checking first
941 * if the channel is available, ALSA behaves very
942 * strangely and doesn't fail the call */
943 if (snd_mixer_selem_has_playback_channel(me, c)) {
947 r = snd_mixer_selem_set_playback_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding));
949 decibel_fix_get_step(e->db_fix, &value, rounding);
955 if (deferred_volume) {
956 if ((r = element_get_nearest_alsa_dB(me, c, PA_ALSA_DIRECTION_OUTPUT, &value)) >= 0)
957 r = snd_mixer_selem_set_playback_dB(me, c, value, 0);
959 if ((r = snd_mixer_selem_set_playback_dB(me, c, value, rounding)) >= 0)
960 r = snd_mixer_selem_get_playback_dB(me, c, &value);
964 if ((r = snd_mixer_selem_ask_playback_dB_vol(me, value, rounding, &alsa_val)) >= 0)
965 r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value);
971 if (snd_mixer_selem_has_capture_channel(me, c)) {
975 r = snd_mixer_selem_set_capture_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding));
977 decibel_fix_get_step(e->db_fix, &value, rounding);
983 if (deferred_volume) {
984 if ((r = element_get_nearest_alsa_dB(me, c, PA_ALSA_DIRECTION_INPUT, &value)) >= 0)
985 r = snd_mixer_selem_set_capture_dB(me, c, value, 0);
987 if ((r = snd_mixer_selem_set_capture_dB(me, c, value, rounding)) >= 0)
988 r = snd_mixer_selem_get_capture_dB(me, c, &value);
992 if ((r = snd_mixer_selem_ask_capture_dB_vol(me, value, rounding, &alsa_val)) >= 0)
993 r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value);
1003 #ifdef HAVE_VALGRIND_MEMCHECK_H
1004 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
1007 f = from_alsa_dB(value);
1012 value = to_alsa_volume(f, e->min_volume, e->max_volume);
1014 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1015 if (snd_mixer_selem_has_playback_channel(me, c)) {
1016 if ((r = snd_mixer_selem_set_playback_volume(me, c, value)) >= 0)
1017 r = snd_mixer_selem_get_playback_volume(me, c, &value);
1021 if (snd_mixer_selem_has_capture_channel(me, c)) {
1022 if ((r = snd_mixer_selem_set_capture_volume(me, c, value)) >= 0)
1023 r = snd_mixer_selem_get_capture_volume(me, c, &value);
1031 f = from_alsa_volume(value, e->min_volume, e->max_volume);
1034 for (k = 0; k < cm->channels; k++)
1035 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
1036 if (rv.values[k] < f)
1039 mask |= e->masks[c][e->n_channels-1];
1042 for (k = 0; k < cm->channels; k++)
1043 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
1044 rv.values[k] = PA_VOLUME_NORM;
1050 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) {
1059 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
1064 rv = *v; /* Remaining adjustment */
1065 pa_cvolume_reset(v, cm->channels); /* Adjustment done */
1067 PA_LLIST_FOREACH(e, p->elements) {
1070 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
1073 pa_assert(!p->has_dB || e->has_dB);
1076 if (element_set_volume(e, m, cm, &ev, deferred_volume, write_to_hw) < 0)
1084 pa_sw_cvolume_multiply(v, v, &ev);
1085 pa_sw_cvolume_divide(&rv, &rv, &ev);
1091 static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t b) {
1092 snd_mixer_elem_t *me;
1093 snd_mixer_selem_id_t *sid;
1099 SELEM_INIT(sid, e->alsa_name);
1100 if (!(me = snd_mixer_find_selem(m, sid))) {
1101 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1105 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1106 r = snd_mixer_selem_set_playback_switch_all(me, b);
1108 r = snd_mixer_selem_set_capture_switch_all(me, b);
1111 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1116 int pa_alsa_path_set_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t muted) {
1125 PA_LLIST_FOREACH(e, p->elements) {
1127 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
1130 if (element_set_switch(e, m, !muted) < 0)
1137 /* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
1138 * function sets all channels of the volume element to e->min_volume, 0 dB or
1139 * e->constant_volume. */
1140 static int element_set_constant_volume(pa_alsa_element *e, snd_mixer_t *m) {
1141 snd_mixer_elem_t *me = NULL;
1142 snd_mixer_selem_id_t *sid = NULL;
1145 pa_bool_t volume_set = FALSE;
1150 SELEM_INIT(sid, e->alsa_name);
1151 if (!(me = snd_mixer_find_selem(m, sid))) {
1152 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1156 switch (e->volume_use) {
1157 case PA_ALSA_VOLUME_OFF:
1158 volume = e->min_volume;
1162 case PA_ALSA_VOLUME_ZERO:
1166 volume = decibel_fix_get_step(e->db_fix, &dB, (e->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1));
1171 case PA_ALSA_VOLUME_CONSTANT:
1172 volume = e->constant_volume;
1177 pa_assert_not_reached();
1181 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1182 r = snd_mixer_selem_set_playback_volume_all(me, volume);
1184 r = snd_mixer_selem_set_capture_volume_all(me, volume);
1186 pa_assert(e->volume_use == PA_ALSA_VOLUME_ZERO);
1187 pa_assert(!e->db_fix);
1189 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1190 r = snd_mixer_selem_set_playback_dB_all(me, 0, +1);
1192 r = snd_mixer_selem_set_capture_dB_all(me, 0, -1);
1196 pa_log_warn("Failed to set volume of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1201 int pa_alsa_path_select(pa_alsa_path *p, pa_alsa_setting *s, snd_mixer_t *m, bool device_is_muted) {
1208 pa_log_debug("Activating path %s", p->name);
1209 pa_alsa_path_dump(p);
1211 /* First turn on hw mute if available, to avoid noise
1212 * when setting the mixer controls. */
1213 if (p->mute_during_activation) {
1214 PA_LLIST_FOREACH(e, p->elements) {
1215 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
1216 /* If the muting fails here, that's not a critical problem for
1217 * selecting a path, so we ignore the return value.
1218 * element_set_switch() will print a warning anyway, so this
1219 * won't be a silent failure either. */
1220 (void) element_set_switch(e, m, FALSE);
1224 PA_LLIST_FOREACH(e, p->elements) {
1226 switch (e->switch_use) {
1227 case PA_ALSA_SWITCH_OFF:
1228 r = element_set_switch(e, m, FALSE);
1231 case PA_ALSA_SWITCH_ON:
1232 r = element_set_switch(e, m, TRUE);
1235 case PA_ALSA_SWITCH_MUTE:
1236 case PA_ALSA_SWITCH_IGNORE:
1237 case PA_ALSA_SWITCH_SELECT:
1245 switch (e->volume_use) {
1246 case PA_ALSA_VOLUME_OFF:
1247 case PA_ALSA_VOLUME_ZERO:
1248 case PA_ALSA_VOLUME_CONSTANT:
1249 r = element_set_constant_volume(e, m);
1252 case PA_ALSA_VOLUME_MERGE:
1253 case PA_ALSA_VOLUME_IGNORE:
1263 setting_select(s, m);
1265 /* Finally restore hw mute to the device mute status. */
1266 if (p->mute_during_activation) {
1267 PA_LLIST_FOREACH(e, p->elements) {
1268 if (e->switch_use == PA_ALSA_SWITCH_MUTE) {
1269 if (element_set_switch(e, m, !device_is_muted) < 0)
1278 static int check_required(pa_alsa_element *e, snd_mixer_elem_t *me) {
1279 pa_bool_t has_switch;
1280 pa_bool_t has_enumeration;
1281 pa_bool_t has_volume;
1286 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1288 snd_mixer_selem_has_playback_switch(me) ||
1289 (e->direction_try_other && snd_mixer_selem_has_capture_switch(me));
1292 snd_mixer_selem_has_capture_switch(me) ||
1293 (e->direction_try_other && snd_mixer_selem_has_playback_switch(me));
1296 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1298 snd_mixer_selem_has_playback_volume(me) ||
1299 (e->direction_try_other && snd_mixer_selem_has_capture_volume(me));
1302 snd_mixer_selem_has_capture_volume(me) ||
1303 (e->direction_try_other && snd_mixer_selem_has_playback_volume(me));
1306 has_enumeration = snd_mixer_selem_is_enumerated(me);
1308 if ((e->required == PA_ALSA_REQUIRED_SWITCH && !has_switch) ||
1309 (e->required == PA_ALSA_REQUIRED_VOLUME && !has_volume) ||
1310 (e->required == PA_ALSA_REQUIRED_ENUMERATION && !has_enumeration))
1313 if (e->required == PA_ALSA_REQUIRED_ANY && !(has_switch || has_volume || has_enumeration))
1316 if ((e->required_absent == PA_ALSA_REQUIRED_SWITCH && has_switch) ||
1317 (e->required_absent == PA_ALSA_REQUIRED_VOLUME && has_volume) ||
1318 (e->required_absent == PA_ALSA_REQUIRED_ENUMERATION && has_enumeration))
1321 if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration))
1324 if (e->required_any != PA_ALSA_REQUIRED_IGNORE) {
1325 switch (e->required_any) {
1326 case PA_ALSA_REQUIRED_VOLUME:
1327 e->path->req_any_present |= (e->volume_use != PA_ALSA_VOLUME_IGNORE);
1329 case PA_ALSA_REQUIRED_SWITCH:
1330 e->path->req_any_present |= (e->switch_use != PA_ALSA_SWITCH_IGNORE);
1332 case PA_ALSA_REQUIRED_ENUMERATION:
1333 e->path->req_any_present |= (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
1335 case PA_ALSA_REQUIRED_ANY:
1336 e->path->req_any_present |=
1337 (e->volume_use != PA_ALSA_VOLUME_IGNORE) ||
1338 (e->switch_use != PA_ALSA_SWITCH_IGNORE) ||
1339 (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
1342 pa_assert_not_reached();
1346 if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1348 PA_LLIST_FOREACH(o, e->options) {
1349 e->path->req_any_present |= (o->required_any != PA_ALSA_REQUIRED_IGNORE) &&
1351 if (o->required != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx < 0)
1353 if (o->required_absent != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx >= 0)
1361 static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
1362 snd_mixer_selem_id_t *sid;
1363 snd_mixer_elem_t *me;
1369 SELEM_INIT(sid, e->alsa_name);
1371 if (!(me = snd_mixer_find_selem(m, sid))) {
1373 if (e->required != PA_ALSA_REQUIRED_IGNORE)
1376 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1377 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1378 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1383 if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
1384 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1386 if (!snd_mixer_selem_has_playback_switch(me)) {
1387 if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
1388 e->direction = PA_ALSA_DIRECTION_INPUT;
1390 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1395 if (!snd_mixer_selem_has_capture_switch(me)) {
1396 if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
1397 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1399 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1403 if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
1404 e->direction_try_other = FALSE;
1407 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1409 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1411 if (!snd_mixer_selem_has_playback_volume(me)) {
1412 if (e->direction_try_other && snd_mixer_selem_has_capture_volume(me))
1413 e->direction = PA_ALSA_DIRECTION_INPUT;
1415 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1420 if (!snd_mixer_selem_has_capture_volume(me)) {
1421 if (e->direction_try_other && snd_mixer_selem_has_playback_volume(me))
1422 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1424 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1428 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1429 long min_dB = 0, max_dB = 0;
1432 e->direction_try_other = FALSE;
1434 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1435 r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume);
1437 r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
1440 pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1444 if (e->min_volume >= e->max_volume) {
1445 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);
1446 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1448 } else if (e->volume_use == PA_ALSA_VOLUME_CONSTANT &&
1449 (e->min_volume > e->constant_volume || e->max_volume < e->constant_volume)) {
1450 pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
1451 e->constant_volume, e->alsa_name, e->min_volume, e->max_volume);
1452 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1456 pa_channel_position_t p;
1459 ((e->min_volume > e->db_fix->min_step) ||
1460 (e->max_volume < e->db_fix->max_step))) {
1461 pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1462 "real hardware range (%li-%li). Disabling the decibel fix.", e->alsa_name,
1463 e->db_fix->min_step, e->db_fix->max_step,
1464 e->min_volume, e->max_volume);
1466 decibel_fix_free(e->db_fix);
1472 e->min_volume = e->db_fix->min_step;
1473 e->max_volume = e->db_fix->max_step;
1474 min_dB = e->db_fix->db_values[0];
1475 max_dB = e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step];
1476 } else if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1477 e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0;
1479 e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0;
1481 /* Check that the kernel driver returns consistent limits with
1482 * both _get_*_dB_range() and _ask_*_vol_dB(). */
1483 if (e->has_dB && !e->db_fix) {
1484 long min_dB_checked = 0;
1485 long max_dB_checked = 0;
1487 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1488 r = snd_mixer_selem_ask_playback_vol_dB(me, e->min_volume, &min_dB_checked);
1490 r = snd_mixer_selem_ask_capture_vol_dB(me, e->min_volume, &min_dB_checked);
1493 pa_log_warn("Failed to query the dB value for %s at volume level %li", e->alsa_name, e->min_volume);
1497 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1498 r = snd_mixer_selem_ask_playback_vol_dB(me, e->max_volume, &max_dB_checked);
1500 r = snd_mixer_selem_ask_capture_vol_dB(me, e->max_volume, &max_dB_checked);
1503 pa_log_warn("Failed to query the dB value for %s at volume level %li", e->alsa_name, e->max_volume);
1507 if (min_dB != min_dB_checked || max_dB != max_dB_checked) {
1508 pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
1509 "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
1510 "%0.2f dB at level %li.",
1512 min_dB / 100.0, max_dB / 100.0,
1513 min_dB_checked / 100.0, e->min_volume, max_dB_checked / 100.0, e->max_volume);
1519 #ifdef HAVE_VALGRIND_MEMCHECK_H
1520 VALGRIND_MAKE_MEM_DEFINED(&min_dB, sizeof(min_dB));
1521 VALGRIND_MAKE_MEM_DEFINED(&max_dB, sizeof(max_dB));
1524 e->min_dB = ((double) min_dB) / 100.0;
1525 e->max_dB = ((double) max_dB) / 100.0;
1527 if (min_dB >= max_dB) {
1528 pa_assert(!e->db_fix);
1529 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);
1534 if (e->volume_limit >= 0) {
1535 if (e->volume_limit <= e->min_volume || e->volume_limit > e->max_volume)
1536 pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1537 "%li-%li. The volume limit is ignored.",
1538 e->alsa_name, e->path->name, e->volume_limit, e->min_volume + 1, e->max_volume);
1541 e->max_volume = e->volume_limit;
1545 e->db_fix->max_step = e->max_volume;
1546 e->max_dB = ((double) e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step]) / 100.0;
1549 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1550 r = snd_mixer_selem_ask_playback_vol_dB(me, e->max_volume, &max_dB);
1552 r = snd_mixer_selem_ask_capture_vol_dB(me, e->max_volume, &max_dB);
1555 pa_log_warn("Failed to get dB value of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1558 e->max_dB = ((double) max_dB) / 100.0;
1564 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1565 is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
1567 is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
1572 if (!e->override_map) {
1573 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1574 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1577 e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
1580 e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
1583 e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
1586 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1588 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1591 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1592 e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1594 e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1597 if (e->n_channels <= 0) {
1598 pa_log_warn("Volume element %s with no channels?", e->alsa_name);
1602 if (e->n_channels > 2) {
1603 /* FIXME: In some places code like this is used:
1605 * e->masks[alsa_channel_ids[p]][e->n_channels-1]
1607 * The definition of e->masks is
1609 * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][2];
1611 * Since the array size is fixed at 2, we obviously
1612 * don't support elements with more than two
1614 pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e->alsa_name, e->n_channels);
1618 if (!e->override_map) {
1619 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1620 pa_bool_t has_channel;
1622 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1625 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1626 has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1628 has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1630 e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
1635 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1636 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1639 e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
1647 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1650 PA_LLIST_FOREACH(o, e->options)
1651 o->alsa_idx = pa_streq(o->alsa_name, "on") ? 1 : 0;
1652 } else if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1656 if ((n = snd_mixer_selem_get_enum_items(me)) < 0) {
1657 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n));
1661 PA_LLIST_FOREACH(o, e->options) {
1664 for (i = 0; i < n; i++) {
1667 if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
1670 if (!pa_streq(buf, o->alsa_name))
1678 if (check_required(e, me) < 0)
1684 static int jack_probe(pa_alsa_jack *j, snd_hctl_t *h) {
1689 j->has_control = pa_alsa_find_jack(h, j->alsa_name) != NULL;
1691 if (j->has_control) {
1692 if (j->required_absent != PA_ALSA_REQUIRED_IGNORE)
1694 if (j->required_any != PA_ALSA_REQUIRED_IGNORE)
1695 j->path->req_any_present = TRUE;
1697 if (j->required != PA_ALSA_REQUIRED_IGNORE)
1704 static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, pa_bool_t prefixed) {
1711 if (!pa_startswith(section, "Element "))
1717 /* This is not an element section, but an enum section? */
1718 if (strchr(section, ':'))
1721 if (p->last_element && pa_streq(p->last_element->alsa_name, section))
1722 return p->last_element;
1724 PA_LLIST_FOREACH(e, p->elements)
1725 if (pa_streq(e->alsa_name, section))
1728 e = pa_xnew0(pa_alsa_element, 1);
1730 e->alsa_name = pa_xstrdup(section);
1731 e->direction = p->direction;
1732 e->volume_limit = -1;
1734 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
1737 p->last_element = e;
1741 static pa_alsa_jack* jack_get(pa_alsa_path *p, const char *section) {
1744 if (!pa_startswith(section, "Jack "))
1748 if (p->last_jack && pa_streq(p->last_jack->name, section))
1749 return p->last_jack;
1751 PA_LLIST_FOREACH(j, p->jacks)
1752 if (pa_streq(j->name, section))
1755 j = pa_xnew0(pa_alsa_jack, 1);
1756 j->state_unplugged = PA_AVAILABLE_NO;
1757 j->state_plugged = PA_AVAILABLE_YES;
1759 j->name = pa_xstrdup(section);
1760 j->alsa_name = pa_sprintf_malloc("%s Jack", section);
1761 PA_LLIST_INSERT_AFTER(pa_alsa_jack, p->jacks, p->last_jack, j);
1768 static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
1774 if (!pa_startswith(section, "Option "))
1779 /* This is not an enum section, but an element section? */
1780 if (!(on = strchr(section, ':')))
1783 en = pa_xstrndup(section, on - section);
1786 if (p->last_option &&
1787 pa_streq(p->last_option->element->alsa_name, en) &&
1788 pa_streq(p->last_option->alsa_name, on)) {
1790 return p->last_option;
1793 pa_assert_se(e = element_get(p, en, FALSE));
1796 PA_LLIST_FOREACH(o, e->options)
1797 if (pa_streq(o->alsa_name, on))
1800 o = pa_xnew0(pa_alsa_option, 1);
1802 o->alsa_name = pa_xstrdup(on);
1805 if (p->last_option && p->last_option->element == e)
1806 PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
1808 PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
1815 static int element_parse_switch(pa_config_parser_state *state) {
1821 p = state->userdata;
1823 if (!(e = element_get(p, state->section, TRUE))) {
1824 pa_log("[%s:%u] Switch makes no sense in '%s'", state->filename, state->lineno, state->section);
1828 if (pa_streq(state->rvalue, "ignore"))
1829 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1830 else if (pa_streq(state->rvalue, "mute"))
1831 e->switch_use = PA_ALSA_SWITCH_MUTE;
1832 else if (pa_streq(state->rvalue, "off"))
1833 e->switch_use = PA_ALSA_SWITCH_OFF;
1834 else if (pa_streq(state->rvalue, "on"))
1835 e->switch_use = PA_ALSA_SWITCH_ON;
1836 else if (pa_streq(state->rvalue, "select"))
1837 e->switch_use = PA_ALSA_SWITCH_SELECT;
1839 pa_log("[%s:%u] Switch invalid of '%s'", state->filename, state->lineno, state->section);
1846 static int element_parse_volume(pa_config_parser_state *state) {
1852 p = state->userdata;
1854 if (!(e = element_get(p, state->section, TRUE))) {
1855 pa_log("[%s:%u] Volume makes no sense in '%s'", state->filename, state->lineno, state->section);
1859 if (pa_streq(state->rvalue, "ignore"))
1860 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1861 else if (pa_streq(state->rvalue, "merge"))
1862 e->volume_use = PA_ALSA_VOLUME_MERGE;
1863 else if (pa_streq(state->rvalue, "off"))
1864 e->volume_use = PA_ALSA_VOLUME_OFF;
1865 else if (pa_streq(state->rvalue, "zero"))
1866 e->volume_use = PA_ALSA_VOLUME_ZERO;
1870 if (pa_atou(state->rvalue, &constant) >= 0) {
1871 e->volume_use = PA_ALSA_VOLUME_CONSTANT;
1872 e->constant_volume = constant;
1874 pa_log("[%s:%u] Volume invalid of '%s'", state->filename, state->lineno, state->section);
1882 static int element_parse_enumeration(pa_config_parser_state *state) {
1888 p = state->userdata;
1890 if (!(e = element_get(p, state->section, TRUE))) {
1891 pa_log("[%s:%u] Enumeration makes no sense in '%s'", state->filename, state->lineno, state->section);
1895 if (pa_streq(state->rvalue, "ignore"))
1896 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1897 else if (pa_streq(state->rvalue, "select"))
1898 e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
1900 pa_log("[%s:%u] Enumeration invalid of '%s'", state->filename, state->lineno, state->section);
1907 static int option_parse_priority(pa_config_parser_state *state) {
1914 p = state->userdata;
1916 if (!(o = option_get(p, state->section))) {
1917 pa_log("[%s:%u] Priority makes no sense in '%s'", state->filename, state->lineno, state->section);
1921 if (pa_atou(state->rvalue, &prio) < 0) {
1922 pa_log("[%s:%u] Priority invalid of '%s'", state->filename, state->lineno, state->section);
1930 static int option_parse_name(pa_config_parser_state *state) {
1936 p = state->userdata;
1938 if (!(o = option_get(p, state->section))) {
1939 pa_log("[%s:%u] Name makes no sense in '%s'", state->filename, state->lineno, state->section);
1944 o->name = pa_xstrdup(state->rvalue);
1949 static int element_parse_required(pa_config_parser_state *state) {
1954 pa_alsa_required_t req;
1958 p = state->userdata;
1960 e = element_get(p, state->section, TRUE);
1961 o = option_get(p, state->section);
1962 j = jack_get(p, state->section);
1963 if (!e && !o && !j) {
1964 pa_log("[%s:%u] Required makes no sense in '%s'", state->filename, state->lineno, state->section);
1968 if (pa_streq(state->rvalue, "ignore"))
1969 req = PA_ALSA_REQUIRED_IGNORE;
1970 else if (pa_streq(state->rvalue, "switch") && e)
1971 req = PA_ALSA_REQUIRED_SWITCH;
1972 else if (pa_streq(state->rvalue, "volume") && e)
1973 req = PA_ALSA_REQUIRED_VOLUME;
1974 else if (pa_streq(state->rvalue, "enumeration"))
1975 req = PA_ALSA_REQUIRED_ENUMERATION;
1976 else if (pa_streq(state->rvalue, "any"))
1977 req = PA_ALSA_REQUIRED_ANY;
1979 pa_log("[%s:%u] Required invalid of '%s'", state->filename, state->lineno, state->section);
1983 if (pa_streq(state->lvalue, "required-absent")) {
1985 e->required_absent = req;
1987 o->required_absent = req;
1989 j->required_absent = req;
1991 else if (pa_streq(state->lvalue, "required-any")) {
1993 e->required_any = req;
1994 e->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
1997 o->required_any = req;
1998 o->element->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
2001 j->required_any = req;
2002 j->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
2018 static int element_parse_direction(pa_config_parser_state *state) {
2024 p = state->userdata;
2026 if (!(e = element_get(p, state->section, TRUE))) {
2027 pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section);
2031 if (pa_streq(state->rvalue, "playback"))
2032 e->direction = PA_ALSA_DIRECTION_OUTPUT;
2033 else if (pa_streq(state->rvalue, "capture"))
2034 e->direction = PA_ALSA_DIRECTION_INPUT;
2036 pa_log("[%s:%u] Direction invalid of '%s'", state->filename, state->lineno, state->section);
2043 static int element_parse_direction_try_other(pa_config_parser_state *state) {
2050 p = state->userdata;
2052 if (!(e = element_get(p, state->section, TRUE))) {
2053 pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section);
2057 if ((yes = pa_parse_boolean(state->rvalue)) < 0) {
2058 pa_log("[%s:%u] Direction invalid of '%s'", state->filename, state->lineno, state->section);
2062 e->direction_try_other = !!yes;
2066 static int element_parse_volume_limit(pa_config_parser_state *state) {
2073 p = state->userdata;
2075 if (!(e = element_get(p, state->section, TRUE))) {
2076 pa_log("[%s:%u] volume-limit makes no sense in '%s'", state->filename, state->lineno, state->section);
2080 if (pa_atol(state->rvalue, &volume_limit) < 0 || volume_limit < 0) {
2081 pa_log("[%s:%u] Invalid value for volume-limit", state->filename, state->lineno);
2085 e->volume_limit = volume_limit;
2089 static pa_channel_position_mask_t parse_mask(const char *m) {
2090 pa_channel_position_mask_t v;
2092 if (pa_streq(m, "all-left"))
2093 v = PA_CHANNEL_POSITION_MASK_LEFT;
2094 else if (pa_streq(m, "all-right"))
2095 v = PA_CHANNEL_POSITION_MASK_RIGHT;
2096 else if (pa_streq(m, "all-center"))
2097 v = PA_CHANNEL_POSITION_MASK_CENTER;
2098 else if (pa_streq(m, "all-front"))
2099 v = PA_CHANNEL_POSITION_MASK_FRONT;
2100 else if (pa_streq(m, "all-rear"))
2101 v = PA_CHANNEL_POSITION_MASK_REAR;
2102 else if (pa_streq(m, "all-side"))
2103 v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER;
2104 else if (pa_streq(m, "all-top"))
2105 v = PA_CHANNEL_POSITION_MASK_TOP;
2106 else if (pa_streq(m, "all-no-lfe"))
2107 v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE);
2108 else if (pa_streq(m, "all"))
2109 v = PA_CHANNEL_POSITION_MASK_ALL;
2111 pa_channel_position_t p;
2113 if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
2116 v = PA_CHANNEL_POSITION_MASK(p);
2122 static int element_parse_override_map(pa_config_parser_state *state) {
2125 const char *split_state = NULL;
2131 p = state->userdata;
2133 if (!(e = element_get(p, state->section, TRUE))) {
2134 pa_log("[%s:%u] Override map makes no sense in '%s'", state->filename, state->lineno, state->section);
2138 while ((n = pa_split(state->rvalue, ",", &split_state))) {
2139 pa_channel_position_mask_t m;
2144 if ((m = parse_mask(n)) == 0) {
2145 pa_log("[%s:%u] Override map '%s' invalid in '%s'", state->filename, state->lineno, n, state->section);
2151 if (pa_streq(state->lvalue, "override-map.1"))
2152 e->masks[i++][0] = m;
2154 e->masks[i++][1] = m;
2156 /* Later on we might add override-map.3 and so on here ... */
2161 e->override_map = TRUE;
2166 static int jack_parse_state(pa_config_parser_state *state) {
2173 p = state->userdata;
2175 if (!(j = jack_get(p, state->section))) {
2176 pa_log("[%s:%u] state makes no sense in '%s'", state->filename, state->lineno, state->section);
2180 if (pa_streq(state->rvalue, "yes"))
2181 pa = PA_AVAILABLE_YES;
2182 else if (pa_streq(state->rvalue, "no"))
2183 pa = PA_AVAILABLE_NO;
2184 else if (pa_streq(state->rvalue, "unknown"))
2185 pa = PA_AVAILABLE_UNKNOWN;
2187 pa_log("[%s:%u] state must be 'yes', 'no' or 'unknown' in '%s'", state->filename, state->lineno, state->section);
2191 if (pa_streq(state->lvalue, "state.unplugged"))
2192 j->state_unplugged = pa;
2194 j->state_plugged = pa;
2195 pa_assert(pa_streq(state->lvalue, "state.plugged"));
2201 static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
2202 snd_mixer_selem_id_t *sid;
2203 snd_mixer_elem_t *me;
2209 SELEM_INIT(sid, e->alsa_name);
2210 if (!(me = snd_mixer_find_selem(m, sid))) {
2211 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2215 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
2217 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
2218 r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
2220 r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
2223 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
2226 pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
2228 if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0)
2229 pa_log_warn("Failed to set enumeration of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
2235 static int setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
2242 PA_IDXSET_FOREACH(o, s->options, idx)
2243 element_set_option(o->element, m, o->alsa_idx);
2248 static int option_verify(pa_alsa_option *o) {
2249 static const struct description_map well_known_descriptions[] = {
2250 { "input", N_("Input") },
2251 { "input-docking", N_("Docking Station Input") },
2252 { "input-docking-microphone", N_("Docking Station Microphone") },
2253 { "input-docking-linein", N_("Docking Station Line In") },
2254 { "input-linein", N_("Line In") },
2255 { "input-microphone", N_("Microphone") },
2256 { "input-microphone-front", N_("Front Microphone") },
2257 { "input-microphone-rear", N_("Rear Microphone") },
2258 { "input-microphone-external", N_("External Microphone") },
2259 { "input-microphone-internal", N_("Internal Microphone") },
2260 { "input-radio", N_("Radio") },
2261 { "input-video", N_("Video") },
2262 { "input-agc-on", N_("Automatic Gain Control") },
2263 { "input-agc-off", N_("No Automatic Gain Control") },
2264 { "input-boost-on", N_("Boost") },
2265 { "input-boost-off", N_("No Boost") },
2266 { "output-amplifier-on", N_("Amplifier") },
2267 { "output-amplifier-off", N_("No Amplifier") },
2268 { "output-bass-boost-on", N_("Bass Boost") },
2269 { "output-bass-boost-off", N_("No Bass Boost") },
2270 { "output-speaker", N_("Speaker") },
2271 { "output-headphones", N_("Headphones") }
2277 pa_log("No name set for option %s", o->alsa_name);
2281 if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
2282 o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
2283 pa_log("Element %s of option %s not set for select.", o->element->alsa_name, o->name);
2287 if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
2288 !pa_streq(o->alsa_name, "on") &&
2289 !pa_streq(o->alsa_name, "off")) {
2290 pa_log("Switch %s options need be named off or on ", o->element->alsa_name);
2294 if (!o->description)
2295 o->description = pa_xstrdup(lookup_description(o->name,
2296 well_known_descriptions,
2297 PA_ELEMENTSOF(well_known_descriptions)));
2298 if (!o->description)
2299 o->description = pa_xstrdup(o->name);
2304 static int element_verify(pa_alsa_element *e) {
2309 // 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);
2310 if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
2311 (e->required_any != PA_ALSA_REQUIRED_IGNORE && e->required_any == e->required_absent) ||
2312 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required_any != PA_ALSA_REQUIRED_IGNORE) ||
2313 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
2314 pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name);
2318 if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
2319 pa_log("Element %s cannot set select for both switch and enumeration.", e->alsa_name);
2323 PA_LLIST_FOREACH(o, e->options)
2324 if (option_verify(o) < 0)
2330 static int path_verify(pa_alsa_path *p) {
2331 static const struct description_map well_known_descriptions[] = {
2332 { "analog-input", N_("Analog Input") },
2333 { "analog-input-microphone", N_("Microphone") },
2334 { "analog-input-microphone-front", N_("Front Microphone") },
2335 { "analog-input-microphone-rear", N_("Rear Microphone") },
2336 { "analog-input-microphone-dock", N_("Dock Microphone") },
2337 { "analog-input-microphone-internal", N_("Internal Microphone") },
2338 { "analog-input-microphone-headset", N_("Headset Microphone") },
2339 { "analog-input-linein", N_("Line In") },
2340 { "analog-input-radio", N_("Radio") },
2341 { "analog-input-video", N_("Video") },
2342 { "analog-output", N_("Analog Output") },
2343 { "analog-output-headphones", N_("Headphones") },
2344 { "analog-output-lfe-on-mono", N_("LFE on Separate Mono Output") },
2345 { "analog-output-lineout", N_("Line Out") },
2346 { "analog-output-mono", N_("Analog Mono Output") },
2347 { "analog-output-speaker", N_("Speakers") },
2348 { "hdmi-output", N_("HDMI / DisplayPort") },
2349 { "iec958-stereo-output", N_("Digital Output (S/PDIF)") },
2350 { "iec958-stereo-input", N_("Digital Input (S/PDIF)") },
2351 { "iec958-passthrough-output", N_("Digital Passthrough (S/PDIF)") }
2358 PA_LLIST_FOREACH(e, p->elements)
2359 if (element_verify(e) < 0)
2362 if (!p->description)
2363 p->description = pa_xstrdup(lookup_description(p->description_key ? p->description_key : p->name,
2364 well_known_descriptions,
2365 PA_ELEMENTSOF(well_known_descriptions)));
2367 if (!p->description) {
2368 if (p->description_key)
2369 pa_log_warn("Path %s: Unrecognized description key: %s", p->name, p->description_key);
2371 p->description = pa_xstrdup(p->name);
2377 static const char *get_default_paths_dir(void) {
2378 if (pa_run_from_build_tree())
2379 return PA_SRCDIR "/modules/alsa/mixer/paths/";
2381 return PA_ALSA_PATHS_DIR;
2384 pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa_direction_t direction) {
2389 bool mute_during_activation = false;
2391 pa_config_item items[] = {
2393 { "priority", pa_config_parse_unsigned, NULL, "General" },
2394 { "description-key", pa_config_parse_string, NULL, "General" },
2395 { "description", pa_config_parse_string, NULL, "General" },
2396 { "mute-during-activation", pa_config_parse_bool, NULL, "General" },
2397 { "eld-device", pa_config_parse_int, NULL, "General" },
2400 { "priority", option_parse_priority, NULL, NULL },
2401 { "name", option_parse_name, NULL, NULL },
2404 { "state.plugged", jack_parse_state, NULL, NULL },
2405 { "state.unplugged", jack_parse_state, NULL, NULL },
2408 { "switch", element_parse_switch, NULL, NULL },
2409 { "volume", element_parse_volume, NULL, NULL },
2410 { "enumeration", element_parse_enumeration, NULL, NULL },
2411 { "override-map.1", element_parse_override_map, NULL, NULL },
2412 { "override-map.2", element_parse_override_map, NULL, NULL },
2413 /* ... later on we might add override-map.3 and so on here ... */
2414 { "required", element_parse_required, NULL, NULL },
2415 { "required-any", element_parse_required, NULL, NULL },
2416 { "required-absent", element_parse_required, NULL, NULL },
2417 { "direction", element_parse_direction, NULL, NULL },
2418 { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
2419 { "volume-limit", element_parse_volume_limit, NULL, NULL },
2420 { NULL, NULL, NULL, NULL }
2425 p = pa_xnew0(pa_alsa_path, 1);
2426 n = pa_path_get_filename(fname);
2427 p->name = pa_xstrndup(n, strcspn(n, "."));
2428 p->proplist = pa_proplist_new();
2429 p->direction = direction;
2432 items[0].data = &p->priority;
2433 items[1].data = &p->description_key;
2434 items[2].data = &p->description;
2435 items[3].data = &mute_during_activation;
2436 items[4].data = &p->eld_device;
2439 paths_dir = get_default_paths_dir();
2441 fn = pa_maybe_prefix_path(fname, paths_dir);
2443 r = pa_config_parse(fn, NULL, items, p->proplist, p);
2449 p->mute_during_activation = mute_during_activation;
2451 if (path_verify(p) < 0)
2457 pa_alsa_path_free(p);
2461 pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction) {
2467 p = pa_xnew0(pa_alsa_path, 1);
2468 p->name = pa_xstrdup(element);
2469 p->direction = direction;
2471 e = pa_xnew0(pa_alsa_element, 1);
2473 e->alsa_name = pa_xstrdup(element);
2474 e->direction = direction;
2475 e->volume_limit = -1;
2477 e->switch_use = PA_ALSA_SWITCH_MUTE;
2478 e->volume_use = PA_ALSA_VOLUME_MERGE;
2480 PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
2481 p->last_element = e;
2485 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
2486 pa_alsa_option *o, *n;
2490 for (o = e->options; o; o = n) {
2493 if (o->alsa_idx < 0) {
2494 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
2500 e->switch_use != PA_ALSA_SWITCH_IGNORE ||
2501 e->volume_use != PA_ALSA_VOLUME_IGNORE ||
2502 e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
2505 static void path_drop_unsupported(pa_alsa_path *p) {
2506 pa_alsa_element *e, *n;
2510 for (e = p->elements; e; e = n) {
2513 if (!element_drop_unsupported(e)) {
2514 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
2520 static void path_make_options_unique(pa_alsa_path *p) {
2522 pa_alsa_option *o, *u;
2524 PA_LLIST_FOREACH(e, p->elements) {
2525 PA_LLIST_FOREACH(o, e->options) {
2529 for (u = o->next; u; u = u->next)
2530 if (pa_streq(u->name, o->name))
2536 m = pa_xstrdup(o->name);
2538 /* OK, this name is not unique, hence let's rename */
2539 for (i = 1, u = o; u; u = u->next) {
2542 if (!pa_streq(u->name, m))
2545 nn = pa_sprintf_malloc("%s-%u", m, i);
2549 nd = pa_sprintf_malloc("%s %u", u->description, i);
2550 pa_xfree(u->description);
2551 u->description = nd;
2561 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
2564 for (; e; e = e->next)
2565 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
2566 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
2572 for (o = e->options; o; o = o->next) {
2576 s = pa_xnewdup(pa_alsa_setting, template, 1);
2577 s->options = pa_idxset_copy(template->options);
2578 s->name = pa_sprintf_malloc("%s+%s", template->name, o->name);
2580 (template->description[0] && o->description[0])
2581 ? pa_sprintf_malloc("%s / %s", template->description, o->description)
2582 : (template->description[0]
2583 ? pa_xstrdup(template->description)
2584 : pa_xstrdup(o->description));
2586 s->priority = PA_MAX(template->priority, o->priority);
2588 s = pa_xnew0(pa_alsa_setting, 1);
2589 s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2590 s->name = pa_xstrdup(o->name);
2591 s->description = pa_xstrdup(o->description);
2592 s->priority = o->priority;
2595 pa_idxset_put(s->options, o, NULL);
2597 if (element_create_settings(e->next, s))
2598 /* This is not a leaf, so let's get rid of it */
2601 /* This is a leaf, so let's add it */
2602 PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2604 e->path->last_setting = s;
2611 static void path_create_settings(pa_alsa_path *p) {
2614 element_create_settings(p->elements, NULL);
2617 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, snd_hctl_t *hctl, pa_bool_t ignore_dB) {
2620 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2621 pa_channel_position_t t;
2622 pa_channel_position_mask_t path_volume_channels = 0;
2628 return p->supported ? 0 : -1;
2634 pa_log_debug("Probing path '%s'", p->name);
2636 PA_LLIST_FOREACH(j, p->jacks) {
2637 if (jack_probe(j, hctl) < 0) {
2638 p->supported = FALSE;
2639 pa_log_debug("Probe of jack '%s' failed.", j->alsa_name);
2642 pa_log_debug("Probe of jack '%s' succeeded (%s)", j->alsa_name, j->has_control ? "found!" : "not found");
2645 PA_LLIST_FOREACH(e, p->elements) {
2646 if (element_probe(e, m) < 0) {
2647 p->supported = FALSE;
2648 pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2651 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);
2656 if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2658 if (!p->has_volume) {
2659 p->min_volume = e->min_volume;
2660 p->max_volume = e->max_volume;
2664 if (!p->has_volume) {
2665 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2666 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2667 min_dB[t] = e->min_dB;
2668 max_dB[t] = e->max_dB;
2669 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2676 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2677 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2678 min_dB[t] += e->min_dB;
2679 max_dB[t] += e->max_dB;
2680 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2683 /* Hmm, there's another element before us
2684 * which cannot do dB volumes, so we we need
2685 * to 'neutralize' this slider */
2686 e->volume_use = PA_ALSA_VOLUME_ZERO;
2687 pa_log_info("Zeroing volume of '%s' on path '%s'", e->alsa_name, p->name);
2690 } else if (p->has_volume) {
2691 /* We can't use this volume, so let's ignore it */
2692 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2693 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e->alsa_name, p->name);
2695 p->has_volume = TRUE;
2698 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2702 if (p->has_req_any && !p->req_any_present) {
2703 p->supported = FALSE;
2704 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name);
2708 path_drop_unsupported(p);
2709 path_make_options_unique(p);
2710 path_create_settings(p);
2712 p->supported = TRUE;
2714 p->min_dB = INFINITY;
2715 p->max_dB = -INFINITY;
2717 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2718 if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) {
2719 if (p->min_dB > min_dB[t])
2720 p->min_dB = min_dB[t];
2722 if (p->max_dB < max_dB[t])
2723 p->max_dB = max_dB[t];
2730 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2733 pa_log_debug("Setting %s (%s) priority=%u",
2735 pa_strnull(s->description),
2739 void pa_alsa_jack_dump(pa_alsa_jack *j) {
2742 pa_log_debug("Jack %s, alsa_name='%s', detection %s", j->name, j->alsa_name, j->has_control ? "possible" : "unavailable");
2745 void pa_alsa_option_dump(pa_alsa_option *o) {
2748 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2750 pa_strnull(o->name),
2751 pa_strnull(o->description),
2756 void pa_alsa_element_dump(pa_alsa_element *e) {
2760 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",
2770 (long long unsigned) e->merged_mask,
2772 pa_yes_no(e->override_map));
2774 PA_LLIST_FOREACH(o, e->options)
2775 pa_alsa_option_dump(o);
2778 void pa_alsa_path_dump(pa_alsa_path *p) {
2784 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2785 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2787 pa_strnull(p->description),
2790 pa_yes_no(p->probed),
2791 pa_yes_no(p->supported),
2792 pa_yes_no(p->has_mute),
2793 pa_yes_no(p->has_volume),
2794 pa_yes_no(p->has_dB),
2795 p->min_volume, p->max_volume,
2796 p->min_dB, p->max_dB);
2798 PA_LLIST_FOREACH(e, p->elements)
2799 pa_alsa_element_dump(e);
2801 PA_LLIST_FOREACH(j, p->jacks)
2802 pa_alsa_jack_dump(j);
2804 PA_LLIST_FOREACH(s, p->settings)
2805 pa_alsa_setting_dump(s);
2808 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2809 snd_mixer_selem_id_t *sid;
2810 snd_mixer_elem_t *me;
2816 SELEM_INIT(sid, e->alsa_name);
2817 if (!(me = snd_mixer_find_selem(m, sid))) {
2818 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2822 snd_mixer_elem_set_callback(me, cb);
2823 snd_mixer_elem_set_callback_private(me, userdata);
2826 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2833 PA_LLIST_FOREACH(e, p->elements)
2834 element_set_callback(e, m, cb, userdata);
2837 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2845 PA_HASHMAP_FOREACH(p, ps->paths, state)
2846 pa_alsa_path_set_callback(p, m, cb, userdata);
2849 static pa_alsa_path *profile_set_get_path(pa_alsa_profile_set *ps, const char *path_name) {
2853 pa_assert(path_name);
2855 if ((path = pa_hashmap_get(ps->output_paths, path_name)))
2858 return pa_hashmap_get(ps->input_paths, path_name);
2861 static void profile_set_add_path(pa_alsa_profile_set *ps, pa_alsa_path *path) {
2865 switch (path->direction) {
2866 case PA_ALSA_DIRECTION_OUTPUT:
2867 pa_assert_se(pa_hashmap_put(ps->output_paths, path->name, path) >= 0);
2870 case PA_ALSA_DIRECTION_INPUT:
2871 pa_assert_se(pa_hashmap_put(ps->input_paths, path->name, path) >= 0);
2875 pa_assert_not_reached();
2879 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction, const char *paths_dir) {
2880 pa_alsa_path_set *ps;
2881 char **pn = NULL, **en = NULL, **ie;
2882 pa_alsa_decibel_fix *db_fix;
2883 void *state, *state2;
2886 pa_assert(m->profile_set);
2887 pa_assert(m->profile_set->decibel_fixes);
2888 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2890 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2893 ps = pa_xnew0(pa_alsa_path_set, 1);
2894 ps->direction = direction;
2895 ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2897 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2898 pn = m->output_path_names;
2900 pn = m->input_path_names;
2905 for (in = pn; *in; in++) {
2906 pa_alsa_path *p = NULL;
2907 pa_bool_t duplicate = FALSE;
2910 for (kn = pn; kn < in; kn++)
2911 if (pa_streq(*kn, *in)) {
2919 p = profile_set_get_path(m->profile_set, *in);
2921 if (p && p->direction != direction) {
2922 pa_log("Configuration error: Path %s is used both as an input and as an output path.", p->name);
2927 char *fn = pa_sprintf_malloc("%s.conf", *in);
2928 p = pa_alsa_path_new(paths_dir, fn, direction);
2931 profile_set_add_path(m->profile_set, p);
2935 pa_hashmap_put(ps->paths, p, p);
2942 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2943 en = m->output_element;
2945 en = m->input_element;
2950 for (ie = en; *ie; ie++) {
2954 p = pa_alsa_path_synthesize(*ie, direction);
2956 /* Mark all other passed elements for require-absent */
2957 for (je = en; *je; je++) {
2963 e = pa_xnew0(pa_alsa_element, 1);
2965 e->alsa_name = pa_xstrdup(*je);
2966 e->direction = direction;
2967 e->required_absent = PA_ALSA_REQUIRED_ANY;
2968 e->volume_limit = -1;
2970 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2971 p->last_element = e;
2974 pa_hashmap_put(ps->paths, *ie, p);
2978 /* Assign decibel fixes to elements. */
2979 PA_HASHMAP_FOREACH(db_fix, m->profile_set->decibel_fixes, state) {
2982 PA_HASHMAP_FOREACH(p, ps->paths, state2) {
2985 PA_LLIST_FOREACH(e, p->elements) {
2986 if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_name)) {
2987 /* The profile set that contains the dB fix may be freed
2988 * before the element, so we have to copy the dB fix
2990 e->db_fix = pa_xnewdup(pa_alsa_decibel_fix, db_fix, 1);
2991 e->db_fix->profile_set = NULL;
2992 e->db_fix->name = pa_xstrdup(db_fix->name);
2993 e->db_fix->db_values = pa_xmemdup(db_fix->db_values, (db_fix->max_step - db_fix->min_step + 1) * sizeof(long));
3003 pa_alsa_path_set_free(ps);
3008 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
3013 pa_log_debug("Path Set %p, direction=%i",
3017 PA_HASHMAP_FOREACH(p, ps->paths, state)
3018 pa_alsa_path_dump(p);
3021 static pa_bool_t options_have_option(pa_alsa_option *options, const char *alsa_name) {
3025 pa_assert(alsa_name);
3027 PA_LLIST_FOREACH(o, options) {
3028 if (pa_streq(o->alsa_name, alsa_name))
3034 static pa_bool_t enumeration_is_subset(pa_alsa_option *a_options, pa_alsa_option *b_options) {
3035 pa_alsa_option *oa, *ob;
3037 if (!a_options) return TRUE;
3038 if (!b_options) return FALSE;
3040 /* If there is an option A offers that B does not, then A is not a subset of B. */
3041 PA_LLIST_FOREACH(oa, a_options) {
3042 pa_bool_t found = FALSE;
3043 PA_LLIST_FOREACH(ob, b_options) {
3044 if (pa_streq(oa->alsa_name, ob->alsa_name)) {
3056 * Compares two elements to see if a is a subset of b
3058 static pa_bool_t element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_t *m) {
3064 * Every state is a subset of itself (with caveats for volume_limits and options)
3065 * IGNORE is a subset of every other state */
3067 /* Check the volume_use */
3068 if (a->volume_use != PA_ALSA_VOLUME_IGNORE) {
3070 /* "Constant" is subset of "Constant" only when their constant values are equal */
3071 if (a->volume_use == PA_ALSA_VOLUME_CONSTANT && b->volume_use == PA_ALSA_VOLUME_CONSTANT && a->constant_volume != b->constant_volume)
3074 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
3075 if (a->volume_use != b->volume_use && b->volume_use != PA_ALSA_VOLUME_MERGE)
3078 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
3079 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
3080 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
3081 if (b->volume_use == PA_ALSA_VOLUME_MERGE && b->volume_limit >= 0) {
3084 if (a->volume_use == PA_ALSA_VOLUME_CONSTANT)
3085 a_limit = a->constant_volume;
3086 else if (a->volume_use == PA_ALSA_VOLUME_ZERO) {
3090 int rounding = (a->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1);
3091 a_limit = decibel_fix_get_step(a->db_fix, &dB, rounding);
3093 snd_mixer_selem_id_t *sid;
3094 snd_mixer_elem_t *me;
3096 SELEM_INIT(sid, a->alsa_name);
3097 if (!(me = snd_mixer_find_selem(m, sid))) {
3098 pa_log_warn("Element %s seems to have disappeared.", a->alsa_name);
3102 if (a->direction == PA_ALSA_DIRECTION_OUTPUT) {
3103 if (snd_mixer_selem_ask_playback_dB_vol(me, dB, +1, &a_limit) < 0)
3106 if (snd_mixer_selem_ask_capture_dB_vol(me, dB, -1, &a_limit) < 0)
3110 } else if (a->volume_use == PA_ALSA_VOLUME_OFF)
3111 a_limit = a->min_volume;
3112 else if (a->volume_use == PA_ALSA_VOLUME_MERGE)
3113 a_limit = a->volume_limit;
3115 /* This should never be reached */
3118 if (a_limit > b->volume_limit)
3122 if (a->volume_use == PA_ALSA_VOLUME_MERGE) {
3124 /* If override-maps are different, they're not subsets */
3125 if (a->n_channels != b->n_channels)
3127 for (s = 0; s <= SND_MIXER_SCHN_LAST; s++)
3128 if (a->masks[s][a->n_channels-1] != b->masks[s][b->n_channels-1]) {
3129 pa_log_debug("Element %s is not a subset - mask a: 0x%" PRIx64 ", mask b: 0x%" PRIx64 ", at channel %d",
3130 a->alsa_name, a->masks[s][a->n_channels-1], b->masks[s][b->n_channels-1], s);
3136 if (a->switch_use != PA_ALSA_SWITCH_IGNORE) {
3137 /* "On" is a subset of "Mute".
3138 * "Off" is a subset of "Mute".
3139 * "On" is a subset of "Select", if there is an "Option:On" in B.
3140 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3141 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3143 if (a->switch_use != b->switch_use) {
3145 if (a->switch_use == PA_ALSA_SWITCH_SELECT || a->switch_use == PA_ALSA_SWITCH_MUTE
3146 || b->switch_use == PA_ALSA_SWITCH_OFF || b->switch_use == PA_ALSA_SWITCH_ON)
3149 if (b->switch_use == PA_ALSA_SWITCH_SELECT) {
3150 if (a->switch_use == PA_ALSA_SWITCH_ON) {
3151 if (!options_have_option(b->options, "on"))
3153 } else if (a->switch_use == PA_ALSA_SWITCH_OFF) {
3154 if (!options_have_option(b->options, "off"))
3158 } else if (a->switch_use == PA_ALSA_SWITCH_SELECT) {
3159 if (!enumeration_is_subset(a->options, b->options))
3164 if (a->enumeration_use != PA_ALSA_ENUMERATION_IGNORE) {
3165 if (b->enumeration_use == PA_ALSA_ENUMERATION_IGNORE)
3167 if (!enumeration_is_subset(a->options, b->options))
3174 static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
3181 /* If we only have one path, then don't bother */
3182 if (pa_hashmap_size(ps->paths) < 2)
3185 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3189 PA_HASHMAP_FOREACH(p2, ps->paths, state2) {
3190 pa_alsa_element *ea, *eb;
3191 pa_alsa_jack *ja, *jb;
3192 bool is_subset = true;
3197 /* If a has a jack that b does not have, a is not a subset */
3198 PA_LLIST_FOREACH(ja, p->jacks) {
3199 bool exists = false;
3201 if (!ja->has_control)
3204 PA_LLIST_FOREACH(jb, p2->jacks) {
3205 if (jb->has_control && pa_streq(jb->alsa_name, ja->alsa_name) &&
3206 (ja->state_plugged == jb->state_plugged) &&
3207 (ja->state_unplugged == jb->state_unplugged)) {
3219 /* Compare the elements of each set... */
3226 else if ((ea && !eb) || (!ea && eb))
3228 else if (pa_streq(ea->alsa_name, eb->alsa_name)) {
3229 if (element_is_subset(ea, eb, m)) {
3239 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p->name, p2->name);
3240 pa_hashmap_remove(ps->paths, p);
3247 static pa_alsa_path* path_set_find_path_by_description(pa_alsa_path_set *ps, const char* description, pa_alsa_path *ignore) {
3251 PA_HASHMAP_FOREACH(p, ps->paths, state)
3252 if (p != ignore && pa_streq(p->description, description))
3258 static void path_set_make_path_descriptions_unique(pa_alsa_path_set *ps) {
3259 pa_alsa_path *p, *q;
3260 void *state, *state2;
3262 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3264 char *old_description;
3266 q = path_set_find_path_by_description(ps, p->description, p);
3271 old_description = pa_xstrdup(p->description);
3273 /* OK, this description is not unique, hence let's rename */
3275 PA_HASHMAP_FOREACH(q, ps->paths, state2) {
3276 char *new_description;
3278 if (!pa_streq(q->description, old_description))
3281 new_description = pa_sprintf_malloc("%s %u", q->description, i);
3282 pa_xfree(q->description);
3283 q->description = new_description;
3288 pa_xfree(old_description);
3292 static void mapping_free(pa_alsa_mapping *m) {
3296 pa_xfree(m->description);
3298 pa_proplist_free(m->proplist);
3300 pa_xstrfreev(m->device_strings);
3301 pa_xstrfreev(m->input_path_names);
3302 pa_xstrfreev(m->output_path_names);
3303 pa_xstrfreev(m->input_element);
3304 pa_xstrfreev(m->output_element);
3305 if (m->input_path_set)
3306 pa_alsa_path_set_free(m->input_path_set);
3307 if (m->output_path_set)
3308 pa_alsa_path_set_free(m->output_path_set);
3310 pa_assert(!m->input_pcm);
3311 pa_assert(!m->output_pcm);
3313 pa_alsa_ucm_mapping_context_free(&m->ucm_context);
3318 static void profile_free(pa_alsa_profile *p) {
3322 pa_xfree(p->description);
3324 pa_xstrfreev(p->input_mapping_names);
3325 pa_xstrfreev(p->output_mapping_names);
3327 if (p->input_mappings)
3328 pa_idxset_free(p->input_mappings, NULL);
3330 if (p->output_mappings)
3331 pa_idxset_free(p->output_mappings, NULL);
3336 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
3339 if (ps->input_paths)
3340 pa_hashmap_free(ps->input_paths, (pa_free_cb_t) pa_alsa_path_free);
3342 if (ps->output_paths)
3343 pa_hashmap_free(ps->output_paths, (pa_free_cb_t) pa_alsa_path_free);
3346 pa_hashmap_free(ps->profiles, (pa_free_cb_t) profile_free);
3349 pa_hashmap_free(ps->mappings, (pa_free_cb_t) mapping_free);
3351 if (ps->decibel_fixes)
3352 pa_hashmap_free(ps->decibel_fixes, (pa_free_cb_t) decibel_fix_free);
3357 pa_alsa_mapping *pa_alsa_mapping_get(pa_alsa_profile_set *ps, const char *name) {
3360 if (!pa_startswith(name, "Mapping "))
3365 if ((m = pa_hashmap_get(ps->mappings, name)))
3368 m = pa_xnew0(pa_alsa_mapping, 1);
3369 m->profile_set = ps;
3370 m->name = pa_xstrdup(name);
3371 pa_channel_map_init(&m->channel_map);
3372 m->proplist = pa_proplist_new();
3374 pa_hashmap_put(ps->mappings, m->name, m);
3379 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
3382 if (!pa_startswith(name, "Profile "))
3387 if ((p = pa_hashmap_get(ps->profiles, name)))
3390 p = pa_xnew0(pa_alsa_profile, 1);
3391 p->profile_set = ps;
3392 p->name = pa_xstrdup(name);
3394 pa_hashmap_put(ps->profiles, p->name, p);
3399 static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) {
3400 pa_alsa_decibel_fix *db_fix;
3402 if (!pa_startswith(name, "DecibelFix "))
3407 if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name)))
3410 db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
3411 db_fix->profile_set = ps;
3412 db_fix->name = pa_xstrdup(name);
3414 pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix);
3419 static int mapping_parse_device_strings(pa_config_parser_state *state) {
3420 pa_alsa_profile_set *ps;
3425 ps = state->userdata;
3427 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3428 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3432 pa_xstrfreev(m->device_strings);
3433 if (!(m->device_strings = pa_split_spaces_strv(state->rvalue))) {
3434 pa_log("[%s:%u] Device string list empty of '%s'", state->filename, state->lineno, state->section);
3441 static int mapping_parse_channel_map(pa_config_parser_state *state) {
3442 pa_alsa_profile_set *ps;
3447 ps = state->userdata;
3449 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3450 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3454 if (!(pa_channel_map_parse(&m->channel_map, state->rvalue))) {
3455 pa_log("[%s:%u] Channel map invalid of '%s'", state->filename, state->lineno, state->section);
3462 static int mapping_parse_paths(pa_config_parser_state *state) {
3463 pa_alsa_profile_set *ps;
3468 ps = state->userdata;
3470 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3471 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3475 if (pa_streq(state->lvalue, "paths-input")) {
3476 pa_xstrfreev(m->input_path_names);
3477 m->input_path_names = pa_split_spaces_strv(state->rvalue);
3479 pa_xstrfreev(m->output_path_names);
3480 m->output_path_names = pa_split_spaces_strv(state->rvalue);
3486 static int mapping_parse_element(pa_config_parser_state *state) {
3487 pa_alsa_profile_set *ps;
3492 ps = state->userdata;
3494 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3495 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3499 if (pa_streq(state->lvalue, "element-input")) {
3500 pa_xstrfreev(m->input_element);
3501 m->input_element = pa_split_spaces_strv(state->rvalue);
3503 pa_xstrfreev(m->output_element);
3504 m->output_element = pa_split_spaces_strv(state->rvalue);
3510 static int mapping_parse_direction(pa_config_parser_state *state) {
3511 pa_alsa_profile_set *ps;
3516 ps = state->userdata;
3518 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3519 pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
3523 if (pa_streq(state->rvalue, "input"))
3524 m->direction = PA_ALSA_DIRECTION_INPUT;
3525 else if (pa_streq(state->rvalue, "output"))
3526 m->direction = PA_ALSA_DIRECTION_OUTPUT;
3527 else if (pa_streq(state->rvalue, "any"))
3528 m->direction = PA_ALSA_DIRECTION_ANY;
3530 pa_log("[%s:%u] Direction %s invalid.", state->filename, state->lineno, state->rvalue);
3537 static int mapping_parse_description(pa_config_parser_state *state) {
3538 pa_alsa_profile_set *ps;
3544 ps = state->userdata;
3546 if ((m = pa_alsa_mapping_get(ps, state->section))) {
3547 pa_xfree(m->description);
3548 m->description = pa_xstrdup(state->rvalue);
3549 } else if ((p = profile_get(ps, state->section))) {
3550 pa_xfree(p->description);
3551 p->description = pa_xstrdup(state->rvalue);
3553 pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
3560 static int mapping_parse_priority(pa_config_parser_state *state) {
3561 pa_alsa_profile_set *ps;
3568 ps = state->userdata;
3570 if (pa_atou(state->rvalue, &prio) < 0) {
3571 pa_log("[%s:%u] Priority invalid of '%s'", state->filename, state->lineno, state->section);
3575 if ((m = pa_alsa_mapping_get(ps, state->section)))
3577 else if ((p = profile_get(ps, state->section)))
3580 pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
3587 static int profile_parse_mappings(pa_config_parser_state *state) {
3588 pa_alsa_profile_set *ps;
3593 ps = state->userdata;
3595 if (!(p = profile_get(ps, state->section))) {
3596 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3600 if (pa_streq(state->lvalue, "input-mappings")) {
3601 pa_xstrfreev(p->input_mapping_names);
3602 p->input_mapping_names = pa_split_spaces_strv(state->rvalue);
3604 pa_xstrfreev(p->output_mapping_names);
3605 p->output_mapping_names = pa_split_spaces_strv(state->rvalue);
3611 static int profile_parse_skip_probe(pa_config_parser_state *state) {
3612 pa_alsa_profile_set *ps;
3618 ps = state->userdata;
3620 if (!(p = profile_get(ps, state->section))) {
3621 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3625 if ((b = pa_parse_boolean(state->rvalue)) < 0) {
3626 pa_log("[%s:%u] Skip probe invalid of '%s'", state->filename, state->lineno, state->section);
3635 static int decibel_fix_parse_db_values(pa_config_parser_state *state) {
3636 pa_alsa_profile_set *ps;
3637 pa_alsa_decibel_fix *db_fix;
3641 unsigned n = 8; /* Current size of the db_values table. */
3642 unsigned min_step = 0;
3643 unsigned max_step = 0;
3644 unsigned i = 0; /* Index to the items table. */
3645 unsigned prev_step = 0;
3650 ps = state->userdata;
3652 if (!(db_fix = decibel_fix_get(ps, state->section))) {
3653 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3657 if (!(items = pa_split_spaces_strv(state->rvalue))) {
3658 pa_log("[%s:%u] Value missing", state->filename, state->lineno);
3662 db_values = pa_xnew(long, n);
3664 while ((item = items[i++])) {
3665 char *s = item; /* Step value string. */
3666 char *d = item; /* dB value string. */
3670 /* Move d forward until it points to a colon or to the end of the item. */
3671 for (; *d && *d != ':'; ++d);
3674 /* item started with colon. */
3675 pa_log("[%s:%u] No step value found in %s", state->filename, state->lineno, item);
3679 if (!*d || !*(d + 1)) {
3680 /* No colon found, or it was the last character in item. */
3681 pa_log("[%s:%u] No dB value found in %s", state->filename, state->lineno, item);
3685 /* pa_atou() needs a null-terminating string. Let's replace the colon
3686 * with a zero byte. */
3689 if (pa_atou(s, &step) < 0) {
3690 pa_log("[%s:%u] Invalid step value: %s", state->filename, state->lineno, s);
3694 if (pa_atod(d, &db) < 0) {
3695 pa_log("[%s:%u] Invalid dB value: %s", state->filename, state->lineno, d);
3699 if (step <= prev_step && i != 1) {
3700 pa_log("[%s:%u] Step value %u not greater than the previous value %u", state->filename, state->lineno, step, prev_step);
3704 if (db < prev_db && i != 1) {
3705 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", state->filename, state->lineno, db, prev_db);
3711 db_values[0] = (long) (db * 100.0);
3715 /* Interpolate linearly. */
3716 double db_increment = (db - prev_db) / (step - prev_step);
3718 for (; prev_step < step; ++prev_step, prev_db += db_increment) {
3720 /* Reallocate the db_values table if it's about to overflow. */
3721 if (prev_step + 1 - min_step == n) {
3723 db_values = pa_xrenew(long, db_values, n);
3726 db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
3733 db_fix->min_step = min_step;
3734 db_fix->max_step = max_step;
3735 pa_xfree(db_fix->db_values);
3736 db_fix->db_values = db_values;
3738 pa_xstrfreev(items);
3743 pa_xstrfreev(items);
3744 pa_xfree(db_values);
3749 static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
3750 pa_alsa_direction_t direction) {
3754 snd_pcm_t *pcm_handle;
3755 pa_alsa_path_set *ps;
3756 snd_mixer_t *mixer_handle;
3757 snd_hctl_t *hctl_handle;
3759 if (direction == PA_ALSA_DIRECTION_OUTPUT) {
3760 if (m->output_path_set)
3761 return; /* Already probed */
3762 m->output_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3763 pcm_handle = m->output_pcm;
3765 if (m->input_path_set)
3766 return; /* Already probed */
3767 m->input_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3768 pcm_handle = m->input_pcm;
3772 return; /* No paths */
3774 pa_assert(pcm_handle);
3776 mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL, &hctl_handle);
3777 if (!mixer_handle || !hctl_handle) {
3778 /* Cannot open mixer, remove all entries */
3779 pa_hashmap_remove_all(ps->paths, NULL);
3783 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3784 if (pa_alsa_path_probe(p, mixer_handle, hctl_handle, m->profile_set->ignore_dB) < 0) {
3785 pa_hashmap_remove(ps->paths, p);
3789 path_set_condense(ps, mixer_handle);
3790 path_set_make_path_descriptions_unique(ps);
3793 snd_mixer_close(mixer_handle);
3795 pa_log_debug("Available mixer paths (after tidying):");
3796 pa_alsa_path_set_dump(ps);
3799 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
3801 static const struct description_map well_known_descriptions[] = {
3802 { "analog-mono", N_("Analog Mono") },
3803 { "analog-stereo", N_("Analog Stereo") },
3804 { "analog-surround-21", N_("Analog Surround 2.1") },
3805 { "analog-surround-30", N_("Analog Surround 3.0") },
3806 { "analog-surround-31", N_("Analog Surround 3.1") },
3807 { "analog-surround-40", N_("Analog Surround 4.0") },
3808 { "analog-surround-41", N_("Analog Surround 4.1") },
3809 { "analog-surround-50", N_("Analog Surround 5.0") },
3810 { "analog-surround-51", N_("Analog Surround 5.1") },
3811 { "analog-surround-61", N_("Analog Surround 6.0") },
3812 { "analog-surround-61", N_("Analog Surround 6.1") },
3813 { "analog-surround-70", N_("Analog Surround 7.0") },
3814 { "analog-surround-71", N_("Analog Surround 7.1") },
3815 { "analog-4-channel-input", N_("Analog 4-channel Input") },
3816 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3817 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3818 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3819 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3820 { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
3821 { "hdmi-stereo", N_("Digital Stereo (HDMI)") },
3822 { "hdmi-surround-51", N_("Digital Surround 5.1 (HDMI)") }
3827 if (!pa_channel_map_valid(&m->channel_map)) {
3828 pa_log("Mapping %s is missing channel map.", m->name);
3832 if (!m->device_strings) {
3833 pa_log("Mapping %s is missing device strings.", m->name);
3837 if ((m->input_path_names && m->input_element) ||
3838 (m->output_path_names && m->output_element)) {
3839 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
3843 if (!m->description)
3844 m->description = pa_xstrdup(lookup_description(m->name,
3845 well_known_descriptions,
3846 PA_ELEMENTSOF(well_known_descriptions)));
3848 if (!m->description)
3849 m->description = pa_xstrdup(m->name);
3852 if (pa_channel_map_equal(&m->channel_map, bonus))
3854 else if (m->channel_map.channels == bonus->channels)
3861 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
3862 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
3866 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3868 pa_strnull(m->description),
3870 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
3871 pa_yes_no(m->supported),
3875 static void profile_set_add_auto_pair(
3876 pa_alsa_profile_set *ps,
3877 pa_alsa_mapping *m, /* output */
3878 pa_alsa_mapping *n /* input */) {
3886 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
3889 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
3893 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
3895 name = pa_sprintf_malloc("output:%s", m->name);
3897 name = pa_sprintf_malloc("input:%s", n->name);
3899 if (pa_hashmap_get(ps->profiles, name)) {
3904 p = pa_xnew0(pa_alsa_profile, 1);
3905 p->profile_set = ps;
3909 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3910 pa_idxset_put(p->output_mappings, m, NULL);
3911 p->priority += m->priority * 100;
3915 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3916 pa_idxset_put(p->input_mappings, n, NULL);
3917 p->priority += n->priority;
3920 pa_hashmap_put(ps->profiles, p->name, p);
3923 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
3924 pa_alsa_mapping *m, *n;
3925 void *m_state, *n_state;
3929 /* The order is important here:
3930 1) try single inputs and outputs before trying their
3931 combination, because if the half-duplex test failed, we don't have
3933 2) try the output right before the input combinations with
3934 that output, because then the output_pcm is not closed between tests.
3936 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3937 profile_set_add_auto_pair(ps, NULL, n);
3939 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
3940 profile_set_add_auto_pair(ps, m, NULL);
3942 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3943 profile_set_add_auto_pair(ps, m, n);
3948 static int profile_verify(pa_alsa_profile *p) {
3950 static const struct description_map well_known_descriptions[] = {
3951 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3952 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3953 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3954 { "off", N_("Off") }
3959 /* Replace the output mapping names by the actual mappings */
3960 if (p->output_mapping_names) {
3963 pa_assert(!p->output_mappings);
3964 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3966 for (name = p->output_mapping_names; *name; name++) {
3969 pa_bool_t duplicate = FALSE;
3971 for (in = name + 1; *in; in++)
3972 if (pa_streq(*name, *in)) {
3980 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
3981 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
3985 pa_idxset_put(p->output_mappings, m, NULL);
3991 pa_xstrfreev(p->output_mapping_names);
3992 p->output_mapping_names = NULL;
3995 /* Replace the input mapping names by the actual mappings */
3996 if (p->input_mapping_names) {
3999 pa_assert(!p->input_mappings);
4000 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4002 for (name = p->input_mapping_names; *name; name++) {
4005 pa_bool_t duplicate = FALSE;
4007 for (in = name + 1; *in; in++)
4008 if (pa_streq(*name, *in)) {
4016 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
4017 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
4021 pa_idxset_put(p->input_mappings, m, NULL);
4027 pa_xstrfreev(p->input_mapping_names);
4028 p->input_mapping_names = NULL;
4031 if (!p->input_mappings && !p->output_mappings) {
4032 pa_log("Profile '%s' lacks mappings.", p->name);
4036 if (!p->description)
4037 p->description = pa_xstrdup(lookup_description(p->name,
4038 well_known_descriptions,
4039 PA_ELEMENTSOF(well_known_descriptions)));
4041 if (!p->description) {
4046 sb = pa_strbuf_new();
4048 if (p->output_mappings)
4049 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4050 if (!pa_strbuf_isempty(sb))
4051 pa_strbuf_puts(sb, " + ");
4053 pa_strbuf_printf(sb, _("%s Output"), m->description);
4056 if (p->input_mappings)
4057 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4058 if (!pa_strbuf_isempty(sb))
4059 pa_strbuf_puts(sb, " + ");
4061 pa_strbuf_printf(sb, _("%s Input"), m->description);
4064 p->description = pa_strbuf_tostring_free(sb);
4070 void pa_alsa_profile_dump(pa_alsa_profile *p) {
4075 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4077 pa_strnull(p->description),
4079 pa_yes_no(p->supported),
4080 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
4081 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
4083 if (p->input_mappings)
4084 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4085 pa_log_debug("Input %s", m->name);
4087 if (p->output_mappings)
4088 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4089 pa_log_debug("Output %s", m->name);
4092 static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
4095 /* Check that the dB mapping has been configured. Since "db-values" is
4096 * currently the only option in the DecibelFix section, and decibel fix
4097 * objects don't get created if a DecibelFix section is empty, this is
4098 * actually a redundant check. Having this may prevent future bugs,
4100 if (!db_fix->db_values) {
4101 pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
4108 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
4109 char *db_values = NULL;
4113 if (db_fix->db_values) {
4115 unsigned long i, nsteps;
4117 pa_assert(db_fix->min_step <= db_fix->max_step);
4118 nsteps = db_fix->max_step - db_fix->min_step + 1;
4120 buf = pa_strbuf_new();
4121 for (i = 0; i < nsteps; ++i)
4122 pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
4124 db_values = pa_strbuf_tostring_free(buf);
4127 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4128 db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
4130 pa_xfree(db_values);
4133 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
4134 pa_alsa_profile_set *ps;
4137 pa_alsa_decibel_fix *db_fix;
4142 static pa_config_item items[] = {
4144 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
4147 { "device-strings", mapping_parse_device_strings, NULL, NULL },
4148 { "channel-map", mapping_parse_channel_map, NULL, NULL },
4149 { "paths-input", mapping_parse_paths, NULL, NULL },
4150 { "paths-output", mapping_parse_paths, NULL, NULL },
4151 { "element-input", mapping_parse_element, NULL, NULL },
4152 { "element-output", mapping_parse_element, NULL, NULL },
4153 { "direction", mapping_parse_direction, NULL, NULL },
4155 /* Shared by [Mapping ...] and [Profile ...] */
4156 { "description", mapping_parse_description, NULL, NULL },
4157 { "priority", mapping_parse_priority, NULL, NULL },
4160 { "input-mappings", profile_parse_mappings, NULL, NULL },
4161 { "output-mappings", profile_parse_mappings, NULL, NULL },
4162 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
4164 /* [DecibelFix ...] */
4165 { "db-values", decibel_fix_parse_db_values, NULL, NULL },
4166 { NULL, NULL, NULL, NULL }
4169 ps = pa_xnew0(pa_alsa_profile_set, 1);
4170 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4171 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4172 ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4173 ps->input_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4174 ps->output_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4176 items[0].data = &ps->auto_profiles;
4179 fname = "default.conf";
4181 fn = pa_maybe_prefix_path(fname,
4182 pa_run_from_build_tree() ? PA_SRCDIR "/modules/alsa/mixer/profile-sets/" :
4183 PA_ALSA_PROFILE_SETS_DIR);
4185 r = pa_config_parse(fn, NULL, items, NULL, ps);
4191 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4192 if (mapping_verify(m, bonus) < 0)
4195 if (ps->auto_profiles)
4196 profile_set_add_auto(ps);
4198 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4199 if (profile_verify(p) < 0)
4202 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4203 if (decibel_fix_verify(db_fix) < 0)
4209 pa_alsa_profile_set_free(ps);
4213 static void profile_finalize_probing(pa_alsa_profile *to_be_finalized, pa_alsa_profile *next) {
4217 if (!to_be_finalized)
4220 if (to_be_finalized->output_mappings)
4221 PA_IDXSET_FOREACH(m, to_be_finalized->output_mappings, idx) {
4226 if (to_be_finalized->supported)
4229 /* If this mapping is also in the next profile, we won't close the
4230 * pcm handle here, because it would get immediately reopened
4232 if (next && next->output_mappings && pa_idxset_get_by_data(next->output_mappings, m, NULL))
4235 snd_pcm_close(m->output_pcm);
4236 m->output_pcm = NULL;
4239 if (to_be_finalized->input_mappings)
4240 PA_IDXSET_FOREACH(m, to_be_finalized->input_mappings, idx) {
4245 if (to_be_finalized->supported)
4248 /* If this mapping is also in the next profile, we won't close the
4249 * pcm handle here, because it would get immediately reopened
4251 if (next && next->input_mappings && pa_idxset_get_by_data(next->input_mappings, m, NULL))
4254 snd_pcm_close(m->input_pcm);
4255 m->input_pcm = NULL;
4259 static snd_pcm_t* mapping_open_pcm(pa_alsa_mapping *m,
4260 const pa_sample_spec *ss,
4263 unsigned default_n_fragments,
4264 unsigned default_fragment_size_msec) {
4266 pa_sample_spec try_ss = *ss;
4267 pa_channel_map try_map = m->channel_map;
4268 snd_pcm_uframes_t try_period_size, try_buffer_size;
4270 try_ss.channels = try_map.channels;
4273 pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
4274 pa_frame_size(&try_ss);
4275 try_buffer_size = default_n_fragments * try_period_size;
4277 return pa_alsa_open_by_template(
4278 m->device_strings, dev_id, NULL, &try_ss,
4279 &try_map, mode, &try_period_size,
4280 &try_buffer_size, 0, NULL, NULL, TRUE);
4283 static void paths_drop_unsupported(pa_hashmap* h) {
4290 p = pa_hashmap_iterate(h, &state, &key);
4292 if (p->supported <= 0) {
4293 pa_hashmap_remove(h, key);
4294 pa_alsa_path_free(p);
4296 p = pa_hashmap_iterate(h, &state, &key);
4300 void pa_alsa_profile_set_probe(
4301 pa_alsa_profile_set *ps,
4303 const pa_sample_spec *ss,
4304 unsigned default_n_fragments,
4305 unsigned default_fragment_size_msec) {
4308 pa_alsa_profile *p, *last = NULL;
4310 pa_hashmap *broken_inputs, *broken_outputs;
4319 broken_inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4320 broken_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4322 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
4325 /* Skip if this is already marked that it is supported (i.e. from the config file) */
4326 if (!p->supported) {
4328 profile_finalize_probing(last, p);
4329 p->supported = TRUE;
4331 if (p->output_mappings) {
4332 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4333 if (pa_hashmap_get(broken_outputs, m) == m) {
4334 pa_log_debug("Skipping profile %s - will not be able to open output:%s", p->name, m->name);
4335 p->supported = FALSE;
4341 if (p->input_mappings && p->supported) {
4342 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4343 if (pa_hashmap_get(broken_inputs, m) == m) {
4344 pa_log_debug("Skipping profile %s - will not be able to open input:%s", p->name, m->name);
4345 p->supported = FALSE;
4352 pa_log_debug("Looking at profile %s", p->name);
4354 /* Check if we can open all new ones */
4355 if (p->output_mappings && p->supported)
4356 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4361 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
4362 if (!(m->output_pcm = mapping_open_pcm(m, ss, dev_id,
4363 SND_PCM_STREAM_PLAYBACK,
4364 default_n_fragments,
4365 default_fragment_size_msec))) {
4366 p->supported = FALSE;
4367 if (pa_idxset_size(p->output_mappings) == 1 &&
4368 ((!p->input_mappings) || pa_idxset_size(p->input_mappings) == 0)) {
4369 pa_log_debug("Caching failure to open output:%s", m->name);
4370 pa_hashmap_put(broken_outputs, m, m);
4376 if (p->input_mappings && p->supported)
4377 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4382 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
4383 if (!(m->input_pcm = mapping_open_pcm(m, ss, dev_id,
4384 SND_PCM_STREAM_CAPTURE,
4385 default_n_fragments,
4386 default_fragment_size_msec))) {
4387 p->supported = FALSE;
4388 if (pa_idxset_size(p->input_mappings) == 1 &&
4389 ((!p->output_mappings) || pa_idxset_size(p->output_mappings) == 0)) {
4390 pa_log_debug("Caching failure to open input:%s", m->name);
4391 pa_hashmap_put(broken_inputs, m, m);
4403 pa_log_debug("Profile %s supported.", p->name);
4405 if (p->output_mappings)
4406 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4408 mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT);
4410 if (p->input_mappings)
4411 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4413 mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT);
4417 profile_finalize_probing(last, NULL);
4419 pa_alsa_profile_set_drop_unsupported(ps);
4421 paths_drop_unsupported(ps->input_paths);
4422 paths_drop_unsupported(ps->output_paths);
4423 pa_hashmap_free(broken_inputs, NULL);
4424 pa_hashmap_free(broken_outputs, NULL);
4429 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
4432 pa_alsa_decibel_fix *db_fix;
4437 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4440 pa_yes_no(ps->auto_profiles),
4441 pa_yes_no(ps->probed),
4442 pa_hashmap_size(ps->mappings),
4443 pa_hashmap_size(ps->profiles),
4444 pa_hashmap_size(ps->decibel_fixes));
4446 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4447 pa_alsa_mapping_dump(m);
4449 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4450 pa_alsa_profile_dump(p);
4452 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4453 pa_alsa_decibel_fix_dump(db_fix);
4456 void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set *ps) {
4461 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
4462 if (!p->supported) {
4463 pa_hashmap_remove(ps->profiles, p->name);
4468 PA_HASHMAP_FOREACH(m, ps->mappings, state) {
4469 if (m->supported <= 0) {
4470 pa_hashmap_remove(ps->mappings, m->name);
4476 static pa_device_port* device_port_alsa_init(pa_hashmap *ports, /* card ports */
4478 const char* description,
4480 pa_alsa_setting *setting,
4481 pa_card_profile *cp,
4482 pa_hashmap *extra, /* sink/source ports */
4489 p = pa_hashmap_get(ports, name);
4492 pa_alsa_port_data *data;
4493 pa_device_port_new_data port_data;
4495 pa_device_port_new_data_init(&port_data);
4496 pa_device_port_new_data_set_name(&port_data, name);
4497 pa_device_port_new_data_set_description(&port_data, description);
4498 pa_device_port_new_data_set_direction(&port_data, path->direction == PA_ALSA_DIRECTION_OUTPUT ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT);
4500 p = pa_device_port_new(core, &port_data, sizeof(pa_alsa_port_data));
4501 pa_device_port_new_data_done(&port_data);
4503 pa_hashmap_put(ports, p->name, p);
4504 pa_proplist_update(p->proplist, PA_UPDATE_REPLACE, path->proplist);
4506 data = PA_DEVICE_PORT_DATA(p);
4508 data->setting = setting;
4513 pa_hashmap_put(p->profiles, cp->name, cp);
4516 pa_hashmap_put(extra, p->name, p);
4517 pa_device_port_ref(p);
4523 void pa_alsa_path_set_add_ports(
4524 pa_alsa_path_set *ps,
4525 pa_card_profile *cp,
4526 pa_hashmap *ports, /* card ports */
4527 pa_hashmap *extra, /* sink/source ports */
4538 PA_HASHMAP_FOREACH(path, ps->paths, state) {
4539 if (!path->settings || !path->settings->next) {
4540 /* If there is no or just one setting we only need a
4542 pa_device_port *port = device_port_alsa_init(ports, path->name,
4543 path->description, path, path->settings, cp, extra, core);
4544 port->priority = path->priority * 100;
4548 PA_LLIST_FOREACH(s, path->settings) {
4549 pa_device_port *port;
4552 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
4554 if (s->description[0])
4555 d = pa_sprintf_malloc("%s / %s", path->description, s->description);
4557 d = pa_xstrdup(path->description);
4559 port = device_port_alsa_init(ports, n, d, path, s, cp, extra, core);
4560 port->priority = path->priority * 100 + s->priority;
4569 void pa_alsa_add_ports(void *sink_or_source_new_data, pa_alsa_path_set *ps, pa_card *card) {
4572 pa_assert(sink_or_source_new_data);
4575 if (ps->direction == PA_ALSA_DIRECTION_OUTPUT)
4576 ports = ((pa_sink_new_data *) sink_or_source_new_data)->ports;
4578 ports = ((pa_source_new_data *) sink_or_source_new_data)->ports;
4580 if (ps->paths && pa_hashmap_size(ps->paths) > 0) {
4582 pa_alsa_path_set_add_ports(ps, NULL, card->ports, ports, card->core);
4585 pa_log_debug("Added %u ports", pa_hashmap_size(ports));