2 This file is part of PulseAudio.
4 Copyright 2004-2009 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 #include <sys/types.h>
28 #include <asoundlib.h>
31 #ifdef HAVE_VALGRIND_MEMCHECK_H
32 #include <valgrind/memcheck.h>
35 #include <pulse/mainloop-api.h>
36 #include <pulse/sample.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/volume.h>
40 #include <pulse/xmalloc.h>
41 #include <pulse/utf8.h>
43 #include <pulsecore/i18n.h>
44 #include <pulsecore/log.h>
45 #include <pulsecore/macro.h>
46 #include <pulsecore/core-util.h>
47 #include <pulsecore/conf-parser.h>
48 #include <pulsecore/strbuf.h>
50 #include "alsa-mixer.h"
51 #include "alsa-util.h"
53 struct description_map {
55 const char *description;
58 static const char *lookup_description(const char *name, const struct description_map dm[], unsigned n) {
61 for (i = 0; i < n; i++)
62 if (pa_streq(dm[i].name, name))
63 return _(dm[i].description);
68 struct pa_alsa_fdlist {
71 /* This is a temporary buffer used to avoid lots of mallocs */
72 struct pollfd *work_fds;
77 pa_defer_event *defer;
82 void (*cb)(void *userdata);
86 static void io_cb(pa_mainloop_api *a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
88 struct pa_alsa_fdlist *fdl = userdata;
91 unsigned short revents;
95 pa_assert(fdl->mixer);
97 pa_assert(fdl->work_fds);
104 memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds);
106 for (i = 0; i < fdl->num_fds; i++) {
107 if (e == fdl->ios[i]) {
108 if (events & PA_IO_EVENT_INPUT)
109 fdl->work_fds[i].revents |= POLLIN;
110 if (events & PA_IO_EVENT_OUTPUT)
111 fdl->work_fds[i].revents |= POLLOUT;
112 if (events & PA_IO_EVENT_ERROR)
113 fdl->work_fds[i].revents |= POLLERR;
114 if (events & PA_IO_EVENT_HANGUP)
115 fdl->work_fds[i].revents |= POLLHUP;
120 pa_assert(i != fdl->num_fds);
122 if ((err = snd_mixer_poll_descriptors_revents(fdl->mixer, fdl->work_fds, fdl->num_fds, &revents)) < 0) {
123 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
127 a->defer_enable(fdl->defer, 1);
130 snd_mixer_handle_events(fdl->mixer);
133 static void defer_cb(pa_mainloop_api *a, pa_defer_event *e, void *userdata) {
134 struct pa_alsa_fdlist *fdl = userdata;
141 pa_assert(fdl->mixer);
143 a->defer_enable(fdl->defer, 0);
145 if ((n = snd_mixer_poll_descriptors_count(fdl->mixer)) < 0) {
146 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
149 num_fds = (unsigned) n;
151 if (num_fds != fdl->num_fds) {
155 pa_xfree(fdl->work_fds);
156 fdl->fds = pa_xnew0(struct pollfd, num_fds);
157 fdl->work_fds = pa_xnew(struct pollfd, num_fds);
160 memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds);
162 if ((err = snd_mixer_poll_descriptors(fdl->mixer, fdl->work_fds, num_fds)) < 0) {
163 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
169 if (memcmp(fdl->fds, fdl->work_fds, sizeof(struct pollfd) * num_fds) == 0)
173 for (i = 0; i < fdl->num_fds; i++)
174 a->io_free(fdl->ios[i]);
176 if (num_fds != fdl->num_fds) {
183 fdl->ios = pa_xnew(pa_io_event*, num_fds);
186 temp = fdl->work_fds;
187 fdl->work_fds = fdl->fds;
190 fdl->num_fds = num_fds;
192 for (i = 0;i < num_fds;i++)
193 fdl->ios[i] = a->io_new(a, fdl->fds[i].fd,
194 ((fdl->fds[i].events & POLLIN) ? PA_IO_EVENT_INPUT : 0) |
195 ((fdl->fds[i].events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0),
199 struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) {
200 struct pa_alsa_fdlist *fdl;
202 fdl = pa_xnew0(struct pa_alsa_fdlist, 1);
207 void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {
212 fdl->m->defer_free(fdl->defer);
218 for (i = 0; i < fdl->num_fds; i++)
219 fdl->m->io_free(fdl->ios[i]);
226 pa_xfree(fdl->work_fds);
231 int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api *m) {
233 pa_assert(mixer_handle);
237 fdl->mixer = mixer_handle;
239 fdl->defer = m->defer_new(m, defer_cb, fdl);
244 struct pa_alsa_mixer_pdata {
246 pa_rtpoll_item *poll_item;
251 struct pa_alsa_mixer_pdata *pa_alsa_mixer_pdata_new(void) {
252 struct pa_alsa_mixer_pdata *pd;
254 pd = pa_xnew0(struct pa_alsa_mixer_pdata, 1);
259 void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata *pd) {
263 pa_rtpoll_item_free(pd->poll_item);
269 static int rtpoll_work_cb(pa_rtpoll_item *i) {
270 struct pa_alsa_mixer_pdata *pd;
273 unsigned short revents = 0;
276 pd = pa_rtpoll_item_get_userdata(i);
278 pa_assert_fp(i == pd->poll_item);
280 p = pa_rtpoll_item_get_pollfd(i, &n_fds);
282 if ((err = snd_mixer_poll_descriptors_revents(pd->mixer, p, n_fds, &revents)) < 0) {
283 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
289 if (revents & (POLLNVAL | POLLERR)) {
290 pa_log_debug("Device disconnected, stopping poll on mixer");
292 } else if (revents & POLLERR) {
293 /* This shouldn't happen. */
294 pa_log_error("Got a POLLERR (revents = %04x), stopping poll on mixer", revents);
298 err = snd_mixer_handle_events(pd->mixer);
300 if (PA_LIKELY(err >= 0)) {
301 pa_rtpoll_item_free(i);
302 pa_alsa_set_mixer_rtpoll(pd, pd->mixer, pd->rtpoll);
304 pa_log_error("Error handling mixer event: %s", pa_alsa_strerror(err));
313 pa_rtpoll_item_free(i);
315 pd->poll_item = NULL;
322 int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata *pd, snd_mixer_t *mixer, pa_rtpoll *rtp) {
331 if ((n = snd_mixer_poll_descriptors_count(mixer)) < 0) {
332 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
336 i = pa_rtpoll_item_new(rtp, PA_RTPOLL_LATE, (unsigned) n);
338 p = pa_rtpoll_item_get_pollfd(i, NULL);
340 memset(p, 0, sizeof(struct pollfd) * n);
342 if ((err = snd_mixer_poll_descriptors(mixer, p, (unsigned) n)) < 0) {
343 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
344 pa_rtpoll_item_free(i);
352 pa_rtpoll_item_set_userdata(i, pd);
353 pa_rtpoll_item_set_work_callback(i, rtpoll_work_cb);
358 static int prepare_mixer(snd_mixer_t *mixer, const char *dev) {
364 if ((err = snd_mixer_attach(mixer, dev)) < 0) {
365 pa_log_info("Unable to attach to mixer %s: %s", dev, pa_alsa_strerror(err));
369 if ((err = snd_mixer_selem_register(mixer, NULL, NULL)) < 0) {
370 pa_log_warn("Unable to register mixer: %s", pa_alsa_strerror(err));
374 if ((err = snd_mixer_load(mixer)) < 0) {
375 pa_log_warn("Unable to load mixer: %s", pa_alsa_strerror(err));
379 pa_log_info("Successfully attached to mixer '%s'", dev);
383 snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device) {
387 snd_pcm_info_t* info;
388 snd_pcm_info_alloca(&info);
392 if ((err = snd_mixer_open(&m, 0)) < 0) {
393 pa_log("Error opening mixer: %s", pa_alsa_strerror(err));
397 /* First, try by name */
398 if ((dev = snd_pcm_name(pcm)))
399 if (prepare_mixer(m, dev) >= 0) {
401 *ctl_device = pa_xstrdup(dev);
406 /* Then, try by card index */
407 if (snd_pcm_info(pcm, info) >= 0) {
411 if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
413 md = pa_sprintf_malloc("hw:%i", card_idx);
415 if (!dev || !pa_streq(dev, md))
416 if (prepare_mixer(m, md) >= 0) {
434 static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_MAX] = {
435 [PA_CHANNEL_POSITION_MONO] = SND_MIXER_SCHN_MONO, /* The ALSA name is just an alias! */
437 [PA_CHANNEL_POSITION_FRONT_CENTER] = SND_MIXER_SCHN_FRONT_CENTER,
438 [PA_CHANNEL_POSITION_FRONT_LEFT] = SND_MIXER_SCHN_FRONT_LEFT,
439 [PA_CHANNEL_POSITION_FRONT_RIGHT] = SND_MIXER_SCHN_FRONT_RIGHT,
441 [PA_CHANNEL_POSITION_REAR_CENTER] = SND_MIXER_SCHN_REAR_CENTER,
442 [PA_CHANNEL_POSITION_REAR_LEFT] = SND_MIXER_SCHN_REAR_LEFT,
443 [PA_CHANNEL_POSITION_REAR_RIGHT] = SND_MIXER_SCHN_REAR_RIGHT,
445 [PA_CHANNEL_POSITION_LFE] = SND_MIXER_SCHN_WOOFER,
447 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
448 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
450 [PA_CHANNEL_POSITION_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT,
451 [PA_CHANNEL_POSITION_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT,
453 [PA_CHANNEL_POSITION_AUX0] = SND_MIXER_SCHN_UNKNOWN,
454 [PA_CHANNEL_POSITION_AUX1] = SND_MIXER_SCHN_UNKNOWN,
455 [PA_CHANNEL_POSITION_AUX2] = SND_MIXER_SCHN_UNKNOWN,
456 [PA_CHANNEL_POSITION_AUX3] = SND_MIXER_SCHN_UNKNOWN,
457 [PA_CHANNEL_POSITION_AUX4] = SND_MIXER_SCHN_UNKNOWN,
458 [PA_CHANNEL_POSITION_AUX5] = SND_MIXER_SCHN_UNKNOWN,
459 [PA_CHANNEL_POSITION_AUX6] = SND_MIXER_SCHN_UNKNOWN,
460 [PA_CHANNEL_POSITION_AUX7] = SND_MIXER_SCHN_UNKNOWN,
461 [PA_CHANNEL_POSITION_AUX8] = SND_MIXER_SCHN_UNKNOWN,
462 [PA_CHANNEL_POSITION_AUX9] = SND_MIXER_SCHN_UNKNOWN,
463 [PA_CHANNEL_POSITION_AUX10] = SND_MIXER_SCHN_UNKNOWN,
464 [PA_CHANNEL_POSITION_AUX11] = SND_MIXER_SCHN_UNKNOWN,
465 [PA_CHANNEL_POSITION_AUX12] = SND_MIXER_SCHN_UNKNOWN,
466 [PA_CHANNEL_POSITION_AUX13] = SND_MIXER_SCHN_UNKNOWN,
467 [PA_CHANNEL_POSITION_AUX14] = SND_MIXER_SCHN_UNKNOWN,
468 [PA_CHANNEL_POSITION_AUX15] = SND_MIXER_SCHN_UNKNOWN,
469 [PA_CHANNEL_POSITION_AUX16] = SND_MIXER_SCHN_UNKNOWN,
470 [PA_CHANNEL_POSITION_AUX17] = SND_MIXER_SCHN_UNKNOWN,
471 [PA_CHANNEL_POSITION_AUX18] = SND_MIXER_SCHN_UNKNOWN,
472 [PA_CHANNEL_POSITION_AUX19] = SND_MIXER_SCHN_UNKNOWN,
473 [PA_CHANNEL_POSITION_AUX20] = SND_MIXER_SCHN_UNKNOWN,
474 [PA_CHANNEL_POSITION_AUX21] = SND_MIXER_SCHN_UNKNOWN,
475 [PA_CHANNEL_POSITION_AUX22] = SND_MIXER_SCHN_UNKNOWN,
476 [PA_CHANNEL_POSITION_AUX23] = SND_MIXER_SCHN_UNKNOWN,
477 [PA_CHANNEL_POSITION_AUX24] = SND_MIXER_SCHN_UNKNOWN,
478 [PA_CHANNEL_POSITION_AUX25] = SND_MIXER_SCHN_UNKNOWN,
479 [PA_CHANNEL_POSITION_AUX26] = SND_MIXER_SCHN_UNKNOWN,
480 [PA_CHANNEL_POSITION_AUX27] = SND_MIXER_SCHN_UNKNOWN,
481 [PA_CHANNEL_POSITION_AUX28] = SND_MIXER_SCHN_UNKNOWN,
482 [PA_CHANNEL_POSITION_AUX29] = SND_MIXER_SCHN_UNKNOWN,
483 [PA_CHANNEL_POSITION_AUX30] = SND_MIXER_SCHN_UNKNOWN,
484 [PA_CHANNEL_POSITION_AUX31] = SND_MIXER_SCHN_UNKNOWN,
486 [PA_CHANNEL_POSITION_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN,
488 [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SND_MIXER_SCHN_UNKNOWN,
489 [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SND_MIXER_SCHN_UNKNOWN,
490 [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SND_MIXER_SCHN_UNKNOWN,
492 [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SND_MIXER_SCHN_UNKNOWN,
493 [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SND_MIXER_SCHN_UNKNOWN,
494 [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SND_MIXER_SCHN_UNKNOWN
497 static void setting_free(pa_alsa_setting *s) {
501 pa_idxset_free(s->options, NULL, NULL);
504 pa_xfree(s->description);
508 static void option_free(pa_alsa_option *o) {
511 pa_xfree(o->alsa_name);
513 pa_xfree(o->description);
517 static void decibel_fix_free(pa_alsa_decibel_fix *db_fix) {
520 pa_xfree(db_fix->name);
521 pa_xfree(db_fix->db_values);
526 static void element_free(pa_alsa_element *e) {
530 while ((o = e->options)) {
531 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
536 decibel_fix_free(e->db_fix);
538 pa_xfree(e->alsa_name);
542 void pa_alsa_path_free(pa_alsa_path *p) {
548 while ((e = p->elements)) {
549 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
553 while ((s = p->settings)) {
554 PA_LLIST_REMOVE(pa_alsa_setting, p->settings, s);
559 pa_xfree(p->description);
563 void pa_alsa_path_set_free(pa_alsa_path_set *ps) {
567 pa_hashmap_free(ps->paths, NULL, NULL);
572 static long to_alsa_dB(pa_volume_t v) {
573 return (long) (pa_sw_volume_to_dB(v) * 100.0);
576 static pa_volume_t from_alsa_dB(long v) {
577 return pa_sw_volume_from_dB((double) v / 100.0);
580 static long to_alsa_volume(pa_volume_t v, long min, long max) {
583 w = (long) round(((double) v * (double) (max - min)) / PA_VOLUME_NORM) + min;
584 return PA_CLAMP_UNLIKELY(w, min, max);
587 static pa_volume_t from_alsa_volume(long v, long min, long max) {
588 return (pa_volume_t) round(((double) (v - min) * PA_VOLUME_NORM) / (double) (max - min));
591 #define SELEM_INIT(sid, name) \
593 snd_mixer_selem_id_alloca(&(sid)); \
594 snd_mixer_selem_id_set_name((sid), (name)); \
595 snd_mixer_selem_id_set_index((sid), 0); \
598 static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
599 snd_mixer_selem_id_t *sid;
600 snd_mixer_elem_t *me;
601 snd_mixer_selem_channel_id_t c;
602 pa_channel_position_mask_t mask = 0;
610 SELEM_INIT(sid, e->alsa_name);
611 if (!(me = snd_mixer_find_selem(m, sid))) {
612 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
616 pa_cvolume_mute(v, cm->channels);
618 /* We take the highest volume of all channels that match */
620 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
627 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
628 if (snd_mixer_selem_has_playback_channel(me, c)) {
630 if ((r = snd_mixer_selem_get_playback_volume(me, c, &value)) >= 0) {
631 /* If the channel volume is outside the limits set
632 * by the dB fix, we clamp the hw volume to be
633 * within the limits. */
634 if (value < e->db_fix->min_step) {
635 value = e->db_fix->min_step;
636 snd_mixer_selem_set_playback_volume(me, c, value);
637 pa_log_debug("Playback volume for element %s channel %i was below 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);
640 } else if (value > e->db_fix->max_step) {
641 value = e->db_fix->max_step;
642 snd_mixer_selem_set_playback_volume(me, c, value);
643 pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
644 "Volume reset to %0.2f dB.", e->alsa_name, c,
645 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
648 /* Volume step -> dB value conversion. */
649 value = e->db_fix->db_values[value - e->db_fix->min_step];
652 r = snd_mixer_selem_get_playback_dB(me, c, &value);
656 if (snd_mixer_selem_has_capture_channel(me, c)) {
658 if ((r = snd_mixer_selem_get_capture_volume(me, c, &value)) >= 0) {
659 /* If the channel volume is outside the limits set
660 * by the dB fix, we clamp the hw volume to be
661 * within the limits. */
662 if (value < e->db_fix->min_step) {
663 value = e->db_fix->min_step;
664 snd_mixer_selem_set_capture_volume(me, c, value);
665 pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
666 "Volume reset to %0.2f dB.", e->alsa_name, c,
667 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
668 } else if (value > e->db_fix->max_step) {
669 value = e->db_fix->max_step;
670 snd_mixer_selem_set_capture_volume(me, c, value);
671 pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
672 "Volume reset to %0.2f dB.", e->alsa_name, c,
673 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
676 /* Volume step -> dB value conversion. */
677 value = e->db_fix->db_values[value - e->db_fix->min_step];
680 r = snd_mixer_selem_get_capture_dB(me, c, &value);
688 #ifdef HAVE_VALGRIND_MEMCHECK_H
689 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
692 f = from_alsa_dB(value);
697 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
698 if (snd_mixer_selem_has_playback_channel(me, c))
699 r = snd_mixer_selem_get_playback_volume(me, c, &value);
703 if (snd_mixer_selem_has_capture_channel(me, c))
704 r = snd_mixer_selem_get_capture_volume(me, c, &value);
712 f = from_alsa_volume(value, e->min_volume, e->max_volume);
715 for (k = 0; k < cm->channels; k++)
716 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
717 if (v->values[k] < f)
720 mask |= e->masks[c][e->n_channels-1];
723 for (k = 0; k < cm->channels; k++)
724 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
725 v->values[k] = PA_VOLUME_NORM;
730 int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
741 pa_cvolume_reset(v, cm->channels);
743 PA_LLIST_FOREACH(e, p->elements) {
746 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
749 pa_assert(!p->has_dB || e->has_dB);
751 if (element_get_volume(e, m, cm, &ev) < 0)
754 /* If we have no dB information all we can do is take the first element and leave */
760 pa_sw_cvolume_multiply(v, v, &ev);
766 static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t *b) {
767 snd_mixer_selem_id_t *sid;
768 snd_mixer_elem_t *me;
769 snd_mixer_selem_channel_id_t c;
775 SELEM_INIT(sid, e->alsa_name);
776 if (!(me = snd_mixer_find_selem(m, sid))) {
777 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
781 /* We return muted if at least one channel is muted */
783 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
787 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
788 if (snd_mixer_selem_has_playback_channel(me, c))
789 r = snd_mixer_selem_get_playback_switch(me, c, &value);
793 if (snd_mixer_selem_has_capture_channel(me, c))
794 r = snd_mixer_selem_get_capture_switch(me, c, &value);
812 int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t *muted) {
822 PA_LLIST_FOREACH(e, p->elements) {
825 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
828 if (element_get_switch(e, m, &b) < 0)
841 /* Finds the closest item in db_fix->db_values and returns the corresponding
842 * step. *db_value is replaced with the value from the db_values table.
843 * Rounding is done based on the rounding parameter: -1 means rounding down and
844 * +1 means rounding up. */
845 static long decibel_fix_get_step(pa_alsa_decibel_fix *db_fix, long *db_value, int rounding) {
851 pa_assert(rounding != 0);
853 max_i = db_fix->max_step - db_fix->min_step;
856 for (i = 0; i < max_i; i++) {
857 if (db_fix->db_values[i] >= *db_value)
861 for (i = 0; i < max_i; i++) {
862 if (db_fix->db_values[i + 1] > *db_value)
867 *db_value = db_fix->db_values[i];
869 return i + db_fix->min_step;
872 /* Alsa lib documentation says for snd_mixer_selem_set_playback_dB() direction argument,
873 * that "-1 = accurate or first below, 0 = accurate, 1 = accurate or first above".
874 * But even with accurate nearest dB volume step is not selected, so that is why we need
875 * this function. Returns 0 and nearest selectable volume in *value_dB on success or
876 * negative error code if fails. */
877 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) {
887 if (d == PA_ALSA_DIRECTION_OUTPUT) {
888 if ((r = snd_mixer_selem_ask_playback_dB_vol(me, *value_dB, +1, &alsa_val)) >= 0)
889 r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value_high);
894 if (value_high == *value_dB)
897 if ((r = snd_mixer_selem_ask_playback_dB_vol(me, *value_dB, -1, &alsa_val)) >= 0)
898 r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value_low);
900 if ((r = snd_mixer_selem_ask_capture_dB_vol(me, *value_dB, +1, &alsa_val)) >= 0)
901 r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value_high);
906 if (value_high == *value_dB)
909 if ((r = snd_mixer_selem_ask_capture_dB_vol(me, *value_dB, -1, &alsa_val)) >= 0)
910 r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value_low);
916 if (labs(value_high - *value_dB) < labs(value_low - *value_dB))
917 *value_dB = value_high;
919 *value_dB = value_low;
924 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) {
926 snd_mixer_selem_id_t *sid;
928 snd_mixer_elem_t *me;
929 snd_mixer_selem_channel_id_t c;
930 pa_channel_position_mask_t mask = 0;
937 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
939 SELEM_INIT(sid, e->alsa_name);
940 if (!(me = snd_mixer_find_selem(m, sid))) {
941 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
945 pa_cvolume_mute(&rv, cm->channels);
947 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
949 pa_volume_t f = PA_VOLUME_MUTED;
950 pa_bool_t found = FALSE;
952 for (k = 0; k < cm->channels; k++)
953 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) {
955 if (v->values[k] > f)
960 /* Hmm, so this channel does not exist in the volume
961 * struct, so let's bind it to the overall max of the
963 f = pa_cvolume_max(v);
967 long value = to_alsa_dB(f);
970 if (e->volume_limit >= 0 && value > (e->max_dB * 100))
971 value = e->max_dB * 100;
973 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
974 /* If we call set_playback_volume() without checking first
975 * if the channel is available, ALSA behaves very
976 * strangely and doesn't fail the call */
977 if (snd_mixer_selem_has_playback_channel(me, c)) {
981 r = snd_mixer_selem_set_playback_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding));
983 decibel_fix_get_step(e->db_fix, &value, rounding);
989 if (deferred_volume) {
990 if ((r = element_get_nearest_alsa_dB(me, c, PA_ALSA_DIRECTION_OUTPUT, &value)) >= 0)
991 r = snd_mixer_selem_set_playback_dB(me, c, value, 0);
993 if ((r = snd_mixer_selem_set_playback_dB(me, c, value, rounding)) >= 0)
994 r = snd_mixer_selem_get_playback_dB(me, c, &value);
998 if ((r = snd_mixer_selem_ask_playback_dB_vol(me, value, rounding, &alsa_val)) >= 0)
999 r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value);
1005 if (snd_mixer_selem_has_capture_channel(me, c)) {
1009 r = snd_mixer_selem_set_capture_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding));
1011 decibel_fix_get_step(e->db_fix, &value, rounding);
1017 if (deferred_volume) {
1018 if ((r = element_get_nearest_alsa_dB(me, c, PA_ALSA_DIRECTION_INPUT, &value)) >= 0)
1019 r = snd_mixer_selem_set_capture_dB(me, c, value, 0);
1021 if ((r = snd_mixer_selem_set_capture_dB(me, c, value, rounding)) >= 0)
1022 r = snd_mixer_selem_get_capture_dB(me, c, &value);
1026 if ((r = snd_mixer_selem_ask_capture_dB_vol(me, value, rounding, &alsa_val)) >= 0)
1027 r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value);
1037 #ifdef HAVE_VALGRIND_MEMCHECK_H
1038 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
1041 f = from_alsa_dB(value);
1046 value = to_alsa_volume(f, e->min_volume, e->max_volume);
1048 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1049 if (snd_mixer_selem_has_playback_channel(me, c)) {
1050 if ((r = snd_mixer_selem_set_playback_volume(me, c, value)) >= 0)
1051 r = snd_mixer_selem_get_playback_volume(me, c, &value);
1055 if (snd_mixer_selem_has_capture_channel(me, c)) {
1056 if ((r = snd_mixer_selem_set_capture_volume(me, c, value)) >= 0)
1057 r = snd_mixer_selem_get_capture_volume(me, c, &value);
1065 f = from_alsa_volume(value, e->min_volume, e->max_volume);
1068 for (k = 0; k < cm->channels; k++)
1069 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
1070 if (rv.values[k] < f)
1073 mask |= e->masks[c][e->n_channels-1];
1076 for (k = 0; k < cm->channels; k++)
1077 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
1078 rv.values[k] = PA_VOLUME_NORM;
1084 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) {
1093 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
1098 rv = *v; /* Remaining adjustment */
1099 pa_cvolume_reset(v, cm->channels); /* Adjustment done */
1101 PA_LLIST_FOREACH(e, p->elements) {
1104 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
1107 pa_assert(!p->has_dB || e->has_dB);
1110 if (element_set_volume(e, m, cm, &ev, deferred_volume, write_to_hw) < 0)
1118 pa_sw_cvolume_multiply(v, v, &ev);
1119 pa_sw_cvolume_divide(&rv, &rv, &ev);
1125 static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t b) {
1126 snd_mixer_elem_t *me;
1127 snd_mixer_selem_id_t *sid;
1133 SELEM_INIT(sid, e->alsa_name);
1134 if (!(me = snd_mixer_find_selem(m, sid))) {
1135 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1139 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1140 r = snd_mixer_selem_set_playback_switch_all(me, b);
1142 r = snd_mixer_selem_set_capture_switch_all(me, b);
1145 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1150 int pa_alsa_path_set_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t muted) {
1159 PA_LLIST_FOREACH(e, p->elements) {
1161 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
1164 if (element_set_switch(e, m, !muted) < 0)
1171 /* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
1172 * function sets all channels of the volume element to e->min_volume, 0 dB or
1173 * e->constant_volume. */
1174 static int element_set_constant_volume(pa_alsa_element *e, snd_mixer_t *m) {
1175 snd_mixer_elem_t *me = NULL;
1176 snd_mixer_selem_id_t *sid = NULL;
1179 pa_bool_t volume_set = FALSE;
1184 SELEM_INIT(sid, e->alsa_name);
1185 if (!(me = snd_mixer_find_selem(m, sid))) {
1186 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1190 switch (e->volume_use) {
1191 case PA_ALSA_VOLUME_OFF:
1192 volume = e->min_volume;
1196 case PA_ALSA_VOLUME_ZERO:
1200 volume = decibel_fix_get_step(e->db_fix, &dB, (e->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1));
1205 case PA_ALSA_VOLUME_CONSTANT:
1206 volume = e->constant_volume;
1211 pa_assert_not_reached();
1215 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1216 r = snd_mixer_selem_set_playback_volume_all(me, volume);
1218 r = snd_mixer_selem_set_capture_volume_all(me, volume);
1220 pa_assert(e->volume_use == PA_ALSA_VOLUME_ZERO);
1221 pa_assert(!e->db_fix);
1223 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1224 r = snd_mixer_selem_set_playback_dB_all(me, 0, +1);
1226 r = snd_mixer_selem_set_capture_dB_all(me, 0, -1);
1230 pa_log_warn("Failed to set volume of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1235 int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
1242 pa_log_debug("Activating path %s", p->name);
1243 pa_alsa_path_dump(p);
1245 PA_LLIST_FOREACH(e, p->elements) {
1247 switch (e->switch_use) {
1248 case PA_ALSA_SWITCH_OFF:
1249 r = element_set_switch(e, m, FALSE);
1252 case PA_ALSA_SWITCH_ON:
1253 r = element_set_switch(e, m, TRUE);
1256 case PA_ALSA_SWITCH_MUTE:
1257 case PA_ALSA_SWITCH_IGNORE:
1258 case PA_ALSA_SWITCH_SELECT:
1266 switch (e->volume_use) {
1267 case PA_ALSA_VOLUME_OFF:
1268 case PA_ALSA_VOLUME_ZERO:
1269 case PA_ALSA_VOLUME_CONSTANT:
1270 r = element_set_constant_volume(e, m);
1273 case PA_ALSA_VOLUME_MERGE:
1274 case PA_ALSA_VOLUME_IGNORE:
1286 static int check_required(pa_alsa_element *e, snd_mixer_elem_t *me) {
1287 pa_bool_t has_switch;
1288 pa_bool_t has_enumeration;
1289 pa_bool_t has_volume;
1294 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1296 snd_mixer_selem_has_playback_switch(me) ||
1297 (e->direction_try_other && snd_mixer_selem_has_capture_switch(me));
1300 snd_mixer_selem_has_capture_switch(me) ||
1301 (e->direction_try_other && snd_mixer_selem_has_playback_switch(me));
1304 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1306 snd_mixer_selem_has_playback_volume(me) ||
1307 (e->direction_try_other && snd_mixer_selem_has_capture_volume(me));
1310 snd_mixer_selem_has_capture_volume(me) ||
1311 (e->direction_try_other && snd_mixer_selem_has_playback_volume(me));
1314 has_enumeration = snd_mixer_selem_is_enumerated(me);
1316 if ((e->required == PA_ALSA_REQUIRED_SWITCH && !has_switch) ||
1317 (e->required == PA_ALSA_REQUIRED_VOLUME && !has_volume) ||
1318 (e->required == PA_ALSA_REQUIRED_ENUMERATION && !has_enumeration))
1321 if (e->required == PA_ALSA_REQUIRED_ANY && !(has_switch || has_volume || has_enumeration))
1324 if ((e->required_absent == PA_ALSA_REQUIRED_SWITCH && has_switch) ||
1325 (e->required_absent == PA_ALSA_REQUIRED_VOLUME && has_volume) ||
1326 (e->required_absent == PA_ALSA_REQUIRED_ENUMERATION && has_enumeration))
1329 if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration))
1332 if (e->required_any != PA_ALSA_REQUIRED_IGNORE) {
1333 switch (e->required_any) {
1334 case PA_ALSA_REQUIRED_VOLUME:
1335 e->path->req_any_present |= (e->volume_use != PA_ALSA_VOLUME_IGNORE);
1337 case PA_ALSA_REQUIRED_SWITCH:
1338 e->path->req_any_present |= (e->switch_use != PA_ALSA_SWITCH_IGNORE);
1340 case PA_ALSA_REQUIRED_ENUMERATION:
1341 e->path->req_any_present |= (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
1343 case PA_ALSA_REQUIRED_ANY:
1344 e->path->req_any_present |=
1345 (e->volume_use != PA_ALSA_VOLUME_IGNORE) ||
1346 (e->switch_use != PA_ALSA_SWITCH_IGNORE) ||
1347 (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
1350 pa_assert_not_reached();
1354 if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1356 PA_LLIST_FOREACH(o, e->options) {
1357 e->path->req_any_present |= (o->required_any != PA_ALSA_REQUIRED_IGNORE) &&
1359 if (o->required != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx < 0)
1361 if (o->required_absent != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx >= 0)
1369 static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
1370 snd_mixer_selem_id_t *sid;
1371 snd_mixer_elem_t *me;
1377 SELEM_INIT(sid, e->alsa_name);
1379 if (!(me = snd_mixer_find_selem(m, sid))) {
1381 if (e->required != PA_ALSA_REQUIRED_IGNORE)
1384 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1385 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1386 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1391 if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
1392 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1394 if (!snd_mixer_selem_has_playback_switch(me)) {
1395 if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
1396 e->direction = PA_ALSA_DIRECTION_INPUT;
1398 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1403 if (!snd_mixer_selem_has_capture_switch(me)) {
1404 if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
1405 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1407 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1411 if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
1412 e->direction_try_other = FALSE;
1415 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1417 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1419 if (!snd_mixer_selem_has_playback_volume(me)) {
1420 if (e->direction_try_other && snd_mixer_selem_has_capture_volume(me))
1421 e->direction = PA_ALSA_DIRECTION_INPUT;
1423 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1428 if (!snd_mixer_selem_has_capture_volume(me)) {
1429 if (e->direction_try_other && snd_mixer_selem_has_playback_volume(me))
1430 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1432 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1436 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1437 long min_dB = 0, max_dB = 0;
1440 e->direction_try_other = FALSE;
1442 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1443 r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume);
1445 r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
1448 pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1452 if (e->min_volume >= e->max_volume) {
1453 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);
1454 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1456 } else if (e->volume_use == PA_ALSA_VOLUME_CONSTANT &&
1457 (e->min_volume > e->constant_volume || e->max_volume < e->constant_volume)) {
1458 pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
1459 e->constant_volume, e->alsa_name, e->min_volume, e->max_volume);
1460 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1464 pa_channel_position_t p;
1467 ((e->min_volume > e->db_fix->min_step) ||
1468 (e->max_volume < e->db_fix->max_step))) {
1469 pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1470 "real hardware range (%li-%li). Disabling the decibel fix.", e->alsa_name,
1471 e->db_fix->min_step, e->db_fix->max_step,
1472 e->min_volume, e->max_volume);
1474 decibel_fix_free(e->db_fix);
1480 e->min_volume = e->db_fix->min_step;
1481 e->max_volume = e->db_fix->max_step;
1482 min_dB = e->db_fix->db_values[0];
1483 max_dB = e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step];
1484 } else if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1485 e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0;
1487 e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0;
1489 /* Check that the kernel driver returns consistent limits with
1490 * both _get_*_dB_range() and _ask_*_vol_dB(). */
1491 if (e->has_dB && !e->db_fix) {
1492 long min_dB_checked = 0;
1493 long max_dB_checked = 0;
1495 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1496 r = snd_mixer_selem_ask_playback_vol_dB(me, e->min_volume, &min_dB_checked);
1498 r = snd_mixer_selem_ask_capture_vol_dB(me, e->min_volume, &min_dB_checked);
1501 pa_log_warn("Failed to query the dB value for %s at volume level %li", e->alsa_name, e->min_volume);
1505 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1506 r = snd_mixer_selem_ask_playback_vol_dB(me, e->max_volume, &max_dB_checked);
1508 r = snd_mixer_selem_ask_capture_vol_dB(me, e->max_volume, &max_dB_checked);
1511 pa_log_warn("Failed to query the dB value for %s at volume level %li", e->alsa_name, e->max_volume);
1515 if (min_dB != min_dB_checked || max_dB != max_dB_checked) {
1516 pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
1517 "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
1518 "%0.2f dB at level %li.",
1520 min_dB / 100.0, max_dB / 100.0,
1521 min_dB_checked / 100.0, e->min_volume, max_dB_checked / 100.0, e->max_volume);
1527 #ifdef HAVE_VALGRIND_MEMCHECK_H
1528 VALGRIND_MAKE_MEM_DEFINED(&min_dB, sizeof(min_dB));
1529 VALGRIND_MAKE_MEM_DEFINED(&max_dB, sizeof(max_dB));
1532 e->min_dB = ((double) min_dB) / 100.0;
1533 e->max_dB = ((double) max_dB) / 100.0;
1535 if (min_dB >= max_dB) {
1536 pa_assert(!e->db_fix);
1537 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);
1542 if (e->volume_limit >= 0) {
1543 if (e->volume_limit <= e->min_volume || e->volume_limit > e->max_volume)
1544 pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1545 "%li-%li. The volume limit is ignored.",
1546 e->alsa_name, e->path->name, e->volume_limit, e->min_volume + 1, e->max_volume);
1549 e->max_volume = e->volume_limit;
1553 e->db_fix->max_step = e->max_volume;
1554 e->max_dB = ((double) e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step]) / 100.0;
1557 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1558 r = snd_mixer_selem_ask_playback_vol_dB(me, e->max_volume, &max_dB);
1560 r = snd_mixer_selem_ask_capture_vol_dB(me, e->max_volume, &max_dB);
1563 pa_log_warn("Failed to get dB value of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1566 e->max_dB = ((double) max_dB) / 100.0;
1572 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1573 is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
1575 is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
1580 if (!e->override_map) {
1581 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1582 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1585 e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
1588 e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
1591 e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
1594 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1596 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1599 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1600 e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1602 e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1605 if (e->n_channels <= 0) {
1606 pa_log_warn("Volume element %s with no channels?", e->alsa_name);
1610 if (e->n_channels > 2) {
1611 /* FIXME: In some places code like this is used:
1613 * e->masks[alsa_channel_ids[p]][e->n_channels-1]
1615 * The definition of e->masks is
1617 * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST][2];
1619 * Since the array size is fixed at 2, we obviously
1620 * don't support elements with more than two
1622 pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e->alsa_name, e->n_channels);
1626 if (!e->override_map) {
1627 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1628 pa_bool_t has_channel;
1630 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1633 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1634 has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1636 has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1638 e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
1643 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1644 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1647 e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
1655 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1658 PA_LLIST_FOREACH(o, e->options)
1659 o->alsa_idx = pa_streq(o->alsa_name, "on") ? 1 : 0;
1660 } else if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1664 if ((n = snd_mixer_selem_get_enum_items(me)) < 0) {
1665 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n));
1669 PA_LLIST_FOREACH(o, e->options) {
1672 for (i = 0; i < n; i++) {
1675 if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
1678 if (!pa_streq(buf, o->alsa_name))
1686 if (check_required(e, me) < 0)
1692 static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, pa_bool_t prefixed) {
1699 if (!pa_startswith(section, "Element "))
1705 /* This is not an element section, but an enum section? */
1706 if (strchr(section, ':'))
1709 if (p->last_element && pa_streq(p->last_element->alsa_name, section))
1710 return p->last_element;
1712 PA_LLIST_FOREACH(e, p->elements)
1713 if (pa_streq(e->alsa_name, section))
1716 e = pa_xnew0(pa_alsa_element, 1);
1718 e->alsa_name = pa_xstrdup(section);
1719 e->direction = p->direction;
1720 e->volume_limit = -1;
1722 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
1725 p->last_element = e;
1729 static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
1735 if (!pa_startswith(section, "Option "))
1740 /* This is not an enum section, but an element section? */
1741 if (!(on = strchr(section, ':')))
1744 en = pa_xstrndup(section, on - section);
1747 if (p->last_option &&
1748 pa_streq(p->last_option->element->alsa_name, en) &&
1749 pa_streq(p->last_option->alsa_name, on)) {
1751 return p->last_option;
1754 pa_assert_se(e = element_get(p, en, FALSE));
1757 PA_LLIST_FOREACH(o, e->options)
1758 if (pa_streq(o->alsa_name, on))
1761 o = pa_xnew0(pa_alsa_option, 1);
1763 o->alsa_name = pa_xstrdup(on);
1766 if (p->last_option && p->last_option->element == e)
1767 PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
1769 PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
1776 static int element_parse_switch(
1777 const char *filename,
1779 const char *section,
1785 pa_alsa_path *p = userdata;
1790 if (!(e = element_get(p, section, TRUE))) {
1791 pa_log("[%s:%u] Switch makes no sense in '%s'", filename, line, section);
1795 if (pa_streq(rvalue, "ignore"))
1796 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1797 else if (pa_streq(rvalue, "mute"))
1798 e->switch_use = PA_ALSA_SWITCH_MUTE;
1799 else if (pa_streq(rvalue, "off"))
1800 e->switch_use = PA_ALSA_SWITCH_OFF;
1801 else if (pa_streq(rvalue, "on"))
1802 e->switch_use = PA_ALSA_SWITCH_ON;
1803 else if (pa_streq(rvalue, "select"))
1804 e->switch_use = PA_ALSA_SWITCH_SELECT;
1806 pa_log("[%s:%u] Switch invalid of '%s'", filename, line, section);
1813 static int element_parse_volume(
1814 const char *filename,
1816 const char *section,
1822 pa_alsa_path *p = userdata;
1827 if (!(e = element_get(p, section, TRUE))) {
1828 pa_log("[%s:%u] Volume makes no sense in '%s'", filename, line, section);
1832 if (pa_streq(rvalue, "ignore"))
1833 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1834 else if (pa_streq(rvalue, "merge"))
1835 e->volume_use = PA_ALSA_VOLUME_MERGE;
1836 else if (pa_streq(rvalue, "off"))
1837 e->volume_use = PA_ALSA_VOLUME_OFF;
1838 else if (pa_streq(rvalue, "zero"))
1839 e->volume_use = PA_ALSA_VOLUME_ZERO;
1843 if (pa_atou(rvalue, &constant) >= 0) {
1844 e->volume_use = PA_ALSA_VOLUME_CONSTANT;
1845 e->constant_volume = constant;
1847 pa_log("[%s:%u] Volume invalid of '%s'", filename, line, section);
1855 static int element_parse_enumeration(
1856 const char *filename,
1858 const char *section,
1864 pa_alsa_path *p = userdata;
1869 if (!(e = element_get(p, section, TRUE))) {
1870 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename, line, section);
1874 if (pa_streq(rvalue, "ignore"))
1875 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1876 else if (pa_streq(rvalue, "select"))
1877 e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
1879 pa_log("[%s:%u] Enumeration invalid of '%s'", filename, line, section);
1886 static int option_parse_priority(
1887 const char *filename,
1889 const char *section,
1895 pa_alsa_path *p = userdata;
1901 if (!(o = option_get(p, section))) {
1902 pa_log("[%s:%u] Priority makes no sense in '%s'", filename, line, section);
1906 if (pa_atou(rvalue, &prio) < 0) {
1907 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
1915 static int option_parse_name(
1916 const char *filename,
1918 const char *section,
1924 pa_alsa_path *p = userdata;
1929 if (!(o = option_get(p, section))) {
1930 pa_log("[%s:%u] Name makes no sense in '%s'", filename, line, section);
1935 o->name = pa_xstrdup(rvalue);
1940 static int element_parse_required(
1941 const char *filename,
1943 const char *section,
1949 pa_alsa_path *p = userdata;
1952 pa_alsa_required_t req;
1956 e = element_get(p, section, TRUE);
1957 o = option_get(p, section);
1959 pa_log("[%s:%u] Required makes no sense in '%s'", filename, line, section);
1963 if (pa_streq(rvalue, "ignore"))
1964 req = PA_ALSA_REQUIRED_IGNORE;
1965 else if (pa_streq(rvalue, "switch") && e)
1966 req = PA_ALSA_REQUIRED_SWITCH;
1967 else if (pa_streq(rvalue, "volume") && e)
1968 req = PA_ALSA_REQUIRED_VOLUME;
1969 else if (pa_streq(rvalue, "enumeration"))
1970 req = PA_ALSA_REQUIRED_ENUMERATION;
1971 else if (pa_streq(rvalue, "any"))
1972 req = PA_ALSA_REQUIRED_ANY;
1974 pa_log("[%s:%u] Required invalid of '%s'", filename, line, section);
1978 if (pa_streq(lvalue, "required-absent")) {
1980 e->required_absent = req;
1982 o->required_absent = req;
1984 else if (pa_streq(lvalue, "required-any")) {
1986 e->required_any = req;
1987 e->path->has_req_any = TRUE;
1990 o->required_any = req;
1991 o->element->path->has_req_any = TRUE;
2004 static int element_parse_direction(
2005 const char *filename,
2007 const char *section,
2013 pa_alsa_path *p = userdata;
2018 if (!(e = element_get(p, section, TRUE))) {
2019 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
2023 if (pa_streq(rvalue, "playback"))
2024 e->direction = PA_ALSA_DIRECTION_OUTPUT;
2025 else if (pa_streq(rvalue, "capture"))
2026 e->direction = PA_ALSA_DIRECTION_INPUT;
2028 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
2035 static int element_parse_direction_try_other(
2036 const char *filename,
2038 const char *section,
2044 pa_alsa_path *p = userdata;
2048 if (!(e = element_get(p, section, TRUE))) {
2049 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
2053 if ((yes = pa_parse_boolean(rvalue)) < 0) {
2054 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
2058 e->direction_try_other = !!yes;
2062 static int element_parse_volume_limit(
2063 const char *filename,
2065 const char *section,
2071 pa_alsa_path *p = userdata;
2075 if (!(e = element_get(p, section, TRUE))) {
2076 pa_log("[%s:%u] volume-limit makes no sense in '%s'", filename, line, section);
2080 if (pa_atol(rvalue, &volume_limit) < 0 || volume_limit < 0) {
2081 pa_log("[%s:%u] Invalid value for volume-limit", filename, line);
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(
2123 const char *filename,
2125 const char *section,
2131 pa_alsa_path *p = userdata;
2133 const char *state = NULL;
2137 if (!(e = element_get(p, section, TRUE))) {
2138 pa_log("[%s:%u] Override map makes no sense in '%s'", filename, line, section);
2142 while ((n = pa_split(rvalue, ",", &state))) {
2143 pa_channel_position_mask_t m;
2148 if ((m = parse_mask(n)) == 0) {
2149 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename, line, n, section);
2155 if (pa_streq(lvalue, "override-map.1"))
2156 e->masks[i++][0] = m;
2158 e->masks[i++][1] = m;
2160 /* Later on we might add override-map.3 and so on here ... */
2165 e->override_map = TRUE;
2170 static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
2171 snd_mixer_selem_id_t *sid;
2172 snd_mixer_elem_t *me;
2178 SELEM_INIT(sid, e->alsa_name);
2179 if (!(me = snd_mixer_find_selem(m, sid))) {
2180 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2184 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
2186 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
2187 r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
2189 r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
2192 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
2195 pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
2197 if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0)
2198 pa_log_warn("Failed to set enumeration of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
2204 int pa_alsa_setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
2211 PA_IDXSET_FOREACH(o, s->options, idx)
2212 element_set_option(o->element, m, o->alsa_idx);
2217 static int option_verify(pa_alsa_option *o) {
2218 static const struct description_map well_known_descriptions[] = {
2219 { "input", N_("Input") },
2220 { "input-docking", N_("Docking Station Input") },
2221 { "input-docking-microphone", N_("Docking Station Microphone") },
2222 { "input-docking-linein", N_("Docking Station Line In") },
2223 { "input-linein", N_("Line In") },
2224 { "input-microphone", N_("Microphone") },
2225 { "input-microphone-front", N_("Front Microphone") },
2226 { "input-microphone-rear", N_("Rear Microphone") },
2227 { "input-microphone-external", N_("External Microphone") },
2228 { "input-microphone-internal", N_("Internal Microphone") },
2229 { "input-radio", N_("Radio") },
2230 { "input-video", N_("Video") },
2231 { "input-agc-on", N_("Automatic Gain Control") },
2232 { "input-agc-off", N_("No Automatic Gain Control") },
2233 { "input-boost-on", N_("Boost") },
2234 { "input-boost-off", N_("No Boost") },
2235 { "output-amplifier-on", N_("Amplifier") },
2236 { "output-amplifier-off", N_("No Amplifier") },
2237 { "output-bass-boost-on", N_("Bass Boost") },
2238 { "output-bass-boost-off", N_("No Bass Boost") },
2239 { "output-speaker", N_("Speaker") },
2240 { "output-headphones", N_("Headphones") }
2246 pa_log("No name set for option %s", o->alsa_name);
2250 if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
2251 o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
2252 pa_log("Element %s of option %s not set for select.", o->element->alsa_name, o->name);
2256 if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
2257 !pa_streq(o->alsa_name, "on") &&
2258 !pa_streq(o->alsa_name, "off")) {
2259 pa_log("Switch %s options need be named off or on ", o->element->alsa_name);
2263 if (!o->description)
2264 o->description = pa_xstrdup(lookup_description(o->name,
2265 well_known_descriptions,
2266 PA_ELEMENTSOF(well_known_descriptions)));
2267 if (!o->description)
2268 o->description = pa_xstrdup(o->name);
2273 static int element_verify(pa_alsa_element *e) {
2278 // 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);
2279 if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
2280 (e->required_any != PA_ALSA_REQUIRED_IGNORE && e->required_any == e->required_absent) ||
2281 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required_any != PA_ALSA_REQUIRED_IGNORE) ||
2282 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
2283 pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name);
2287 if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
2288 pa_log("Element %s cannot set select for both switch and enumeration.", e->alsa_name);
2292 PA_LLIST_FOREACH(o, e->options)
2293 if (option_verify(o) < 0)
2299 static int path_verify(pa_alsa_path *p) {
2300 static const struct description_map well_known_descriptions[] = {
2301 { "analog-input", N_("Analog Input") },
2302 { "analog-input-microphone", N_("Microphone") },
2303 { "analog-input-microphone-front", N_("Front Microphone") },
2304 { "analog-input-microphone-rear", N_("Rear Microphone") },
2305 { "analog-input-microphone-dock", N_("Dock Microphone") },
2306 { "analog-input-microphone-internal", N_("Internal Microphone") },
2307 { "analog-input-linein", N_("Line In") },
2308 { "analog-input-radio", N_("Radio") },
2309 { "analog-input-video", N_("Video") },
2310 { "analog-output", N_("Analog Output") },
2311 { "analog-output-headphones", N_("Headphones") },
2312 { "analog-output-lfe-on-mono", N_("LFE on Separate Mono Output") },
2313 { "analog-output-lineout", N_("Line Out") },
2314 { "analog-output-mono", N_("Analog Mono Output") },
2315 { "analog-output-speaker", N_("Speakers") },
2316 { "hdmi-output", N_("HDMI / DisplayPort") },
2317 { "iec958-stereo-output", N_("Digital Output (S/PDIF)") },
2318 { "iec958-passthrough-output", N_("Digital Passthrough (S/PDIF)") }
2325 PA_LLIST_FOREACH(e, p->elements)
2326 if (element_verify(e) < 0)
2329 if (!p->description)
2330 p->description = pa_xstrdup(lookup_description(p->name,
2331 well_known_descriptions,
2332 PA_ELEMENTSOF(well_known_descriptions)));
2334 if (!p->description)
2335 p->description = pa_xstrdup(p->name);
2340 static const char *get_default_paths_dir(void) {
2341 if (pa_run_from_build_tree())
2342 return PA_BUILDDIR "/modules/alsa/mixer/paths/";
2344 return PA_ALSA_PATHS_DIR;
2347 pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa_direction_t direction) {
2353 pa_config_item items[] = {
2355 { "priority", pa_config_parse_unsigned, NULL, "General" },
2356 { "description", pa_config_parse_string, NULL, "General" },
2357 { "name", pa_config_parse_string, NULL, "General" },
2360 { "priority", option_parse_priority, NULL, NULL },
2361 { "name", option_parse_name, NULL, NULL },
2364 { "switch", element_parse_switch, NULL, NULL },
2365 { "volume", element_parse_volume, NULL, NULL },
2366 { "enumeration", element_parse_enumeration, NULL, NULL },
2367 { "override-map.1", element_parse_override_map, NULL, NULL },
2368 { "override-map.2", element_parse_override_map, NULL, NULL },
2369 /* ... later on we might add override-map.3 and so on here ... */
2370 { "required", element_parse_required, NULL, NULL },
2371 { "required-any", element_parse_required, NULL, NULL },
2372 { "required-absent", element_parse_required, NULL, NULL },
2373 { "direction", element_parse_direction, NULL, NULL },
2374 { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
2375 { "volume-limit", element_parse_volume_limit, NULL, NULL },
2376 { NULL, NULL, NULL, NULL }
2381 p = pa_xnew0(pa_alsa_path, 1);
2382 n = pa_path_get_filename(fname);
2383 p->name = pa_xstrndup(n, strcspn(n, "."));
2384 p->direction = direction;
2386 items[0].data = &p->priority;
2387 items[1].data = &p->description;
2388 items[2].data = &p->name;
2391 paths_dir = get_default_paths_dir();
2393 fn = pa_maybe_prefix_path(fname, paths_dir);
2395 r = pa_config_parse(fn, NULL, items, p);
2401 if (path_verify(p) < 0)
2407 pa_alsa_path_free(p);
2411 pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction) {
2417 p = pa_xnew0(pa_alsa_path, 1);
2418 p->name = pa_xstrdup(element);
2419 p->direction = direction;
2421 e = pa_xnew0(pa_alsa_element, 1);
2423 e->alsa_name = pa_xstrdup(element);
2424 e->direction = direction;
2425 e->volume_limit = -1;
2427 e->switch_use = PA_ALSA_SWITCH_MUTE;
2428 e->volume_use = PA_ALSA_VOLUME_MERGE;
2430 PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
2431 p->last_element = e;
2435 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
2436 pa_alsa_option *o, *n;
2440 for (o = e->options; o; o = n) {
2443 if (o->alsa_idx < 0) {
2444 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
2450 e->switch_use != PA_ALSA_SWITCH_IGNORE ||
2451 e->volume_use != PA_ALSA_VOLUME_IGNORE ||
2452 e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
2455 static void path_drop_unsupported(pa_alsa_path *p) {
2456 pa_alsa_element *e, *n;
2460 for (e = p->elements; e; e = n) {
2463 if (!element_drop_unsupported(e)) {
2464 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
2470 static void path_make_options_unique(pa_alsa_path *p) {
2472 pa_alsa_option *o, *u;
2474 PA_LLIST_FOREACH(e, p->elements) {
2475 PA_LLIST_FOREACH(o, e->options) {
2479 for (u = o->next; u; u = u->next)
2480 if (pa_streq(u->name, o->name))
2486 m = pa_xstrdup(o->name);
2488 /* OK, this name is not unique, hence let's rename */
2489 for (i = 1, u = o; u; u = u->next) {
2492 if (!pa_streq(u->name, m))
2495 nn = pa_sprintf_malloc("%s-%u", m, i);
2499 nd = pa_sprintf_malloc("%s %u", u->description, i);
2500 pa_xfree(u->description);
2501 u->description = nd;
2511 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
2514 for (; e; e = e->next)
2515 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
2516 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
2522 for (o = e->options; o; o = o->next) {
2526 s = pa_xnewdup(pa_alsa_setting, template, 1);
2527 s->options = pa_idxset_copy(template->options);
2528 s->name = pa_sprintf_malloc(_("%s+%s"), template->name, o->name);
2530 (template->description[0] && o->description[0])
2531 ? pa_sprintf_malloc(_("%s / %s"), template->description, o->description)
2532 : (template->description[0]
2533 ? pa_xstrdup(template->description)
2534 : pa_xstrdup(o->description));
2536 s->priority = PA_MAX(template->priority, o->priority);
2538 s = pa_xnew0(pa_alsa_setting, 1);
2539 s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2540 s->name = pa_xstrdup(o->name);
2541 s->description = pa_xstrdup(o->description);
2542 s->priority = o->priority;
2545 pa_idxset_put(s->options, o, NULL);
2547 if (element_create_settings(e->next, s))
2548 /* This is not a leaf, so let's get rid of it */
2551 /* This is a leaf, so let's add it */
2552 PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2554 e->path->last_setting = s;
2561 static void path_create_settings(pa_alsa_path *p) {
2564 element_create_settings(p->elements, NULL);
2567 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) {
2569 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2570 pa_channel_position_t t;
2571 pa_channel_position_mask_t path_volume_channels = 0;
2577 return p->supported ? 0 : -1;
2583 pa_log_debug("Probing path '%s'", p->name);
2585 PA_LLIST_FOREACH(e, p->elements) {
2586 if (element_probe(e, m) < 0) {
2587 p->supported = FALSE;
2588 pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2591 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);
2596 if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2598 if (!p->has_volume) {
2599 p->min_volume = e->min_volume;
2600 p->max_volume = e->max_volume;
2604 if (!p->has_volume) {
2605 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2606 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2607 min_dB[t] = e->min_dB;
2608 max_dB[t] = e->max_dB;
2609 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2616 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2617 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2618 min_dB[t] += e->min_dB;
2619 max_dB[t] += e->max_dB;
2620 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2623 /* Hmm, there's another element before us
2624 * which cannot do dB volumes, so we we need
2625 * to 'neutralize' this slider */
2626 e->volume_use = PA_ALSA_VOLUME_ZERO;
2627 pa_log_info("Zeroing volume of '%s' on path '%s'", e->alsa_name, p->name);
2630 } else if (p->has_volume) {
2631 /* We can't use this volume, so let's ignore it */
2632 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2633 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e->alsa_name, p->name);
2635 p->has_volume = TRUE;
2638 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2642 if (p->has_req_any && !p->req_any_present) {
2643 p->supported = FALSE;
2644 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name);
2648 path_drop_unsupported(p);
2649 path_make_options_unique(p);
2650 path_create_settings(p);
2652 p->supported = TRUE;
2654 p->min_dB = INFINITY;
2655 p->max_dB = -INFINITY;
2657 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2658 if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) {
2659 if (p->min_dB > min_dB[t])
2660 p->min_dB = min_dB[t];
2662 if (p->max_dB < max_dB[t])
2663 p->max_dB = max_dB[t];
2670 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2673 pa_log_debug("Setting %s (%s) priority=%u",
2675 pa_strnull(s->description),
2679 void pa_alsa_option_dump(pa_alsa_option *o) {
2682 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2684 pa_strnull(o->name),
2685 pa_strnull(o->description),
2690 void pa_alsa_element_dump(pa_alsa_element *e) {
2694 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",
2704 (long long unsigned) e->merged_mask,
2706 pa_yes_no(e->override_map));
2708 PA_LLIST_FOREACH(o, e->options)
2709 pa_alsa_option_dump(o);
2712 void pa_alsa_path_dump(pa_alsa_path *p) {
2717 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2718 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2720 pa_strnull(p->description),
2723 pa_yes_no(p->probed),
2724 pa_yes_no(p->supported),
2725 pa_yes_no(p->has_mute),
2726 pa_yes_no(p->has_volume),
2727 pa_yes_no(p->has_dB),
2728 p->min_volume, p->max_volume,
2729 p->min_dB, p->max_dB);
2731 PA_LLIST_FOREACH(e, p->elements)
2732 pa_alsa_element_dump(e);
2734 PA_LLIST_FOREACH(s, p->settings)
2735 pa_alsa_setting_dump(s);
2738 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2739 snd_mixer_selem_id_t *sid;
2740 snd_mixer_elem_t *me;
2746 SELEM_INIT(sid, e->alsa_name);
2747 if (!(me = snd_mixer_find_selem(m, sid))) {
2748 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2752 snd_mixer_elem_set_callback(me, cb);
2753 snd_mixer_elem_set_callback_private(me, userdata);
2756 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2763 PA_LLIST_FOREACH(e, p->elements)
2764 element_set_callback(e, m, cb, userdata);
2767 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2775 PA_HASHMAP_FOREACH(p, ps->paths, state)
2776 pa_alsa_path_set_callback(p, m, cb, userdata);
2779 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction, const char *paths_dir) {
2780 pa_alsa_path_set *ps;
2781 char **pn = NULL, **en = NULL, **ie;
2782 pa_alsa_decibel_fix *db_fix;
2783 void *state, *state2;
2787 pa_assert(m->profile_set);
2788 pa_assert(m->profile_set->decibel_fixes);
2789 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2791 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2794 ps = pa_xnew0(pa_alsa_path_set, 1);
2795 ps->direction = direction;
2796 ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2798 if (direction == PA_ALSA_DIRECTION_OUTPUT) {
2799 pn = m->output_path_names;
2800 cache = m->profile_set->output_paths;
2802 else if (direction == PA_ALSA_DIRECTION_INPUT) {
2803 pn = m->input_path_names;
2804 cache = m->profile_set->input_paths;
2810 for (in = pn; *in; in++) {
2811 pa_alsa_path *p = NULL;
2812 pa_bool_t duplicate = FALSE;
2815 for (kn = pn; kn < in; kn++)
2816 if (pa_streq(*kn, *in)) {
2824 p = pa_hashmap_get(cache, *in);
2826 char *fn = pa_sprintf_malloc("%s.conf", *in);
2827 p = pa_alsa_path_new(paths_dir, fn, direction);
2830 pa_hashmap_put(cache, *in, p);
2832 pa_assert(pa_hashmap_get(cache, *in) == p);
2834 pa_hashmap_put(ps->paths, p, p);
2841 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2842 en = m->output_element;
2843 else if (direction == PA_ALSA_DIRECTION_INPUT)
2844 en = m->input_element;
2847 pa_alsa_path_set_free(ps);
2851 for (ie = en; *ie; ie++) {
2855 p = pa_alsa_path_synthesize(*ie, direction);
2857 /* Mark all other passed elements for require-absent */
2858 for (je = en; *je; je++) {
2864 e = pa_xnew0(pa_alsa_element, 1);
2866 e->alsa_name = pa_xstrdup(*je);
2867 e->direction = direction;
2868 e->required_absent = PA_ALSA_REQUIRED_ANY;
2869 e->volume_limit = -1;
2871 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2872 p->last_element = e;
2875 pa_hashmap_put(ps->paths, *ie, p);
2879 /* Assign decibel fixes to elements. */
2880 PA_HASHMAP_FOREACH(db_fix, m->profile_set->decibel_fixes, state) {
2883 PA_HASHMAP_FOREACH(p, ps->paths, state2) {
2886 PA_LLIST_FOREACH(e, p->elements) {
2887 if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_name)) {
2888 /* The profile set that contains the dB fix may be freed
2889 * before the element, so we have to copy the dB fix
2891 e->db_fix = pa_xnewdup(pa_alsa_decibel_fix, db_fix, 1);
2892 e->db_fix->profile_set = NULL;
2893 e->db_fix->name = pa_xstrdup(db_fix->name);
2894 e->db_fix->db_values = pa_xmemdup(db_fix->db_values, (db_fix->max_step - db_fix->min_step + 1) * sizeof(long));
2903 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2908 pa_log_debug("Path Set %p, direction=%i",
2912 PA_HASHMAP_FOREACH(p, ps->paths, state)
2913 pa_alsa_path_dump(p);
2917 static pa_bool_t options_have_option(pa_alsa_option *options, const char *alsa_name) {
2921 pa_assert(alsa_name);
2923 PA_LLIST_FOREACH(o, options) {
2924 if (pa_streq(o->alsa_name, alsa_name))
2930 static pa_bool_t enumeration_is_subset(pa_alsa_option *a_options, pa_alsa_option *b_options) {
2931 pa_alsa_option *oa, *ob;
2933 if (!a_options) return TRUE;
2934 if (!b_options) return FALSE;
2936 /* If there is an option A offers that B does not, then A is not a subset of B. */
2937 PA_LLIST_FOREACH(oa, a_options) {
2938 pa_bool_t found = FALSE;
2939 PA_LLIST_FOREACH(ob, b_options) {
2940 if (pa_streq(oa->alsa_name, ob->alsa_name)) {
2952 * Compares two elements to see if a is a subset of b
2954 static pa_bool_t element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_t *m) {
2960 * Every state is a subset of itself (with caveats for volume_limits and options)
2961 * IGNORE is a subset of every other state */
2963 /* Check the volume_use */
2964 if (a->volume_use != PA_ALSA_VOLUME_IGNORE) {
2966 /* "Constant" is subset of "Constant" only when their constant values are equal */
2967 if (a->volume_use == PA_ALSA_VOLUME_CONSTANT && b->volume_use == PA_ALSA_VOLUME_CONSTANT && a->constant_volume != b->constant_volume)
2970 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
2971 if (a->volume_use != b->volume_use && b->volume_use != PA_ALSA_VOLUME_MERGE)
2974 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
2975 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
2976 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
2977 if (b->volume_use == PA_ALSA_VOLUME_MERGE && b->volume_limit >= 0) {
2980 if (a->volume_use == PA_ALSA_VOLUME_CONSTANT)
2981 a_limit = a->constant_volume;
2982 else if (a->volume_use == PA_ALSA_VOLUME_ZERO) {
2986 int rounding = (a->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1);
2987 a_limit = decibel_fix_get_step(a->db_fix, &dB, rounding);
2989 snd_mixer_selem_id_t *sid;
2990 snd_mixer_elem_t *me;
2992 SELEM_INIT(sid, a->alsa_name);
2993 if (!(me = snd_mixer_find_selem(m, sid))) {
2994 pa_log_warn("Element %s seems to have disappeared.", a->alsa_name);
2998 if (a->direction == PA_ALSA_DIRECTION_OUTPUT) {
2999 if (snd_mixer_selem_ask_playback_dB_vol(me, dB, +1, &a_limit) < 0)
3002 if (snd_mixer_selem_ask_capture_dB_vol(me, dB, -1, &a_limit) < 0)
3006 } else if (a->volume_use == PA_ALSA_VOLUME_OFF)
3007 a_limit = a->min_volume;
3008 else if (a->volume_use == PA_ALSA_VOLUME_MERGE)
3009 a_limit = a->volume_limit;
3011 /* This should never be reached */
3014 if (a_limit > b->volume_limit)
3019 if (a->switch_use != PA_ALSA_SWITCH_IGNORE) {
3020 /* "On" is a subset of "Mute".
3021 * "Off" is a subset of "Mute".
3022 * "On" is a subset of "Select", if there is an "Option:On" in B.
3023 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3024 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3026 if (a->switch_use != b->switch_use) {
3028 if (a->switch_use == PA_ALSA_SWITCH_SELECT || a->switch_use == PA_ALSA_SWITCH_MUTE
3029 || b->switch_use == PA_ALSA_SWITCH_OFF || b->switch_use == PA_ALSA_SWITCH_ON)
3032 if (b->switch_use == PA_ALSA_SWITCH_SELECT) {
3033 if (a->switch_use == PA_ALSA_SWITCH_ON) {
3034 if (!options_have_option(b->options, "on"))
3036 } else if (a->switch_use == PA_ALSA_SWITCH_OFF) {
3037 if (!options_have_option(b->options, "off"))
3041 } else if (a->switch_use == PA_ALSA_SWITCH_SELECT) {
3042 if (!enumeration_is_subset(a->options, b->options))
3047 if (a->enumeration_use != PA_ALSA_ENUMERATION_IGNORE) {
3048 if (b->enumeration_use == PA_ALSA_ENUMERATION_IGNORE)
3050 if (!enumeration_is_subset(a->options, b->options))
3057 static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
3064 /* If we only have one path, then don't bother */
3065 if (pa_hashmap_size(ps->paths) < 2)
3068 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3072 PA_HASHMAP_FOREACH(p2, ps->paths, state2) {
3073 pa_alsa_element *ea, *eb;
3074 pa_bool_t is_subset = TRUE;
3079 /* Compare the elements of each set... */
3080 pa_assert_se(ea = p->elements);
3081 pa_assert_se(eb = p2->elements);
3084 if (pa_streq(ea->alsa_name, eb->alsa_name)) {
3085 if (element_is_subset(ea, eb, m)) {
3088 if ((ea && !eb) || (!ea && eb))
3090 else if (!ea && !eb)
3100 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p->name, p2->name);
3101 pa_hashmap_remove(ps->paths, p);
3108 static pa_alsa_path* path_set_find_path_by_name(pa_alsa_path_set *ps, const char* name, pa_alsa_path *ignore)
3113 PA_HASHMAP_FOREACH(p, ps->paths, state)
3114 if (p != ignore && pa_streq(p->name, name))
3119 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
3120 pa_alsa_path *p, *q;
3121 void *state, *state2;
3123 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3127 q = path_set_find_path_by_name(ps, p->name, p);
3132 m = pa_xstrdup(p->name);
3134 /* OK, this name is not unique, hence let's rename */
3136 PA_HASHMAP_FOREACH(q, ps->paths, state2) {
3139 if (!pa_streq(q->name, m))
3142 nn = pa_sprintf_malloc("%s-%u", m, i);
3146 nd = pa_sprintf_malloc("%s %u", q->description, i);
3147 pa_xfree(q->description);
3148 q->description = nd;
3157 static void mapping_free(pa_alsa_mapping *m) {
3161 pa_xfree(m->description);
3163 pa_xstrfreev(m->device_strings);
3164 pa_xstrfreev(m->input_path_names);
3165 pa_xstrfreev(m->output_path_names);
3166 pa_xstrfreev(m->input_element);
3167 pa_xstrfreev(m->output_element);
3168 if (m->input_path_set)
3169 pa_alsa_path_set_free(m->input_path_set);
3170 if (m->output_path_set)
3171 pa_alsa_path_set_free(m->output_path_set);
3173 pa_assert(!m->input_pcm);
3174 pa_assert(!m->output_pcm);
3179 static void profile_free(pa_alsa_profile *p) {
3183 pa_xfree(p->description);
3185 pa_xstrfreev(p->input_mapping_names);
3186 pa_xstrfreev(p->output_mapping_names);
3188 if (p->input_mappings)
3189 pa_idxset_free(p->input_mappings, NULL, NULL);
3191 if (p->output_mappings)
3192 pa_idxset_free(p->output_mappings, NULL, NULL);
3197 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
3200 if (ps->input_paths) {
3203 while ((p = pa_hashmap_steal_first(ps->input_paths)))
3204 pa_alsa_path_free(p);
3206 pa_hashmap_free(ps->input_paths, NULL, NULL);
3209 if (ps->output_paths) {
3212 while ((p = pa_hashmap_steal_first(ps->output_paths)))
3213 pa_alsa_path_free(p);
3215 pa_hashmap_free(ps->output_paths, NULL, NULL);
3221 while ((p = pa_hashmap_steal_first(ps->profiles)))
3224 pa_hashmap_free(ps->profiles, NULL, NULL);
3230 while ((m = pa_hashmap_steal_first(ps->mappings)))
3233 pa_hashmap_free(ps->mappings, NULL, NULL);
3236 if (ps->decibel_fixes) {
3237 pa_alsa_decibel_fix *db_fix;
3239 while ((db_fix = pa_hashmap_steal_first(ps->decibel_fixes)))
3240 decibel_fix_free(db_fix);
3242 pa_hashmap_free(ps->decibel_fixes, NULL, NULL);
3248 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
3251 if (!pa_startswith(name, "Mapping "))
3256 if ((m = pa_hashmap_get(ps->mappings, name)))
3259 m = pa_xnew0(pa_alsa_mapping, 1);
3260 m->profile_set = ps;
3261 m->name = pa_xstrdup(name);
3262 pa_channel_map_init(&m->channel_map);
3264 pa_hashmap_put(ps->mappings, m->name, m);
3269 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
3272 if (!pa_startswith(name, "Profile "))
3277 if ((p = pa_hashmap_get(ps->profiles, name)))
3280 p = pa_xnew0(pa_alsa_profile, 1);
3281 p->profile_set = ps;
3282 p->name = pa_xstrdup(name);
3284 pa_hashmap_put(ps->profiles, p->name, p);
3289 static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) {
3290 pa_alsa_decibel_fix *db_fix;
3292 if (!pa_startswith(name, "DecibelFix "))
3297 if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name)))
3300 db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
3301 db_fix->profile_set = ps;
3302 db_fix->name = pa_xstrdup(name);
3304 pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix);
3309 static int mapping_parse_device_strings(
3310 const char *filename,
3312 const char *section,
3318 pa_alsa_profile_set *ps = userdata;
3323 if (!(m = mapping_get(ps, section))) {
3324 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3328 pa_xstrfreev(m->device_strings);
3329 if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
3330 pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
3337 static int mapping_parse_channel_map(
3338 const char *filename,
3340 const char *section,
3346 pa_alsa_profile_set *ps = userdata;
3351 if (!(m = mapping_get(ps, section))) {
3352 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3356 if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
3357 pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
3364 static int mapping_parse_paths(
3365 const char *filename,
3367 const char *section,
3373 pa_alsa_profile_set *ps = userdata;
3378 if (!(m = mapping_get(ps, section))) {
3379 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3383 if (pa_streq(lvalue, "paths-input")) {
3384 pa_xstrfreev(m->input_path_names);
3385 m->input_path_names = pa_split_spaces_strv(rvalue);
3387 pa_xstrfreev(m->output_path_names);
3388 m->output_path_names = pa_split_spaces_strv(rvalue);
3394 static int mapping_parse_element(
3395 const char *filename,
3397 const char *section,
3403 pa_alsa_profile_set *ps = userdata;
3408 if (!(m = mapping_get(ps, section))) {
3409 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3413 if (pa_streq(lvalue, "element-input")) {
3414 pa_xstrfreev(m->input_element);
3415 m->input_element = pa_split_spaces_strv(rvalue);
3417 pa_xstrfreev(m->output_element);
3418 m->output_element = pa_split_spaces_strv(rvalue);
3424 static int mapping_parse_direction(
3425 const char *filename,
3427 const char *section,
3433 pa_alsa_profile_set *ps = userdata;
3438 if (!(m = mapping_get(ps, section))) {
3439 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3443 if (pa_streq(rvalue, "input"))
3444 m->direction = PA_ALSA_DIRECTION_INPUT;
3445 else if (pa_streq(rvalue, "output"))
3446 m->direction = PA_ALSA_DIRECTION_OUTPUT;
3447 else if (pa_streq(rvalue, "any"))
3448 m->direction = PA_ALSA_DIRECTION_ANY;
3450 pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
3457 static int mapping_parse_description(
3458 const char *filename,
3460 const char *section,
3466 pa_alsa_profile_set *ps = userdata;
3472 if ((m = mapping_get(ps, section))) {
3473 pa_xfree(m->description);
3474 m->description = pa_xstrdup(rvalue);
3475 } else if ((p = profile_get(ps, section))) {
3476 pa_xfree(p->description);
3477 p->description = pa_xstrdup(rvalue);
3479 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3486 static int mapping_parse_priority(
3487 const char *filename,
3489 const char *section,
3495 pa_alsa_profile_set *ps = userdata;
3502 if (pa_atou(rvalue, &prio) < 0) {
3503 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
3507 if ((m = mapping_get(ps, section)))
3509 else if ((p = profile_get(ps, section)))
3512 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3519 static int profile_parse_mappings(
3520 const char *filename,
3522 const char *section,
3528 pa_alsa_profile_set *ps = userdata;
3533 if (!(p = profile_get(ps, section))) {
3534 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3538 if (pa_streq(lvalue, "input-mappings")) {
3539 pa_xstrfreev(p->input_mapping_names);
3540 p->input_mapping_names = pa_split_spaces_strv(rvalue);
3542 pa_xstrfreev(p->output_mapping_names);
3543 p->output_mapping_names = pa_split_spaces_strv(rvalue);
3549 static int profile_parse_skip_probe(
3550 const char *filename,
3552 const char *section,
3558 pa_alsa_profile_set *ps = userdata;
3564 if (!(p = profile_get(ps, section))) {
3565 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3569 if ((b = pa_parse_boolean(rvalue)) < 0) {
3570 pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
3579 static int decibel_fix_parse_db_values(
3580 const char *filename,
3582 const char *section,
3588 pa_alsa_profile_set *ps = userdata;
3589 pa_alsa_decibel_fix *db_fix;
3593 unsigned n = 8; /* Current size of the db_values table. */
3594 unsigned min_step = 0;
3595 unsigned max_step = 0;
3596 unsigned i = 0; /* Index to the items table. */
3597 unsigned prev_step = 0;
3600 pa_assert(filename);
3606 if (!(db_fix = decibel_fix_get(ps, section))) {
3607 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3611 if (!(items = pa_split_spaces_strv(rvalue))) {
3612 pa_log("[%s:%u] Value missing", pa_strnull(filename), line);
3616 db_values = pa_xnew(long, n);
3618 while ((item = items[i++])) {
3619 char *s = item; /* Step value string. */
3620 char *d = item; /* dB value string. */
3624 /* Move d forward until it points to a colon or to the end of the item. */
3625 for (; *d && *d != ':'; ++d);
3628 /* item started with colon. */
3629 pa_log("[%s:%u] No step value found in %s", filename, line, item);
3633 if (!*d || !*(d + 1)) {
3634 /* No colon found, or it was the last character in item. */
3635 pa_log("[%s:%u] No dB value found in %s", filename, line, item);
3639 /* pa_atou() needs a null-terminating string. Let's replace the colon
3640 * with a zero byte. */
3643 if (pa_atou(s, &step) < 0) {
3644 pa_log("[%s:%u] Invalid step value: %s", filename, line, s);
3648 if (pa_atod(d, &db) < 0) {
3649 pa_log("[%s:%u] Invalid dB value: %s", filename, line, d);
3653 if (step <= prev_step && i != 1) {
3654 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename, line, step, prev_step);
3658 if (db < prev_db && i != 1) {
3659 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename, line, db, prev_db);
3665 db_values[0] = (long) (db * 100.0);
3669 /* Interpolate linearly. */
3670 double db_increment = (db - prev_db) / (step - prev_step);
3672 for (; prev_step < step; ++prev_step, prev_db += db_increment) {
3674 /* Reallocate the db_values table if it's about to overflow. */
3675 if (prev_step + 1 - min_step == n) {
3677 db_values = pa_xrenew(long, db_values, n);
3680 db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
3687 db_fix->min_step = min_step;
3688 db_fix->max_step = max_step;
3689 pa_xfree(db_fix->db_values);
3690 db_fix->db_values = db_values;
3692 pa_xstrfreev(items);
3697 pa_xstrfreev(items);
3698 pa_xfree(db_values);
3703 static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
3704 pa_alsa_direction_t direction) {
3708 snd_pcm_t *pcm_handle;
3709 pa_alsa_path_set *ps;
3710 snd_mixer_t *mixer_handle;
3712 if (direction == PA_ALSA_DIRECTION_OUTPUT) {
3713 if (m->output_path_set)
3714 return; /* Already probed */
3715 m->output_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3716 pcm_handle = m->output_pcm;
3718 if (m->input_path_set)
3719 return; /* Already probed */
3720 m->input_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3721 pcm_handle = m->input_pcm;
3725 return; /* No paths */
3727 pa_assert(pcm_handle);
3729 mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL);
3730 if (!mixer_handle) {
3731 /* Cannot open mixer, remove all entries */
3732 while (pa_hashmap_steal_first(ps->paths));
3737 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3738 if (pa_alsa_path_probe(p, mixer_handle, m->profile_set->ignore_dB) < 0) {
3739 pa_hashmap_remove(ps->paths, p);
3743 path_set_condense(ps, mixer_handle);
3744 path_set_make_paths_unique(ps);
3747 snd_mixer_close(mixer_handle);
3749 pa_log_debug("Available mixer paths (after tidying):");
3750 pa_alsa_path_set_dump(ps);
3753 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
3755 static const struct description_map well_known_descriptions[] = {
3756 { "analog-mono", N_("Analog Mono") },
3757 { "analog-stereo", N_("Analog Stereo") },
3758 { "analog-surround-21", N_("Analog Surround 2.1") },
3759 { "analog-surround-30", N_("Analog Surround 3.0") },
3760 { "analog-surround-31", N_("Analog Surround 3.1") },
3761 { "analog-surround-40", N_("Analog Surround 4.0") },
3762 { "analog-surround-41", N_("Analog Surround 4.1") },
3763 { "analog-surround-50", N_("Analog Surround 5.0") },
3764 { "analog-surround-51", N_("Analog Surround 5.1") },
3765 { "analog-surround-61", N_("Analog Surround 6.0") },
3766 { "analog-surround-61", N_("Analog Surround 6.1") },
3767 { "analog-surround-70", N_("Analog Surround 7.0") },
3768 { "analog-surround-71", N_("Analog Surround 7.1") },
3769 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3770 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3771 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3772 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3773 { "hdmi-stereo", N_("Digital Stereo (HDMI)") },
3774 { "hdmi-surround-51", N_("Digital Surround 5.1 (HDMI)") }
3779 if (!pa_channel_map_valid(&m->channel_map)) {
3780 pa_log("Mapping %s is missing channel map.", m->name);
3784 if (!m->device_strings) {
3785 pa_log("Mapping %s is missing device strings.", m->name);
3789 if ((m->input_path_names && m->input_element) ||
3790 (m->output_path_names && m->output_element)) {
3791 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
3795 if (!m->description)
3796 m->description = pa_xstrdup(lookup_description(m->name,
3797 well_known_descriptions,
3798 PA_ELEMENTSOF(well_known_descriptions)));
3800 if (!m->description)
3801 m->description = pa_xstrdup(m->name);
3804 if (pa_channel_map_equal(&m->channel_map, bonus))
3806 else if (m->channel_map.channels == bonus->channels)
3813 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
3814 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
3818 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3820 pa_strnull(m->description),
3822 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
3823 pa_yes_no(m->supported),
3827 static void profile_set_add_auto_pair(
3828 pa_alsa_profile_set *ps,
3829 pa_alsa_mapping *m, /* output */
3830 pa_alsa_mapping *n /* input */) {
3838 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
3841 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
3845 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
3847 name = pa_sprintf_malloc("output:%s", m->name);
3849 name = pa_sprintf_malloc("input:%s", n->name);
3851 if (pa_hashmap_get(ps->profiles, name)) {
3856 p = pa_xnew0(pa_alsa_profile, 1);
3857 p->profile_set = ps;
3861 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3862 pa_idxset_put(p->output_mappings, m, NULL);
3863 p->priority += m->priority * 100;
3867 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3868 pa_idxset_put(p->input_mappings, n, NULL);
3869 p->priority += n->priority;
3872 pa_hashmap_put(ps->profiles, p->name, p);
3875 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
3876 pa_alsa_mapping *m, *n;
3877 void *m_state, *n_state;
3881 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
3882 profile_set_add_auto_pair(ps, m, NULL);
3884 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3885 profile_set_add_auto_pair(ps, m, n);
3888 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3889 profile_set_add_auto_pair(ps, NULL, n);
3892 static int profile_verify(pa_alsa_profile *p) {
3894 static const struct description_map well_known_descriptions[] = {
3895 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3896 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3897 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3898 { "off", N_("Off") }
3903 /* Replace the output mapping names by the actual mappings */
3904 if (p->output_mapping_names) {
3907 pa_assert(!p->output_mappings);
3908 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3910 for (name = p->output_mapping_names; *name; name++) {
3913 pa_bool_t duplicate = FALSE;
3915 for (in = name + 1; *in; in++)
3916 if (pa_streq(*name, *in)) {
3924 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
3925 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
3929 pa_idxset_put(p->output_mappings, m, NULL);
3935 pa_xstrfreev(p->output_mapping_names);
3936 p->output_mapping_names = NULL;
3939 /* Replace the input mapping names by the actual mappings */
3940 if (p->input_mapping_names) {
3943 pa_assert(!p->input_mappings);
3944 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3946 for (name = p->input_mapping_names; *name; name++) {
3949 pa_bool_t duplicate = FALSE;
3951 for (in = name + 1; *in; in++)
3952 if (pa_streq(*name, *in)) {
3960 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
3961 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
3965 pa_idxset_put(p->input_mappings, m, NULL);
3971 pa_xstrfreev(p->input_mapping_names);
3972 p->input_mapping_names = NULL;
3975 if (!p->input_mappings && !p->output_mappings) {
3976 pa_log("Profile '%s' lacks mappings.", p->name);
3980 if (!p->description)
3981 p->description = pa_xstrdup(lookup_description(p->name,
3982 well_known_descriptions,
3983 PA_ELEMENTSOF(well_known_descriptions)));
3985 if (!p->description) {
3990 sb = pa_strbuf_new();
3992 if (p->output_mappings)
3993 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3994 if (!pa_strbuf_isempty(sb))
3995 pa_strbuf_puts(sb, " + ");
3997 pa_strbuf_printf(sb, _("%s Output"), m->description);
4000 if (p->input_mappings)
4001 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4002 if (!pa_strbuf_isempty(sb))
4003 pa_strbuf_puts(sb, " + ");
4005 pa_strbuf_printf(sb, _("%s Input"), m->description);
4008 p->description = pa_strbuf_tostring_free(sb);
4014 void pa_alsa_profile_dump(pa_alsa_profile *p) {
4019 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4021 pa_strnull(p->description),
4023 pa_yes_no(p->supported),
4024 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
4025 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
4027 if (p->input_mappings)
4028 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4029 pa_log_debug("Input %s", m->name);
4031 if (p->output_mappings)
4032 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4033 pa_log_debug("Output %s", m->name);
4036 static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
4039 /* Check that the dB mapping has been configured. Since "db-values" is
4040 * currently the only option in the DecibelFix section, and decibel fix
4041 * objects don't get created if a DecibelFix section is empty, this is
4042 * actually a redundant check. Having this may prevent future bugs,
4044 if (!db_fix->db_values) {
4045 pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
4052 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
4053 char *db_values = NULL;
4057 if (db_fix->db_values) {
4059 unsigned long i, nsteps;
4061 pa_assert(db_fix->min_step <= db_fix->max_step);
4062 nsteps = db_fix->max_step - db_fix->min_step + 1;
4064 buf = pa_strbuf_new();
4065 for (i = 0; i < nsteps; ++i)
4066 pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
4068 db_values = pa_strbuf_tostring_free(buf);
4071 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4072 db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
4074 pa_xfree(db_values);
4077 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
4078 pa_alsa_profile_set *ps;
4081 pa_alsa_decibel_fix *db_fix;
4086 static pa_config_item items[] = {
4088 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
4091 { "device-strings", mapping_parse_device_strings, NULL, NULL },
4092 { "channel-map", mapping_parse_channel_map, NULL, NULL },
4093 { "paths-input", mapping_parse_paths, NULL, NULL },
4094 { "paths-output", mapping_parse_paths, NULL, NULL },
4095 { "element-input", mapping_parse_element, NULL, NULL },
4096 { "element-output", mapping_parse_element, NULL, NULL },
4097 { "direction", mapping_parse_direction, NULL, NULL },
4099 /* Shared by [Mapping ...] and [Profile ...] */
4100 { "description", mapping_parse_description, NULL, NULL },
4101 { "priority", mapping_parse_priority, NULL, NULL },
4104 { "input-mappings", profile_parse_mappings, NULL, NULL },
4105 { "output-mappings", profile_parse_mappings, NULL, NULL },
4106 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
4108 /* [DecibelFix ...] */
4109 { "db-values", decibel_fix_parse_db_values, NULL, NULL },
4110 { NULL, NULL, NULL, NULL }
4113 ps = pa_xnew0(pa_alsa_profile_set, 1);
4114 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4115 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4116 ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4117 ps->input_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4118 ps->output_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4120 items[0].data = &ps->auto_profiles;
4123 fname = "default.conf";
4125 fn = pa_maybe_prefix_path(fname,
4126 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
4127 PA_ALSA_PROFILE_SETS_DIR);
4129 r = pa_config_parse(fn, NULL, items, ps);
4135 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4136 if (mapping_verify(m, bonus) < 0)
4139 if (ps->auto_profiles)
4140 profile_set_add_auto(ps);
4142 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4143 if (profile_verify(p) < 0)
4146 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4147 if (decibel_fix_verify(db_fix) < 0)
4153 pa_alsa_profile_set_free(ps);
4157 static void profile_finalize_probing(pa_alsa_profile *to_be_finalized, pa_alsa_profile *next) {
4161 if (!to_be_finalized)
4164 if (to_be_finalized->output_mappings)
4165 PA_IDXSET_FOREACH(m, to_be_finalized->output_mappings, idx) {
4170 if (to_be_finalized->supported)
4173 /* If this mapping is also in the next profile, we won't close the
4174 * pcm handle here, because it would get immediately reopened
4176 if (next && next->output_mappings && pa_idxset_get_by_data(next->output_mappings, m, NULL))
4179 snd_pcm_close(m->output_pcm);
4180 m->output_pcm = NULL;
4183 if (to_be_finalized->input_mappings)
4184 PA_IDXSET_FOREACH(m, to_be_finalized->input_mappings, idx) {
4189 if (to_be_finalized->supported)
4192 /* If this mapping is also in the next profile, we won't close the
4193 * pcm handle here, because it would get immediately reopened
4195 if (next && next->input_mappings && pa_idxset_get_by_data(next->input_mappings, m, NULL))
4198 snd_pcm_close(m->input_pcm);
4199 m->input_pcm = NULL;
4203 static snd_pcm_t* mapping_open_pcm(pa_alsa_mapping *m,
4204 const pa_sample_spec *ss,
4207 unsigned default_n_fragments,
4208 unsigned default_fragment_size_msec) {
4210 pa_sample_spec try_ss = *ss;
4211 pa_channel_map try_map = m->channel_map;
4212 snd_pcm_uframes_t try_period_size, try_buffer_size;
4214 try_ss.channels = try_map.channels;
4217 pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
4218 pa_frame_size(&try_ss);
4219 try_buffer_size = default_n_fragments * try_period_size;
4221 return pa_alsa_open_by_template(
4222 m->device_strings, dev_id, NULL, &try_ss,
4223 &try_map, mode, &try_period_size,
4224 &try_buffer_size, 0, NULL, NULL, TRUE);
4227 void pa_alsa_profile_set_probe(
4228 pa_alsa_profile_set *ps,
4230 const pa_sample_spec *ss,
4231 unsigned default_n_fragments,
4232 unsigned default_fragment_size_msec) {
4235 pa_alsa_profile *p, *last = NULL;
4245 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
4248 /* Skip if this is already marked that it is supported (i.e. from the config file) */
4249 if (!p->supported) {
4251 pa_log_debug("Looking at profile %s", p->name);
4252 profile_finalize_probing(last, p);
4253 p->supported = TRUE;
4255 /* Check if we can open all new ones */
4256 if (p->output_mappings)
4257 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4262 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
4263 if (!(m->output_pcm = mapping_open_pcm(m, ss, dev_id,
4264 SND_PCM_STREAM_PLAYBACK,
4265 default_n_fragments,
4266 default_fragment_size_msec))) {
4267 p->supported = FALSE;
4272 if (p->input_mappings && p->supported)
4273 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4278 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
4279 if (!(m->input_pcm = mapping_open_pcm(m, ss, dev_id,
4280 SND_PCM_STREAM_CAPTURE,
4281 default_n_fragments,
4282 default_fragment_size_msec))) {
4283 p->supported = FALSE;
4294 pa_log_debug("Profile %s supported.", p->name);
4296 if (p->output_mappings)
4297 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4299 mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT);
4301 if (p->input_mappings)
4302 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4304 mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT);
4308 profile_finalize_probing(last, NULL);
4310 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4311 if (!p->supported) {
4312 pa_hashmap_remove(ps->profiles, p->name);
4316 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4317 if (m->supported <= 0) {
4318 pa_hashmap_remove(ps->mappings, m->name);
4325 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
4328 pa_alsa_decibel_fix *db_fix;
4333 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4336 pa_yes_no(ps->auto_profiles),
4337 pa_yes_no(ps->probed),
4338 pa_hashmap_size(ps->mappings),
4339 pa_hashmap_size(ps->profiles),
4340 pa_hashmap_size(ps->decibel_fixes));
4342 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4343 pa_alsa_mapping_dump(m);
4345 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4346 pa_alsa_profile_dump(p);
4348 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4349 pa_alsa_decibel_fix_dump(db_fix);
4352 static pa_device_port* device_port_alsa_init(pa_hashmap *ports,
4354 const char* description,
4356 pa_alsa_setting *setting,
4357 pa_card_profile *cp,
4361 pa_device_port * p = pa_hashmap_get(ports, name);
4363 pa_alsa_port_data *data;
4365 p = pa_device_port_new(core, name, description, sizeof(pa_alsa_port_data));
4367 pa_hashmap_put(ports, name, p);
4368 p->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4370 data = PA_DEVICE_PORT_DATA(p);
4372 data->setting = setting;
4375 p->is_input |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_INPUT;
4376 p->is_output |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_OUTPUT;
4379 pa_hashmap_put(p->profiles, cp->name, cp);
4382 pa_hashmap_put(extra, name, p);
4383 pa_device_port_ref(p);
4389 void pa_alsa_path_set_add_ports(
4390 pa_alsa_path_set *ps,
4391 pa_card_profile *cp,
4404 PA_HASHMAP_FOREACH(path, ps->paths, state) {
4405 if (!path->settings || !path->settings->next) {
4406 /* If there is no or just one setting we only need a
4408 pa_device_port *port = device_port_alsa_init(ports, path->name,
4409 path->description, path, path->settings, cp, extra, core);
4410 port->priority = path->priority * 100;
4414 PA_LLIST_FOREACH(s, path->settings) {
4415 pa_device_port *port;
4418 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
4420 if (s->description[0])
4421 d = pa_sprintf_malloc(_("%s / %s"), path->description, s->description);
4423 d = pa_xstrdup(path->description);
4425 port = device_port_alsa_init(ports, n, d, path, s, cp, extra, core);
4426 port->priority = path->priority * 100 + s->priority;
4435 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps, pa_card *card) {
4441 if (ps->paths && pa_hashmap_size(ps->paths) > 0) {
4443 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4444 pa_alsa_path_set_add_ports(ps, NULL, card->ports, *p, card->core);
4447 pa_log_debug("Added %u ports", *p ? pa_hashmap_size(*p) : 0);