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_("Analog Microphone") },
2303 { "analog-input-microphone-front", N_("Front Microphone") },
2304 { "analog-input-microphone-rear", N_("Rear Microphone") },
2305 { "analog-input-microphone-dock", N_("Docking Station Microphone") },
2306 { "analog-input-microphone-internal", N_("Internal Microphone") },
2307 { "analog-input-linein", N_("Analog Line-In") },
2308 { "analog-input-radio", N_("Analog Radio") },
2309 { "analog-input-video", N_("Analog Video") },
2310 { "analog-output", N_("Analog Output") },
2311 { "analog-output-headphones", N_("Analog Headphones") },
2312 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
2313 { "analog-output-mono", N_("Analog Mono Output") },
2314 { "analog-output-speaker", N_("Analog Speakers") },
2315 { "iec958-stereo-output", N_("Digital Output (IEC958)") },
2316 { "iec958-passthrough-output", N_("Digital Passthrough (IEC958)") }
2323 PA_LLIST_FOREACH(e, p->elements)
2324 if (element_verify(e) < 0)
2327 if (!p->description)
2328 p->description = pa_xstrdup(lookup_description(p->name,
2329 well_known_descriptions,
2330 PA_ELEMENTSOF(well_known_descriptions)));
2332 if (!p->description)
2333 p->description = pa_xstrdup(p->name);
2338 static const char *get_default_paths_dir(void) {
2339 if (pa_run_from_build_tree())
2340 return PA_BUILDDIR "/modules/alsa/mixer/paths/";
2342 return PA_ALSA_PATHS_DIR;
2345 pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa_direction_t direction) {
2351 pa_config_item items[] = {
2353 { "priority", pa_config_parse_unsigned, NULL, "General" },
2354 { "description", pa_config_parse_string, NULL, "General" },
2355 { "name", pa_config_parse_string, NULL, "General" },
2358 { "priority", option_parse_priority, NULL, NULL },
2359 { "name", option_parse_name, NULL, NULL },
2362 { "switch", element_parse_switch, NULL, NULL },
2363 { "volume", element_parse_volume, NULL, NULL },
2364 { "enumeration", element_parse_enumeration, NULL, NULL },
2365 { "override-map.1", element_parse_override_map, NULL, NULL },
2366 { "override-map.2", element_parse_override_map, NULL, NULL },
2367 /* ... later on we might add override-map.3 and so on here ... */
2368 { "required", element_parse_required, NULL, NULL },
2369 { "required-any", element_parse_required, NULL, NULL },
2370 { "required-absent", element_parse_required, NULL, NULL },
2371 { "direction", element_parse_direction, NULL, NULL },
2372 { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
2373 { "volume-limit", element_parse_volume_limit, NULL, NULL },
2374 { NULL, NULL, NULL, NULL }
2379 p = pa_xnew0(pa_alsa_path, 1);
2380 n = pa_path_get_filename(fname);
2381 p->name = pa_xstrndup(n, strcspn(n, "."));
2382 p->direction = direction;
2384 items[0].data = &p->priority;
2385 items[1].data = &p->description;
2386 items[2].data = &p->name;
2389 paths_dir = get_default_paths_dir();
2391 fn = pa_maybe_prefix_path(fname, paths_dir);
2393 r = pa_config_parse(fn, NULL, items, p);
2399 if (path_verify(p) < 0)
2405 pa_alsa_path_free(p);
2409 pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction) {
2415 p = pa_xnew0(pa_alsa_path, 1);
2416 p->name = pa_xstrdup(element);
2417 p->direction = direction;
2419 e = pa_xnew0(pa_alsa_element, 1);
2421 e->alsa_name = pa_xstrdup(element);
2422 e->direction = direction;
2423 e->volume_limit = -1;
2425 e->switch_use = PA_ALSA_SWITCH_MUTE;
2426 e->volume_use = PA_ALSA_VOLUME_MERGE;
2428 PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
2429 p->last_element = e;
2433 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
2434 pa_alsa_option *o, *n;
2438 for (o = e->options; o; o = n) {
2441 if (o->alsa_idx < 0) {
2442 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
2448 e->switch_use != PA_ALSA_SWITCH_IGNORE ||
2449 e->volume_use != PA_ALSA_VOLUME_IGNORE ||
2450 e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
2453 static void path_drop_unsupported(pa_alsa_path *p) {
2454 pa_alsa_element *e, *n;
2458 for (e = p->elements; e; e = n) {
2461 if (!element_drop_unsupported(e)) {
2462 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
2468 static void path_make_options_unique(pa_alsa_path *p) {
2470 pa_alsa_option *o, *u;
2472 PA_LLIST_FOREACH(e, p->elements) {
2473 PA_LLIST_FOREACH(o, e->options) {
2477 for (u = o->next; u; u = u->next)
2478 if (pa_streq(u->name, o->name))
2484 m = pa_xstrdup(o->name);
2486 /* OK, this name is not unique, hence let's rename */
2487 for (i = 1, u = o; u; u = u->next) {
2490 if (!pa_streq(u->name, m))
2493 nn = pa_sprintf_malloc("%s-%u", m, i);
2497 nd = pa_sprintf_malloc("%s %u", u->description, i);
2498 pa_xfree(u->description);
2499 u->description = nd;
2509 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
2512 for (; e; e = e->next)
2513 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
2514 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
2520 for (o = e->options; o; o = o->next) {
2524 s = pa_xnewdup(pa_alsa_setting, template, 1);
2525 s->options = pa_idxset_copy(template->options);
2526 s->name = pa_sprintf_malloc(_("%s+%s"), template->name, o->name);
2528 (template->description[0] && o->description[0])
2529 ? pa_sprintf_malloc(_("%s / %s"), template->description, o->description)
2530 : (template->description[0]
2531 ? pa_xstrdup(template->description)
2532 : pa_xstrdup(o->description));
2534 s->priority = PA_MAX(template->priority, o->priority);
2536 s = pa_xnew0(pa_alsa_setting, 1);
2537 s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2538 s->name = pa_xstrdup(o->name);
2539 s->description = pa_xstrdup(o->description);
2540 s->priority = o->priority;
2543 pa_idxset_put(s->options, o, NULL);
2545 if (element_create_settings(e->next, s))
2546 /* This is not a leaf, so let's get rid of it */
2549 /* This is a leaf, so let's add it */
2550 PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2552 e->path->last_setting = s;
2559 static void path_create_settings(pa_alsa_path *p) {
2562 element_create_settings(p->elements, NULL);
2565 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) {
2567 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2568 pa_channel_position_t t;
2569 pa_channel_position_mask_t path_volume_channels = 0;
2575 return p->supported ? 0 : -1;
2581 pa_log_debug("Probing path '%s'", p->name);
2583 PA_LLIST_FOREACH(e, p->elements) {
2584 if (element_probe(e, m) < 0) {
2585 p->supported = FALSE;
2586 pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2589 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);
2594 if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2596 if (!p->has_volume) {
2597 p->min_volume = e->min_volume;
2598 p->max_volume = e->max_volume;
2602 if (!p->has_volume) {
2603 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2604 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2605 min_dB[t] = e->min_dB;
2606 max_dB[t] = e->max_dB;
2607 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2614 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2615 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2616 min_dB[t] += e->min_dB;
2617 max_dB[t] += e->max_dB;
2618 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2621 /* Hmm, there's another element before us
2622 * which cannot do dB volumes, so we we need
2623 * to 'neutralize' this slider */
2624 e->volume_use = PA_ALSA_VOLUME_ZERO;
2625 pa_log_info("Zeroing volume of '%s' on path '%s'", e->alsa_name, p->name);
2628 } else if (p->has_volume) {
2629 /* We can't use this volume, so let's ignore it */
2630 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2631 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e->alsa_name, p->name);
2633 p->has_volume = TRUE;
2636 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2640 if (p->has_req_any && !p->req_any_present) {
2641 p->supported = FALSE;
2642 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name);
2646 path_drop_unsupported(p);
2647 path_make_options_unique(p);
2648 path_create_settings(p);
2650 p->supported = TRUE;
2652 p->min_dB = INFINITY;
2653 p->max_dB = -INFINITY;
2655 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2656 if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) {
2657 if (p->min_dB > min_dB[t])
2658 p->min_dB = min_dB[t];
2660 if (p->max_dB < max_dB[t])
2661 p->max_dB = max_dB[t];
2668 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2671 pa_log_debug("Setting %s (%s) priority=%u",
2673 pa_strnull(s->description),
2677 void pa_alsa_option_dump(pa_alsa_option *o) {
2680 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2682 pa_strnull(o->name),
2683 pa_strnull(o->description),
2688 void pa_alsa_element_dump(pa_alsa_element *e) {
2692 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",
2702 (long long unsigned) e->merged_mask,
2704 pa_yes_no(e->override_map));
2706 PA_LLIST_FOREACH(o, e->options)
2707 pa_alsa_option_dump(o);
2710 void pa_alsa_path_dump(pa_alsa_path *p) {
2715 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2716 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2718 pa_strnull(p->description),
2721 pa_yes_no(p->probed),
2722 pa_yes_no(p->supported),
2723 pa_yes_no(p->has_mute),
2724 pa_yes_no(p->has_volume),
2725 pa_yes_no(p->has_dB),
2726 p->min_volume, p->max_volume,
2727 p->min_dB, p->max_dB);
2729 PA_LLIST_FOREACH(e, p->elements)
2730 pa_alsa_element_dump(e);
2732 PA_LLIST_FOREACH(s, p->settings)
2733 pa_alsa_setting_dump(s);
2736 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2737 snd_mixer_selem_id_t *sid;
2738 snd_mixer_elem_t *me;
2744 SELEM_INIT(sid, e->alsa_name);
2745 if (!(me = snd_mixer_find_selem(m, sid))) {
2746 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2750 snd_mixer_elem_set_callback(me, cb);
2751 snd_mixer_elem_set_callback_private(me, userdata);
2754 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2761 PA_LLIST_FOREACH(e, p->elements)
2762 element_set_callback(e, m, cb, userdata);
2765 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2773 PA_HASHMAP_FOREACH(p, ps->paths, state)
2774 pa_alsa_path_set_callback(p, m, cb, userdata);
2777 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction, const char *paths_dir) {
2778 pa_alsa_path_set *ps;
2779 char **pn = NULL, **en = NULL, **ie;
2780 pa_alsa_decibel_fix *db_fix;
2781 void *state, *state2;
2785 pa_assert(m->profile_set);
2786 pa_assert(m->profile_set->decibel_fixes);
2787 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2789 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2792 ps = pa_xnew0(pa_alsa_path_set, 1);
2793 ps->direction = direction;
2794 ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2796 if (direction == PA_ALSA_DIRECTION_OUTPUT) {
2797 pn = m->output_path_names;
2798 cache = m->profile_set->output_paths;
2800 else if (direction == PA_ALSA_DIRECTION_INPUT) {
2801 pn = m->input_path_names;
2802 cache = m->profile_set->input_paths;
2808 for (in = pn; *in; in++) {
2809 pa_alsa_path *p = NULL;
2810 pa_bool_t duplicate = FALSE;
2813 for (kn = pn; kn < in; kn++)
2814 if (pa_streq(*kn, *in)) {
2822 p = pa_hashmap_get(cache, *in);
2824 char *fn = pa_sprintf_malloc("%s.conf", *in);
2825 p = pa_alsa_path_new(paths_dir, fn, direction);
2828 pa_hashmap_put(cache, *in, p);
2830 pa_assert(pa_hashmap_get(cache, *in) == p);
2832 pa_hashmap_put(ps->paths, p, p);
2839 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2840 en = m->output_element;
2841 else if (direction == PA_ALSA_DIRECTION_INPUT)
2842 en = m->input_element;
2845 pa_alsa_path_set_free(ps);
2849 for (ie = en; *ie; ie++) {
2853 p = pa_alsa_path_synthesize(*ie, direction);
2855 /* Mark all other passed elements for require-absent */
2856 for (je = en; *je; je++) {
2862 e = pa_xnew0(pa_alsa_element, 1);
2864 e->alsa_name = pa_xstrdup(*je);
2865 e->direction = direction;
2866 e->required_absent = PA_ALSA_REQUIRED_ANY;
2867 e->volume_limit = -1;
2869 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2870 p->last_element = e;
2873 pa_hashmap_put(ps->paths, *ie, p);
2877 /* Assign decibel fixes to elements. */
2878 PA_HASHMAP_FOREACH(db_fix, m->profile_set->decibel_fixes, state) {
2881 PA_HASHMAP_FOREACH(p, ps->paths, state2) {
2884 PA_LLIST_FOREACH(e, p->elements) {
2885 if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_name)) {
2886 /* The profile set that contains the dB fix may be freed
2887 * before the element, so we have to copy the dB fix
2889 e->db_fix = pa_xnewdup(pa_alsa_decibel_fix, db_fix, 1);
2890 e->db_fix->profile_set = NULL;
2891 e->db_fix->name = pa_xstrdup(db_fix->name);
2892 e->db_fix->db_values = pa_xmemdup(db_fix->db_values, (db_fix->max_step - db_fix->min_step + 1) * sizeof(long));
2901 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2906 pa_log_debug("Path Set %p, direction=%i",
2910 PA_HASHMAP_FOREACH(p, ps->paths, state)
2911 pa_alsa_path_dump(p);
2915 static pa_bool_t options_have_option(pa_alsa_option *options, const char *alsa_name) {
2919 pa_assert(alsa_name);
2921 PA_LLIST_FOREACH(o, options) {
2922 if (pa_streq(o->alsa_name, alsa_name))
2928 static pa_bool_t enumeration_is_subset(pa_alsa_option *a_options, pa_alsa_option *b_options) {
2929 pa_alsa_option *oa, *ob;
2931 if (!a_options) return TRUE;
2932 if (!b_options) return FALSE;
2934 /* If there is an option A offers that B does not, then A is not a subset of B. */
2935 PA_LLIST_FOREACH(oa, a_options) {
2936 pa_bool_t found = FALSE;
2937 PA_LLIST_FOREACH(ob, b_options) {
2938 if (pa_streq(oa->alsa_name, ob->alsa_name)) {
2950 * Compares two elements to see if a is a subset of b
2952 static pa_bool_t element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_t *m) {
2958 * Every state is a subset of itself (with caveats for volume_limits and options)
2959 * IGNORE is a subset of every other state */
2961 /* Check the volume_use */
2962 if (a->volume_use != PA_ALSA_VOLUME_IGNORE) {
2964 /* "Constant" is subset of "Constant" only when their constant values are equal */
2965 if (a->volume_use == PA_ALSA_VOLUME_CONSTANT && b->volume_use == PA_ALSA_VOLUME_CONSTANT && a->constant_volume != b->constant_volume)
2968 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
2969 if (a->volume_use != b->volume_use && b->volume_use != PA_ALSA_VOLUME_MERGE)
2972 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
2973 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
2974 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
2975 if (b->volume_use == PA_ALSA_VOLUME_MERGE && b->volume_limit >= 0) {
2978 if (a->volume_use == PA_ALSA_VOLUME_CONSTANT)
2979 a_limit = a->constant_volume;
2980 else if (a->volume_use == PA_ALSA_VOLUME_ZERO) {
2984 int rounding = (a->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1);
2985 a_limit = decibel_fix_get_step(a->db_fix, &dB, rounding);
2987 snd_mixer_selem_id_t *sid;
2988 snd_mixer_elem_t *me;
2990 SELEM_INIT(sid, a->alsa_name);
2991 if (!(me = snd_mixer_find_selem(m, sid))) {
2992 pa_log_warn("Element %s seems to have disappeared.", a->alsa_name);
2996 if (a->direction == PA_ALSA_DIRECTION_OUTPUT) {
2997 if (snd_mixer_selem_ask_playback_dB_vol(me, dB, +1, &a_limit) < 0)
3000 if (snd_mixer_selem_ask_capture_dB_vol(me, dB, -1, &a_limit) < 0)
3004 } else if (a->volume_use == PA_ALSA_VOLUME_OFF)
3005 a_limit = a->min_volume;
3006 else if (a->volume_use == PA_ALSA_VOLUME_MERGE)
3007 a_limit = a->volume_limit;
3009 /* This should never be reached */
3012 if (a_limit > b->volume_limit)
3017 if (a->switch_use != PA_ALSA_SWITCH_IGNORE) {
3018 /* "On" is a subset of "Mute".
3019 * "Off" is a subset of "Mute".
3020 * "On" is a subset of "Select", if there is an "Option:On" in B.
3021 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3022 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3024 if (a->switch_use != b->switch_use) {
3026 if (a->switch_use == PA_ALSA_SWITCH_SELECT || a->switch_use == PA_ALSA_SWITCH_MUTE
3027 || b->switch_use == PA_ALSA_SWITCH_OFF || b->switch_use == PA_ALSA_SWITCH_ON)
3030 if (b->switch_use == PA_ALSA_SWITCH_SELECT) {
3031 if (a->switch_use == PA_ALSA_SWITCH_ON) {
3032 if (!options_have_option(b->options, "on"))
3034 } else if (a->switch_use == PA_ALSA_SWITCH_OFF) {
3035 if (!options_have_option(b->options, "off"))
3039 } else if (a->switch_use == PA_ALSA_SWITCH_SELECT) {
3040 if (!enumeration_is_subset(a->options, b->options))
3045 if (a->enumeration_use != PA_ALSA_ENUMERATION_IGNORE) {
3046 if (b->enumeration_use == PA_ALSA_ENUMERATION_IGNORE)
3048 if (!enumeration_is_subset(a->options, b->options))
3055 static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
3062 /* If we only have one path, then don't bother */
3063 if (pa_hashmap_size(ps->paths) < 2)
3066 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3070 PA_HASHMAP_FOREACH(p2, ps->paths, state2) {
3071 pa_alsa_element *ea, *eb;
3072 pa_bool_t is_subset = TRUE;
3077 /* Compare the elements of each set... */
3078 pa_assert_se(ea = p->elements);
3079 pa_assert_se(eb = p2->elements);
3082 if (pa_streq(ea->alsa_name, eb->alsa_name)) {
3083 if (element_is_subset(ea, eb, m)) {
3086 if ((ea && !eb) || (!ea && eb))
3088 else if (!ea && !eb)
3098 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p->name, p2->name);
3099 pa_hashmap_remove(ps->paths, p);
3106 static pa_alsa_path* path_set_find_path_by_name(pa_alsa_path_set *ps, const char* name, pa_alsa_path *ignore)
3111 PA_HASHMAP_FOREACH(p, ps->paths, state)
3112 if (p != ignore && pa_streq(p->name, name))
3117 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
3118 pa_alsa_path *p, *q;
3119 void *state, *state2;
3121 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3125 q = path_set_find_path_by_name(ps, p->name, p);
3130 m = pa_xstrdup(p->name);
3132 /* OK, this name is not unique, hence let's rename */
3134 PA_HASHMAP_FOREACH(q, ps->paths, state2) {
3137 if (!pa_streq(q->name, m))
3140 nn = pa_sprintf_malloc("%s-%u", m, i);
3144 nd = pa_sprintf_malloc("%s %u", q->description, i);
3145 pa_xfree(q->description);
3146 q->description = nd;
3155 static void mapping_free(pa_alsa_mapping *m) {
3159 pa_xfree(m->description);
3161 pa_xstrfreev(m->device_strings);
3162 pa_xstrfreev(m->input_path_names);
3163 pa_xstrfreev(m->output_path_names);
3164 pa_xstrfreev(m->input_element);
3165 pa_xstrfreev(m->output_element);
3166 if (m->input_path_set)
3167 pa_alsa_path_set_free(m->input_path_set);
3168 if (m->output_path_set)
3169 pa_alsa_path_set_free(m->output_path_set);
3171 pa_assert(!m->input_pcm);
3172 pa_assert(!m->output_pcm);
3177 static void profile_free(pa_alsa_profile *p) {
3181 pa_xfree(p->description);
3183 pa_xstrfreev(p->input_mapping_names);
3184 pa_xstrfreev(p->output_mapping_names);
3186 if (p->input_mappings)
3187 pa_idxset_free(p->input_mappings, NULL, NULL);
3189 if (p->output_mappings)
3190 pa_idxset_free(p->output_mappings, NULL, NULL);
3195 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
3198 if (ps->input_paths) {
3201 while ((p = pa_hashmap_steal_first(ps->input_paths)))
3202 pa_alsa_path_free(p);
3204 pa_hashmap_free(ps->input_paths, NULL, NULL);
3207 if (ps->output_paths) {
3210 while ((p = pa_hashmap_steal_first(ps->output_paths)))
3211 pa_alsa_path_free(p);
3213 pa_hashmap_free(ps->output_paths, NULL, NULL);
3219 while ((p = pa_hashmap_steal_first(ps->profiles)))
3222 pa_hashmap_free(ps->profiles, NULL, NULL);
3228 while ((m = pa_hashmap_steal_first(ps->mappings)))
3231 pa_hashmap_free(ps->mappings, NULL, NULL);
3234 if (ps->decibel_fixes) {
3235 pa_alsa_decibel_fix *db_fix;
3237 while ((db_fix = pa_hashmap_steal_first(ps->decibel_fixes)))
3238 decibel_fix_free(db_fix);
3240 pa_hashmap_free(ps->decibel_fixes, NULL, NULL);
3246 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
3249 if (!pa_startswith(name, "Mapping "))
3254 if ((m = pa_hashmap_get(ps->mappings, name)))
3257 m = pa_xnew0(pa_alsa_mapping, 1);
3258 m->profile_set = ps;
3259 m->name = pa_xstrdup(name);
3260 pa_channel_map_init(&m->channel_map);
3262 pa_hashmap_put(ps->mappings, m->name, m);
3267 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
3270 if (!pa_startswith(name, "Profile "))
3275 if ((p = pa_hashmap_get(ps->profiles, name)))
3278 p = pa_xnew0(pa_alsa_profile, 1);
3279 p->profile_set = ps;
3280 p->name = pa_xstrdup(name);
3282 pa_hashmap_put(ps->profiles, p->name, p);
3287 static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) {
3288 pa_alsa_decibel_fix *db_fix;
3290 if (!pa_startswith(name, "DecibelFix "))
3295 if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name)))
3298 db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
3299 db_fix->profile_set = ps;
3300 db_fix->name = pa_xstrdup(name);
3302 pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix);
3307 static int mapping_parse_device_strings(
3308 const char *filename,
3310 const char *section,
3316 pa_alsa_profile_set *ps = userdata;
3321 if (!(m = mapping_get(ps, section))) {
3322 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3326 pa_xstrfreev(m->device_strings);
3327 if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
3328 pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
3335 static int mapping_parse_channel_map(
3336 const char *filename,
3338 const char *section,
3344 pa_alsa_profile_set *ps = userdata;
3349 if (!(m = mapping_get(ps, section))) {
3350 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3354 if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
3355 pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
3362 static int mapping_parse_paths(
3363 const char *filename,
3365 const char *section,
3371 pa_alsa_profile_set *ps = userdata;
3376 if (!(m = mapping_get(ps, section))) {
3377 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3381 if (pa_streq(lvalue, "paths-input")) {
3382 pa_xstrfreev(m->input_path_names);
3383 m->input_path_names = pa_split_spaces_strv(rvalue);
3385 pa_xstrfreev(m->output_path_names);
3386 m->output_path_names = pa_split_spaces_strv(rvalue);
3392 static int mapping_parse_element(
3393 const char *filename,
3395 const char *section,
3401 pa_alsa_profile_set *ps = userdata;
3406 if (!(m = mapping_get(ps, section))) {
3407 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3411 if (pa_streq(lvalue, "element-input")) {
3412 pa_xstrfreev(m->input_element);
3413 m->input_element = pa_split_spaces_strv(rvalue);
3415 pa_xstrfreev(m->output_element);
3416 m->output_element = pa_split_spaces_strv(rvalue);
3422 static int mapping_parse_direction(
3423 const char *filename,
3425 const char *section,
3431 pa_alsa_profile_set *ps = userdata;
3436 if (!(m = mapping_get(ps, section))) {
3437 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3441 if (pa_streq(rvalue, "input"))
3442 m->direction = PA_ALSA_DIRECTION_INPUT;
3443 else if (pa_streq(rvalue, "output"))
3444 m->direction = PA_ALSA_DIRECTION_OUTPUT;
3445 else if (pa_streq(rvalue, "any"))
3446 m->direction = PA_ALSA_DIRECTION_ANY;
3448 pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
3455 static int mapping_parse_description(
3456 const char *filename,
3458 const char *section,
3464 pa_alsa_profile_set *ps = userdata;
3470 if ((m = mapping_get(ps, section))) {
3471 pa_xfree(m->description);
3472 m->description = pa_xstrdup(rvalue);
3473 } else if ((p = profile_get(ps, section))) {
3474 pa_xfree(p->description);
3475 p->description = pa_xstrdup(rvalue);
3477 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3484 static int mapping_parse_priority(
3485 const char *filename,
3487 const char *section,
3493 pa_alsa_profile_set *ps = userdata;
3500 if (pa_atou(rvalue, &prio) < 0) {
3501 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
3505 if ((m = mapping_get(ps, section)))
3507 else if ((p = profile_get(ps, section)))
3510 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3517 static int profile_parse_mappings(
3518 const char *filename,
3520 const char *section,
3526 pa_alsa_profile_set *ps = userdata;
3531 if (!(p = profile_get(ps, section))) {
3532 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3536 if (pa_streq(lvalue, "input-mappings")) {
3537 pa_xstrfreev(p->input_mapping_names);
3538 p->input_mapping_names = pa_split_spaces_strv(rvalue);
3540 pa_xstrfreev(p->output_mapping_names);
3541 p->output_mapping_names = pa_split_spaces_strv(rvalue);
3547 static int profile_parse_skip_probe(
3548 const char *filename,
3550 const char *section,
3556 pa_alsa_profile_set *ps = userdata;
3562 if (!(p = profile_get(ps, section))) {
3563 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3567 if ((b = pa_parse_boolean(rvalue)) < 0) {
3568 pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
3577 static int decibel_fix_parse_db_values(
3578 const char *filename,
3580 const char *section,
3586 pa_alsa_profile_set *ps = userdata;
3587 pa_alsa_decibel_fix *db_fix;
3591 unsigned n = 8; /* Current size of the db_values table. */
3592 unsigned min_step = 0;
3593 unsigned max_step = 0;
3594 unsigned i = 0; /* Index to the items table. */
3595 unsigned prev_step = 0;
3598 pa_assert(filename);
3604 if (!(db_fix = decibel_fix_get(ps, section))) {
3605 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3609 if (!(items = pa_split_spaces_strv(rvalue))) {
3610 pa_log("[%s:%u] Value missing", pa_strnull(filename), line);
3614 db_values = pa_xnew(long, n);
3616 while ((item = items[i++])) {
3617 char *s = item; /* Step value string. */
3618 char *d = item; /* dB value string. */
3622 /* Move d forward until it points to a colon or to the end of the item. */
3623 for (; *d && *d != ':'; ++d);
3626 /* item started with colon. */
3627 pa_log("[%s:%u] No step value found in %s", filename, line, item);
3631 if (!*d || !*(d + 1)) {
3632 /* No colon found, or it was the last character in item. */
3633 pa_log("[%s:%u] No dB value found in %s", filename, line, item);
3637 /* pa_atou() needs a null-terminating string. Let's replace the colon
3638 * with a zero byte. */
3641 if (pa_atou(s, &step) < 0) {
3642 pa_log("[%s:%u] Invalid step value: %s", filename, line, s);
3646 if (pa_atod(d, &db) < 0) {
3647 pa_log("[%s:%u] Invalid dB value: %s", filename, line, d);
3651 if (step <= prev_step && i != 1) {
3652 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename, line, step, prev_step);
3656 if (db < prev_db && i != 1) {
3657 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename, line, db, prev_db);
3663 db_values[0] = (long) (db * 100.0);
3667 /* Interpolate linearly. */
3668 double db_increment = (db - prev_db) / (step - prev_step);
3670 for (; prev_step < step; ++prev_step, prev_db += db_increment) {
3672 /* Reallocate the db_values table if it's about to overflow. */
3673 if (prev_step + 1 - min_step == n) {
3675 db_values = pa_xrenew(long, db_values, n);
3678 db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
3685 db_fix->min_step = min_step;
3686 db_fix->max_step = max_step;
3687 pa_xfree(db_fix->db_values);
3688 db_fix->db_values = db_values;
3690 pa_xstrfreev(items);
3695 pa_xstrfreev(items);
3696 pa_xfree(db_values);
3701 static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
3702 pa_alsa_direction_t direction) {
3706 snd_pcm_t *pcm_handle;
3707 pa_alsa_path_set *ps;
3708 snd_mixer_t *mixer_handle;
3710 if (direction == PA_ALSA_DIRECTION_OUTPUT) {
3711 if (m->output_path_set)
3712 return; /* Already probed */
3713 m->output_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3714 pcm_handle = m->output_pcm;
3716 if (m->input_path_set)
3717 return; /* Already probed */
3718 m->input_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3719 pcm_handle = m->input_pcm;
3723 return; /* No paths */
3725 pa_assert(pcm_handle);
3727 mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL);
3728 if (!mixer_handle) {
3729 /* Cannot open mixer, remove all entries */
3730 while (pa_hashmap_steal_first(ps->paths));
3735 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3736 if (pa_alsa_path_probe(p, mixer_handle, m->profile_set->ignore_dB) < 0) {
3737 pa_hashmap_remove(ps->paths, p);
3741 path_set_condense(ps, mixer_handle);
3742 path_set_make_paths_unique(ps);
3745 snd_mixer_close(mixer_handle);
3747 pa_log_debug("Available mixer paths (after tidying):");
3748 pa_alsa_path_set_dump(ps);
3751 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
3753 static const struct description_map well_known_descriptions[] = {
3754 { "analog-mono", N_("Analog Mono") },
3755 { "analog-stereo", N_("Analog Stereo") },
3756 { "analog-surround-21", N_("Analog Surround 2.1") },
3757 { "analog-surround-30", N_("Analog Surround 3.0") },
3758 { "analog-surround-31", N_("Analog Surround 3.1") },
3759 { "analog-surround-40", N_("Analog Surround 4.0") },
3760 { "analog-surround-41", N_("Analog Surround 4.1") },
3761 { "analog-surround-50", N_("Analog Surround 5.0") },
3762 { "analog-surround-51", N_("Analog Surround 5.1") },
3763 { "analog-surround-61", N_("Analog Surround 6.0") },
3764 { "analog-surround-61", N_("Analog Surround 6.1") },
3765 { "analog-surround-70", N_("Analog Surround 7.0") },
3766 { "analog-surround-71", N_("Analog Surround 7.1") },
3767 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3768 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3769 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3770 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3771 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3776 if (!pa_channel_map_valid(&m->channel_map)) {
3777 pa_log("Mapping %s is missing channel map.", m->name);
3781 if (!m->device_strings) {
3782 pa_log("Mapping %s is missing device strings.", m->name);
3786 if ((m->input_path_names && m->input_element) ||
3787 (m->output_path_names && m->output_element)) {
3788 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
3792 if (!m->description)
3793 m->description = pa_xstrdup(lookup_description(m->name,
3794 well_known_descriptions,
3795 PA_ELEMENTSOF(well_known_descriptions)));
3797 if (!m->description)
3798 m->description = pa_xstrdup(m->name);
3801 if (pa_channel_map_equal(&m->channel_map, bonus))
3803 else if (m->channel_map.channels == bonus->channels)
3810 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
3811 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
3815 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3817 pa_strnull(m->description),
3819 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
3820 pa_yes_no(m->supported),
3824 static void profile_set_add_auto_pair(
3825 pa_alsa_profile_set *ps,
3826 pa_alsa_mapping *m, /* output */
3827 pa_alsa_mapping *n /* input */) {
3835 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
3838 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
3842 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
3844 name = pa_sprintf_malloc("output:%s", m->name);
3846 name = pa_sprintf_malloc("input:%s", n->name);
3848 if (pa_hashmap_get(ps->profiles, name)) {
3853 p = pa_xnew0(pa_alsa_profile, 1);
3854 p->profile_set = ps;
3858 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3859 pa_idxset_put(p->output_mappings, m, NULL);
3860 p->priority += m->priority * 100;
3864 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3865 pa_idxset_put(p->input_mappings, n, NULL);
3866 p->priority += n->priority;
3869 pa_hashmap_put(ps->profiles, p->name, p);
3872 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
3873 pa_alsa_mapping *m, *n;
3874 void *m_state, *n_state;
3878 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
3879 profile_set_add_auto_pair(ps, m, NULL);
3881 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3882 profile_set_add_auto_pair(ps, m, n);
3885 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3886 profile_set_add_auto_pair(ps, NULL, n);
3889 static int profile_verify(pa_alsa_profile *p) {
3891 static const struct description_map well_known_descriptions[] = {
3892 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3893 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3894 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3895 { "off", N_("Off") }
3900 /* Replace the output mapping names by the actual mappings */
3901 if (p->output_mapping_names) {
3904 pa_assert(!p->output_mappings);
3905 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3907 for (name = p->output_mapping_names; *name; name++) {
3910 pa_bool_t duplicate = FALSE;
3912 for (in = name + 1; *in; in++)
3913 if (pa_streq(*name, *in)) {
3921 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
3922 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
3926 pa_idxset_put(p->output_mappings, m, NULL);
3932 pa_xstrfreev(p->output_mapping_names);
3933 p->output_mapping_names = NULL;
3936 /* Replace the input mapping names by the actual mappings */
3937 if (p->input_mapping_names) {
3940 pa_assert(!p->input_mappings);
3941 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3943 for (name = p->input_mapping_names; *name; name++) {
3946 pa_bool_t duplicate = FALSE;
3948 for (in = name + 1; *in; in++)
3949 if (pa_streq(*name, *in)) {
3957 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
3958 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
3962 pa_idxset_put(p->input_mappings, m, NULL);
3968 pa_xstrfreev(p->input_mapping_names);
3969 p->input_mapping_names = NULL;
3972 if (!p->input_mappings && !p->output_mappings) {
3973 pa_log("Profile '%s' lacks mappings.", p->name);
3977 if (!p->description)
3978 p->description = pa_xstrdup(lookup_description(p->name,
3979 well_known_descriptions,
3980 PA_ELEMENTSOF(well_known_descriptions)));
3982 if (!p->description) {
3987 sb = pa_strbuf_new();
3989 if (p->output_mappings)
3990 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3991 if (!pa_strbuf_isempty(sb))
3992 pa_strbuf_puts(sb, " + ");
3994 pa_strbuf_printf(sb, _("%s Output"), m->description);
3997 if (p->input_mappings)
3998 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3999 if (!pa_strbuf_isempty(sb))
4000 pa_strbuf_puts(sb, " + ");
4002 pa_strbuf_printf(sb, _("%s Input"), m->description);
4005 p->description = pa_strbuf_tostring_free(sb);
4011 void pa_alsa_profile_dump(pa_alsa_profile *p) {
4016 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4018 pa_strnull(p->description),
4020 pa_yes_no(p->supported),
4021 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
4022 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
4024 if (p->input_mappings)
4025 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4026 pa_log_debug("Input %s", m->name);
4028 if (p->output_mappings)
4029 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4030 pa_log_debug("Output %s", m->name);
4033 static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
4036 /* Check that the dB mapping has been configured. Since "db-values" is
4037 * currently the only option in the DecibelFix section, and decibel fix
4038 * objects don't get created if a DecibelFix section is empty, this is
4039 * actually a redundant check. Having this may prevent future bugs,
4041 if (!db_fix->db_values) {
4042 pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
4049 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
4050 char *db_values = NULL;
4054 if (db_fix->db_values) {
4056 unsigned long i, nsteps;
4058 pa_assert(db_fix->min_step <= db_fix->max_step);
4059 nsteps = db_fix->max_step - db_fix->min_step + 1;
4061 buf = pa_strbuf_new();
4062 for (i = 0; i < nsteps; ++i)
4063 pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
4065 db_values = pa_strbuf_tostring_free(buf);
4068 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4069 db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
4071 pa_xfree(db_values);
4074 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
4075 pa_alsa_profile_set *ps;
4078 pa_alsa_decibel_fix *db_fix;
4083 static pa_config_item items[] = {
4085 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
4088 { "device-strings", mapping_parse_device_strings, NULL, NULL },
4089 { "channel-map", mapping_parse_channel_map, NULL, NULL },
4090 { "paths-input", mapping_parse_paths, NULL, NULL },
4091 { "paths-output", mapping_parse_paths, NULL, NULL },
4092 { "element-input", mapping_parse_element, NULL, NULL },
4093 { "element-output", mapping_parse_element, NULL, NULL },
4094 { "direction", mapping_parse_direction, NULL, NULL },
4096 /* Shared by [Mapping ...] and [Profile ...] */
4097 { "description", mapping_parse_description, NULL, NULL },
4098 { "priority", mapping_parse_priority, NULL, NULL },
4101 { "input-mappings", profile_parse_mappings, NULL, NULL },
4102 { "output-mappings", profile_parse_mappings, NULL, NULL },
4103 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
4105 /* [DecibelFix ...] */
4106 { "db-values", decibel_fix_parse_db_values, NULL, NULL },
4107 { NULL, NULL, NULL, NULL }
4110 ps = pa_xnew0(pa_alsa_profile_set, 1);
4111 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4112 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4113 ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4114 ps->input_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4115 ps->output_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4117 items[0].data = &ps->auto_profiles;
4120 fname = "default.conf";
4122 fn = pa_maybe_prefix_path(fname,
4123 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
4124 PA_ALSA_PROFILE_SETS_DIR);
4126 r = pa_config_parse(fn, NULL, items, ps);
4132 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4133 if (mapping_verify(m, bonus) < 0)
4136 if (ps->auto_profiles)
4137 profile_set_add_auto(ps);
4139 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4140 if (profile_verify(p) < 0)
4143 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4144 if (decibel_fix_verify(db_fix) < 0)
4150 pa_alsa_profile_set_free(ps);
4154 void pa_alsa_profile_set_probe(
4155 pa_alsa_profile_set *ps,
4157 const pa_sample_spec *ss,
4158 unsigned default_n_fragments,
4159 unsigned default_fragment_size_msec) {
4162 pa_alsa_profile *p, *last = NULL;
4172 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
4173 pa_sample_spec try_ss;
4174 pa_channel_map try_map;
4175 snd_pcm_uframes_t try_period_size, try_buffer_size;
4178 /* Is this already marked that it is supported? (i.e. from the config file) */
4182 pa_log_debug("Looking at profile %s", p->name);
4184 /* Close PCMs from the last iteration we don't need anymore */
4185 if (last && last->output_mappings)
4186 PA_IDXSET_FOREACH(m, last->output_mappings, idx) {
4191 if (last->supported)
4194 if (!p->output_mappings || !pa_idxset_get_by_data(p->output_mappings, m, NULL)) {
4195 snd_pcm_close(m->output_pcm);
4196 m->output_pcm = NULL;
4200 if (last && last->input_mappings)
4201 PA_IDXSET_FOREACH(m, last->input_mappings, idx) {
4206 if (last->supported)
4209 if (!p->input_mappings || !pa_idxset_get_by_data(p->input_mappings, m, NULL)) {
4210 snd_pcm_close(m->input_pcm);
4211 m->input_pcm = NULL;
4215 p->supported = TRUE;
4217 /* Check if we can open all new ones */
4218 if (p->output_mappings)
4219 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4224 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
4225 try_map = m->channel_map;
4227 try_ss.channels = try_map.channels;
4230 pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
4231 pa_frame_size(&try_ss);
4232 try_buffer_size = default_n_fragments * try_period_size;
4234 if (!(m ->output_pcm = pa_alsa_open_by_template(
4239 SND_PCM_STREAM_PLAYBACK,
4240 &try_period_size, &try_buffer_size, 0, NULL, NULL,
4242 p->supported = FALSE;
4247 if (p->input_mappings && p->supported)
4248 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4253 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
4254 try_map = m->channel_map;
4256 try_ss.channels = try_map.channels;
4259 pa_usec_to_bytes(default_fragment_size_msec*PA_USEC_PER_MSEC, &try_ss) /
4260 pa_frame_size(&try_ss);
4261 try_buffer_size = default_n_fragments * try_period_size;
4263 if (!(m ->input_pcm = pa_alsa_open_by_template(
4268 SND_PCM_STREAM_CAPTURE,
4269 &try_period_size, &try_buffer_size, 0, NULL, NULL,
4271 p->supported = FALSE;
4282 pa_log_debug("Profile %s supported.", p->name);
4284 if (p->output_mappings)
4285 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4287 mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT);
4289 if (p->input_mappings)
4290 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4292 mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT);
4300 if (last->output_mappings)
4301 PA_IDXSET_FOREACH(m, last->output_mappings, idx)
4302 if (m->output_pcm) {
4304 if (last->supported)
4307 snd_pcm_close(m->output_pcm);
4308 m->output_pcm = NULL;
4311 if (last->input_mappings)
4312 PA_IDXSET_FOREACH(m, last->input_mappings, idx)
4315 if (last->supported)
4318 snd_pcm_close(m->input_pcm);
4319 m->input_pcm = NULL;
4323 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4324 if (!p->supported) {
4325 pa_hashmap_remove(ps->profiles, p->name);
4329 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4330 if (m->supported <= 0) {
4331 pa_hashmap_remove(ps->mappings, m->name);
4338 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
4341 pa_alsa_decibel_fix *db_fix;
4346 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4349 pa_yes_no(ps->auto_profiles),
4350 pa_yes_no(ps->probed),
4351 pa_hashmap_size(ps->mappings),
4352 pa_hashmap_size(ps->profiles),
4353 pa_hashmap_size(ps->decibel_fixes));
4355 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4356 pa_alsa_mapping_dump(m);
4358 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4359 pa_alsa_profile_dump(p);
4361 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4362 pa_alsa_decibel_fix_dump(db_fix);
4365 static pa_device_port* device_port_alsa_init(pa_hashmap *ports,
4367 const char* description,
4369 pa_alsa_setting *setting,
4370 pa_card_profile *cp,
4374 pa_device_port * p = pa_hashmap_get(ports, name);
4376 pa_alsa_port_data *data;
4378 p = pa_device_port_new(core, name, description, sizeof(pa_alsa_port_data));
4380 pa_hashmap_put(ports, name, p);
4381 p->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4383 data = PA_DEVICE_PORT_DATA(p);
4385 data->setting = setting;
4388 p->is_input |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_INPUT;
4389 p->is_output |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_OUTPUT;
4392 pa_hashmap_put(p->profiles, cp->name, cp);
4395 pa_hashmap_put(extra, name, p);
4396 pa_device_port_ref(p);
4402 void pa_alsa_path_set_add_ports(
4403 pa_alsa_path_set *ps,
4404 pa_card_profile *cp,
4417 PA_HASHMAP_FOREACH(path, ps->paths, state) {
4418 if (!path->settings || !path->settings->next) {
4419 /* If there is no or just one setting we only need a
4421 pa_device_port *port = device_port_alsa_init(ports, path->name,
4422 path->description, path, path->settings, cp, extra, core);
4423 port->priority = path->priority * 100;
4427 PA_LLIST_FOREACH(s, path->settings) {
4428 pa_device_port *port;
4431 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
4433 if (s->description[0])
4434 d = pa_sprintf_malloc(_("%s / %s"), path->description, s->description);
4436 d = pa_xstrdup(path->description);
4438 port = device_port_alsa_init(ports, n, d, path, s, cp, extra, core);
4439 port->priority = path->priority * 100 + s->priority;
4448 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps, pa_card *card) {
4454 if (ps->paths && pa_hashmap_size(ps->paths) > 0) {
4456 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4457 pa_alsa_path_set_add_ports(ps, NULL, card->ports, *p, card->core);
4460 pa_log_debug("Added %u ports", *p ? pa_hashmap_size(*p) : 0);