2 This file is part of PulseAudio.
4 Copyright 2004-2009 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 #include <sys/types.h>
28 #include <asoundlib.h>
31 #ifdef HAVE_VALGRIND_MEMCHECK_H
32 #include <valgrind/memcheck.h>
35 #include <pulse/mainloop-api.h>
36 #include <pulse/sample.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/volume.h>
40 #include <pulse/xmalloc.h>
41 #include <pulse/utf8.h>
43 #include <pulsecore/i18n.h>
44 #include <pulsecore/log.h>
45 #include <pulsecore/macro.h>
46 #include <pulsecore/core-util.h>
47 #include <pulsecore/conf-parser.h>
48 #include <pulsecore/strbuf.h>
50 #include "alsa-mixer.h"
51 #include "alsa-util.h"
53 struct description_map
{
55 const char *description
;
58 static const char *lookup_description(const char *name
, const struct description_map dm
[], unsigned n
) {
61 for (i
= 0; i
< n
; i
++)
62 if (pa_streq(dm
[i
].name
, name
))
63 return _(dm
[i
].description
);
68 struct pa_alsa_fdlist
{
71 /* This is a temporary buffer used to avoid lots of mallocs */
72 struct pollfd
*work_fds
;
77 pa_defer_event
*defer
;
82 void (*cb
)(void *userdata
);
86 static void io_cb(pa_mainloop_api
*a
, pa_io_event
*e
, int fd
, pa_io_event_flags_t events
, void *userdata
) {
88 struct pa_alsa_fdlist
*fdl
= userdata
;
91 unsigned short revents
;
95 pa_assert(fdl
->mixer
);
97 pa_assert(fdl
->work_fds
);
104 memcpy(fdl
->work_fds
, fdl
->fds
, sizeof(struct pollfd
) * fdl
->num_fds
);
106 for (i
= 0; i
< fdl
->num_fds
; i
++) {
107 if (e
== fdl
->ios
[i
]) {
108 if (events
& PA_IO_EVENT_INPUT
)
109 fdl
->work_fds
[i
].revents
|= POLLIN
;
110 if (events
& PA_IO_EVENT_OUTPUT
)
111 fdl
->work_fds
[i
].revents
|= POLLOUT
;
112 if (events
& PA_IO_EVENT_ERROR
)
113 fdl
->work_fds
[i
].revents
|= POLLERR
;
114 if (events
& PA_IO_EVENT_HANGUP
)
115 fdl
->work_fds
[i
].revents
|= POLLHUP
;
120 pa_assert(i
!= fdl
->num_fds
);
122 if ((err
= snd_mixer_poll_descriptors_revents(fdl
->mixer
, fdl
->work_fds
, fdl
->num_fds
, &revents
)) < 0) {
123 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err
));
127 a
->defer_enable(fdl
->defer
, 1);
130 snd_mixer_handle_events(fdl
->mixer
);
133 static void defer_cb(pa_mainloop_api
*a
, pa_defer_event
*e
, void *userdata
) {
134 struct pa_alsa_fdlist
*fdl
= userdata
;
141 pa_assert(fdl
->mixer
);
143 a
->defer_enable(fdl
->defer
, 0);
145 if ((n
= snd_mixer_poll_descriptors_count(fdl
->mixer
)) < 0) {
146 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n
));
149 num_fds
= (unsigned) n
;
151 if (num_fds
!= fdl
->num_fds
) {
155 pa_xfree(fdl
->work_fds
);
156 fdl
->fds
= pa_xnew0(struct pollfd
, num_fds
);
157 fdl
->work_fds
= pa_xnew(struct pollfd
, num_fds
);
160 memset(fdl
->work_fds
, 0, sizeof(struct pollfd
) * num_fds
);
162 if ((err
= snd_mixer_poll_descriptors(fdl
->mixer
, fdl
->work_fds
, num_fds
)) < 0) {
163 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err
));
169 if (memcmp(fdl
->fds
, fdl
->work_fds
, sizeof(struct pollfd
) * num_fds
) == 0)
173 for (i
= 0; i
< fdl
->num_fds
; i
++)
174 a
->io_free(fdl
->ios
[i
]);
176 if (num_fds
!= fdl
->num_fds
) {
183 fdl
->ios
= pa_xnew(pa_io_event
*, num_fds
);
186 temp
= fdl
->work_fds
;
187 fdl
->work_fds
= fdl
->fds
;
190 fdl
->num_fds
= num_fds
;
192 for (i
= 0;i
< num_fds
;i
++)
193 fdl
->ios
[i
] = a
->io_new(a
, fdl
->fds
[i
].fd
,
194 ((fdl
->fds
[i
].events
& POLLIN
) ? PA_IO_EVENT_INPUT
: 0) |
195 ((fdl
->fds
[i
].events
& POLLOUT
) ? PA_IO_EVENT_OUTPUT
: 0),
199 struct pa_alsa_fdlist
*pa_alsa_fdlist_new(void) {
200 struct pa_alsa_fdlist
*fdl
;
202 fdl
= pa_xnew0(struct pa_alsa_fdlist
, 1);
207 void pa_alsa_fdlist_free(struct pa_alsa_fdlist
*fdl
) {
212 fdl
->m
->defer_free(fdl
->defer
);
218 for (i
= 0; i
< fdl
->num_fds
; i
++)
219 fdl
->m
->io_free(fdl
->ios
[i
]);
226 pa_xfree(fdl
->work_fds
);
231 int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist
*fdl
, snd_mixer_t
*mixer_handle
, pa_mainloop_api
*m
) {
233 pa_assert(mixer_handle
);
237 fdl
->mixer
= mixer_handle
;
239 fdl
->defer
= m
->defer_new(m
, defer_cb
, fdl
);
244 struct pa_alsa_mixer_pdata
{
246 pa_rtpoll_item
*poll_item
;
251 struct pa_alsa_mixer_pdata
*pa_alsa_mixer_pdata_new(void) {
252 struct pa_alsa_mixer_pdata
*pd
;
254 pd
= pa_xnew0(struct pa_alsa_mixer_pdata
, 1);
259 void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata
*pd
) {
263 pa_rtpoll_item_free(pd
->poll_item
);
269 static int rtpoll_work_cb(pa_rtpoll_item
*i
) {
270 struct pa_alsa_mixer_pdata
*pd
;
273 unsigned short revents
= 0;
276 pd
= pa_rtpoll_item_get_userdata(i
);
278 pa_assert_fp(i
== pd
->poll_item
);
280 p
= pa_rtpoll_item_get_pollfd(i
, &n_fds
);
282 if ((err
= snd_mixer_poll_descriptors_revents(pd
->mixer
, p
, n_fds
, &revents
)) < 0) {
283 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err
));
289 if (revents
& (POLLNVAL
| POLLERR
)) {
290 pa_log_debug("Device disconnected, stopping poll on mixer");
292 } else if (revents
& POLLERR
) {
293 /* This shouldn't happen. */
294 pa_log_error("Got a POLLERR (revents = %04x), stopping poll on mixer", revents
);
298 err
= snd_mixer_handle_events(pd
->mixer
);
300 if (PA_LIKELY(err
>= 0)) {
301 pa_rtpoll_item_free(i
);
302 pa_alsa_set_mixer_rtpoll(pd
, pd
->mixer
, pd
->rtpoll
);
304 pa_log_error("Error handling mixer event: %s", pa_alsa_strerror(err
));
313 pa_rtpoll_item_free(i
);
315 pd
->poll_item
= NULL
;
322 int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata
*pd
, snd_mixer_t
*mixer
, pa_rtpoll
*rtp
) {
331 if ((n
= snd_mixer_poll_descriptors_count(mixer
)) < 0) {
332 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n
));
336 i
= pa_rtpoll_item_new(rtp
, PA_RTPOLL_LATE
, (unsigned) n
);
338 p
= pa_rtpoll_item_get_pollfd(i
, NULL
);
340 memset(p
, 0, sizeof(struct pollfd
) * n
);
342 if ((err
= snd_mixer_poll_descriptors(mixer
, p
, (unsigned) n
)) < 0) {
343 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err
));
344 pa_rtpoll_item_free(i
);
352 pa_rtpoll_item_set_userdata(i
, pd
);
353 pa_rtpoll_item_set_work_callback(i
, rtpoll_work_cb
);
358 static int prepare_mixer(snd_mixer_t
*mixer
, const char *dev
) {
364 if ((err
= snd_mixer_attach(mixer
, dev
)) < 0) {
365 pa_log_info("Unable to attach to mixer %s: %s", dev
, pa_alsa_strerror(err
));
369 if ((err
= snd_mixer_selem_register(mixer
, NULL
, NULL
)) < 0) {
370 pa_log_warn("Unable to register mixer: %s", pa_alsa_strerror(err
));
374 if ((err
= snd_mixer_load(mixer
)) < 0) {
375 pa_log_warn("Unable to load mixer: %s", pa_alsa_strerror(err
));
379 pa_log_info("Successfully attached to mixer '%s'", dev
);
383 snd_mixer_t
*pa_alsa_open_mixer_for_pcm(snd_pcm_t
*pcm
, char **ctl_device
) {
387 snd_pcm_info_t
* info
;
388 snd_pcm_info_alloca(&info
);
392 if ((err
= snd_mixer_open(&m
, 0)) < 0) {
393 pa_log("Error opening mixer: %s", pa_alsa_strerror(err
));
397 /* First, try by name */
398 if ((dev
= snd_pcm_name(pcm
)))
399 if (prepare_mixer(m
, dev
) >= 0) {
401 *ctl_device
= pa_xstrdup(dev
);
406 /* Then, try by card index */
407 if (snd_pcm_info(pcm
, info
) >= 0) {
411 if ((card_idx
= snd_pcm_info_get_card(info
)) >= 0) {
413 md
= pa_sprintf_malloc("hw:%i", card_idx
);
415 if (!dev
|| !pa_streq(dev
, md
))
416 if (prepare_mixer(m
, md
) >= 0) {
434 static const snd_mixer_selem_channel_id_t alsa_channel_ids
[PA_CHANNEL_POSITION_MAX
] = {
435 [PA_CHANNEL_POSITION_MONO
] = SND_MIXER_SCHN_MONO
, /* The ALSA name is just an alias! */
437 [PA_CHANNEL_POSITION_FRONT_CENTER
] = SND_MIXER_SCHN_FRONT_CENTER
,
438 [PA_CHANNEL_POSITION_FRONT_LEFT
] = SND_MIXER_SCHN_FRONT_LEFT
,
439 [PA_CHANNEL_POSITION_FRONT_RIGHT
] = SND_MIXER_SCHN_FRONT_RIGHT
,
441 [PA_CHANNEL_POSITION_REAR_CENTER
] = SND_MIXER_SCHN_REAR_CENTER
,
442 [PA_CHANNEL_POSITION_REAR_LEFT
] = SND_MIXER_SCHN_REAR_LEFT
,
443 [PA_CHANNEL_POSITION_REAR_RIGHT
] = SND_MIXER_SCHN_REAR_RIGHT
,
445 [PA_CHANNEL_POSITION_LFE
] = SND_MIXER_SCHN_WOOFER
,
447 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
448 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
450 [PA_CHANNEL_POSITION_SIDE_LEFT
] = SND_MIXER_SCHN_SIDE_LEFT
,
451 [PA_CHANNEL_POSITION_SIDE_RIGHT
] = SND_MIXER_SCHN_SIDE_RIGHT
,
453 [PA_CHANNEL_POSITION_AUX0
] = SND_MIXER_SCHN_UNKNOWN
,
454 [PA_CHANNEL_POSITION_AUX1
] = SND_MIXER_SCHN_UNKNOWN
,
455 [PA_CHANNEL_POSITION_AUX2
] = SND_MIXER_SCHN_UNKNOWN
,
456 [PA_CHANNEL_POSITION_AUX3
] = SND_MIXER_SCHN_UNKNOWN
,
457 [PA_CHANNEL_POSITION_AUX4
] = SND_MIXER_SCHN_UNKNOWN
,
458 [PA_CHANNEL_POSITION_AUX5
] = SND_MIXER_SCHN_UNKNOWN
,
459 [PA_CHANNEL_POSITION_AUX6
] = SND_MIXER_SCHN_UNKNOWN
,
460 [PA_CHANNEL_POSITION_AUX7
] = SND_MIXER_SCHN_UNKNOWN
,
461 [PA_CHANNEL_POSITION_AUX8
] = SND_MIXER_SCHN_UNKNOWN
,
462 [PA_CHANNEL_POSITION_AUX9
] = SND_MIXER_SCHN_UNKNOWN
,
463 [PA_CHANNEL_POSITION_AUX10
] = SND_MIXER_SCHN_UNKNOWN
,
464 [PA_CHANNEL_POSITION_AUX11
] = SND_MIXER_SCHN_UNKNOWN
,
465 [PA_CHANNEL_POSITION_AUX12
] = SND_MIXER_SCHN_UNKNOWN
,
466 [PA_CHANNEL_POSITION_AUX13
] = SND_MIXER_SCHN_UNKNOWN
,
467 [PA_CHANNEL_POSITION_AUX14
] = SND_MIXER_SCHN_UNKNOWN
,
468 [PA_CHANNEL_POSITION_AUX15
] = SND_MIXER_SCHN_UNKNOWN
,
469 [PA_CHANNEL_POSITION_AUX16
] = SND_MIXER_SCHN_UNKNOWN
,
470 [PA_CHANNEL_POSITION_AUX17
] = SND_MIXER_SCHN_UNKNOWN
,
471 [PA_CHANNEL_POSITION_AUX18
] = SND_MIXER_SCHN_UNKNOWN
,
472 [PA_CHANNEL_POSITION_AUX19
] = SND_MIXER_SCHN_UNKNOWN
,
473 [PA_CHANNEL_POSITION_AUX20
] = SND_MIXER_SCHN_UNKNOWN
,
474 [PA_CHANNEL_POSITION_AUX21
] = SND_MIXER_SCHN_UNKNOWN
,
475 [PA_CHANNEL_POSITION_AUX22
] = SND_MIXER_SCHN_UNKNOWN
,
476 [PA_CHANNEL_POSITION_AUX23
] = SND_MIXER_SCHN_UNKNOWN
,
477 [PA_CHANNEL_POSITION_AUX24
] = SND_MIXER_SCHN_UNKNOWN
,
478 [PA_CHANNEL_POSITION_AUX25
] = SND_MIXER_SCHN_UNKNOWN
,
479 [PA_CHANNEL_POSITION_AUX26
] = SND_MIXER_SCHN_UNKNOWN
,
480 [PA_CHANNEL_POSITION_AUX27
] = SND_MIXER_SCHN_UNKNOWN
,
481 [PA_CHANNEL_POSITION_AUX28
] = SND_MIXER_SCHN_UNKNOWN
,
482 [PA_CHANNEL_POSITION_AUX29
] = SND_MIXER_SCHN_UNKNOWN
,
483 [PA_CHANNEL_POSITION_AUX30
] = SND_MIXER_SCHN_UNKNOWN
,
484 [PA_CHANNEL_POSITION_AUX31
] = SND_MIXER_SCHN_UNKNOWN
,
486 [PA_CHANNEL_POSITION_TOP_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
488 [PA_CHANNEL_POSITION_TOP_FRONT_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
489 [PA_CHANNEL_POSITION_TOP_FRONT_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
490 [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
,
492 [PA_CHANNEL_POSITION_TOP_REAR_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
493 [PA_CHANNEL_POSITION_TOP_REAR_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
494 [PA_CHANNEL_POSITION_TOP_REAR_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
497 static void setting_free(pa_alsa_setting
*s
) {
501 pa_idxset_free(s
->options
, NULL
, NULL
);
504 pa_xfree(s
->description
);
508 static void option_free(pa_alsa_option
*o
) {
511 pa_xfree(o
->alsa_name
);
513 pa_xfree(o
->description
);
517 static void decibel_fix_free(pa_alsa_decibel_fix
*db_fix
) {
520 pa_xfree(db_fix
->name
);
521 pa_xfree(db_fix
->db_values
);
526 static void element_free(pa_alsa_element
*e
) {
530 while ((o
= e
->options
)) {
531 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
536 decibel_fix_free(e
->db_fix
);
538 pa_xfree(e
->alsa_name
);
542 void pa_alsa_path_free(pa_alsa_path
*p
) {
548 while ((e
= p
->elements
)) {
549 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
553 while ((s
= p
->settings
)) {
554 PA_LLIST_REMOVE(pa_alsa_setting
, p
->settings
, s
);
559 pa_xfree(p
->description
);
563 void pa_alsa_path_set_free(pa_alsa_path_set
*ps
) {
567 pa_hashmap_free(ps
->paths
, NULL
, NULL
);
572 static long to_alsa_dB(pa_volume_t v
) {
573 return (long) (pa_sw_volume_to_dB(v
) * 100.0);
576 static pa_volume_t
from_alsa_dB(long v
) {
577 return pa_sw_volume_from_dB((double) v
/ 100.0);
580 static long to_alsa_volume(pa_volume_t v
, long min
, long max
) {
583 w
= (long) round(((double) v
* (double) (max
- min
)) / PA_VOLUME_NORM
) + min
;
584 return PA_CLAMP_UNLIKELY(w
, min
, max
);
587 static pa_volume_t
from_alsa_volume(long v
, long min
, long max
) {
588 return (pa_volume_t
) round(((double) (v
- min
) * PA_VOLUME_NORM
) / (double) (max
- min
));
591 #define SELEM_INIT(sid, name) \
593 snd_mixer_selem_id_alloca(&(sid)); \
594 snd_mixer_selem_id_set_name((sid), (name)); \
595 snd_mixer_selem_id_set_index((sid), 0); \
598 static int element_get_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
599 snd_mixer_selem_id_t
*sid
;
600 snd_mixer_elem_t
*me
;
601 snd_mixer_selem_channel_id_t c
;
602 pa_channel_position_mask_t mask
= 0;
610 SELEM_INIT(sid
, e
->alsa_name
);
611 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
612 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
616 pa_cvolume_mute(v
, cm
->channels
);
618 /* We take the highest volume of all channels that match */
620 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
627 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
628 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
630 if ((r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
)) >= 0) {
631 /* If the channel volume is outside the limits set
632 * by the dB fix, we clamp the hw volume to be
633 * within the limits. */
634 if (value
< e
->db_fix
->min_step
) {
635 value
= e
->db_fix
->min_step
;
636 snd_mixer_selem_set_playback_volume(me
, c
, value
);
637 pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
638 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
639 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
640 } else if (value
> e
->db_fix
->max_step
) {
641 value
= e
->db_fix
->max_step
;
642 snd_mixer_selem_set_playback_volume(me
, c
, value
);
643 pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
644 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
645 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
648 /* Volume step -> dB value conversion. */
649 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
652 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
656 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
658 if ((r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
)) >= 0) {
659 /* If the channel volume is outside the limits set
660 * by the dB fix, we clamp the hw volume to be
661 * within the limits. */
662 if (value
< e
->db_fix
->min_step
) {
663 value
= e
->db_fix
->min_step
;
664 snd_mixer_selem_set_capture_volume(me
, c
, value
);
665 pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
666 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
667 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
668 } else if (value
> e
->db_fix
->max_step
) {
669 value
= e
->db_fix
->max_step
;
670 snd_mixer_selem_set_capture_volume(me
, c
, value
);
671 pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
672 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
673 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
676 /* Volume step -> dB value conversion. */
677 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
680 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
688 #ifdef HAVE_VALGRIND_MEMCHECK_H
689 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
692 f
= from_alsa_dB(value
);
697 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
698 if (snd_mixer_selem_has_playback_channel(me
, c
))
699 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
703 if (snd_mixer_selem_has_capture_channel(me
, c
))
704 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
712 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
715 for (k
= 0; k
< cm
->channels
; k
++)
716 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
717 if (v
->values
[k
] < f
)
720 mask
|= e
->masks
[c
][e
->n_channels
-1];
723 for (k
= 0; k
< cm
->channels
; k
++)
724 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
725 v
->values
[k
] = PA_VOLUME_NORM
;
730 int pa_alsa_path_get_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
741 pa_cvolume_reset(v
, cm
->channels
);
743 PA_LLIST_FOREACH(e
, p
->elements
) {
746 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
749 pa_assert(!p
->has_dB
|| e
->has_dB
);
751 if (element_get_volume(e
, m
, cm
, &ev
) < 0)
754 /* If we have no dB information all we can do is take the first element and leave */
760 pa_sw_cvolume_multiply(v
, v
, &ev
);
766 static int element_get_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t
*b
) {
767 snd_mixer_selem_id_t
*sid
;
768 snd_mixer_elem_t
*me
;
769 snd_mixer_selem_channel_id_t c
;
775 SELEM_INIT(sid
, e
->alsa_name
);
776 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
777 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
781 /* We return muted if at least one channel is muted */
783 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
787 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
788 if (snd_mixer_selem_has_playback_channel(me
, c
))
789 r
= snd_mixer_selem_get_playback_switch(me
, c
, &value
);
793 if (snd_mixer_selem_has_capture_channel(me
, c
))
794 r
= snd_mixer_selem_get_capture_switch(me
, c
, &value
);
812 int pa_alsa_path_get_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t
*muted
) {
822 PA_LLIST_FOREACH(e
, p
->elements
) {
825 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
828 if (element_get_switch(e
, m
, &b
) < 0)
841 /* Finds the closest item in db_fix->db_values and returns the corresponding
842 * step. *db_value is replaced with the value from the db_values table.
843 * Rounding is done based on the rounding parameter: -1 means rounding down and
844 * +1 means rounding up. */
845 static long decibel_fix_get_step(pa_alsa_decibel_fix
*db_fix
, long *db_value
, int rounding
) {
851 pa_assert(rounding
!= 0);
853 max_i
= db_fix
->max_step
- db_fix
->min_step
;
856 for (i
= 0; i
< max_i
; i
++) {
857 if (db_fix
->db_values
[i
] >= *db_value
)
861 for (i
= 0; i
< max_i
; i
++) {
862 if (db_fix
->db_values
[i
+ 1] > *db_value
)
867 *db_value
= db_fix
->db_values
[i
];
869 return i
+ db_fix
->min_step
;
872 /* Alsa lib documentation says for snd_mixer_selem_set_playback_dB() direction argument,
873 * that "-1 = accurate or first below, 0 = accurate, 1 = accurate or first above".
874 * But even with accurate nearest dB volume step is not selected, so that is why we need
875 * this function. Returns 0 and nearest selectable volume in *value_dB on success or
876 * negative error code if fails. */
877 static int element_get_nearest_alsa_dB(snd_mixer_elem_t
*me
, snd_mixer_selem_channel_id_t c
, pa_alsa_direction_t d
, long *value_dB
) {
887 if (d
== PA_ALSA_DIRECTION_OUTPUT
) {
888 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, *value_dB
, +1, &alsa_val
)) >= 0)
889 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value_high
);
894 if (value_high
== *value_dB
)
897 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, *value_dB
, -1, &alsa_val
)) >= 0)
898 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value_low
);
900 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, *value_dB
, +1, &alsa_val
)) >= 0)
901 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value_high
);
906 if (value_high
== *value_dB
)
909 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, *value_dB
, -1, &alsa_val
)) >= 0)
910 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value_low
);
916 if (labs(value_high
- *value_dB
) < labs(value_low
- *value_dB
))
917 *value_dB
= value_high
;
919 *value_dB
= value_low
;
924 static int element_set_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
, pa_bool_t deferred_volume
, pa_bool_t write_to_hw
) {
926 snd_mixer_selem_id_t
*sid
;
928 snd_mixer_elem_t
*me
;
929 snd_mixer_selem_channel_id_t c
;
930 pa_channel_position_mask_t mask
= 0;
937 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
939 SELEM_INIT(sid
, e
->alsa_name
);
940 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
941 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
945 pa_cvolume_mute(&rv
, cm
->channels
);
947 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
949 pa_volume_t f
= PA_VOLUME_MUTED
;
950 pa_bool_t found
= FALSE
;
952 for (k
= 0; k
< cm
->channels
; k
++)
953 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
])) {
955 if (v
->values
[k
] > f
)
960 /* Hmm, so this channel does not exist in the volume
961 * struct, so let's bind it to the overall max of the
963 f
= pa_cvolume_max(v
);
967 long value
= to_alsa_dB(f
);
970 if (e
->volume_limit
>= 0 && value
> (e
->max_dB
* 100))
971 value
= e
->max_dB
* 100;
973 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
974 /* If we call set_playback_volume() without checking first
975 * if the channel is available, ALSA behaves very
976 * strangely and doesn't fail the call */
977 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
981 r
= snd_mixer_selem_set_playback_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
983 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
989 if (deferred_volume
) {
990 if ((r
= element_get_nearest_alsa_dB(me
, c
, PA_ALSA_DIRECTION_OUTPUT
, &value
)) >= 0)
991 r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, 0);
993 if ((r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, rounding
)) >= 0)
994 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
998 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
999 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value
);
1005 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
1009 r
= snd_mixer_selem_set_capture_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
1011 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
1017 if (deferred_volume
) {
1018 if ((r
= element_get_nearest_alsa_dB(me
, c
, PA_ALSA_DIRECTION_INPUT
, &value
)) >= 0)
1019 r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, 0);
1021 if ((r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, rounding
)) >= 0)
1022 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
1026 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
1027 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value
);
1037 #ifdef HAVE_VALGRIND_MEMCHECK_H
1038 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
1041 f
= from_alsa_dB(value
);
1046 value
= to_alsa_volume(f
, e
->min_volume
, e
->max_volume
);
1048 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1049 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
1050 if ((r
= snd_mixer_selem_set_playback_volume(me
, c
, value
)) >= 0)
1051 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
1055 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
1056 if ((r
= snd_mixer_selem_set_capture_volume(me
, c
, value
)) >= 0)
1057 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
1065 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
1068 for (k
= 0; k
< cm
->channels
; k
++)
1069 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
1070 if (rv
.values
[k
] < f
)
1073 mask
|= e
->masks
[c
][e
->n_channels
-1];
1076 for (k
= 0; k
< cm
->channels
; k
++)
1077 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
1078 rv
.values
[k
] = PA_VOLUME_NORM
;
1084 int pa_alsa_path_set_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
, pa_bool_t deferred_volume
, pa_bool_t write_to_hw
) {
1093 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
1098 rv
= *v
; /* Remaining adjustment */
1099 pa_cvolume_reset(v
, cm
->channels
); /* Adjustment done */
1101 PA_LLIST_FOREACH(e
, p
->elements
) {
1104 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
1107 pa_assert(!p
->has_dB
|| e
->has_dB
);
1110 if (element_set_volume(e
, m
, cm
, &ev
, deferred_volume
, write_to_hw
) < 0)
1118 pa_sw_cvolume_multiply(v
, v
, &ev
);
1119 pa_sw_cvolume_divide(&rv
, &rv
, &ev
);
1125 static int element_set_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t b
) {
1126 snd_mixer_elem_t
*me
;
1127 snd_mixer_selem_id_t
*sid
;
1133 SELEM_INIT(sid
, e
->alsa_name
);
1134 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1135 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1139 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1140 r
= snd_mixer_selem_set_playback_switch_all(me
, b
);
1142 r
= snd_mixer_selem_set_capture_switch_all(me
, b
);
1145 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1150 int pa_alsa_path_set_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t muted
) {
1159 PA_LLIST_FOREACH(e
, p
->elements
) {
1161 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
1164 if (element_set_switch(e
, m
, !muted
) < 0)
1171 /* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
1172 * function sets all channels of the volume element to e->min_volume, 0 dB or
1173 * e->constant_volume. */
1174 static int element_set_constant_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1175 snd_mixer_elem_t
*me
= NULL
;
1176 snd_mixer_selem_id_t
*sid
= NULL
;
1179 pa_bool_t volume_set
= FALSE
;
1184 SELEM_INIT(sid
, e
->alsa_name
);
1185 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1186 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1190 switch (e
->volume_use
) {
1191 case PA_ALSA_VOLUME_OFF
:
1192 volume
= e
->min_volume
;
1196 case PA_ALSA_VOLUME_ZERO
:
1200 volume
= decibel_fix_get_step(e
->db_fix
, &dB
, (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
? +1 : -1));
1205 case PA_ALSA_VOLUME_CONSTANT
:
1206 volume
= e
->constant_volume
;
1211 pa_assert_not_reached();
1215 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1216 r
= snd_mixer_selem_set_playback_volume_all(me
, volume
);
1218 r
= snd_mixer_selem_set_capture_volume_all(me
, volume
);
1220 pa_assert(e
->volume_use
== PA_ALSA_VOLUME_ZERO
);
1221 pa_assert(!e
->db_fix
);
1223 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1224 r
= snd_mixer_selem_set_playback_dB_all(me
, 0, +1);
1226 r
= snd_mixer_selem_set_capture_dB_all(me
, 0, -1);
1230 pa_log_warn("Failed to set volume of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1235 int pa_alsa_path_select(pa_alsa_path
*p
, snd_mixer_t
*m
) {
1242 pa_log_debug("Activating path %s", p
->name
);
1243 pa_alsa_path_dump(p
);
1245 PA_LLIST_FOREACH(e
, p
->elements
) {
1247 switch (e
->switch_use
) {
1248 case PA_ALSA_SWITCH_OFF
:
1249 r
= element_set_switch(e
, m
, FALSE
);
1252 case PA_ALSA_SWITCH_ON
:
1253 r
= element_set_switch(e
, m
, TRUE
);
1256 case PA_ALSA_SWITCH_MUTE
:
1257 case PA_ALSA_SWITCH_IGNORE
:
1258 case PA_ALSA_SWITCH_SELECT
:
1266 switch (e
->volume_use
) {
1267 case PA_ALSA_VOLUME_OFF
:
1268 case PA_ALSA_VOLUME_ZERO
:
1269 case PA_ALSA_VOLUME_CONSTANT
:
1270 r
= element_set_constant_volume(e
, m
);
1273 case PA_ALSA_VOLUME_MERGE
:
1274 case PA_ALSA_VOLUME_IGNORE
:
1286 static int check_required(pa_alsa_element
*e
, snd_mixer_elem_t
*me
) {
1287 pa_bool_t has_switch
;
1288 pa_bool_t has_enumeration
;
1289 pa_bool_t has_volume
;
1294 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1296 snd_mixer_selem_has_playback_switch(me
) ||
1297 (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
));
1300 snd_mixer_selem_has_capture_switch(me
) ||
1301 (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
));
1304 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1306 snd_mixer_selem_has_playback_volume(me
) ||
1307 (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
));
1310 snd_mixer_selem_has_capture_volume(me
) ||
1311 (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
));
1314 has_enumeration
= snd_mixer_selem_is_enumerated(me
);
1316 if ((e
->required
== PA_ALSA_REQUIRED_SWITCH
&& !has_switch
) ||
1317 (e
->required
== PA_ALSA_REQUIRED_VOLUME
&& !has_volume
) ||
1318 (e
->required
== PA_ALSA_REQUIRED_ENUMERATION
&& !has_enumeration
))
1321 if (e
->required
== PA_ALSA_REQUIRED_ANY
&& !(has_switch
|| has_volume
|| has_enumeration
))
1324 if ((e
->required_absent
== PA_ALSA_REQUIRED_SWITCH
&& has_switch
) ||
1325 (e
->required_absent
== PA_ALSA_REQUIRED_VOLUME
&& has_volume
) ||
1326 (e
->required_absent
== PA_ALSA_REQUIRED_ENUMERATION
&& has_enumeration
))
1329 if (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& (has_switch
|| has_volume
|| has_enumeration
))
1332 if (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) {
1333 switch (e
->required_any
) {
1334 case PA_ALSA_REQUIRED_VOLUME
:
1335 e
->path
->req_any_present
|= (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
);
1337 case PA_ALSA_REQUIRED_SWITCH
:
1338 e
->path
->req_any_present
|= (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
);
1340 case PA_ALSA_REQUIRED_ENUMERATION
:
1341 e
->path
->req_any_present
|= (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1343 case PA_ALSA_REQUIRED_ANY
:
1344 e
->path
->req_any_present
|=
1345 (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) ||
1346 (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) ||
1347 (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1350 pa_assert_not_reached();
1354 if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1356 PA_LLIST_FOREACH(o
, e
->options
) {
1357 e
->path
->req_any_present
|= (o
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) &&
1359 if (o
->required
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
< 0)
1361 if (o
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
>= 0)
1369 static int element_probe(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1370 snd_mixer_selem_id_t
*sid
;
1371 snd_mixer_elem_t
*me
;
1377 SELEM_INIT(sid
, e
->alsa_name
);
1379 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1381 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1384 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1385 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1386 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1391 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1392 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1394 if (!snd_mixer_selem_has_playback_switch(me
)) {
1395 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1396 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1398 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1403 if (!snd_mixer_selem_has_capture_switch(me
)) {
1404 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1405 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1407 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1411 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1412 e
->direction_try_other
= FALSE
;
1415 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1417 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1419 if (!snd_mixer_selem_has_playback_volume(me
)) {
1420 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1421 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1423 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1428 if (!snd_mixer_selem_has_capture_volume(me
)) {
1429 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1430 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1432 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1436 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1437 long min_dB
= 0, max_dB
= 0;
1440 e
->direction_try_other
= FALSE
;
1442 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1443 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1445 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1448 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1452 if (e
->min_volume
>= e
->max_volume
) {
1453 pa_log_warn("Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense.", e
->min_volume
, e
->max_volume
);
1454 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1456 } else if (e
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&&
1457 (e
->min_volume
> e
->constant_volume
|| e
->max_volume
< e
->constant_volume
)) {
1458 pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
1459 e
->constant_volume
, e
->alsa_name
, e
->min_volume
, e
->max_volume
);
1460 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1464 pa_channel_position_t p
;
1467 ((e
->min_volume
> e
->db_fix
->min_step
) ||
1468 (e
->max_volume
< e
->db_fix
->max_step
))) {
1469 pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1470 "real hardware range (%li-%li). Disabling the decibel fix.", e
->alsa_name
,
1471 e
->db_fix
->min_step
, e
->db_fix
->max_step
,
1472 e
->min_volume
, e
->max_volume
);
1474 decibel_fix_free(e
->db_fix
);
1480 e
->min_volume
= e
->db_fix
->min_step
;
1481 e
->max_volume
= e
->db_fix
->max_step
;
1482 min_dB
= e
->db_fix
->db_values
[0];
1483 max_dB
= e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
];
1484 } else if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1485 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1487 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1489 /* Check that the kernel driver returns consistent limits with
1490 * both _get_*_dB_range() and _ask_*_vol_dB(). */
1491 if (e
->has_dB
&& !e
->db_fix
) {
1492 long min_dB_checked
= 0;
1493 long max_dB_checked
= 0;
1495 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1496 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1498 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1501 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->min_volume
);
1505 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1506 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1508 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1511 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->max_volume
);
1515 if (min_dB
!= min_dB_checked
|| max_dB
!= max_dB_checked
) {
1516 pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
1517 "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
1518 "%0.2f dB at level %li.",
1520 min_dB
/ 100.0, max_dB
/ 100.0,
1521 min_dB_checked
/ 100.0, e
->min_volume
, max_dB_checked
/ 100.0, e
->max_volume
);
1527 #ifdef HAVE_VALGRIND_MEMCHECK_H
1528 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1529 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1532 e
->min_dB
= ((double) min_dB
) / 100.0;
1533 e
->max_dB
= ((double) max_dB
) / 100.0;
1535 if (min_dB
>= max_dB
) {
1536 pa_assert(!e
->db_fix
);
1537 pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.", e
->min_dB
, e
->max_dB
);
1542 if (e
->volume_limit
>= 0) {
1543 if (e
->volume_limit
<= e
->min_volume
|| e
->volume_limit
> e
->max_volume
)
1544 pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1545 "%li-%li. The volume limit is ignored.",
1546 e
->alsa_name
, e
->path
->name
, e
->volume_limit
, e
->min_volume
+ 1, e
->max_volume
);
1549 e
->max_volume
= e
->volume_limit
;
1553 e
->db_fix
->max_step
= e
->max_volume
;
1554 e
->max_dB
= ((double) e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
]) / 100.0;
1557 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1558 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB
);
1560 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB
);
1563 pa_log_warn("Failed to get dB value of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1566 e
->max_dB
= ((double) max_dB
) / 100.0;
1572 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1573 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1575 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1580 if (!e
->override_map
) {
1581 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1582 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1585 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1588 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1591 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1594 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1596 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1599 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1600 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1602 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1605 if (e
->n_channels
<= 0) {
1606 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1610 if (e
->n_channels
> 2) {
1611 /* FIXME: In some places code like this is used:
1613 * e->masks[alsa_channel_ids[p]][e->n_channels-1]
1615 * The definition of e->masks is
1617 * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST][2];
1619 * Since the array size is fixed at 2, we obviously
1620 * don't support elements with more than two
1622 pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e
->alsa_name
, e
->n_channels
);
1626 if (!e
->override_map
) {
1627 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1628 pa_bool_t has_channel
;
1630 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1633 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1634 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1636 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1638 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1643 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1644 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1647 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1655 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1658 PA_LLIST_FOREACH(o
, e
->options
)
1659 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1660 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1664 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1665 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1669 PA_LLIST_FOREACH(o
, e
->options
) {
1672 for (i
= 0; i
< n
; i
++) {
1675 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1678 if (!pa_streq(buf
, o
->alsa_name
))
1686 if (check_required(e
, me
) < 0)
1692 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, pa_bool_t prefixed
) {
1699 if (!pa_startswith(section
, "Element "))
1705 /* This is not an element section, but an enum section? */
1706 if (strchr(section
, ':'))
1709 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1710 return p
->last_element
;
1712 PA_LLIST_FOREACH(e
, p
->elements
)
1713 if (pa_streq(e
->alsa_name
, section
))
1716 e
= pa_xnew0(pa_alsa_element
, 1);
1718 e
->alsa_name
= pa_xstrdup(section
);
1719 e
->direction
= p
->direction
;
1720 e
->volume_limit
= -1;
1722 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1725 p
->last_element
= e
;
1729 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1735 if (!pa_startswith(section
, "Option "))
1740 /* This is not an enum section, but an element section? */
1741 if (!(on
= strchr(section
, ':')))
1744 en
= pa_xstrndup(section
, on
- section
);
1747 if (p
->last_option
&&
1748 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1749 pa_streq(p
->last_option
->alsa_name
, on
)) {
1751 return p
->last_option
;
1754 pa_assert_se(e
= element_get(p
, en
, FALSE
));
1757 PA_LLIST_FOREACH(o
, e
->options
)
1758 if (pa_streq(o
->alsa_name
, on
))
1761 o
= pa_xnew0(pa_alsa_option
, 1);
1763 o
->alsa_name
= pa_xstrdup(on
);
1766 if (p
->last_option
&& p
->last_option
->element
== e
)
1767 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1769 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1776 static int element_parse_switch(
1777 const char *filename
,
1779 const char *section
,
1785 pa_alsa_path
*p
= userdata
;
1790 if (!(e
= element_get(p
, section
, TRUE
))) {
1791 pa_log("[%s:%u] Switch makes no sense in '%s'", filename
, line
, section
);
1795 if (pa_streq(rvalue
, "ignore"))
1796 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1797 else if (pa_streq(rvalue
, "mute"))
1798 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1799 else if (pa_streq(rvalue
, "off"))
1800 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1801 else if (pa_streq(rvalue
, "on"))
1802 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1803 else if (pa_streq(rvalue
, "select"))
1804 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1806 pa_log("[%s:%u] Switch invalid of '%s'", filename
, line
, section
);
1813 static int element_parse_volume(
1814 const char *filename
,
1816 const char *section
,
1822 pa_alsa_path
*p
= userdata
;
1827 if (!(e
= element_get(p
, section
, TRUE
))) {
1828 pa_log("[%s:%u] Volume makes no sense in '%s'", filename
, line
, section
);
1832 if (pa_streq(rvalue
, "ignore"))
1833 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1834 else if (pa_streq(rvalue
, "merge"))
1835 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1836 else if (pa_streq(rvalue
, "off"))
1837 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1838 else if (pa_streq(rvalue
, "zero"))
1839 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1843 if (pa_atou(rvalue
, &constant
) >= 0) {
1844 e
->volume_use
= PA_ALSA_VOLUME_CONSTANT
;
1845 e
->constant_volume
= constant
;
1847 pa_log("[%s:%u] Volume invalid of '%s'", filename
, line
, section
);
1855 static int element_parse_enumeration(
1856 const char *filename
,
1858 const char *section
,
1864 pa_alsa_path
*p
= userdata
;
1869 if (!(e
= element_get(p
, section
, TRUE
))) {
1870 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename
, line
, section
);
1874 if (pa_streq(rvalue
, "ignore"))
1875 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1876 else if (pa_streq(rvalue
, "select"))
1877 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1879 pa_log("[%s:%u] Enumeration invalid of '%s'", filename
, line
, section
);
1886 static int option_parse_priority(
1887 const char *filename
,
1889 const char *section
,
1895 pa_alsa_path
*p
= userdata
;
1901 if (!(o
= option_get(p
, section
))) {
1902 pa_log("[%s:%u] Priority makes no sense in '%s'", filename
, line
, section
);
1906 if (pa_atou(rvalue
, &prio
) < 0) {
1907 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
1915 static int option_parse_name(
1916 const char *filename
,
1918 const char *section
,
1924 pa_alsa_path
*p
= userdata
;
1929 if (!(o
= option_get(p
, section
))) {
1930 pa_log("[%s:%u] Name makes no sense in '%s'", filename
, line
, section
);
1935 o
->name
= pa_xstrdup(rvalue
);
1940 static int element_parse_required(
1941 const char *filename
,
1943 const char *section
,
1949 pa_alsa_path
*p
= userdata
;
1952 pa_alsa_required_t req
;
1956 e
= element_get(p
, section
, TRUE
);
1957 o
= option_get(p
, section
);
1959 pa_log("[%s:%u] Required makes no sense in '%s'", filename
, line
, section
);
1963 if (pa_streq(rvalue
, "ignore"))
1964 req
= PA_ALSA_REQUIRED_IGNORE
;
1965 else if (pa_streq(rvalue
, "switch") && e
)
1966 req
= PA_ALSA_REQUIRED_SWITCH
;
1967 else if (pa_streq(rvalue
, "volume") && e
)
1968 req
= PA_ALSA_REQUIRED_VOLUME
;
1969 else if (pa_streq(rvalue
, "enumeration"))
1970 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1971 else if (pa_streq(rvalue
, "any"))
1972 req
= PA_ALSA_REQUIRED_ANY
;
1974 pa_log("[%s:%u] Required invalid of '%s'", filename
, line
, section
);
1978 if (pa_streq(lvalue
, "required-absent")) {
1980 e
->required_absent
= req
;
1982 o
->required_absent
= req
;
1984 else if (pa_streq(lvalue
, "required-any")) {
1986 e
->required_any
= req
;
1987 e
->path
->has_req_any
= TRUE
;
1990 o
->required_any
= req
;
1991 o
->element
->path
->has_req_any
= TRUE
;
2004 static int element_parse_direction(
2005 const char *filename
,
2007 const char *section
,
2013 pa_alsa_path
*p
= userdata
;
2018 if (!(e
= element_get(p
, section
, TRUE
))) {
2019 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
2023 if (pa_streq(rvalue
, "playback"))
2024 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
2025 else if (pa_streq(rvalue
, "capture"))
2026 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
2028 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
2035 static int element_parse_direction_try_other(
2036 const char *filename
,
2038 const char *section
,
2044 pa_alsa_path
*p
= userdata
;
2048 if (!(e
= element_get(p
, section
, TRUE
))) {
2049 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
2053 if ((yes
= pa_parse_boolean(rvalue
)) < 0) {
2054 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
2058 e
->direction_try_other
= !!yes
;
2062 static int element_parse_volume_limit(
2063 const char *filename
,
2065 const char *section
,
2071 pa_alsa_path
*p
= userdata
;
2075 if (!(e
= element_get(p
, section
, TRUE
))) {
2076 pa_log("[%s:%u] volume-limit makes no sense in '%s'", filename
, line
, section
);
2080 if (pa_atol(rvalue
, &volume_limit
) < 0 || volume_limit
< 0) {
2081 pa_log("[%s:%u] Invalid value for volume-limit", filename
, line
);
2085 e
->volume_limit
= volume_limit
;
2089 static pa_channel_position_mask_t
parse_mask(const char *m
) {
2090 pa_channel_position_mask_t v
;
2092 if (pa_streq(m
, "all-left"))
2093 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
2094 else if (pa_streq(m
, "all-right"))
2095 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
2096 else if (pa_streq(m
, "all-center"))
2097 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
2098 else if (pa_streq(m
, "all-front"))
2099 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
2100 else if (pa_streq(m
, "all-rear"))
2101 v
= PA_CHANNEL_POSITION_MASK_REAR
;
2102 else if (pa_streq(m
, "all-side"))
2103 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
2104 else if (pa_streq(m
, "all-top"))
2105 v
= PA_CHANNEL_POSITION_MASK_TOP
;
2106 else if (pa_streq(m
, "all-no-lfe"))
2107 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
2108 else if (pa_streq(m
, "all"))
2109 v
= PA_CHANNEL_POSITION_MASK_ALL
;
2111 pa_channel_position_t p
;
2113 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
2116 v
= PA_CHANNEL_POSITION_MASK(p
);
2122 static int element_parse_override_map(
2123 const char *filename
,
2125 const char *section
,
2131 pa_alsa_path
*p
= userdata
;
2133 const char *state
= NULL
;
2137 if (!(e
= element_get(p
, section
, TRUE
))) {
2138 pa_log("[%s:%u] Override map makes no sense in '%s'", filename
, line
, section
);
2142 while ((n
= pa_split(rvalue
, ",", &state
))) {
2143 pa_channel_position_mask_t m
;
2148 if ((m
= parse_mask(n
)) == 0) {
2149 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename
, line
, n
, section
);
2155 if (pa_streq(lvalue
, "override-map.1"))
2156 e
->masks
[i
++][0] = m
;
2158 e
->masks
[i
++][1] = m
;
2160 /* Later on we might add override-map.3 and so on here ... */
2165 e
->override_map
= TRUE
;
2170 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
2171 snd_mixer_selem_id_t
*sid
;
2172 snd_mixer_elem_t
*me
;
2178 SELEM_INIT(sid
, e
->alsa_name
);
2179 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2180 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2184 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
2186 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
2187 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
2189 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
2192 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2195 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
2197 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
2198 pa_log_warn("Failed to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2204 int pa_alsa_setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
2211 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
2212 element_set_option(o
->element
, m
, o
->alsa_idx
);
2217 static int option_verify(pa_alsa_option
*o
) {
2218 static const struct description_map well_known_descriptions
[] = {
2219 { "input", N_("Input") },
2220 { "input-docking", N_("Docking Station Input") },
2221 { "input-docking-microphone", N_("Docking Station Microphone") },
2222 { "input-docking-linein", N_("Docking Station Line In") },
2223 { "input-linein", N_("Line In") },
2224 { "input-microphone", N_("Microphone") },
2225 { "input-microphone-front", N_("Front Microphone") },
2226 { "input-microphone-rear", N_("Rear Microphone") },
2227 { "input-microphone-external", N_("External Microphone") },
2228 { "input-microphone-internal", N_("Internal Microphone") },
2229 { "input-radio", N_("Radio") },
2230 { "input-video", N_("Video") },
2231 { "input-agc-on", N_("Automatic Gain Control") },
2232 { "input-agc-off", N_("No Automatic Gain Control") },
2233 { "input-boost-on", N_("Boost") },
2234 { "input-boost-off", N_("No Boost") },
2235 { "output-amplifier-on", N_("Amplifier") },
2236 { "output-amplifier-off", N_("No Amplifier") },
2237 { "output-bass-boost-on", N_("Bass Boost") },
2238 { "output-bass-boost-off", N_("No Bass Boost") },
2239 { "output-speaker", N_("Speaker") },
2240 { "output-headphones", N_("Headphones") }
2246 pa_log("No name set for option %s", o
->alsa_name
);
2250 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
2251 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
2252 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
2256 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
2257 !pa_streq(o
->alsa_name
, "on") &&
2258 !pa_streq(o
->alsa_name
, "off")) {
2259 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
2263 if (!o
->description
)
2264 o
->description
= pa_xstrdup(lookup_description(o
->name
,
2265 well_known_descriptions
,
2266 PA_ELEMENTSOF(well_known_descriptions
)));
2267 if (!o
->description
)
2268 o
->description
= pa_xstrdup(o
->name
);
2273 static int element_verify(pa_alsa_element
*e
) {
2278 // pa_log_debug("Element %s, path %s: r=%d, r-any=%d, r-abs=%d", e->alsa_name, e->path->name, e->required, e->required_any, e->required_absent);
2279 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
2280 (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required_any
== e
->required_absent
) ||
2281 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) ||
2282 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
2283 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
2287 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
2288 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
2292 PA_LLIST_FOREACH(o
, e
->options
)
2293 if (option_verify(o
) < 0)
2299 static int path_verify(pa_alsa_path
*p
) {
2300 static const struct description_map well_known_descriptions
[] = {
2301 { "analog-input", N_("Analog Input") },
2302 { "analog-input-microphone", N_("Microphone") },
2303 { "analog-input-microphone-front", N_("Front Microphone") },
2304 { "analog-input-microphone-rear", N_("Rear Microphone") },
2305 { "analog-input-microphone-dock", N_("Dock Microphone") },
2306 { "analog-input-microphone-internal", N_("Internal Microphone") },
2307 { "analog-input-linein", N_("Line In") },
2308 { "analog-input-radio", N_("Radio") },
2309 { "analog-input-video", N_("Video") },
2310 { "analog-output", N_("Analog Output") },
2311 { "analog-output-headphones", N_("Headphones") },
2312 { "analog-output-lfe-on-mono", N_("LFE on Separate Mono Output") },
2313 { "analog-output-lineout", N_("Line Out") },
2314 { "analog-output-mono", N_("Analog Mono Output") },
2315 { "analog-output-speaker", N_("Speakers") },
2316 { "hdmi-output", N_("HDMI / DisplayPort") },
2317 { "iec958-stereo-output", N_("Digital Output (S/PDIF)") },
2318 { "iec958-passthrough-output", N_("Digital Passthrough (S/PDIF)") }
2325 PA_LLIST_FOREACH(e
, p
->elements
)
2326 if (element_verify(e
) < 0)
2329 if (!p
->description
)
2330 p
->description
= pa_xstrdup(lookup_description(p
->name
,
2331 well_known_descriptions
,
2332 PA_ELEMENTSOF(well_known_descriptions
)));
2334 if (!p
->description
)
2335 p
->description
= pa_xstrdup(p
->name
);
2340 static const char *get_default_paths_dir(void) {
2341 if (pa_run_from_build_tree())
2342 return PA_BUILDDIR
"/modules/alsa/mixer/paths/";
2344 return PA_ALSA_PATHS_DIR
;
2347 pa_alsa_path
* pa_alsa_path_new(const char *paths_dir
, const char *fname
, pa_alsa_direction_t direction
) {
2353 pa_config_item items
[] = {
2355 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
2356 { "description", pa_config_parse_string
, NULL
, "General" },
2357 { "name", pa_config_parse_string
, NULL
, "General" },
2360 { "priority", option_parse_priority
, NULL
, NULL
},
2361 { "name", option_parse_name
, NULL
, NULL
},
2364 { "switch", element_parse_switch
, NULL
, NULL
},
2365 { "volume", element_parse_volume
, NULL
, NULL
},
2366 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
2367 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
2368 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
2369 /* ... later on we might add override-map.3 and so on here ... */
2370 { "required", element_parse_required
, NULL
, NULL
},
2371 { "required-any", element_parse_required
, NULL
, NULL
},
2372 { "required-absent", element_parse_required
, NULL
, NULL
},
2373 { "direction", element_parse_direction
, NULL
, NULL
},
2374 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2375 { "volume-limit", element_parse_volume_limit
, NULL
, NULL
},
2376 { NULL
, NULL
, NULL
, NULL
}
2381 p
= pa_xnew0(pa_alsa_path
, 1);
2382 n
= pa_path_get_filename(fname
);
2383 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2384 p
->direction
= direction
;
2386 items
[0].data
= &p
->priority
;
2387 items
[1].data
= &p
->description
;
2388 items
[2].data
= &p
->name
;
2391 paths_dir
= get_default_paths_dir();
2393 fn
= pa_maybe_prefix_path(fname
, paths_dir
);
2395 r
= pa_config_parse(fn
, NULL
, items
, p
);
2401 if (path_verify(p
) < 0)
2407 pa_alsa_path_free(p
);
2411 pa_alsa_path
*pa_alsa_path_synthesize(const char *element
, pa_alsa_direction_t direction
) {
2417 p
= pa_xnew0(pa_alsa_path
, 1);
2418 p
->name
= pa_xstrdup(element
);
2419 p
->direction
= direction
;
2421 e
= pa_xnew0(pa_alsa_element
, 1);
2423 e
->alsa_name
= pa_xstrdup(element
);
2424 e
->direction
= direction
;
2425 e
->volume_limit
= -1;
2427 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2428 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2430 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2431 p
->last_element
= e
;
2435 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2436 pa_alsa_option
*o
, *n
;
2440 for (o
= e
->options
; o
; o
= n
) {
2443 if (o
->alsa_idx
< 0) {
2444 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2450 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2451 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2452 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2455 static void path_drop_unsupported(pa_alsa_path
*p
) {
2456 pa_alsa_element
*e
, *n
;
2460 for (e
= p
->elements
; e
; e
= n
) {
2463 if (!element_drop_unsupported(e
)) {
2464 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2470 static void path_make_options_unique(pa_alsa_path
*p
) {
2472 pa_alsa_option
*o
, *u
;
2474 PA_LLIST_FOREACH(e
, p
->elements
) {
2475 PA_LLIST_FOREACH(o
, e
->options
) {
2479 for (u
= o
->next
; u
; u
= u
->next
)
2480 if (pa_streq(u
->name
, o
->name
))
2486 m
= pa_xstrdup(o
->name
);
2488 /* OK, this name is not unique, hence let's rename */
2489 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2492 if (!pa_streq(u
->name
, m
))
2495 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2499 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2500 pa_xfree(u
->description
);
2501 u
->description
= nd
;
2511 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2514 for (; e
; e
= e
->next
)
2515 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2516 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2522 for (o
= e
->options
; o
; o
= o
->next
) {
2526 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2527 s
->options
= pa_idxset_copy(template->options
);
2528 s
->name
= pa_sprintf_malloc("%s+%s", template->name
, o
->name
);
2530 (template->description
[0] && o
->description
[0])
2531 ? pa_sprintf_malloc("%s / %s", template->description
, o
->description
)
2532 : (template->description
[0]
2533 ? pa_xstrdup(template->description
)
2534 : pa_xstrdup(o
->description
));
2536 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2538 s
= pa_xnew0(pa_alsa_setting
, 1);
2539 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2540 s
->name
= pa_xstrdup(o
->name
);
2541 s
->description
= pa_xstrdup(o
->description
);
2542 s
->priority
= o
->priority
;
2545 pa_idxset_put(s
->options
, o
, NULL
);
2547 if (element_create_settings(e
->next
, s
))
2548 /* This is not a leaf, so let's get rid of it */
2551 /* This is a leaf, so let's add it */
2552 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2554 e
->path
->last_setting
= s
;
2561 static void path_create_settings(pa_alsa_path
*p
) {
2564 element_create_settings(p
->elements
, NULL
);
2567 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2569 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2570 pa_channel_position_t t
;
2571 pa_channel_position_mask_t path_volume_channels
= 0;
2577 return p
->supported
? 0 : -1;
2583 pa_log_debug("Probing path '%s'", p
->name
);
2585 PA_LLIST_FOREACH(e
, p
->elements
) {
2586 if (element_probe(e
, m
) < 0) {
2587 p
->supported
= FALSE
;
2588 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2591 pa_log_debug("Probe of element '%s' succeeded (volume=%d, switch=%d, enumeration=%d).", e
->alsa_name
, e
->volume_use
, e
->switch_use
, e
->enumeration_use
);
2596 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2598 if (!p
->has_volume
) {
2599 p
->min_volume
= e
->min_volume
;
2600 p
->max_volume
= e
->max_volume
;
2604 if (!p
->has_volume
) {
2605 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2606 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2607 min_dB
[t
] = e
->min_dB
;
2608 max_dB
[t
] = e
->max_dB
;
2609 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2616 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2617 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2618 min_dB
[t
] += e
->min_dB
;
2619 max_dB
[t
] += e
->max_dB
;
2620 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2623 /* Hmm, there's another element before us
2624 * which cannot do dB volumes, so we we need
2625 * to 'neutralize' this slider */
2626 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2627 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2630 } else if (p
->has_volume
) {
2631 /* We can't use this volume, so let's ignore it */
2632 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2633 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2635 p
->has_volume
= TRUE
;
2638 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2642 if (p
->has_req_any
&& !p
->req_any_present
) {
2643 p
->supported
= FALSE
;
2644 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2648 path_drop_unsupported(p
);
2649 path_make_options_unique(p
);
2650 path_create_settings(p
);
2652 p
->supported
= TRUE
;
2654 p
->min_dB
= INFINITY
;
2655 p
->max_dB
= -INFINITY
;
2657 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2658 if (path_volume_channels
& PA_CHANNEL_POSITION_MASK(t
)) {
2659 if (p
->min_dB
> min_dB
[t
])
2660 p
->min_dB
= min_dB
[t
];
2662 if (p
->max_dB
< max_dB
[t
])
2663 p
->max_dB
= max_dB
[t
];
2670 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2673 pa_log_debug("Setting %s (%s) priority=%u",
2675 pa_strnull(s
->description
),
2679 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2682 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2684 pa_strnull(o
->name
),
2685 pa_strnull(o
->description
),
2690 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2694 pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, volume_limit=%li, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s",
2704 (long long unsigned) e
->merged_mask
,
2706 pa_yes_no(e
->override_map
));
2708 PA_LLIST_FOREACH(o
, e
->options
)
2709 pa_alsa_option_dump(o
);
2712 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2717 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2718 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2720 pa_strnull(p
->description
),
2723 pa_yes_no(p
->probed
),
2724 pa_yes_no(p
->supported
),
2725 pa_yes_no(p
->has_mute
),
2726 pa_yes_no(p
->has_volume
),
2727 pa_yes_no(p
->has_dB
),
2728 p
->min_volume
, p
->max_volume
,
2729 p
->min_dB
, p
->max_dB
);
2731 PA_LLIST_FOREACH(e
, p
->elements
)
2732 pa_alsa_element_dump(e
);
2734 PA_LLIST_FOREACH(s
, p
->settings
)
2735 pa_alsa_setting_dump(s
);
2738 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2739 snd_mixer_selem_id_t
*sid
;
2740 snd_mixer_elem_t
*me
;
2746 SELEM_INIT(sid
, e
->alsa_name
);
2747 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2748 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2752 snd_mixer_elem_set_callback(me
, cb
);
2753 snd_mixer_elem_set_callback_private(me
, userdata
);
2756 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2763 PA_LLIST_FOREACH(e
, p
->elements
)
2764 element_set_callback(e
, m
, cb
, userdata
);
2767 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2775 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
2776 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2779 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
, const char *paths_dir
) {
2780 pa_alsa_path_set
*ps
;
2781 char **pn
= NULL
, **en
= NULL
, **ie
;
2782 pa_alsa_decibel_fix
*db_fix
;
2783 void *state
, *state2
;
2787 pa_assert(m
->profile_set
);
2788 pa_assert(m
->profile_set
->decibel_fixes
);
2789 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2791 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2794 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2795 ps
->direction
= direction
;
2796 ps
->paths
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2798 if (direction
== PA_ALSA_DIRECTION_OUTPUT
) {
2799 pn
= m
->output_path_names
;
2800 cache
= m
->profile_set
->output_paths
;
2802 else if (direction
== PA_ALSA_DIRECTION_INPUT
) {
2803 pn
= m
->input_path_names
;
2804 cache
= m
->profile_set
->input_paths
;
2810 for (in
= pn
; *in
; in
++) {
2811 pa_alsa_path
*p
= NULL
;
2812 pa_bool_t duplicate
= FALSE
;
2815 for (kn
= pn
; kn
< in
; kn
++)
2816 if (pa_streq(*kn
, *in
)) {
2824 p
= pa_hashmap_get(cache
, *in
);
2826 char *fn
= pa_sprintf_malloc("%s.conf", *in
);
2827 p
= pa_alsa_path_new(paths_dir
, fn
, direction
);
2830 pa_hashmap_put(cache
, *in
, p
);
2832 pa_assert(pa_hashmap_get(cache
, *in
) == p
);
2834 pa_hashmap_put(ps
->paths
, p
, p
);
2841 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2842 en
= m
->output_element
;
2843 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2844 en
= m
->input_element
;
2847 pa_alsa_path_set_free(ps
);
2851 for (ie
= en
; *ie
; ie
++) {
2855 p
= pa_alsa_path_synthesize(*ie
, direction
);
2857 /* Mark all other passed elements for require-absent */
2858 for (je
= en
; *je
; je
++) {
2864 e
= pa_xnew0(pa_alsa_element
, 1);
2866 e
->alsa_name
= pa_xstrdup(*je
);
2867 e
->direction
= direction
;
2868 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2869 e
->volume_limit
= -1;
2871 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2872 p
->last_element
= e
;
2875 pa_hashmap_put(ps
->paths
, *ie
, p
);
2879 /* Assign decibel fixes to elements. */
2880 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2883 PA_HASHMAP_FOREACH(p
, ps
->paths
, state2
) {
2886 PA_LLIST_FOREACH(e
, p
->elements
) {
2887 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2888 /* The profile set that contains the dB fix may be freed
2889 * before the element, so we have to copy the dB fix
2891 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2892 e
->db_fix
->profile_set
= NULL
;
2893 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2894 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
2903 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2908 pa_log_debug("Path Set %p, direction=%i",
2912 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
2913 pa_alsa_path_dump(p
);
2917 static pa_bool_t
options_have_option(pa_alsa_option
*options
, const char *alsa_name
) {
2921 pa_assert(alsa_name
);
2923 PA_LLIST_FOREACH(o
, options
) {
2924 if (pa_streq(o
->alsa_name
, alsa_name
))
2930 static pa_bool_t
enumeration_is_subset(pa_alsa_option
*a_options
, pa_alsa_option
*b_options
) {
2931 pa_alsa_option
*oa
, *ob
;
2933 if (!a_options
) return TRUE
;
2934 if (!b_options
) return FALSE
;
2936 /* If there is an option A offers that B does not, then A is not a subset of B. */
2937 PA_LLIST_FOREACH(oa
, a_options
) {
2938 pa_bool_t found
= FALSE
;
2939 PA_LLIST_FOREACH(ob
, b_options
) {
2940 if (pa_streq(oa
->alsa_name
, ob
->alsa_name
)) {
2952 * Compares two elements to see if a is a subset of b
2954 static pa_bool_t
element_is_subset(pa_alsa_element
*a
, pa_alsa_element
*b
, snd_mixer_t
*m
) {
2960 * Every state is a subset of itself (with caveats for volume_limits and options)
2961 * IGNORE is a subset of every other state */
2963 /* Check the volume_use */
2964 if (a
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
2966 /* "Constant" is subset of "Constant" only when their constant values are equal */
2967 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& b
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& a
->constant_volume
!= b
->constant_volume
)
2970 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
2971 if (a
->volume_use
!= b
->volume_use
&& b
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
2974 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
2975 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
2976 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
2977 if (b
->volume_use
== PA_ALSA_VOLUME_MERGE
&& b
->volume_limit
>= 0) {
2980 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
)
2981 a_limit
= a
->constant_volume
;
2982 else if (a
->volume_use
== PA_ALSA_VOLUME_ZERO
) {
2986 int rounding
= (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
? +1 : -1);
2987 a_limit
= decibel_fix_get_step(a
->db_fix
, &dB
, rounding
);
2989 snd_mixer_selem_id_t
*sid
;
2990 snd_mixer_elem_t
*me
;
2992 SELEM_INIT(sid
, a
->alsa_name
);
2993 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2994 pa_log_warn("Element %s seems to have disappeared.", a
->alsa_name
);
2998 if (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
2999 if (snd_mixer_selem_ask_playback_dB_vol(me
, dB
, +1, &a_limit
) < 0)
3002 if (snd_mixer_selem_ask_capture_dB_vol(me
, dB
, -1, &a_limit
) < 0)
3006 } else if (a
->volume_use
== PA_ALSA_VOLUME_OFF
)
3007 a_limit
= a
->min_volume
;
3008 else if (a
->volume_use
== PA_ALSA_VOLUME_MERGE
)
3009 a_limit
= a
->volume_limit
;
3011 /* This should never be reached */
3014 if (a_limit
> b
->volume_limit
)
3018 if (a
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
3020 /* If override-maps are different, they're not subsets */
3021 if (a
->n_channels
!= b
->n_channels
)
3023 for (s
= 0; s
< SND_MIXER_SCHN_LAST
; s
++)
3024 if (a
->masks
[s
][a
->n_channels
-1] != b
->masks
[s
][b
->n_channels
-1]) {
3025 pa_log_debug("Element %s is not a subset - mask a: 0x%lx, mask b: 0x%lx, at channel %d",
3026 a
->alsa_name
, a
->masks
[s
][a
->n_channels
-1], b
->masks
[s
][b
->n_channels
-1], s
);
3032 if (a
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
3033 /* "On" is a subset of "Mute".
3034 * "Off" is a subset of "Mute".
3035 * "On" is a subset of "Select", if there is an "Option:On" in B.
3036 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3037 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3039 if (a
->switch_use
!= b
->switch_use
) {
3041 if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
|| a
->switch_use
== PA_ALSA_SWITCH_MUTE
3042 || b
->switch_use
== PA_ALSA_SWITCH_OFF
|| b
->switch_use
== PA_ALSA_SWITCH_ON
)
3045 if (b
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3046 if (a
->switch_use
== PA_ALSA_SWITCH_ON
) {
3047 if (!options_have_option(b
->options
, "on"))
3049 } else if (a
->switch_use
== PA_ALSA_SWITCH_OFF
) {
3050 if (!options_have_option(b
->options
, "off"))
3054 } else if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3055 if (!enumeration_is_subset(a
->options
, b
->options
))
3060 if (a
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
) {
3061 if (b
->enumeration_use
== PA_ALSA_ENUMERATION_IGNORE
)
3063 if (!enumeration_is_subset(a
->options
, b
->options
))
3070 static void path_set_condense(pa_alsa_path_set
*ps
, snd_mixer_t
*m
) {
3077 /* If we only have one path, then don't bother */
3078 if (pa_hashmap_size(ps
->paths
) < 2)
3081 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3085 PA_HASHMAP_FOREACH(p2
, ps
->paths
, state2
) {
3086 pa_alsa_element
*ea
, *eb
;
3087 pa_bool_t is_subset
= TRUE
;
3092 /* Compare the elements of each set... */
3093 pa_assert_se(ea
= p
->elements
);
3094 pa_assert_se(eb
= p2
->elements
);
3097 if (pa_streq(ea
->alsa_name
, eb
->alsa_name
)) {
3098 if (element_is_subset(ea
, eb
, m
)) {
3101 if ((ea
&& !eb
) || (!ea
&& eb
))
3103 else if (!ea
&& !eb
)
3113 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p
->name
, p2
->name
);
3114 pa_hashmap_remove(ps
->paths
, p
);
3121 static pa_alsa_path
* path_set_find_path_by_name(pa_alsa_path_set
*ps
, const char* name
, pa_alsa_path
*ignore
)
3126 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
3127 if (p
!= ignore
&& pa_streq(p
->name
, name
))
3132 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
3133 pa_alsa_path
*p
, *q
;
3134 void *state
, *state2
;
3136 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3140 q
= path_set_find_path_by_name(ps
, p
->name
, p
);
3145 m
= pa_xstrdup(p
->name
);
3147 /* OK, this name is not unique, hence let's rename */
3149 PA_HASHMAP_FOREACH(q
, ps
->paths
, state2
) {
3152 if (!pa_streq(q
->name
, m
))
3155 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
3159 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
3160 pa_xfree(q
->description
);
3161 q
->description
= nd
;
3170 static void mapping_free(pa_alsa_mapping
*m
) {
3174 pa_xfree(m
->description
);
3176 pa_xstrfreev(m
->device_strings
);
3177 pa_xstrfreev(m
->input_path_names
);
3178 pa_xstrfreev(m
->output_path_names
);
3179 pa_xstrfreev(m
->input_element
);
3180 pa_xstrfreev(m
->output_element
);
3181 if (m
->input_path_set
)
3182 pa_alsa_path_set_free(m
->input_path_set
);
3183 if (m
->output_path_set
)
3184 pa_alsa_path_set_free(m
->output_path_set
);
3186 pa_assert(!m
->input_pcm
);
3187 pa_assert(!m
->output_pcm
);
3192 static void profile_free(pa_alsa_profile
*p
) {
3196 pa_xfree(p
->description
);
3198 pa_xstrfreev(p
->input_mapping_names
);
3199 pa_xstrfreev(p
->output_mapping_names
);
3201 if (p
->input_mappings
)
3202 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
3204 if (p
->output_mappings
)
3205 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
3210 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
3213 if (ps
->input_paths
) {
3216 while ((p
= pa_hashmap_steal_first(ps
->input_paths
)))
3217 pa_alsa_path_free(p
);
3219 pa_hashmap_free(ps
->input_paths
, NULL
, NULL
);
3222 if (ps
->output_paths
) {
3225 while ((p
= pa_hashmap_steal_first(ps
->output_paths
)))
3226 pa_alsa_path_free(p
);
3228 pa_hashmap_free(ps
->output_paths
, NULL
, NULL
);
3234 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
3237 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
3243 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
3246 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
3249 if (ps
->decibel_fixes
) {
3250 pa_alsa_decibel_fix
*db_fix
;
3252 while ((db_fix
= pa_hashmap_steal_first(ps
->decibel_fixes
)))
3253 decibel_fix_free(db_fix
);
3255 pa_hashmap_free(ps
->decibel_fixes
, NULL
, NULL
);
3261 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
3264 if (!pa_startswith(name
, "Mapping "))
3269 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
3272 m
= pa_xnew0(pa_alsa_mapping
, 1);
3273 m
->profile_set
= ps
;
3274 m
->name
= pa_xstrdup(name
);
3275 pa_channel_map_init(&m
->channel_map
);
3277 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
3282 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
3285 if (!pa_startswith(name
, "Profile "))
3290 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
3293 p
= pa_xnew0(pa_alsa_profile
, 1);
3294 p
->profile_set
= ps
;
3295 p
->name
= pa_xstrdup(name
);
3297 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3302 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
3303 pa_alsa_decibel_fix
*db_fix
;
3305 if (!pa_startswith(name
, "DecibelFix "))
3310 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
3313 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
3314 db_fix
->profile_set
= ps
;
3315 db_fix
->name
= pa_xstrdup(name
);
3317 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
3322 static int mapping_parse_device_strings(
3323 const char *filename
,
3325 const char *section
,
3331 pa_alsa_profile_set
*ps
= userdata
;
3336 if (!(m
= mapping_get(ps
, section
))) {
3337 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3341 pa_xstrfreev(m
->device_strings
);
3342 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
3343 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
3350 static int mapping_parse_channel_map(
3351 const char *filename
,
3353 const char *section
,
3359 pa_alsa_profile_set
*ps
= userdata
;
3364 if (!(m
= mapping_get(ps
, section
))) {
3365 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3369 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
3370 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
3377 static int mapping_parse_paths(
3378 const char *filename
,
3380 const char *section
,
3386 pa_alsa_profile_set
*ps
= userdata
;
3391 if (!(m
= mapping_get(ps
, section
))) {
3392 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3396 if (pa_streq(lvalue
, "paths-input")) {
3397 pa_xstrfreev(m
->input_path_names
);
3398 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
3400 pa_xstrfreev(m
->output_path_names
);
3401 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
3407 static int mapping_parse_element(
3408 const char *filename
,
3410 const char *section
,
3416 pa_alsa_profile_set
*ps
= userdata
;
3421 if (!(m
= mapping_get(ps
, section
))) {
3422 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3426 if (pa_streq(lvalue
, "element-input")) {
3427 pa_xstrfreev(m
->input_element
);
3428 m
->input_element
= pa_split_spaces_strv(rvalue
);
3430 pa_xstrfreev(m
->output_element
);
3431 m
->output_element
= pa_split_spaces_strv(rvalue
);
3437 static int mapping_parse_direction(
3438 const char *filename
,
3440 const char *section
,
3446 pa_alsa_profile_set
*ps
= userdata
;
3451 if (!(m
= mapping_get(ps
, section
))) {
3452 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3456 if (pa_streq(rvalue
, "input"))
3457 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3458 else if (pa_streq(rvalue
, "output"))
3459 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3460 else if (pa_streq(rvalue
, "any"))
3461 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3463 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
3470 static int mapping_parse_description(
3471 const char *filename
,
3473 const char *section
,
3479 pa_alsa_profile_set
*ps
= userdata
;
3485 if ((m
= mapping_get(ps
, section
))) {
3486 pa_xfree(m
->description
);
3487 m
->description
= pa_xstrdup(rvalue
);
3488 } else if ((p
= profile_get(ps
, section
))) {
3489 pa_xfree(p
->description
);
3490 p
->description
= pa_xstrdup(rvalue
);
3492 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3499 static int mapping_parse_priority(
3500 const char *filename
,
3502 const char *section
,
3508 pa_alsa_profile_set
*ps
= userdata
;
3515 if (pa_atou(rvalue
, &prio
) < 0) {
3516 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
3520 if ((m
= mapping_get(ps
, section
)))
3522 else if ((p
= profile_get(ps
, section
)))
3525 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3532 static int profile_parse_mappings(
3533 const char *filename
,
3535 const char *section
,
3541 pa_alsa_profile_set
*ps
= userdata
;
3546 if (!(p
= profile_get(ps
, section
))) {
3547 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3551 if (pa_streq(lvalue
, "input-mappings")) {
3552 pa_xstrfreev(p
->input_mapping_names
);
3553 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
3555 pa_xstrfreev(p
->output_mapping_names
);
3556 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
3562 static int profile_parse_skip_probe(
3563 const char *filename
,
3565 const char *section
,
3571 pa_alsa_profile_set
*ps
= userdata
;
3577 if (!(p
= profile_get(ps
, section
))) {
3578 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3582 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
3583 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
3592 static int decibel_fix_parse_db_values(
3593 const char *filename
,
3595 const char *section
,
3601 pa_alsa_profile_set
*ps
= userdata
;
3602 pa_alsa_decibel_fix
*db_fix
;
3606 unsigned n
= 8; /* Current size of the db_values table. */
3607 unsigned min_step
= 0;
3608 unsigned max_step
= 0;
3609 unsigned i
= 0; /* Index to the items table. */
3610 unsigned prev_step
= 0;
3613 pa_assert(filename
);
3619 if (!(db_fix
= decibel_fix_get(ps
, section
))) {
3620 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3624 if (!(items
= pa_split_spaces_strv(rvalue
))) {
3625 pa_log("[%s:%u] Value missing", pa_strnull(filename
), line
);
3629 db_values
= pa_xnew(long, n
);
3631 while ((item
= items
[i
++])) {
3632 char *s
= item
; /* Step value string. */
3633 char *d
= item
; /* dB value string. */
3637 /* Move d forward until it points to a colon or to the end of the item. */
3638 for (; *d
&& *d
!= ':'; ++d
);
3641 /* item started with colon. */
3642 pa_log("[%s:%u] No step value found in %s", filename
, line
, item
);
3646 if (!*d
|| !*(d
+ 1)) {
3647 /* No colon found, or it was the last character in item. */
3648 pa_log("[%s:%u] No dB value found in %s", filename
, line
, item
);
3652 /* pa_atou() needs a null-terminating string. Let's replace the colon
3653 * with a zero byte. */
3656 if (pa_atou(s
, &step
) < 0) {
3657 pa_log("[%s:%u] Invalid step value: %s", filename
, line
, s
);
3661 if (pa_atod(d
, &db
) < 0) {
3662 pa_log("[%s:%u] Invalid dB value: %s", filename
, line
, d
);
3666 if (step
<= prev_step
&& i
!= 1) {
3667 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename
, line
, step
, prev_step
);
3671 if (db
< prev_db
&& i
!= 1) {
3672 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename
, line
, db
, prev_db
);
3678 db_values
[0] = (long) (db
* 100.0);
3682 /* Interpolate linearly. */
3683 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3685 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3687 /* Reallocate the db_values table if it's about to overflow. */
3688 if (prev_step
+ 1 - min_step
== n
) {
3690 db_values
= pa_xrenew(long, db_values
, n
);
3693 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3700 db_fix
->min_step
= min_step
;
3701 db_fix
->max_step
= max_step
;
3702 pa_xfree(db_fix
->db_values
);
3703 db_fix
->db_values
= db_values
;
3705 pa_xstrfreev(items
);
3710 pa_xstrfreev(items
);
3711 pa_xfree(db_values
);
3716 static void mapping_paths_probe(pa_alsa_mapping
*m
, pa_alsa_profile
*profile
,
3717 pa_alsa_direction_t direction
) {
3721 snd_pcm_t
*pcm_handle
;
3722 pa_alsa_path_set
*ps
;
3723 snd_mixer_t
*mixer_handle
;
3725 if (direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3726 if (m
->output_path_set
)
3727 return; /* Already probed */
3728 m
->output_path_set
= ps
= pa_alsa_path_set_new(m
, direction
, NULL
); /* FIXME: Handle paths_dir */
3729 pcm_handle
= m
->output_pcm
;
3731 if (m
->input_path_set
)
3732 return; /* Already probed */
3733 m
->input_path_set
= ps
= pa_alsa_path_set_new(m
, direction
, NULL
); /* FIXME: Handle paths_dir */
3734 pcm_handle
= m
->input_pcm
;
3738 return; /* No paths */
3740 pa_assert(pcm_handle
);
3742 mixer_handle
= pa_alsa_open_mixer_for_pcm(pcm_handle
, NULL
);
3743 if (!mixer_handle
) {
3744 /* Cannot open mixer, remove all entries */
3745 while (pa_hashmap_steal_first(ps
->paths
));
3750 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3751 if (pa_alsa_path_probe(p
, mixer_handle
, m
->profile_set
->ignore_dB
) < 0) {
3752 pa_hashmap_remove(ps
->paths
, p
);
3756 path_set_condense(ps
, mixer_handle
);
3757 path_set_make_paths_unique(ps
);
3760 snd_mixer_close(mixer_handle
);
3762 pa_log_debug("Available mixer paths (after tidying):");
3763 pa_alsa_path_set_dump(ps
);
3766 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3768 static const struct description_map well_known_descriptions
[] = {
3769 { "analog-mono", N_("Analog Mono") },
3770 { "analog-stereo", N_("Analog Stereo") },
3771 { "analog-surround-21", N_("Analog Surround 2.1") },
3772 { "analog-surround-30", N_("Analog Surround 3.0") },
3773 { "analog-surround-31", N_("Analog Surround 3.1") },
3774 { "analog-surround-40", N_("Analog Surround 4.0") },
3775 { "analog-surround-41", N_("Analog Surround 4.1") },
3776 { "analog-surround-50", N_("Analog Surround 5.0") },
3777 { "analog-surround-51", N_("Analog Surround 5.1") },
3778 { "analog-surround-61", N_("Analog Surround 6.0") },
3779 { "analog-surround-61", N_("Analog Surround 6.1") },
3780 { "analog-surround-70", N_("Analog Surround 7.0") },
3781 { "analog-surround-71", N_("Analog Surround 7.1") },
3782 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3783 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3784 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3785 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3786 { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
3787 { "hdmi-stereo", N_("Digital Stereo (HDMI)") },
3788 { "hdmi-surround-51", N_("Digital Surround 5.1 (HDMI)") }
3793 if (!pa_channel_map_valid(&m
->channel_map
)) {
3794 pa_log("Mapping %s is missing channel map.", m
->name
);
3798 if (!m
->device_strings
) {
3799 pa_log("Mapping %s is missing device strings.", m
->name
);
3803 if ((m
->input_path_names
&& m
->input_element
) ||
3804 (m
->output_path_names
&& m
->output_element
)) {
3805 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3809 if (!m
->description
)
3810 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3811 well_known_descriptions
,
3812 PA_ELEMENTSOF(well_known_descriptions
)));
3814 if (!m
->description
)
3815 m
->description
= pa_xstrdup(m
->name
);
3818 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3820 else if (m
->channel_map
.channels
== bonus
->channels
)
3827 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3828 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3832 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3834 pa_strnull(m
->description
),
3836 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3837 pa_yes_no(m
->supported
),
3841 static void profile_set_add_auto_pair(
3842 pa_alsa_profile_set
*ps
,
3843 pa_alsa_mapping
*m
, /* output */
3844 pa_alsa_mapping
*n
/* input */) {
3852 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3855 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3859 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3861 name
= pa_sprintf_malloc("output:%s", m
->name
);
3863 name
= pa_sprintf_malloc("input:%s", n
->name
);
3865 if (pa_hashmap_get(ps
->profiles
, name
)) {
3870 p
= pa_xnew0(pa_alsa_profile
, 1);
3871 p
->profile_set
= ps
;
3875 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3876 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3877 p
->priority
+= m
->priority
* 100;
3881 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3882 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3883 p
->priority
+= n
->priority
;
3886 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3889 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3890 pa_alsa_mapping
*m
, *n
;
3891 void *m_state
, *n_state
;
3895 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3896 profile_set_add_auto_pair(ps
, m
, NULL
);
3898 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3899 profile_set_add_auto_pair(ps
, m
, n
);
3902 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3903 profile_set_add_auto_pair(ps
, NULL
, n
);
3906 static int profile_verify(pa_alsa_profile
*p
) {
3908 static const struct description_map well_known_descriptions
[] = {
3909 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3910 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3911 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3912 { "off", N_("Off") }
3917 /* Replace the output mapping names by the actual mappings */
3918 if (p
->output_mapping_names
) {
3921 pa_assert(!p
->output_mappings
);
3922 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3924 for (name
= p
->output_mapping_names
; *name
; name
++) {
3927 pa_bool_t duplicate
= FALSE
;
3929 for (in
= name
+ 1; *in
; in
++)
3930 if (pa_streq(*name
, *in
)) {
3938 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3939 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p
->name
, *name
);
3943 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3949 pa_xstrfreev(p
->output_mapping_names
);
3950 p
->output_mapping_names
= NULL
;
3953 /* Replace the input mapping names by the actual mappings */
3954 if (p
->input_mapping_names
) {
3957 pa_assert(!p
->input_mappings
);
3958 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3960 for (name
= p
->input_mapping_names
; *name
; name
++) {
3963 pa_bool_t duplicate
= FALSE
;
3965 for (in
= name
+ 1; *in
; in
++)
3966 if (pa_streq(*name
, *in
)) {
3974 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3975 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p
->name
, *name
);
3979 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3985 pa_xstrfreev(p
->input_mapping_names
);
3986 p
->input_mapping_names
= NULL
;
3989 if (!p
->input_mappings
&& !p
->output_mappings
) {
3990 pa_log("Profile '%s' lacks mappings.", p
->name
);
3994 if (!p
->description
)
3995 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3996 well_known_descriptions
,
3997 PA_ELEMENTSOF(well_known_descriptions
)));
3999 if (!p
->description
) {
4004 sb
= pa_strbuf_new();
4006 if (p
->output_mappings
)
4007 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4008 if (!pa_strbuf_isempty(sb
))
4009 pa_strbuf_puts(sb
, " + ");
4011 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
4014 if (p
->input_mappings
)
4015 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4016 if (!pa_strbuf_isempty(sb
))
4017 pa_strbuf_puts(sb
, " + ");
4019 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
4022 p
->description
= pa_strbuf_tostring_free(sb
);
4028 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
4033 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4035 pa_strnull(p
->description
),
4037 pa_yes_no(p
->supported
),
4038 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
4039 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
4041 if (p
->input_mappings
)
4042 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
4043 pa_log_debug("Input %s", m
->name
);
4045 if (p
->output_mappings
)
4046 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
4047 pa_log_debug("Output %s", m
->name
);
4050 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
4053 /* Check that the dB mapping has been configured. Since "db-values" is
4054 * currently the only option in the DecibelFix section, and decibel fix
4055 * objects don't get created if a DecibelFix section is empty, this is
4056 * actually a redundant check. Having this may prevent future bugs,
4058 if (!db_fix
->db_values
) {
4059 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
4066 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
4067 char *db_values
= NULL
;
4071 if (db_fix
->db_values
) {
4073 unsigned long i
, nsteps
;
4075 pa_assert(db_fix
->min_step
<= db_fix
->max_step
);
4076 nsteps
= db_fix
->max_step
- db_fix
->min_step
+ 1;
4078 buf
= pa_strbuf_new();
4079 for (i
= 0; i
< nsteps
; ++i
)
4080 pa_strbuf_printf(buf
, "[%li]:%0.2f ", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
4082 db_values
= pa_strbuf_tostring_free(buf
);
4085 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4086 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
4088 pa_xfree(db_values
);
4091 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
4092 pa_alsa_profile_set
*ps
;
4095 pa_alsa_decibel_fix
*db_fix
;
4100 static pa_config_item items
[] = {
4102 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
4105 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
4106 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
4107 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
4108 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
4109 { "element-input", mapping_parse_element
, NULL
, NULL
},
4110 { "element-output", mapping_parse_element
, NULL
, NULL
},
4111 { "direction", mapping_parse_direction
, NULL
, NULL
},
4113 /* Shared by [Mapping ...] and [Profile ...] */
4114 { "description", mapping_parse_description
, NULL
, NULL
},
4115 { "priority", mapping_parse_priority
, NULL
, NULL
},
4118 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
4119 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
4120 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
4122 /* [DecibelFix ...] */
4123 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
4124 { NULL
, NULL
, NULL
, NULL
}
4127 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
4128 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4129 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4130 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4131 ps
->input_paths
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4132 ps
->output_paths
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4134 items
[0].data
= &ps
->auto_profiles
;
4137 fname
= "default.conf";
4139 fn
= pa_maybe_prefix_path(fname
,
4140 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
4141 PA_ALSA_PROFILE_SETS_DIR
);
4143 r
= pa_config_parse(fn
, NULL
, items
, ps
);
4149 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4150 if (mapping_verify(m
, bonus
) < 0)
4153 if (ps
->auto_profiles
)
4154 profile_set_add_auto(ps
);
4156 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4157 if (profile_verify(p
) < 0)
4160 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4161 if (decibel_fix_verify(db_fix
) < 0)
4167 pa_alsa_profile_set_free(ps
);
4171 static void profile_finalize_probing(pa_alsa_profile
*to_be_finalized
, pa_alsa_profile
*next
) {
4175 if (!to_be_finalized
)
4178 if (to_be_finalized
->output_mappings
)
4179 PA_IDXSET_FOREACH(m
, to_be_finalized
->output_mappings
, idx
) {
4184 if (to_be_finalized
->supported
)
4187 /* If this mapping is also in the next profile, we won't close the
4188 * pcm handle here, because it would get immediately reopened
4190 if (next
&& next
->output_mappings
&& pa_idxset_get_by_data(next
->output_mappings
, m
, NULL
))
4193 snd_pcm_close(m
->output_pcm
);
4194 m
->output_pcm
= NULL
;
4197 if (to_be_finalized
->input_mappings
)
4198 PA_IDXSET_FOREACH(m
, to_be_finalized
->input_mappings
, idx
) {
4203 if (to_be_finalized
->supported
)
4206 /* If this mapping is also in the next profile, we won't close the
4207 * pcm handle here, because it would get immediately reopened
4209 if (next
&& next
->input_mappings
&& pa_idxset_get_by_data(next
->input_mappings
, m
, NULL
))
4212 snd_pcm_close(m
->input_pcm
);
4213 m
->input_pcm
= NULL
;
4217 static snd_pcm_t
* mapping_open_pcm(pa_alsa_mapping
*m
,
4218 const pa_sample_spec
*ss
,
4221 unsigned default_n_fragments
,
4222 unsigned default_fragment_size_msec
) {
4224 pa_sample_spec try_ss
= *ss
;
4225 pa_channel_map try_map
= m
->channel_map
;
4226 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
4228 try_ss
.channels
= try_map
.channels
;
4231 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
4232 pa_frame_size(&try_ss
);
4233 try_buffer_size
= default_n_fragments
* try_period_size
;
4235 return pa_alsa_open_by_template(
4236 m
->device_strings
, dev_id
, NULL
, &try_ss
,
4237 &try_map
, mode
, &try_period_size
,
4238 &try_buffer_size
, 0, NULL
, NULL
, TRUE
);
4241 static void paths_drop_unsupported(pa_hashmap
* h
) {
4248 p
= pa_hashmap_iterate(h
, &state
, &key
);
4250 if (p
->supported
<= 0) {
4251 pa_hashmap_remove(h
, key
);
4252 pa_alsa_path_free(p
);
4254 p
= pa_hashmap_iterate(h
, &state
, &key
);
4258 void pa_alsa_profile_set_probe(
4259 pa_alsa_profile_set
*ps
,
4261 const pa_sample_spec
*ss
,
4262 unsigned default_n_fragments
,
4263 unsigned default_fragment_size_msec
) {
4266 pa_alsa_profile
*p
, *last
= NULL
;
4276 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
4279 /* Skip if this is already marked that it is supported (i.e. from the config file) */
4280 if (!p
->supported
) {
4282 pa_log_debug("Looking at profile %s", p
->name
);
4283 profile_finalize_probing(last
, p
);
4284 p
->supported
= TRUE
;
4286 /* Check if we can open all new ones */
4287 if (p
->output_mappings
)
4288 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4293 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
4294 if (!(m
->output_pcm
= mapping_open_pcm(m
, ss
, dev_id
,
4295 SND_PCM_STREAM_PLAYBACK
,
4296 default_n_fragments
,
4297 default_fragment_size_msec
))) {
4298 p
->supported
= FALSE
;
4303 if (p
->input_mappings
&& p
->supported
)
4304 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4309 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
4310 if (!(m
->input_pcm
= mapping_open_pcm(m
, ss
, dev_id
,
4311 SND_PCM_STREAM_CAPTURE
,
4312 default_n_fragments
,
4313 default_fragment_size_msec
))) {
4314 p
->supported
= FALSE
;
4325 pa_log_debug("Profile %s supported.", p
->name
);
4327 if (p
->output_mappings
)
4328 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
4330 mapping_paths_probe(m
, p
, PA_ALSA_DIRECTION_OUTPUT
);
4332 if (p
->input_mappings
)
4333 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
4335 mapping_paths_probe(m
, p
, PA_ALSA_DIRECTION_INPUT
);
4339 profile_finalize_probing(last
, NULL
);
4341 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4342 if (!p
->supported
) {
4343 pa_hashmap_remove(ps
->profiles
, p
->name
);
4347 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4348 if (m
->supported
<= 0) {
4349 pa_hashmap_remove(ps
->mappings
, m
->name
);
4353 paths_drop_unsupported(ps
->input_paths
);
4354 paths_drop_unsupported(ps
->output_paths
);
4359 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
4362 pa_alsa_decibel_fix
*db_fix
;
4367 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4370 pa_yes_no(ps
->auto_profiles
),
4371 pa_yes_no(ps
->probed
),
4372 pa_hashmap_size(ps
->mappings
),
4373 pa_hashmap_size(ps
->profiles
),
4374 pa_hashmap_size(ps
->decibel_fixes
));
4376 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4377 pa_alsa_mapping_dump(m
);
4379 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4380 pa_alsa_profile_dump(p
);
4382 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4383 pa_alsa_decibel_fix_dump(db_fix
);
4386 static pa_device_port
* device_port_alsa_init(pa_hashmap
*ports
,
4388 const char* description
,
4390 pa_alsa_setting
*setting
,
4391 pa_card_profile
*cp
,
4395 pa_device_port
* p
= pa_hashmap_get(ports
, name
);
4397 pa_alsa_port_data
*data
;
4399 p
= pa_device_port_new(core
, name
, description
, sizeof(pa_alsa_port_data
));
4401 pa_hashmap_put(ports
, p
->name
, p
);
4402 p
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4404 data
= PA_DEVICE_PORT_DATA(p
);
4406 data
->setting
= setting
;
4409 p
->is_input
|= path
->direction
== PA_ALSA_DIRECTION_ANY
|| path
->direction
== PA_ALSA_DIRECTION_INPUT
;
4410 p
->is_output
|= path
->direction
== PA_ALSA_DIRECTION_ANY
|| path
->direction
== PA_ALSA_DIRECTION_OUTPUT
;
4413 pa_hashmap_put(p
->profiles
, cp
->name
, cp
);
4416 pa_hashmap_put(extra
, p
->name
, p
);
4417 pa_device_port_ref(p
);
4423 void pa_alsa_path_set_add_ports(
4424 pa_alsa_path_set
*ps
,
4425 pa_card_profile
*cp
,
4438 PA_HASHMAP_FOREACH(path
, ps
->paths
, state
) {
4439 if (!path
->settings
|| !path
->settings
->next
) {
4440 /* If there is no or just one setting we only need a
4442 pa_device_port
*port
= device_port_alsa_init(ports
, path
->name
,
4443 path
->description
, path
, path
->settings
, cp
, extra
, core
);
4444 port
->priority
= path
->priority
* 100;
4448 PA_LLIST_FOREACH(s
, path
->settings
) {
4449 pa_device_port
*port
;
4452 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4454 if (s
->description
[0])
4455 d
= pa_sprintf_malloc("%s / %s", path
->description
, s
->description
);
4457 d
= pa_xstrdup(path
->description
);
4459 port
= device_port_alsa_init(ports
, n
, d
, path
, s
, cp
, extra
, core
);
4460 port
->priority
= path
->priority
* 100 + s
->priority
;
4469 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
, pa_card
*card
) {
4475 if (ps
->paths
&& pa_hashmap_size(ps
->paths
) > 0) {
4477 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4478 pa_alsa_path_set_add_ports(ps
, NULL
, card
->ports
, *p
, card
->core
);
4481 pa_log_debug("Added %u ports", *p
? pa_hashmap_size(*p
) : 0);