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
;
78 pa_defer_event
*defer
;
83 void (*cb
)(void *userdata
);
87 static void io_cb(pa_mainloop_api
*a
, pa_io_event
*e
, int fd
, pa_io_event_flags_t events
, void *userdata
) {
89 struct pa_alsa_fdlist
*fdl
= userdata
;
92 unsigned short revents
;
96 pa_assert(fdl
->mixer
|| fdl
->hctl
);
98 pa_assert(fdl
->work_fds
);
105 memcpy(fdl
->work_fds
, fdl
->fds
, sizeof(struct pollfd
) * fdl
->num_fds
);
107 for (i
= 0; i
< fdl
->num_fds
; i
++) {
108 if (e
== fdl
->ios
[i
]) {
109 if (events
& PA_IO_EVENT_INPUT
)
110 fdl
->work_fds
[i
].revents
|= POLLIN
;
111 if (events
& PA_IO_EVENT_OUTPUT
)
112 fdl
->work_fds
[i
].revents
|= POLLOUT
;
113 if (events
& PA_IO_EVENT_ERROR
)
114 fdl
->work_fds
[i
].revents
|= POLLERR
;
115 if (events
& PA_IO_EVENT_HANGUP
)
116 fdl
->work_fds
[i
].revents
|= POLLHUP
;
121 pa_assert(i
!= fdl
->num_fds
);
124 err
= snd_hctl_poll_descriptors_revents(fdl
->hctl
, fdl
->work_fds
, fdl
->num_fds
, &revents
);
126 err
= snd_mixer_poll_descriptors_revents(fdl
->mixer
, fdl
->work_fds
, fdl
->num_fds
, &revents
);
129 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err
));
133 a
->defer_enable(fdl
->defer
, 1);
137 snd_hctl_handle_events(fdl
->hctl
);
139 snd_mixer_handle_events(fdl
->mixer
);
143 static void defer_cb(pa_mainloop_api
*a
, pa_defer_event
*e
, void *userdata
) {
144 struct pa_alsa_fdlist
*fdl
= userdata
;
151 pa_assert(fdl
->mixer
|| fdl
->hctl
);
153 a
->defer_enable(fdl
->defer
, 0);
156 n
= snd_hctl_poll_descriptors_count(fdl
->hctl
);
158 n
= snd_mixer_poll_descriptors_count(fdl
->mixer
);
161 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n
));
164 num_fds
= (unsigned) n
;
166 if (num_fds
!= fdl
->num_fds
) {
170 pa_xfree(fdl
->work_fds
);
171 fdl
->fds
= pa_xnew0(struct pollfd
, num_fds
);
172 fdl
->work_fds
= pa_xnew(struct pollfd
, num_fds
);
175 memset(fdl
->work_fds
, 0, sizeof(struct pollfd
) * num_fds
);
178 err
= snd_hctl_poll_descriptors(fdl
->hctl
, fdl
->work_fds
, num_fds
);
180 err
= snd_mixer_poll_descriptors(fdl
->mixer
, fdl
->work_fds
, num_fds
);
183 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err
));
189 if (memcmp(fdl
->fds
, fdl
->work_fds
, sizeof(struct pollfd
) * num_fds
) == 0)
193 for (i
= 0; i
< fdl
->num_fds
; i
++)
194 a
->io_free(fdl
->ios
[i
]);
196 if (num_fds
!= fdl
->num_fds
) {
203 fdl
->ios
= pa_xnew(pa_io_event
*, num_fds
);
206 temp
= fdl
->work_fds
;
207 fdl
->work_fds
= fdl
->fds
;
210 fdl
->num_fds
= num_fds
;
212 for (i
= 0;i
< num_fds
;i
++)
213 fdl
->ios
[i
] = a
->io_new(a
, fdl
->fds
[i
].fd
,
214 ((fdl
->fds
[i
].events
& POLLIN
) ? PA_IO_EVENT_INPUT
: 0) |
215 ((fdl
->fds
[i
].events
& POLLOUT
) ? PA_IO_EVENT_OUTPUT
: 0),
219 struct pa_alsa_fdlist
*pa_alsa_fdlist_new(void) {
220 struct pa_alsa_fdlist
*fdl
;
222 fdl
= pa_xnew0(struct pa_alsa_fdlist
, 1);
227 void pa_alsa_fdlist_free(struct pa_alsa_fdlist
*fdl
) {
232 fdl
->m
->defer_free(fdl
->defer
);
238 for (i
= 0; i
< fdl
->num_fds
; i
++)
239 fdl
->m
->io_free(fdl
->ios
[i
]);
246 pa_xfree(fdl
->work_fds
);
251 /* We can listen to either a snd_hctl_t or a snd_mixer_t, but not both */
252 int pa_alsa_fdlist_set_handle(struct pa_alsa_fdlist
*fdl
, snd_mixer_t
*mixer_handle
, snd_hctl_t
*hctl_handle
, pa_mainloop_api
*m
) {
254 pa_assert(hctl_handle
|| mixer_handle
);
255 pa_assert(!(hctl_handle
&& mixer_handle
));
259 fdl
->hctl
= hctl_handle
;
260 fdl
->mixer
= mixer_handle
;
262 fdl
->defer
= m
->defer_new(m
, defer_cb
, fdl
);
267 struct pa_alsa_mixer_pdata
{
269 pa_rtpoll_item
*poll_item
;
274 struct pa_alsa_mixer_pdata
*pa_alsa_mixer_pdata_new(void) {
275 struct pa_alsa_mixer_pdata
*pd
;
277 pd
= pa_xnew0(struct pa_alsa_mixer_pdata
, 1);
282 void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata
*pd
) {
286 pa_rtpoll_item_free(pd
->poll_item
);
292 static int rtpoll_work_cb(pa_rtpoll_item
*i
) {
293 struct pa_alsa_mixer_pdata
*pd
;
296 unsigned short revents
= 0;
299 pd
= pa_rtpoll_item_get_userdata(i
);
301 pa_assert_fp(i
== pd
->poll_item
);
303 p
= pa_rtpoll_item_get_pollfd(i
, &n_fds
);
305 if ((err
= snd_mixer_poll_descriptors_revents(pd
->mixer
, p
, n_fds
, &revents
)) < 0) {
306 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err
));
312 if (revents
& (POLLNVAL
| POLLERR
)) {
313 pa_log_debug("Device disconnected, stopping poll on mixer");
315 } else if (revents
& POLLERR
) {
316 /* This shouldn't happen. */
317 pa_log_error("Got a POLLERR (revents = %04x), stopping poll on mixer", revents
);
321 err
= snd_mixer_handle_events(pd
->mixer
);
323 if (PA_LIKELY(err
>= 0)) {
324 pa_rtpoll_item_free(i
);
325 pa_alsa_set_mixer_rtpoll(pd
, pd
->mixer
, pd
->rtpoll
);
327 pa_log_error("Error handling mixer event: %s", pa_alsa_strerror(err
));
336 pa_rtpoll_item_free(i
);
338 pd
->poll_item
= NULL
;
345 int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata
*pd
, snd_mixer_t
*mixer
, pa_rtpoll
*rtp
) {
354 if ((n
= snd_mixer_poll_descriptors_count(mixer
)) < 0) {
355 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n
));
359 i
= pa_rtpoll_item_new(rtp
, PA_RTPOLL_LATE
, (unsigned) n
);
361 p
= pa_rtpoll_item_get_pollfd(i
, NULL
);
363 memset(p
, 0, sizeof(struct pollfd
) * n
);
365 if ((err
= snd_mixer_poll_descriptors(mixer
, p
, (unsigned) n
)) < 0) {
366 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err
));
367 pa_rtpoll_item_free(i
);
375 pa_rtpoll_item_set_userdata(i
, pd
);
376 pa_rtpoll_item_set_work_callback(i
, rtpoll_work_cb
);
383 static const snd_mixer_selem_channel_id_t alsa_channel_ids
[PA_CHANNEL_POSITION_MAX
] = {
384 [PA_CHANNEL_POSITION_MONO
] = SND_MIXER_SCHN_MONO
, /* The ALSA name is just an alias! */
386 [PA_CHANNEL_POSITION_FRONT_CENTER
] = SND_MIXER_SCHN_FRONT_CENTER
,
387 [PA_CHANNEL_POSITION_FRONT_LEFT
] = SND_MIXER_SCHN_FRONT_LEFT
,
388 [PA_CHANNEL_POSITION_FRONT_RIGHT
] = SND_MIXER_SCHN_FRONT_RIGHT
,
390 [PA_CHANNEL_POSITION_REAR_CENTER
] = SND_MIXER_SCHN_REAR_CENTER
,
391 [PA_CHANNEL_POSITION_REAR_LEFT
] = SND_MIXER_SCHN_REAR_LEFT
,
392 [PA_CHANNEL_POSITION_REAR_RIGHT
] = SND_MIXER_SCHN_REAR_RIGHT
,
394 [PA_CHANNEL_POSITION_LFE
] = SND_MIXER_SCHN_WOOFER
,
396 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
397 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
399 [PA_CHANNEL_POSITION_SIDE_LEFT
] = SND_MIXER_SCHN_SIDE_LEFT
,
400 [PA_CHANNEL_POSITION_SIDE_RIGHT
] = SND_MIXER_SCHN_SIDE_RIGHT
,
402 [PA_CHANNEL_POSITION_AUX0
] = SND_MIXER_SCHN_UNKNOWN
,
403 [PA_CHANNEL_POSITION_AUX1
] = SND_MIXER_SCHN_UNKNOWN
,
404 [PA_CHANNEL_POSITION_AUX2
] = SND_MIXER_SCHN_UNKNOWN
,
405 [PA_CHANNEL_POSITION_AUX3
] = SND_MIXER_SCHN_UNKNOWN
,
406 [PA_CHANNEL_POSITION_AUX4
] = SND_MIXER_SCHN_UNKNOWN
,
407 [PA_CHANNEL_POSITION_AUX5
] = SND_MIXER_SCHN_UNKNOWN
,
408 [PA_CHANNEL_POSITION_AUX6
] = SND_MIXER_SCHN_UNKNOWN
,
409 [PA_CHANNEL_POSITION_AUX7
] = SND_MIXER_SCHN_UNKNOWN
,
410 [PA_CHANNEL_POSITION_AUX8
] = SND_MIXER_SCHN_UNKNOWN
,
411 [PA_CHANNEL_POSITION_AUX9
] = SND_MIXER_SCHN_UNKNOWN
,
412 [PA_CHANNEL_POSITION_AUX10
] = SND_MIXER_SCHN_UNKNOWN
,
413 [PA_CHANNEL_POSITION_AUX11
] = SND_MIXER_SCHN_UNKNOWN
,
414 [PA_CHANNEL_POSITION_AUX12
] = SND_MIXER_SCHN_UNKNOWN
,
415 [PA_CHANNEL_POSITION_AUX13
] = SND_MIXER_SCHN_UNKNOWN
,
416 [PA_CHANNEL_POSITION_AUX14
] = SND_MIXER_SCHN_UNKNOWN
,
417 [PA_CHANNEL_POSITION_AUX15
] = SND_MIXER_SCHN_UNKNOWN
,
418 [PA_CHANNEL_POSITION_AUX16
] = SND_MIXER_SCHN_UNKNOWN
,
419 [PA_CHANNEL_POSITION_AUX17
] = SND_MIXER_SCHN_UNKNOWN
,
420 [PA_CHANNEL_POSITION_AUX18
] = SND_MIXER_SCHN_UNKNOWN
,
421 [PA_CHANNEL_POSITION_AUX19
] = SND_MIXER_SCHN_UNKNOWN
,
422 [PA_CHANNEL_POSITION_AUX20
] = SND_MIXER_SCHN_UNKNOWN
,
423 [PA_CHANNEL_POSITION_AUX21
] = SND_MIXER_SCHN_UNKNOWN
,
424 [PA_CHANNEL_POSITION_AUX22
] = SND_MIXER_SCHN_UNKNOWN
,
425 [PA_CHANNEL_POSITION_AUX23
] = SND_MIXER_SCHN_UNKNOWN
,
426 [PA_CHANNEL_POSITION_AUX24
] = SND_MIXER_SCHN_UNKNOWN
,
427 [PA_CHANNEL_POSITION_AUX25
] = SND_MIXER_SCHN_UNKNOWN
,
428 [PA_CHANNEL_POSITION_AUX26
] = SND_MIXER_SCHN_UNKNOWN
,
429 [PA_CHANNEL_POSITION_AUX27
] = SND_MIXER_SCHN_UNKNOWN
,
430 [PA_CHANNEL_POSITION_AUX28
] = SND_MIXER_SCHN_UNKNOWN
,
431 [PA_CHANNEL_POSITION_AUX29
] = SND_MIXER_SCHN_UNKNOWN
,
432 [PA_CHANNEL_POSITION_AUX30
] = SND_MIXER_SCHN_UNKNOWN
,
433 [PA_CHANNEL_POSITION_AUX31
] = SND_MIXER_SCHN_UNKNOWN
,
435 [PA_CHANNEL_POSITION_TOP_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
437 [PA_CHANNEL_POSITION_TOP_FRONT_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
438 [PA_CHANNEL_POSITION_TOP_FRONT_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
439 [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
,
441 [PA_CHANNEL_POSITION_TOP_REAR_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
442 [PA_CHANNEL_POSITION_TOP_REAR_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
443 [PA_CHANNEL_POSITION_TOP_REAR_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
446 static void setting_free(pa_alsa_setting
*s
) {
450 pa_idxset_free(s
->options
, NULL
, NULL
);
453 pa_xfree(s
->description
);
457 static void option_free(pa_alsa_option
*o
) {
460 pa_xfree(o
->alsa_name
);
462 pa_xfree(o
->description
);
466 static void decibel_fix_free(pa_alsa_decibel_fix
*db_fix
) {
469 pa_xfree(db_fix
->name
);
470 pa_xfree(db_fix
->db_values
);
475 static void jack_free(pa_alsa_jack
*j
) {
478 pa_xfree(j
->alsa_name
);
483 static void element_free(pa_alsa_element
*e
) {
487 while ((o
= e
->options
)) {
488 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
493 decibel_fix_free(e
->db_fix
);
495 pa_xfree(e
->alsa_name
);
499 void pa_alsa_path_free(pa_alsa_path
*p
) {
506 while ((j
= p
->jacks
)) {
507 PA_LLIST_REMOVE(pa_alsa_jack
, p
->jacks
, j
);
511 while ((e
= p
->elements
)) {
512 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
516 while ((s
= p
->settings
)) {
517 PA_LLIST_REMOVE(pa_alsa_setting
, p
->settings
, s
);
522 pa_xfree(p
->description
);
526 void pa_alsa_path_set_free(pa_alsa_path_set
*ps
) {
530 pa_hashmap_free(ps
->paths
, NULL
, NULL
);
535 static long to_alsa_dB(pa_volume_t v
) {
536 return (long) (pa_sw_volume_to_dB(v
) * 100.0);
539 static pa_volume_t
from_alsa_dB(long v
) {
540 return pa_sw_volume_from_dB((double) v
/ 100.0);
543 static long to_alsa_volume(pa_volume_t v
, long min
, long max
) {
546 w
= (long) round(((double) v
* (double) (max
- min
)) / PA_VOLUME_NORM
) + min
;
547 return PA_CLAMP_UNLIKELY(w
, min
, max
);
550 static pa_volume_t
from_alsa_volume(long v
, long min
, long max
) {
551 return (pa_volume_t
) round(((double) (v
- min
) * PA_VOLUME_NORM
) / (double) (max
- min
));
554 #define SELEM_INIT(sid, name) \
556 snd_mixer_selem_id_alloca(&(sid)); \
557 snd_mixer_selem_id_set_name((sid), (name)); \
558 snd_mixer_selem_id_set_index((sid), 0); \
561 static int element_get_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
562 snd_mixer_selem_id_t
*sid
;
563 snd_mixer_elem_t
*me
;
564 snd_mixer_selem_channel_id_t c
;
565 pa_channel_position_mask_t mask
= 0;
573 SELEM_INIT(sid
, e
->alsa_name
);
574 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
575 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
579 pa_cvolume_mute(v
, cm
->channels
);
581 /* We take the highest volume of all channels that match */
583 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
590 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
591 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
593 if ((r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
)) >= 0) {
594 /* If the channel volume is outside the limits set
595 * by the dB fix, we clamp the hw volume to be
596 * within the limits. */
597 if (value
< e
->db_fix
->min_step
) {
598 value
= e
->db_fix
->min_step
;
599 snd_mixer_selem_set_playback_volume(me
, c
, value
);
600 pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
601 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
602 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
603 } else if (value
> e
->db_fix
->max_step
) {
604 value
= e
->db_fix
->max_step
;
605 snd_mixer_selem_set_playback_volume(me
, c
, value
);
606 pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
607 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
608 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
611 /* Volume step -> dB value conversion. */
612 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
615 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
619 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
621 if ((r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
)) >= 0) {
622 /* If the channel volume is outside the limits set
623 * by the dB fix, we clamp the hw volume to be
624 * within the limits. */
625 if (value
< e
->db_fix
->min_step
) {
626 value
= e
->db_fix
->min_step
;
627 snd_mixer_selem_set_capture_volume(me
, c
, value
);
628 pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
629 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
630 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
631 } else if (value
> e
->db_fix
->max_step
) {
632 value
= e
->db_fix
->max_step
;
633 snd_mixer_selem_set_capture_volume(me
, c
, value
);
634 pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
635 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
636 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
639 /* Volume step -> dB value conversion. */
640 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
643 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
651 #ifdef HAVE_VALGRIND_MEMCHECK_H
652 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
655 f
= from_alsa_dB(value
);
660 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
661 if (snd_mixer_selem_has_playback_channel(me
, c
))
662 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
666 if (snd_mixer_selem_has_capture_channel(me
, c
))
667 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
675 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
678 for (k
= 0; k
< cm
->channels
; k
++)
679 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
680 if (v
->values
[k
] < f
)
683 mask
|= e
->masks
[c
][e
->n_channels
-1];
686 for (k
= 0; k
< cm
->channels
; k
++)
687 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
688 v
->values
[k
] = PA_VOLUME_NORM
;
693 int pa_alsa_path_get_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
704 pa_cvolume_reset(v
, cm
->channels
);
706 PA_LLIST_FOREACH(e
, p
->elements
) {
709 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
712 pa_assert(!p
->has_dB
|| e
->has_dB
);
714 if (element_get_volume(e
, m
, cm
, &ev
) < 0)
717 /* If we have no dB information all we can do is take the first element and leave */
723 pa_sw_cvolume_multiply(v
, v
, &ev
);
729 static int element_get_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t
*b
) {
730 snd_mixer_selem_id_t
*sid
;
731 snd_mixer_elem_t
*me
;
732 snd_mixer_selem_channel_id_t c
;
738 SELEM_INIT(sid
, e
->alsa_name
);
739 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
740 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
744 /* We return muted if at least one channel is muted */
746 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
750 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
751 if (snd_mixer_selem_has_playback_channel(me
, c
))
752 r
= snd_mixer_selem_get_playback_switch(me
, c
, &value
);
756 if (snd_mixer_selem_has_capture_channel(me
, c
))
757 r
= snd_mixer_selem_get_capture_switch(me
, c
, &value
);
775 int pa_alsa_path_get_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t
*muted
) {
785 PA_LLIST_FOREACH(e
, p
->elements
) {
788 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
791 if (element_get_switch(e
, m
, &b
) < 0)
804 /* Finds the closest item in db_fix->db_values and returns the corresponding
805 * step. *db_value is replaced with the value from the db_values table.
806 * Rounding is done based on the rounding parameter: -1 means rounding down and
807 * +1 means rounding up. */
808 static long decibel_fix_get_step(pa_alsa_decibel_fix
*db_fix
, long *db_value
, int rounding
) {
814 pa_assert(rounding
!= 0);
816 max_i
= db_fix
->max_step
- db_fix
->min_step
;
819 for (i
= 0; i
< max_i
; i
++) {
820 if (db_fix
->db_values
[i
] >= *db_value
)
824 for (i
= 0; i
< max_i
; i
++) {
825 if (db_fix
->db_values
[i
+ 1] > *db_value
)
830 *db_value
= db_fix
->db_values
[i
];
832 return i
+ db_fix
->min_step
;
835 /* Alsa lib documentation says for snd_mixer_selem_set_playback_dB() direction argument,
836 * that "-1 = accurate or first below, 0 = accurate, 1 = accurate or first above".
837 * But even with accurate nearest dB volume step is not selected, so that is why we need
838 * this function. Returns 0 and nearest selectable volume in *value_dB on success or
839 * negative error code if fails. */
840 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
) {
850 if (d
== PA_ALSA_DIRECTION_OUTPUT
) {
851 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, *value_dB
, +1, &alsa_val
)) >= 0)
852 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value_high
);
857 if (value_high
== *value_dB
)
860 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, *value_dB
, -1, &alsa_val
)) >= 0)
861 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value_low
);
863 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, *value_dB
, +1, &alsa_val
)) >= 0)
864 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value_high
);
869 if (value_high
== *value_dB
)
872 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, *value_dB
, -1, &alsa_val
)) >= 0)
873 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value_low
);
879 if (labs(value_high
- *value_dB
) < labs(value_low
- *value_dB
))
880 *value_dB
= value_high
;
882 *value_dB
= value_low
;
887 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
) {
889 snd_mixer_selem_id_t
*sid
;
891 snd_mixer_elem_t
*me
;
892 snd_mixer_selem_channel_id_t c
;
893 pa_channel_position_mask_t mask
= 0;
900 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
902 SELEM_INIT(sid
, e
->alsa_name
);
903 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
904 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
908 pa_cvolume_mute(&rv
, cm
->channels
);
910 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
912 pa_volume_t f
= PA_VOLUME_MUTED
;
913 pa_bool_t found
= FALSE
;
915 for (k
= 0; k
< cm
->channels
; k
++)
916 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
])) {
918 if (v
->values
[k
] > f
)
923 /* Hmm, so this channel does not exist in the volume
924 * struct, so let's bind it to the overall max of the
926 f
= pa_cvolume_max(v
);
930 long value
= to_alsa_dB(f
);
933 if (e
->volume_limit
>= 0 && value
> (e
->max_dB
* 100))
934 value
= e
->max_dB
* 100;
936 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
937 /* If we call set_playback_volume() without checking first
938 * if the channel is available, ALSA behaves very
939 * strangely and doesn't fail the call */
940 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
944 r
= snd_mixer_selem_set_playback_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
946 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
952 if (deferred_volume
) {
953 if ((r
= element_get_nearest_alsa_dB(me
, c
, PA_ALSA_DIRECTION_OUTPUT
, &value
)) >= 0)
954 r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, 0);
956 if ((r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, rounding
)) >= 0)
957 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
961 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
962 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value
);
968 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
972 r
= snd_mixer_selem_set_capture_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
974 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
980 if (deferred_volume
) {
981 if ((r
= element_get_nearest_alsa_dB(me
, c
, PA_ALSA_DIRECTION_INPUT
, &value
)) >= 0)
982 r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, 0);
984 if ((r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, rounding
)) >= 0)
985 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
989 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
990 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value
);
1000 #ifdef HAVE_VALGRIND_MEMCHECK_H
1001 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
1004 f
= from_alsa_dB(value
);
1009 value
= to_alsa_volume(f
, e
->min_volume
, e
->max_volume
);
1011 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1012 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
1013 if ((r
= snd_mixer_selem_set_playback_volume(me
, c
, value
)) >= 0)
1014 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
1018 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
1019 if ((r
= snd_mixer_selem_set_capture_volume(me
, c
, value
)) >= 0)
1020 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
1028 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
1031 for (k
= 0; k
< cm
->channels
; k
++)
1032 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
1033 if (rv
.values
[k
] < f
)
1036 mask
|= e
->masks
[c
][e
->n_channels
-1];
1039 for (k
= 0; k
< cm
->channels
; k
++)
1040 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
1041 rv
.values
[k
] = PA_VOLUME_NORM
;
1047 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
) {
1056 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
1061 rv
= *v
; /* Remaining adjustment */
1062 pa_cvolume_reset(v
, cm
->channels
); /* Adjustment done */
1064 PA_LLIST_FOREACH(e
, p
->elements
) {
1067 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
1070 pa_assert(!p
->has_dB
|| e
->has_dB
);
1073 if (element_set_volume(e
, m
, cm
, &ev
, deferred_volume
, write_to_hw
) < 0)
1081 pa_sw_cvolume_multiply(v
, v
, &ev
);
1082 pa_sw_cvolume_divide(&rv
, &rv
, &ev
);
1088 static int element_set_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t b
) {
1089 snd_mixer_elem_t
*me
;
1090 snd_mixer_selem_id_t
*sid
;
1096 SELEM_INIT(sid
, e
->alsa_name
);
1097 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1098 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1102 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1103 r
= snd_mixer_selem_set_playback_switch_all(me
, b
);
1105 r
= snd_mixer_selem_set_capture_switch_all(me
, b
);
1108 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1113 int pa_alsa_path_set_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t muted
) {
1122 PA_LLIST_FOREACH(e
, p
->elements
) {
1124 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
1127 if (element_set_switch(e
, m
, !muted
) < 0)
1134 /* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
1135 * function sets all channels of the volume element to e->min_volume, 0 dB or
1136 * e->constant_volume. */
1137 static int element_set_constant_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1138 snd_mixer_elem_t
*me
= NULL
;
1139 snd_mixer_selem_id_t
*sid
= NULL
;
1142 pa_bool_t volume_set
= FALSE
;
1147 SELEM_INIT(sid
, e
->alsa_name
);
1148 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1149 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1153 switch (e
->volume_use
) {
1154 case PA_ALSA_VOLUME_OFF
:
1155 volume
= e
->min_volume
;
1159 case PA_ALSA_VOLUME_ZERO
:
1163 volume
= decibel_fix_get_step(e
->db_fix
, &dB
, (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
? +1 : -1));
1168 case PA_ALSA_VOLUME_CONSTANT
:
1169 volume
= e
->constant_volume
;
1174 pa_assert_not_reached();
1178 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1179 r
= snd_mixer_selem_set_playback_volume_all(me
, volume
);
1181 r
= snd_mixer_selem_set_capture_volume_all(me
, volume
);
1183 pa_assert(e
->volume_use
== PA_ALSA_VOLUME_ZERO
);
1184 pa_assert(!e
->db_fix
);
1186 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1187 r
= snd_mixer_selem_set_playback_dB_all(me
, 0, +1);
1189 r
= snd_mixer_selem_set_capture_dB_all(me
, 0, -1);
1193 pa_log_warn("Failed to set volume of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1198 int pa_alsa_path_select(pa_alsa_path
*p
, snd_mixer_t
*m
) {
1205 pa_log_debug("Activating path %s", p
->name
);
1206 pa_alsa_path_dump(p
);
1208 PA_LLIST_FOREACH(e
, p
->elements
) {
1210 switch (e
->switch_use
) {
1211 case PA_ALSA_SWITCH_OFF
:
1212 r
= element_set_switch(e
, m
, FALSE
);
1215 case PA_ALSA_SWITCH_ON
:
1216 r
= element_set_switch(e
, m
, TRUE
);
1219 case PA_ALSA_SWITCH_MUTE
:
1220 case PA_ALSA_SWITCH_IGNORE
:
1221 case PA_ALSA_SWITCH_SELECT
:
1229 switch (e
->volume_use
) {
1230 case PA_ALSA_VOLUME_OFF
:
1231 case PA_ALSA_VOLUME_ZERO
:
1232 case PA_ALSA_VOLUME_CONSTANT
:
1233 r
= element_set_constant_volume(e
, m
);
1236 case PA_ALSA_VOLUME_MERGE
:
1237 case PA_ALSA_VOLUME_IGNORE
:
1249 static int check_required(pa_alsa_element
*e
, snd_mixer_elem_t
*me
) {
1250 pa_bool_t has_switch
;
1251 pa_bool_t has_enumeration
;
1252 pa_bool_t has_volume
;
1257 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1259 snd_mixer_selem_has_playback_switch(me
) ||
1260 (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
));
1263 snd_mixer_selem_has_capture_switch(me
) ||
1264 (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
));
1267 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1269 snd_mixer_selem_has_playback_volume(me
) ||
1270 (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
));
1273 snd_mixer_selem_has_capture_volume(me
) ||
1274 (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
));
1277 has_enumeration
= snd_mixer_selem_is_enumerated(me
);
1279 if ((e
->required
== PA_ALSA_REQUIRED_SWITCH
&& !has_switch
) ||
1280 (e
->required
== PA_ALSA_REQUIRED_VOLUME
&& !has_volume
) ||
1281 (e
->required
== PA_ALSA_REQUIRED_ENUMERATION
&& !has_enumeration
))
1284 if (e
->required
== PA_ALSA_REQUIRED_ANY
&& !(has_switch
|| has_volume
|| has_enumeration
))
1287 if ((e
->required_absent
== PA_ALSA_REQUIRED_SWITCH
&& has_switch
) ||
1288 (e
->required_absent
== PA_ALSA_REQUIRED_VOLUME
&& has_volume
) ||
1289 (e
->required_absent
== PA_ALSA_REQUIRED_ENUMERATION
&& has_enumeration
))
1292 if (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& (has_switch
|| has_volume
|| has_enumeration
))
1295 if (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) {
1296 switch (e
->required_any
) {
1297 case PA_ALSA_REQUIRED_VOLUME
:
1298 e
->path
->req_any_present
|= (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
);
1300 case PA_ALSA_REQUIRED_SWITCH
:
1301 e
->path
->req_any_present
|= (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
);
1303 case PA_ALSA_REQUIRED_ENUMERATION
:
1304 e
->path
->req_any_present
|= (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1306 case PA_ALSA_REQUIRED_ANY
:
1307 e
->path
->req_any_present
|=
1308 (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) ||
1309 (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) ||
1310 (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1313 pa_assert_not_reached();
1317 if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1319 PA_LLIST_FOREACH(o
, e
->options
) {
1320 e
->path
->req_any_present
|= (o
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) &&
1322 if (o
->required
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
< 0)
1324 if (o
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
>= 0)
1332 static int element_probe(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1333 snd_mixer_selem_id_t
*sid
;
1334 snd_mixer_elem_t
*me
;
1340 SELEM_INIT(sid
, e
->alsa_name
);
1342 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1344 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1347 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1348 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1349 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1354 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1355 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1357 if (!snd_mixer_selem_has_playback_switch(me
)) {
1358 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1359 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1361 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1366 if (!snd_mixer_selem_has_capture_switch(me
)) {
1367 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1368 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1370 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1374 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1375 e
->direction_try_other
= FALSE
;
1378 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1380 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1382 if (!snd_mixer_selem_has_playback_volume(me
)) {
1383 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1384 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1386 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1391 if (!snd_mixer_selem_has_capture_volume(me
)) {
1392 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1393 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1395 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1399 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1400 long min_dB
= 0, max_dB
= 0;
1403 e
->direction_try_other
= FALSE
;
1405 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1406 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1408 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1411 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1415 if (e
->min_volume
>= e
->max_volume
) {
1416 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
);
1417 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1419 } else if (e
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&&
1420 (e
->min_volume
> e
->constant_volume
|| e
->max_volume
< e
->constant_volume
)) {
1421 pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
1422 e
->constant_volume
, e
->alsa_name
, e
->min_volume
, e
->max_volume
);
1423 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1427 pa_channel_position_t p
;
1430 ((e
->min_volume
> e
->db_fix
->min_step
) ||
1431 (e
->max_volume
< e
->db_fix
->max_step
))) {
1432 pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1433 "real hardware range (%li-%li). Disabling the decibel fix.", e
->alsa_name
,
1434 e
->db_fix
->min_step
, e
->db_fix
->max_step
,
1435 e
->min_volume
, e
->max_volume
);
1437 decibel_fix_free(e
->db_fix
);
1443 e
->min_volume
= e
->db_fix
->min_step
;
1444 e
->max_volume
= e
->db_fix
->max_step
;
1445 min_dB
= e
->db_fix
->db_values
[0];
1446 max_dB
= e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
];
1447 } else if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1448 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1450 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1452 /* Check that the kernel driver returns consistent limits with
1453 * both _get_*_dB_range() and _ask_*_vol_dB(). */
1454 if (e
->has_dB
&& !e
->db_fix
) {
1455 long min_dB_checked
= 0;
1456 long max_dB_checked
= 0;
1458 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1459 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1461 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1464 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->min_volume
);
1468 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1469 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1471 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1474 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->max_volume
);
1478 if (min_dB
!= min_dB_checked
|| max_dB
!= max_dB_checked
) {
1479 pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
1480 "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
1481 "%0.2f dB at level %li.",
1483 min_dB
/ 100.0, max_dB
/ 100.0,
1484 min_dB_checked
/ 100.0, e
->min_volume
, max_dB_checked
/ 100.0, e
->max_volume
);
1490 #ifdef HAVE_VALGRIND_MEMCHECK_H
1491 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1492 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1495 e
->min_dB
= ((double) min_dB
) / 100.0;
1496 e
->max_dB
= ((double) max_dB
) / 100.0;
1498 if (min_dB
>= max_dB
) {
1499 pa_assert(!e
->db_fix
);
1500 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
);
1505 if (e
->volume_limit
>= 0) {
1506 if (e
->volume_limit
<= e
->min_volume
|| e
->volume_limit
> e
->max_volume
)
1507 pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1508 "%li-%li. The volume limit is ignored.",
1509 e
->alsa_name
, e
->path
->name
, e
->volume_limit
, e
->min_volume
+ 1, e
->max_volume
);
1512 e
->max_volume
= e
->volume_limit
;
1516 e
->db_fix
->max_step
= e
->max_volume
;
1517 e
->max_dB
= ((double) e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
]) / 100.0;
1520 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1521 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB
);
1523 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB
);
1526 pa_log_warn("Failed to get dB value of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1529 e
->max_dB
= ((double) max_dB
) / 100.0;
1535 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1536 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1538 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1543 if (!e
->override_map
) {
1544 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1545 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1548 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1551 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1554 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1557 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1559 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1562 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1563 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1565 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1568 if (e
->n_channels
<= 0) {
1569 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1573 if (e
->n_channels
> 2) {
1574 /* FIXME: In some places code like this is used:
1576 * e->masks[alsa_channel_ids[p]][e->n_channels-1]
1578 * The definition of e->masks is
1580 * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][2];
1582 * Since the array size is fixed at 2, we obviously
1583 * don't support elements with more than two
1585 pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e
->alsa_name
, e
->n_channels
);
1589 if (!e
->override_map
) {
1590 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1591 pa_bool_t has_channel
;
1593 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1596 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1597 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1599 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1601 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1606 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1607 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1610 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1618 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1621 PA_LLIST_FOREACH(o
, e
->options
)
1622 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1623 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1627 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1628 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1632 PA_LLIST_FOREACH(o
, e
->options
) {
1635 for (i
= 0; i
< n
; i
++) {
1638 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1641 if (!pa_streq(buf
, o
->alsa_name
))
1649 if (check_required(e
, me
) < 0)
1655 static int jack_probe(pa_alsa_jack
*j
, snd_hctl_t
*h
) {
1660 j
->has_control
= pa_alsa_find_jack(h
, j
->alsa_name
) != NULL
;
1662 if (j
->has_control
) {
1663 if (j
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
)
1665 if (j
->required_any
!= PA_ALSA_REQUIRED_IGNORE
)
1666 j
->path
->req_any_present
= TRUE
;
1668 if (j
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1675 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, pa_bool_t prefixed
) {
1682 if (!pa_startswith(section
, "Element "))
1688 /* This is not an element section, but an enum section? */
1689 if (strchr(section
, ':'))
1692 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1693 return p
->last_element
;
1695 PA_LLIST_FOREACH(e
, p
->elements
)
1696 if (pa_streq(e
->alsa_name
, section
))
1699 e
= pa_xnew0(pa_alsa_element
, 1);
1701 e
->alsa_name
= pa_xstrdup(section
);
1702 e
->direction
= p
->direction
;
1703 e
->volume_limit
= -1;
1705 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1708 p
->last_element
= e
;
1712 static pa_alsa_jack
* jack_get(pa_alsa_path
*p
, const char *section
) {
1715 if (!pa_startswith(section
, "Jack "))
1719 if (p
->last_jack
&& pa_streq(p
->last_jack
->name
, section
))
1720 return p
->last_jack
;
1722 PA_LLIST_FOREACH(j
, p
->jacks
)
1723 if (pa_streq(j
->name
, section
))
1726 j
= pa_xnew0(pa_alsa_jack
, 1);
1727 j
->state_unplugged
= PA_PORT_AVAILABLE_NO
;
1728 j
->state_plugged
= PA_PORT_AVAILABLE_YES
;
1730 j
->name
= pa_xstrdup(section
);
1731 j
->alsa_name
= pa_sprintf_malloc("%s Jack", section
);
1732 PA_LLIST_INSERT_AFTER(pa_alsa_jack
, p
->jacks
, p
->last_jack
, j
);
1740 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1746 if (!pa_startswith(section
, "Option "))
1751 /* This is not an enum section, but an element section? */
1752 if (!(on
= strchr(section
, ':')))
1755 en
= pa_xstrndup(section
, on
- section
);
1758 if (p
->last_option
&&
1759 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1760 pa_streq(p
->last_option
->alsa_name
, on
)) {
1762 return p
->last_option
;
1765 pa_assert_se(e
= element_get(p
, en
, FALSE
));
1768 PA_LLIST_FOREACH(o
, e
->options
)
1769 if (pa_streq(o
->alsa_name
, on
))
1772 o
= pa_xnew0(pa_alsa_option
, 1);
1774 o
->alsa_name
= pa_xstrdup(on
);
1777 if (p
->last_option
&& p
->last_option
->element
== e
)
1778 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1780 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1787 static int element_parse_switch(pa_config_parser_state
*state
) {
1793 p
= state
->userdata
;
1795 if (!(e
= element_get(p
, state
->section
, TRUE
))) {
1796 pa_log("[%s:%u] Switch makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1800 if (pa_streq(state
->rvalue
, "ignore"))
1801 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1802 else if (pa_streq(state
->rvalue
, "mute"))
1803 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1804 else if (pa_streq(state
->rvalue
, "off"))
1805 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1806 else if (pa_streq(state
->rvalue
, "on"))
1807 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1808 else if (pa_streq(state
->rvalue
, "select"))
1809 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1811 pa_log("[%s:%u] Switch invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1818 static int element_parse_volume(pa_config_parser_state
*state
) {
1824 p
= state
->userdata
;
1826 if (!(e
= element_get(p
, state
->section
, TRUE
))) {
1827 pa_log("[%s:%u] Volume makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1831 if (pa_streq(state
->rvalue
, "ignore"))
1832 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1833 else if (pa_streq(state
->rvalue
, "merge"))
1834 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1835 else if (pa_streq(state
->rvalue
, "off"))
1836 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1837 else if (pa_streq(state
->rvalue
, "zero"))
1838 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1842 if (pa_atou(state
->rvalue
, &constant
) >= 0) {
1843 e
->volume_use
= PA_ALSA_VOLUME_CONSTANT
;
1844 e
->constant_volume
= constant
;
1846 pa_log("[%s:%u] Volume invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1854 static int element_parse_enumeration(pa_config_parser_state
*state
) {
1860 p
= state
->userdata
;
1862 if (!(e
= element_get(p
, state
->section
, TRUE
))) {
1863 pa_log("[%s:%u] Enumeration makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1867 if (pa_streq(state
->rvalue
, "ignore"))
1868 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1869 else if (pa_streq(state
->rvalue
, "select"))
1870 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1872 pa_log("[%s:%u] Enumeration invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1879 static int option_parse_priority(pa_config_parser_state
*state
) {
1886 p
= state
->userdata
;
1888 if (!(o
= option_get(p
, state
->section
))) {
1889 pa_log("[%s:%u] Priority makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1893 if (pa_atou(state
->rvalue
, &prio
) < 0) {
1894 pa_log("[%s:%u] Priority invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1902 static int option_parse_name(pa_config_parser_state
*state
) {
1908 p
= state
->userdata
;
1910 if (!(o
= option_get(p
, state
->section
))) {
1911 pa_log("[%s:%u] Name makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1916 o
->name
= pa_xstrdup(state
->rvalue
);
1921 static int element_parse_required(pa_config_parser_state
*state
) {
1926 pa_alsa_required_t req
;
1930 p
= state
->userdata
;
1932 e
= element_get(p
, state
->section
, TRUE
);
1933 o
= option_get(p
, state
->section
);
1934 j
= jack_get(p
, state
->section
);
1935 if (!e
&& !o
&& !j
) {
1936 pa_log("[%s:%u] Required makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1940 if (pa_streq(state
->rvalue
, "ignore"))
1941 req
= PA_ALSA_REQUIRED_IGNORE
;
1942 else if (pa_streq(state
->rvalue
, "switch") && e
)
1943 req
= PA_ALSA_REQUIRED_SWITCH
;
1944 else if (pa_streq(state
->rvalue
, "volume") && e
)
1945 req
= PA_ALSA_REQUIRED_VOLUME
;
1946 else if (pa_streq(state
->rvalue
, "enumeration"))
1947 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1948 else if (pa_streq(state
->rvalue
, "any"))
1949 req
= PA_ALSA_REQUIRED_ANY
;
1951 pa_log("[%s:%u] Required invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1955 if (pa_streq(state
->lvalue
, "required-absent")) {
1957 e
->required_absent
= req
;
1959 o
->required_absent
= req
;
1961 j
->required_absent
= req
;
1963 else if (pa_streq(state
->lvalue
, "required-any")) {
1965 e
->required_any
= req
;
1966 e
->path
->has_req_any
|= (req
!= PA_ALSA_REQUIRED_IGNORE
);
1969 o
->required_any
= req
;
1970 o
->element
->path
->has_req_any
|= (req
!= PA_ALSA_REQUIRED_IGNORE
);
1973 j
->required_any
= req
;
1974 j
->path
->has_req_any
|= (req
!= PA_ALSA_REQUIRED_IGNORE
);
1990 static int element_parse_direction(pa_config_parser_state
*state
) {
1996 p
= state
->userdata
;
1998 if (!(e
= element_get(p
, state
->section
, TRUE
))) {
1999 pa_log("[%s:%u] Direction makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2003 if (pa_streq(state
->rvalue
, "playback"))
2004 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
2005 else if (pa_streq(state
->rvalue
, "capture"))
2006 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
2008 pa_log("[%s:%u] Direction invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
2015 static int element_parse_direction_try_other(pa_config_parser_state
*state
) {
2022 p
= state
->userdata
;
2024 if (!(e
= element_get(p
, state
->section
, TRUE
))) {
2025 pa_log("[%s:%u] Direction makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2029 if ((yes
= pa_parse_boolean(state
->rvalue
)) < 0) {
2030 pa_log("[%s:%u] Direction invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
2034 e
->direction_try_other
= !!yes
;
2038 static int element_parse_volume_limit(pa_config_parser_state
*state
) {
2045 p
= state
->userdata
;
2047 if (!(e
= element_get(p
, state
->section
, TRUE
))) {
2048 pa_log("[%s:%u] volume-limit makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2052 if (pa_atol(state
->rvalue
, &volume_limit
) < 0 || volume_limit
< 0) {
2053 pa_log("[%s:%u] Invalid value for volume-limit", state
->filename
, state
->lineno
);
2057 e
->volume_limit
= volume_limit
;
2061 static pa_channel_position_mask_t
parse_mask(const char *m
) {
2062 pa_channel_position_mask_t v
;
2064 if (pa_streq(m
, "all-left"))
2065 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
2066 else if (pa_streq(m
, "all-right"))
2067 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
2068 else if (pa_streq(m
, "all-center"))
2069 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
2070 else if (pa_streq(m
, "all-front"))
2071 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
2072 else if (pa_streq(m
, "all-rear"))
2073 v
= PA_CHANNEL_POSITION_MASK_REAR
;
2074 else if (pa_streq(m
, "all-side"))
2075 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
2076 else if (pa_streq(m
, "all-top"))
2077 v
= PA_CHANNEL_POSITION_MASK_TOP
;
2078 else if (pa_streq(m
, "all-no-lfe"))
2079 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
2080 else if (pa_streq(m
, "all"))
2081 v
= PA_CHANNEL_POSITION_MASK_ALL
;
2083 pa_channel_position_t p
;
2085 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
2088 v
= PA_CHANNEL_POSITION_MASK(p
);
2094 static int element_parse_override_map(pa_config_parser_state
*state
) {
2097 const char *split_state
= NULL
;
2103 p
= state
->userdata
;
2105 if (!(e
= element_get(p
, state
->section
, TRUE
))) {
2106 pa_log("[%s:%u] Override map makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2110 while ((n
= pa_split(state
->rvalue
, ",", &split_state
))) {
2111 pa_channel_position_mask_t m
;
2116 if ((m
= parse_mask(n
)) == 0) {
2117 pa_log("[%s:%u] Override map '%s' invalid in '%s'", state
->filename
, state
->lineno
, n
, state
->section
);
2123 if (pa_streq(state
->lvalue
, "override-map.1"))
2124 e
->masks
[i
++][0] = m
;
2126 e
->masks
[i
++][1] = m
;
2128 /* Later on we might add override-map.3 and so on here ... */
2133 e
->override_map
= TRUE
;
2138 static int jack_parse_state(pa_config_parser_state
*state
) {
2141 pa_port_available_t pa
;
2145 p
= state
->userdata
;
2147 if (!(j
= jack_get(p
, state
->section
))) {
2148 pa_log("[%s:%u] state makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2152 if (pa_streq(state
->rvalue
, "yes"))
2153 pa
= PA_PORT_AVAILABLE_YES
;
2154 else if (pa_streq(state
->rvalue
, "no"))
2155 pa
= PA_PORT_AVAILABLE_NO
;
2156 else if (pa_streq(state
->rvalue
, "unknown"))
2157 pa
= PA_PORT_AVAILABLE_UNKNOWN
;
2159 pa_log("[%s:%u] state must be 'yes', 'no' or 'unknown' in '%s'", state
->filename
, state
->lineno
, state
->section
);
2163 if (pa_streq(state
->lvalue
, "state.unplugged"))
2164 j
->state_unplugged
= pa
;
2166 j
->state_plugged
= pa
;
2167 pa_assert(pa_streq(state
->lvalue
, "state.plugged"));
2173 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
2174 snd_mixer_selem_id_t
*sid
;
2175 snd_mixer_elem_t
*me
;
2181 SELEM_INIT(sid
, e
->alsa_name
);
2182 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2183 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2187 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
2189 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
2190 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
2192 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
2195 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2198 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
2200 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
2201 pa_log_warn("Failed to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2207 int pa_alsa_setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
2214 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
2215 element_set_option(o
->element
, m
, o
->alsa_idx
);
2220 static int option_verify(pa_alsa_option
*o
) {
2221 static const struct description_map well_known_descriptions
[] = {
2222 { "input", N_("Input") },
2223 { "input-docking", N_("Docking Station Input") },
2224 { "input-docking-microphone", N_("Docking Station Microphone") },
2225 { "input-docking-linein", N_("Docking Station Line In") },
2226 { "input-linein", N_("Line In") },
2227 { "input-microphone", N_("Microphone") },
2228 { "input-microphone-front", N_("Front Microphone") },
2229 { "input-microphone-rear", N_("Rear Microphone") },
2230 { "input-microphone-external", N_("External Microphone") },
2231 { "input-microphone-internal", N_("Internal Microphone") },
2232 { "input-radio", N_("Radio") },
2233 { "input-video", N_("Video") },
2234 { "input-agc-on", N_("Automatic Gain Control") },
2235 { "input-agc-off", N_("No Automatic Gain Control") },
2236 { "input-boost-on", N_("Boost") },
2237 { "input-boost-off", N_("No Boost") },
2238 { "output-amplifier-on", N_("Amplifier") },
2239 { "output-amplifier-off", N_("No Amplifier") },
2240 { "output-bass-boost-on", N_("Bass Boost") },
2241 { "output-bass-boost-off", N_("No Bass Boost") },
2242 { "output-speaker", N_("Speaker") },
2243 { "output-headphones", N_("Headphones") }
2249 pa_log("No name set for option %s", o
->alsa_name
);
2253 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
2254 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
2255 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
2259 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
2260 !pa_streq(o
->alsa_name
, "on") &&
2261 !pa_streq(o
->alsa_name
, "off")) {
2262 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
2266 if (!o
->description
)
2267 o
->description
= pa_xstrdup(lookup_description(o
->name
,
2268 well_known_descriptions
,
2269 PA_ELEMENTSOF(well_known_descriptions
)));
2270 if (!o
->description
)
2271 o
->description
= pa_xstrdup(o
->name
);
2276 static int element_verify(pa_alsa_element
*e
) {
2281 // 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);
2282 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
2283 (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required_any
== e
->required_absent
) ||
2284 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) ||
2285 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
2286 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
2290 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
2291 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
2295 PA_LLIST_FOREACH(o
, e
->options
)
2296 if (option_verify(o
) < 0)
2302 static int path_verify(pa_alsa_path
*p
) {
2303 static const struct description_map well_known_descriptions
[] = {
2304 { "analog-input", N_("Analog Input") },
2305 { "analog-input-microphone", N_("Microphone") },
2306 { "analog-input-microphone-front", N_("Front Microphone") },
2307 { "analog-input-microphone-rear", N_("Rear Microphone") },
2308 { "analog-input-microphone-dock", N_("Dock Microphone") },
2309 { "analog-input-microphone-internal", N_("Internal Microphone") },
2310 { "analog-input-linein", N_("Line In") },
2311 { "analog-input-radio", N_("Radio") },
2312 { "analog-input-video", N_("Video") },
2313 { "analog-output", N_("Analog Output") },
2314 { "analog-output-headphones", N_("Headphones") },
2315 { "analog-output-lfe-on-mono", N_("LFE on Separate Mono Output") },
2316 { "analog-output-lineout", N_("Line Out") },
2317 { "analog-output-mono", N_("Analog Mono Output") },
2318 { "analog-output-speaker", N_("Speakers") },
2319 { "hdmi-output", N_("HDMI / DisplayPort") },
2320 { "iec958-stereo-output", N_("Digital Output (S/PDIF)") },
2321 { "iec958-passthrough-output", N_("Digital Passthrough (S/PDIF)") }
2328 PA_LLIST_FOREACH(e
, p
->elements
)
2329 if (element_verify(e
) < 0)
2332 if (!p
->description
)
2333 p
->description
= pa_xstrdup(lookup_description(p
->name
,
2334 well_known_descriptions
,
2335 PA_ELEMENTSOF(well_known_descriptions
)));
2337 if (!p
->description
)
2338 p
->description
= pa_xstrdup(p
->name
);
2343 static const char *get_default_paths_dir(void) {
2344 if (pa_run_from_build_tree())
2345 return PA_BUILDDIR
"/modules/alsa/mixer/paths/";
2347 return PA_ALSA_PATHS_DIR
;
2350 pa_alsa_path
* pa_alsa_path_new(const char *paths_dir
, const char *fname
, pa_alsa_direction_t direction
) {
2356 pa_config_item items
[] = {
2358 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
2359 { "description", pa_config_parse_string
, NULL
, "General" },
2360 { "name", pa_config_parse_string
, NULL
, "General" },
2363 { "priority", option_parse_priority
, NULL
, NULL
},
2364 { "name", option_parse_name
, NULL
, NULL
},
2367 { "state.plugged", jack_parse_state
, NULL
, NULL
},
2368 { "state.unplugged", jack_parse_state
, NULL
, NULL
},
2371 { "switch", element_parse_switch
, NULL
, NULL
},
2372 { "volume", element_parse_volume
, NULL
, NULL
},
2373 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
2374 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
2375 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
2376 /* ... later on we might add override-map.3 and so on here ... */
2377 { "required", element_parse_required
, NULL
, NULL
},
2378 { "required-any", element_parse_required
, NULL
, NULL
},
2379 { "required-absent", element_parse_required
, NULL
, NULL
},
2380 { "direction", element_parse_direction
, NULL
, NULL
},
2381 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2382 { "volume-limit", element_parse_volume_limit
, NULL
, NULL
},
2383 { NULL
, NULL
, NULL
, NULL
}
2388 p
= pa_xnew0(pa_alsa_path
, 1);
2389 n
= pa_path_get_filename(fname
);
2390 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2391 p
->direction
= direction
;
2393 items
[0].data
= &p
->priority
;
2394 items
[1].data
= &p
->description
;
2395 items
[2].data
= &p
->name
;
2398 paths_dir
= get_default_paths_dir();
2400 fn
= pa_maybe_prefix_path(fname
, paths_dir
);
2402 r
= pa_config_parse(fn
, NULL
, items
, NULL
, p
);
2408 if (path_verify(p
) < 0)
2414 pa_alsa_path_free(p
);
2418 pa_alsa_path
*pa_alsa_path_synthesize(const char *element
, pa_alsa_direction_t direction
) {
2424 p
= pa_xnew0(pa_alsa_path
, 1);
2425 p
->name
= pa_xstrdup(element
);
2426 p
->direction
= direction
;
2428 e
= pa_xnew0(pa_alsa_element
, 1);
2430 e
->alsa_name
= pa_xstrdup(element
);
2431 e
->direction
= direction
;
2432 e
->volume_limit
= -1;
2434 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2435 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2437 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2438 p
->last_element
= e
;
2442 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2443 pa_alsa_option
*o
, *n
;
2447 for (o
= e
->options
; o
; o
= n
) {
2450 if (o
->alsa_idx
< 0) {
2451 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2457 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2458 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2459 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2462 static void path_drop_unsupported(pa_alsa_path
*p
) {
2463 pa_alsa_element
*e
, *n
;
2467 for (e
= p
->elements
; e
; e
= n
) {
2470 if (!element_drop_unsupported(e
)) {
2471 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2477 static void path_make_options_unique(pa_alsa_path
*p
) {
2479 pa_alsa_option
*o
, *u
;
2481 PA_LLIST_FOREACH(e
, p
->elements
) {
2482 PA_LLIST_FOREACH(o
, e
->options
) {
2486 for (u
= o
->next
; u
; u
= u
->next
)
2487 if (pa_streq(u
->name
, o
->name
))
2493 m
= pa_xstrdup(o
->name
);
2495 /* OK, this name is not unique, hence let's rename */
2496 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2499 if (!pa_streq(u
->name
, m
))
2502 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2506 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2507 pa_xfree(u
->description
);
2508 u
->description
= nd
;
2518 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2521 for (; e
; e
= e
->next
)
2522 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2523 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2529 for (o
= e
->options
; o
; o
= o
->next
) {
2533 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2534 s
->options
= pa_idxset_copy(template->options
);
2535 s
->name
= pa_sprintf_malloc("%s+%s", template->name
, o
->name
);
2537 (template->description
[0] && o
->description
[0])
2538 ? pa_sprintf_malloc("%s / %s", template->description
, o
->description
)
2539 : (template->description
[0]
2540 ? pa_xstrdup(template->description
)
2541 : pa_xstrdup(o
->description
));
2543 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2545 s
= pa_xnew0(pa_alsa_setting
, 1);
2546 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2547 s
->name
= pa_xstrdup(o
->name
);
2548 s
->description
= pa_xstrdup(o
->description
);
2549 s
->priority
= o
->priority
;
2552 pa_idxset_put(s
->options
, o
, NULL
);
2554 if (element_create_settings(e
->next
, s
))
2555 /* This is not a leaf, so let's get rid of it */
2558 /* This is a leaf, so let's add it */
2559 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2561 e
->path
->last_setting
= s
;
2568 static void path_create_settings(pa_alsa_path
*p
) {
2571 element_create_settings(p
->elements
, NULL
);
2574 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_hctl_t
*hctl
, pa_bool_t ignore_dB
) {
2577 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2578 pa_channel_position_t t
;
2579 pa_channel_position_mask_t path_volume_channels
= 0;
2585 return p
->supported
? 0 : -1;
2591 pa_log_debug("Probing path '%s'", p
->name
);
2593 PA_LLIST_FOREACH(j
, p
->jacks
) {
2594 if (jack_probe(j
, hctl
) < 0) {
2595 p
->supported
= FALSE
;
2596 pa_log_debug("Probe of jack '%s' failed.", j
->alsa_name
);
2599 pa_log_debug("Probe of jack '%s' succeeded (%s)", j
->alsa_name
, j
->has_control
? "found!" : "not found");
2602 PA_LLIST_FOREACH(e
, p
->elements
) {
2603 if (element_probe(e
, m
) < 0) {
2604 p
->supported
= FALSE
;
2605 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2608 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
);
2613 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2615 if (!p
->has_volume
) {
2616 p
->min_volume
= e
->min_volume
;
2617 p
->max_volume
= e
->max_volume
;
2621 if (!p
->has_volume
) {
2622 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2623 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2624 min_dB
[t
] = e
->min_dB
;
2625 max_dB
[t
] = e
->max_dB
;
2626 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2633 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2634 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2635 min_dB
[t
] += e
->min_dB
;
2636 max_dB
[t
] += e
->max_dB
;
2637 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2640 /* Hmm, there's another element before us
2641 * which cannot do dB volumes, so we we need
2642 * to 'neutralize' this slider */
2643 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2644 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2647 } else if (p
->has_volume
) {
2648 /* We can't use this volume, so let's ignore it */
2649 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2650 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2652 p
->has_volume
= TRUE
;
2655 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2659 if (p
->has_req_any
&& !p
->req_any_present
) {
2660 p
->supported
= FALSE
;
2661 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2665 path_drop_unsupported(p
);
2666 path_make_options_unique(p
);
2667 path_create_settings(p
);
2669 p
->supported
= TRUE
;
2671 p
->min_dB
= INFINITY
;
2672 p
->max_dB
= -INFINITY
;
2674 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2675 if (path_volume_channels
& PA_CHANNEL_POSITION_MASK(t
)) {
2676 if (p
->min_dB
> min_dB
[t
])
2677 p
->min_dB
= min_dB
[t
];
2679 if (p
->max_dB
< max_dB
[t
])
2680 p
->max_dB
= max_dB
[t
];
2687 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2690 pa_log_debug("Setting %s (%s) priority=%u",
2692 pa_strnull(s
->description
),
2696 void pa_alsa_jack_dump(pa_alsa_jack
*j
) {
2699 pa_log_debug("Jack %s, alsa_name='%s', detection %s", j
->name
, j
->alsa_name
, j
->has_control
? "possible" : "unavailable");
2702 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2705 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2707 pa_strnull(o
->name
),
2708 pa_strnull(o
->description
),
2713 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2717 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",
2727 (long long unsigned) e
->merged_mask
,
2729 pa_yes_no(e
->override_map
));
2731 PA_LLIST_FOREACH(o
, e
->options
)
2732 pa_alsa_option_dump(o
);
2735 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2741 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2742 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2744 pa_strnull(p
->description
),
2747 pa_yes_no(p
->probed
),
2748 pa_yes_no(p
->supported
),
2749 pa_yes_no(p
->has_mute
),
2750 pa_yes_no(p
->has_volume
),
2751 pa_yes_no(p
->has_dB
),
2752 p
->min_volume
, p
->max_volume
,
2753 p
->min_dB
, p
->max_dB
);
2755 PA_LLIST_FOREACH(e
, p
->elements
)
2756 pa_alsa_element_dump(e
);
2758 PA_LLIST_FOREACH(j
, p
->jacks
)
2759 pa_alsa_jack_dump(j
);
2761 PA_LLIST_FOREACH(s
, p
->settings
)
2762 pa_alsa_setting_dump(s
);
2765 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2766 snd_mixer_selem_id_t
*sid
;
2767 snd_mixer_elem_t
*me
;
2773 SELEM_INIT(sid
, e
->alsa_name
);
2774 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2775 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2779 snd_mixer_elem_set_callback(me
, cb
);
2780 snd_mixer_elem_set_callback_private(me
, userdata
);
2783 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2790 PA_LLIST_FOREACH(e
, p
->elements
)
2791 element_set_callback(e
, m
, cb
, userdata
);
2794 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2802 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
2803 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2806 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
, const char *paths_dir
) {
2807 pa_alsa_path_set
*ps
;
2808 char **pn
= NULL
, **en
= NULL
, **ie
;
2809 pa_alsa_decibel_fix
*db_fix
;
2810 void *state
, *state2
;
2814 pa_assert(m
->profile_set
);
2815 pa_assert(m
->profile_set
->decibel_fixes
);
2816 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2818 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2821 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2822 ps
->direction
= direction
;
2823 ps
->paths
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2825 if (direction
== PA_ALSA_DIRECTION_OUTPUT
) {
2826 pn
= m
->output_path_names
;
2827 cache
= m
->profile_set
->output_paths
;
2829 else if (direction
== PA_ALSA_DIRECTION_INPUT
) {
2830 pn
= m
->input_path_names
;
2831 cache
= m
->profile_set
->input_paths
;
2837 for (in
= pn
; *in
; in
++) {
2838 pa_alsa_path
*p
= NULL
;
2839 pa_bool_t duplicate
= FALSE
;
2842 for (kn
= pn
; kn
< in
; kn
++)
2843 if (pa_streq(*kn
, *in
)) {
2851 p
= pa_hashmap_get(cache
, *in
);
2853 char *fn
= pa_sprintf_malloc("%s.conf", *in
);
2854 p
= pa_alsa_path_new(paths_dir
, fn
, direction
);
2857 pa_hashmap_put(cache
, *in
, p
);
2859 pa_assert(pa_hashmap_get(cache
, *in
) == p
);
2861 pa_hashmap_put(ps
->paths
, p
, p
);
2868 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2869 en
= m
->output_element
;
2870 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2871 en
= m
->input_element
;
2874 pa_alsa_path_set_free(ps
);
2878 for (ie
= en
; *ie
; ie
++) {
2882 p
= pa_alsa_path_synthesize(*ie
, direction
);
2884 /* Mark all other passed elements for require-absent */
2885 for (je
= en
; *je
; je
++) {
2891 e
= pa_xnew0(pa_alsa_element
, 1);
2893 e
->alsa_name
= pa_xstrdup(*je
);
2894 e
->direction
= direction
;
2895 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2896 e
->volume_limit
= -1;
2898 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2899 p
->last_element
= e
;
2902 pa_hashmap_put(ps
->paths
, *ie
, p
);
2906 /* Assign decibel fixes to elements. */
2907 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2910 PA_HASHMAP_FOREACH(p
, ps
->paths
, state2
) {
2913 PA_LLIST_FOREACH(e
, p
->elements
) {
2914 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2915 /* The profile set that contains the dB fix may be freed
2916 * before the element, so we have to copy the dB fix
2918 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2919 e
->db_fix
->profile_set
= NULL
;
2920 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2921 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
2930 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2935 pa_log_debug("Path Set %p, direction=%i",
2939 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
2940 pa_alsa_path_dump(p
);
2944 static pa_bool_t
options_have_option(pa_alsa_option
*options
, const char *alsa_name
) {
2948 pa_assert(alsa_name
);
2950 PA_LLIST_FOREACH(o
, options
) {
2951 if (pa_streq(o
->alsa_name
, alsa_name
))
2957 static pa_bool_t
enumeration_is_subset(pa_alsa_option
*a_options
, pa_alsa_option
*b_options
) {
2958 pa_alsa_option
*oa
, *ob
;
2960 if (!a_options
) return TRUE
;
2961 if (!b_options
) return FALSE
;
2963 /* If there is an option A offers that B does not, then A is not a subset of B. */
2964 PA_LLIST_FOREACH(oa
, a_options
) {
2965 pa_bool_t found
= FALSE
;
2966 PA_LLIST_FOREACH(ob
, b_options
) {
2967 if (pa_streq(oa
->alsa_name
, ob
->alsa_name
)) {
2979 * Compares two elements to see if a is a subset of b
2981 static pa_bool_t
element_is_subset(pa_alsa_element
*a
, pa_alsa_element
*b
, snd_mixer_t
*m
) {
2987 * Every state is a subset of itself (with caveats for volume_limits and options)
2988 * IGNORE is a subset of every other state */
2990 /* Check the volume_use */
2991 if (a
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
2993 /* "Constant" is subset of "Constant" only when their constant values are equal */
2994 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& b
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& a
->constant_volume
!= b
->constant_volume
)
2997 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
2998 if (a
->volume_use
!= b
->volume_use
&& b
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
3001 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
3002 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
3003 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
3004 if (b
->volume_use
== PA_ALSA_VOLUME_MERGE
&& b
->volume_limit
>= 0) {
3007 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
)
3008 a_limit
= a
->constant_volume
;
3009 else if (a
->volume_use
== PA_ALSA_VOLUME_ZERO
) {
3013 int rounding
= (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
? +1 : -1);
3014 a_limit
= decibel_fix_get_step(a
->db_fix
, &dB
, rounding
);
3016 snd_mixer_selem_id_t
*sid
;
3017 snd_mixer_elem_t
*me
;
3019 SELEM_INIT(sid
, a
->alsa_name
);
3020 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
3021 pa_log_warn("Element %s seems to have disappeared.", a
->alsa_name
);
3025 if (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3026 if (snd_mixer_selem_ask_playback_dB_vol(me
, dB
, +1, &a_limit
) < 0)
3029 if (snd_mixer_selem_ask_capture_dB_vol(me
, dB
, -1, &a_limit
) < 0)
3033 } else if (a
->volume_use
== PA_ALSA_VOLUME_OFF
)
3034 a_limit
= a
->min_volume
;
3035 else if (a
->volume_use
== PA_ALSA_VOLUME_MERGE
)
3036 a_limit
= a
->volume_limit
;
3038 /* This should never be reached */
3041 if (a_limit
> b
->volume_limit
)
3045 if (a
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
3047 /* If override-maps are different, they're not subsets */
3048 if (a
->n_channels
!= b
->n_channels
)
3050 for (s
= 0; s
<= SND_MIXER_SCHN_LAST
; s
++)
3051 if (a
->masks
[s
][a
->n_channels
-1] != b
->masks
[s
][b
->n_channels
-1]) {
3052 pa_log_debug("Element %s is not a subset - mask a: 0x%" PRIx64
", mask b: 0x%" PRIx64
", at channel %d",
3053 a
->alsa_name
, a
->masks
[s
][a
->n_channels
-1], b
->masks
[s
][b
->n_channels
-1], s
);
3059 if (a
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
3060 /* "On" is a subset of "Mute".
3061 * "Off" is a subset of "Mute".
3062 * "On" is a subset of "Select", if there is an "Option:On" in B.
3063 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3064 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3066 if (a
->switch_use
!= b
->switch_use
) {
3068 if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
|| a
->switch_use
== PA_ALSA_SWITCH_MUTE
3069 || b
->switch_use
== PA_ALSA_SWITCH_OFF
|| b
->switch_use
== PA_ALSA_SWITCH_ON
)
3072 if (b
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3073 if (a
->switch_use
== PA_ALSA_SWITCH_ON
) {
3074 if (!options_have_option(b
->options
, "on"))
3076 } else if (a
->switch_use
== PA_ALSA_SWITCH_OFF
) {
3077 if (!options_have_option(b
->options
, "off"))
3081 } else if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3082 if (!enumeration_is_subset(a
->options
, b
->options
))
3087 if (a
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
) {
3088 if (b
->enumeration_use
== PA_ALSA_ENUMERATION_IGNORE
)
3090 if (!enumeration_is_subset(a
->options
, b
->options
))
3097 static void path_set_condense(pa_alsa_path_set
*ps
, snd_mixer_t
*m
) {
3104 /* If we only have one path, then don't bother */
3105 if (pa_hashmap_size(ps
->paths
) < 2)
3108 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3112 PA_HASHMAP_FOREACH(p2
, ps
->paths
, state2
) {
3113 pa_alsa_element
*ea
, *eb
;
3114 pa_alsa_jack
*ja
, *jb
;
3115 pa_bool_t is_subset
= TRUE
;
3120 /* If a has a jack that b does not have, a is not a subset */
3121 PA_LLIST_FOREACH(ja
, p
->jacks
) {
3122 pa_bool_t exists
= FALSE
;
3124 if (!ja
->has_control
)
3127 PA_LLIST_FOREACH(jb
, p2
->jacks
) {
3128 if (jb
->has_control
&& pa_streq(jb
->alsa_name
, ja
->alsa_name
) &&
3129 (ja
->state_plugged
== jb
->state_plugged
) &&
3130 (ja
->state_unplugged
== jb
->state_unplugged
)) {
3142 /* Compare the elements of each set... */
3143 pa_assert_se(ea
= p
->elements
);
3144 pa_assert_se(eb
= p2
->elements
);
3147 if (pa_streq(ea
->alsa_name
, eb
->alsa_name
)) {
3148 if (element_is_subset(ea
, eb
, m
)) {
3151 if ((ea
&& !eb
) || (!ea
&& eb
))
3153 else if (!ea
&& !eb
)
3163 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p
->name
, p2
->name
);
3164 pa_hashmap_remove(ps
->paths
, p
);
3171 static pa_alsa_path
* path_set_find_path_by_name(pa_alsa_path_set
*ps
, const char* name
, pa_alsa_path
*ignore
)
3176 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
3177 if (p
!= ignore
&& pa_streq(p
->name
, name
))
3182 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
3183 pa_alsa_path
*p
, *q
;
3184 void *state
, *state2
;
3186 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3190 q
= path_set_find_path_by_name(ps
, p
->name
, p
);
3195 m
= pa_xstrdup(p
->name
);
3197 /* OK, this name is not unique, hence let's rename */
3199 PA_HASHMAP_FOREACH(q
, ps
->paths
, state2
) {
3202 if (!pa_streq(q
->name
, m
))
3205 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
3209 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
3210 pa_xfree(q
->description
);
3211 q
->description
= nd
;
3220 static void mapping_free(pa_alsa_mapping
*m
) {
3224 pa_xfree(m
->description
);
3226 pa_xstrfreev(m
->device_strings
);
3227 pa_xstrfreev(m
->input_path_names
);
3228 pa_xstrfreev(m
->output_path_names
);
3229 pa_xstrfreev(m
->input_element
);
3230 pa_xstrfreev(m
->output_element
);
3231 if (m
->input_path_set
)
3232 pa_alsa_path_set_free(m
->input_path_set
);
3233 if (m
->output_path_set
)
3234 pa_alsa_path_set_free(m
->output_path_set
);
3236 pa_assert(!m
->input_pcm
);
3237 pa_assert(!m
->output_pcm
);
3242 static void profile_free(pa_alsa_profile
*p
) {
3246 pa_xfree(p
->description
);
3248 pa_xstrfreev(p
->input_mapping_names
);
3249 pa_xstrfreev(p
->output_mapping_names
);
3251 if (p
->input_mappings
)
3252 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
3254 if (p
->output_mappings
)
3255 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
3260 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
3263 if (ps
->input_paths
) {
3266 while ((p
= pa_hashmap_steal_first(ps
->input_paths
)))
3267 pa_alsa_path_free(p
);
3269 pa_hashmap_free(ps
->input_paths
, NULL
, NULL
);
3272 if (ps
->output_paths
) {
3275 while ((p
= pa_hashmap_steal_first(ps
->output_paths
)))
3276 pa_alsa_path_free(p
);
3278 pa_hashmap_free(ps
->output_paths
, NULL
, NULL
);
3284 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
3287 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
3293 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
3296 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
3299 if (ps
->decibel_fixes
) {
3300 pa_alsa_decibel_fix
*db_fix
;
3302 while ((db_fix
= pa_hashmap_steal_first(ps
->decibel_fixes
)))
3303 decibel_fix_free(db_fix
);
3305 pa_hashmap_free(ps
->decibel_fixes
, NULL
, NULL
);
3311 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
3314 if (!pa_startswith(name
, "Mapping "))
3319 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
3322 m
= pa_xnew0(pa_alsa_mapping
, 1);
3323 m
->profile_set
= ps
;
3324 m
->name
= pa_xstrdup(name
);
3325 pa_channel_map_init(&m
->channel_map
);
3327 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
3332 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
3335 if (!pa_startswith(name
, "Profile "))
3340 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
3343 p
= pa_xnew0(pa_alsa_profile
, 1);
3344 p
->profile_set
= ps
;
3345 p
->name
= pa_xstrdup(name
);
3347 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3352 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
3353 pa_alsa_decibel_fix
*db_fix
;
3355 if (!pa_startswith(name
, "DecibelFix "))
3360 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
3363 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
3364 db_fix
->profile_set
= ps
;
3365 db_fix
->name
= pa_xstrdup(name
);
3367 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
3372 static int mapping_parse_device_strings(pa_config_parser_state
*state
) {
3373 pa_alsa_profile_set
*ps
;
3378 ps
= state
->userdata
;
3380 if (!(m
= mapping_get(ps
, state
->section
))) {
3381 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3385 pa_xstrfreev(m
->device_strings
);
3386 if (!(m
->device_strings
= pa_split_spaces_strv(state
->rvalue
))) {
3387 pa_log("[%s:%u] Device string list empty of '%s'", state
->filename
, state
->lineno
, state
->section
);
3394 static int mapping_parse_channel_map(pa_config_parser_state
*state
) {
3395 pa_alsa_profile_set
*ps
;
3400 ps
= state
->userdata
;
3402 if (!(m
= mapping_get(ps
, state
->section
))) {
3403 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3407 if (!(pa_channel_map_parse(&m
->channel_map
, state
->rvalue
))) {
3408 pa_log("[%s:%u] Channel map invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
3415 static int mapping_parse_paths(pa_config_parser_state
*state
) {
3416 pa_alsa_profile_set
*ps
;
3421 ps
= state
->userdata
;
3423 if (!(m
= mapping_get(ps
, state
->section
))) {
3424 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3428 if (pa_streq(state
->lvalue
, "paths-input")) {
3429 pa_xstrfreev(m
->input_path_names
);
3430 m
->input_path_names
= pa_split_spaces_strv(state
->rvalue
);
3432 pa_xstrfreev(m
->output_path_names
);
3433 m
->output_path_names
= pa_split_spaces_strv(state
->rvalue
);
3439 static int mapping_parse_element(pa_config_parser_state
*state
) {
3440 pa_alsa_profile_set
*ps
;
3445 ps
= state
->userdata
;
3447 if (!(m
= mapping_get(ps
, state
->section
))) {
3448 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3452 if (pa_streq(state
->lvalue
, "element-input")) {
3453 pa_xstrfreev(m
->input_element
);
3454 m
->input_element
= pa_split_spaces_strv(state
->rvalue
);
3456 pa_xstrfreev(m
->output_element
);
3457 m
->output_element
= pa_split_spaces_strv(state
->rvalue
);
3463 static int mapping_parse_direction(pa_config_parser_state
*state
) {
3464 pa_alsa_profile_set
*ps
;
3469 ps
= state
->userdata
;
3471 if (!(m
= mapping_get(ps
, state
->section
))) {
3472 pa_log("[%s:%u] Section name %s invalid.", state
->filename
, state
->lineno
, state
->section
);
3476 if (pa_streq(state
->rvalue
, "input"))
3477 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3478 else if (pa_streq(state
->rvalue
, "output"))
3479 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3480 else if (pa_streq(state
->rvalue
, "any"))
3481 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3483 pa_log("[%s:%u] Direction %s invalid.", state
->filename
, state
->lineno
, state
->rvalue
);
3490 static int mapping_parse_description(pa_config_parser_state
*state
) {
3491 pa_alsa_profile_set
*ps
;
3497 ps
= state
->userdata
;
3499 if ((m
= mapping_get(ps
, state
->section
))) {
3500 pa_xfree(m
->description
);
3501 m
->description
= pa_xstrdup(state
->rvalue
);
3502 } else if ((p
= profile_get(ps
, state
->section
))) {
3503 pa_xfree(p
->description
);
3504 p
->description
= pa_xstrdup(state
->rvalue
);
3506 pa_log("[%s:%u] Section name %s invalid.", state
->filename
, state
->lineno
, state
->section
);
3513 static int mapping_parse_priority(pa_config_parser_state
*state
) {
3514 pa_alsa_profile_set
*ps
;
3521 ps
= state
->userdata
;
3523 if (pa_atou(state
->rvalue
, &prio
) < 0) {
3524 pa_log("[%s:%u] Priority invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
3528 if ((m
= mapping_get(ps
, state
->section
)))
3530 else if ((p
= profile_get(ps
, state
->section
)))
3533 pa_log("[%s:%u] Section name %s invalid.", state
->filename
, state
->lineno
, state
->section
);
3540 static int profile_parse_mappings(pa_config_parser_state
*state
) {
3541 pa_alsa_profile_set
*ps
;
3546 ps
= state
->userdata
;
3548 if (!(p
= profile_get(ps
, state
->section
))) {
3549 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3553 if (pa_streq(state
->lvalue
, "input-mappings")) {
3554 pa_xstrfreev(p
->input_mapping_names
);
3555 p
->input_mapping_names
= pa_split_spaces_strv(state
->rvalue
);
3557 pa_xstrfreev(p
->output_mapping_names
);
3558 p
->output_mapping_names
= pa_split_spaces_strv(state
->rvalue
);
3564 static int profile_parse_skip_probe(pa_config_parser_state
*state
) {
3565 pa_alsa_profile_set
*ps
;
3571 ps
= state
->userdata
;
3573 if (!(p
= profile_get(ps
, state
->section
))) {
3574 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3578 if ((b
= pa_parse_boolean(state
->rvalue
)) < 0) {
3579 pa_log("[%s:%u] Skip probe invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
3588 static int decibel_fix_parse_db_values(pa_config_parser_state
*state
) {
3589 pa_alsa_profile_set
*ps
;
3590 pa_alsa_decibel_fix
*db_fix
;
3594 unsigned n
= 8; /* Current size of the db_values table. */
3595 unsigned min_step
= 0;
3596 unsigned max_step
= 0;
3597 unsigned i
= 0; /* Index to the items table. */
3598 unsigned prev_step
= 0;
3603 ps
= state
->userdata
;
3605 if (!(db_fix
= decibel_fix_get(ps
, state
->section
))) {
3606 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3610 if (!(items
= pa_split_spaces_strv(state
->rvalue
))) {
3611 pa_log("[%s:%u] Value missing", state
->filename
, state
->lineno
);
3615 db_values
= pa_xnew(long, n
);
3617 while ((item
= items
[i
++])) {
3618 char *s
= item
; /* Step value string. */
3619 char *d
= item
; /* dB value string. */
3623 /* Move d forward until it points to a colon or to the end of the item. */
3624 for (; *d
&& *d
!= ':'; ++d
);
3627 /* item started with colon. */
3628 pa_log("[%s:%u] No step value found in %s", state
->filename
, state
->lineno
, item
);
3632 if (!*d
|| !*(d
+ 1)) {
3633 /* No colon found, or it was the last character in item. */
3634 pa_log("[%s:%u] No dB value found in %s", state
->filename
, state
->lineno
, item
);
3638 /* pa_atou() needs a null-terminating string. Let's replace the colon
3639 * with a zero byte. */
3642 if (pa_atou(s
, &step
) < 0) {
3643 pa_log("[%s:%u] Invalid step value: %s", state
->filename
, state
->lineno
, s
);
3647 if (pa_atod(d
, &db
) < 0) {
3648 pa_log("[%s:%u] Invalid dB value: %s", state
->filename
, state
->lineno
, d
);
3652 if (step
<= prev_step
&& i
!= 1) {
3653 pa_log("[%s:%u] Step value %u not greater than the previous value %u", state
->filename
, state
->lineno
, step
, prev_step
);
3657 if (db
< prev_db
&& i
!= 1) {
3658 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", state
->filename
, state
->lineno
, db
, prev_db
);
3664 db_values
[0] = (long) (db
* 100.0);
3668 /* Interpolate linearly. */
3669 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3671 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3673 /* Reallocate the db_values table if it's about to overflow. */
3674 if (prev_step
+ 1 - min_step
== n
) {
3676 db_values
= pa_xrenew(long, db_values
, n
);
3679 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3686 db_fix
->min_step
= min_step
;
3687 db_fix
->max_step
= max_step
;
3688 pa_xfree(db_fix
->db_values
);
3689 db_fix
->db_values
= db_values
;
3691 pa_xstrfreev(items
);
3696 pa_xstrfreev(items
);
3697 pa_xfree(db_values
);
3702 static void mapping_paths_probe(pa_alsa_mapping
*m
, pa_alsa_profile
*profile
,
3703 pa_alsa_direction_t direction
) {
3707 snd_pcm_t
*pcm_handle
;
3708 pa_alsa_path_set
*ps
;
3709 snd_mixer_t
*mixer_handle
;
3710 snd_hctl_t
*hctl_handle
;
3712 if (direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3713 if (m
->output_path_set
)
3714 return; /* Already probed */
3715 m
->output_path_set
= ps
= pa_alsa_path_set_new(m
, direction
, NULL
); /* FIXME: Handle paths_dir */
3716 pcm_handle
= m
->output_pcm
;
3718 if (m
->input_path_set
)
3719 return; /* Already probed */
3720 m
->input_path_set
= ps
= pa_alsa_path_set_new(m
, direction
, NULL
); /* FIXME: Handle paths_dir */
3721 pcm_handle
= m
->input_pcm
;
3725 return; /* No paths */
3727 pa_assert(pcm_handle
);
3729 mixer_handle
= pa_alsa_open_mixer_for_pcm(pcm_handle
, NULL
, &hctl_handle
);
3730 if (!mixer_handle
|| !hctl_handle
) {
3731 /* Cannot open mixer, remove all entries */
3732 while (pa_hashmap_steal_first(ps
->paths
));
3737 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3738 if (pa_alsa_path_probe(p
, mixer_handle
, hctl_handle
, m
->profile_set
->ignore_dB
) < 0) {
3739 pa_hashmap_remove(ps
->paths
, p
);
3743 path_set_condense(ps
, mixer_handle
);
3744 path_set_make_paths_unique(ps
);
3747 snd_mixer_close(mixer_handle
);
3749 pa_log_debug("Available mixer paths (after tidying):");
3750 pa_alsa_path_set_dump(ps
);
3753 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3755 static const struct description_map well_known_descriptions
[] = {
3756 { "analog-mono", N_("Analog Mono") },
3757 { "analog-stereo", N_("Analog Stereo") },
3758 { "analog-surround-21", N_("Analog Surround 2.1") },
3759 { "analog-surround-30", N_("Analog Surround 3.0") },
3760 { "analog-surround-31", N_("Analog Surround 3.1") },
3761 { "analog-surround-40", N_("Analog Surround 4.0") },
3762 { "analog-surround-41", N_("Analog Surround 4.1") },
3763 { "analog-surround-50", N_("Analog Surround 5.0") },
3764 { "analog-surround-51", N_("Analog Surround 5.1") },
3765 { "analog-surround-61", N_("Analog Surround 6.0") },
3766 { "analog-surround-61", N_("Analog Surround 6.1") },
3767 { "analog-surround-70", N_("Analog Surround 7.0") },
3768 { "analog-surround-71", N_("Analog Surround 7.1") },
3769 { "analog-4-channel-input", N_("Analog 4-channel Input") },
3770 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3771 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3772 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3773 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3774 { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
3775 { "hdmi-stereo", N_("Digital Stereo (HDMI)") },
3776 { "hdmi-surround-51", N_("Digital Surround 5.1 (HDMI)") }
3781 if (!pa_channel_map_valid(&m
->channel_map
)) {
3782 pa_log("Mapping %s is missing channel map.", m
->name
);
3786 if (!m
->device_strings
) {
3787 pa_log("Mapping %s is missing device strings.", m
->name
);
3791 if ((m
->input_path_names
&& m
->input_element
) ||
3792 (m
->output_path_names
&& m
->output_element
)) {
3793 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3797 if (!m
->description
)
3798 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3799 well_known_descriptions
,
3800 PA_ELEMENTSOF(well_known_descriptions
)));
3802 if (!m
->description
)
3803 m
->description
= pa_xstrdup(m
->name
);
3806 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3808 else if (m
->channel_map
.channels
== bonus
->channels
)
3815 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3816 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3820 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3822 pa_strnull(m
->description
),
3824 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3825 pa_yes_no(m
->supported
),
3829 static void profile_set_add_auto_pair(
3830 pa_alsa_profile_set
*ps
,
3831 pa_alsa_mapping
*m
, /* output */
3832 pa_alsa_mapping
*n
/* input */) {
3840 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3843 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3847 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3849 name
= pa_sprintf_malloc("output:%s", m
->name
);
3851 name
= pa_sprintf_malloc("input:%s", n
->name
);
3853 if (pa_hashmap_get(ps
->profiles
, name
)) {
3858 p
= pa_xnew0(pa_alsa_profile
, 1);
3859 p
->profile_set
= ps
;
3863 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3864 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3865 p
->priority
+= m
->priority
* 100;
3869 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3870 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3871 p
->priority
+= n
->priority
;
3874 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3877 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3878 pa_alsa_mapping
*m
, *n
;
3879 void *m_state
, *n_state
;
3883 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3884 profile_set_add_auto_pair(ps
, m
, NULL
);
3886 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3887 profile_set_add_auto_pair(ps
, m
, n
);
3890 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3891 profile_set_add_auto_pair(ps
, NULL
, n
);
3894 static int profile_verify(pa_alsa_profile
*p
) {
3896 static const struct description_map well_known_descriptions
[] = {
3897 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3898 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3899 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3900 { "off", N_("Off") }
3905 /* Replace the output mapping names by the actual mappings */
3906 if (p
->output_mapping_names
) {
3909 pa_assert(!p
->output_mappings
);
3910 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3912 for (name
= p
->output_mapping_names
; *name
; name
++) {
3915 pa_bool_t duplicate
= FALSE
;
3917 for (in
= name
+ 1; *in
; in
++)
3918 if (pa_streq(*name
, *in
)) {
3926 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3927 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p
->name
, *name
);
3931 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3937 pa_xstrfreev(p
->output_mapping_names
);
3938 p
->output_mapping_names
= NULL
;
3941 /* Replace the input mapping names by the actual mappings */
3942 if (p
->input_mapping_names
) {
3945 pa_assert(!p
->input_mappings
);
3946 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3948 for (name
= p
->input_mapping_names
; *name
; name
++) {
3951 pa_bool_t duplicate
= FALSE
;
3953 for (in
= name
+ 1; *in
; in
++)
3954 if (pa_streq(*name
, *in
)) {
3962 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3963 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p
->name
, *name
);
3967 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3973 pa_xstrfreev(p
->input_mapping_names
);
3974 p
->input_mapping_names
= NULL
;
3977 if (!p
->input_mappings
&& !p
->output_mappings
) {
3978 pa_log("Profile '%s' lacks mappings.", p
->name
);
3982 if (!p
->description
)
3983 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3984 well_known_descriptions
,
3985 PA_ELEMENTSOF(well_known_descriptions
)));
3987 if (!p
->description
) {
3992 sb
= pa_strbuf_new();
3994 if (p
->output_mappings
)
3995 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3996 if (!pa_strbuf_isempty(sb
))
3997 pa_strbuf_puts(sb
, " + ");
3999 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
4002 if (p
->input_mappings
)
4003 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4004 if (!pa_strbuf_isempty(sb
))
4005 pa_strbuf_puts(sb
, " + ");
4007 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
4010 p
->description
= pa_strbuf_tostring_free(sb
);
4016 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
4021 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4023 pa_strnull(p
->description
),
4025 pa_yes_no(p
->supported
),
4026 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
4027 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
4029 if (p
->input_mappings
)
4030 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
4031 pa_log_debug("Input %s", m
->name
);
4033 if (p
->output_mappings
)
4034 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
4035 pa_log_debug("Output %s", m
->name
);
4038 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
4041 /* Check that the dB mapping has been configured. Since "db-values" is
4042 * currently the only option in the DecibelFix section, and decibel fix
4043 * objects don't get created if a DecibelFix section is empty, this is
4044 * actually a redundant check. Having this may prevent future bugs,
4046 if (!db_fix
->db_values
) {
4047 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
4054 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
4055 char *db_values
= NULL
;
4059 if (db_fix
->db_values
) {
4061 unsigned long i
, nsteps
;
4063 pa_assert(db_fix
->min_step
<= db_fix
->max_step
);
4064 nsteps
= db_fix
->max_step
- db_fix
->min_step
+ 1;
4066 buf
= pa_strbuf_new();
4067 for (i
= 0; i
< nsteps
; ++i
)
4068 pa_strbuf_printf(buf
, "[%li]:%0.2f ", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
4070 db_values
= pa_strbuf_tostring_free(buf
);
4073 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4074 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
4076 pa_xfree(db_values
);
4079 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
4080 pa_alsa_profile_set
*ps
;
4083 pa_alsa_decibel_fix
*db_fix
;
4088 static pa_config_item items
[] = {
4090 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
4093 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
4094 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
4095 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
4096 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
4097 { "element-input", mapping_parse_element
, NULL
, NULL
},
4098 { "element-output", mapping_parse_element
, NULL
, NULL
},
4099 { "direction", mapping_parse_direction
, NULL
, NULL
},
4101 /* Shared by [Mapping ...] and [Profile ...] */
4102 { "description", mapping_parse_description
, NULL
, NULL
},
4103 { "priority", mapping_parse_priority
, NULL
, NULL
},
4106 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
4107 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
4108 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
4110 /* [DecibelFix ...] */
4111 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
4112 { NULL
, NULL
, NULL
, NULL
}
4115 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
4116 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4117 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4118 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4119 ps
->input_paths
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4120 ps
->output_paths
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4122 items
[0].data
= &ps
->auto_profiles
;
4125 fname
= "default.conf";
4127 fn
= pa_maybe_prefix_path(fname
,
4128 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
4129 PA_ALSA_PROFILE_SETS_DIR
);
4131 r
= pa_config_parse(fn
, NULL
, items
, NULL
, ps
);
4137 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4138 if (mapping_verify(m
, bonus
) < 0)
4141 if (ps
->auto_profiles
)
4142 profile_set_add_auto(ps
);
4144 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4145 if (profile_verify(p
) < 0)
4148 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4149 if (decibel_fix_verify(db_fix
) < 0)
4155 pa_alsa_profile_set_free(ps
);
4159 static void profile_finalize_probing(pa_alsa_profile
*to_be_finalized
, pa_alsa_profile
*next
) {
4163 if (!to_be_finalized
)
4166 if (to_be_finalized
->output_mappings
)
4167 PA_IDXSET_FOREACH(m
, to_be_finalized
->output_mappings
, idx
) {
4172 if (to_be_finalized
->supported
)
4175 /* If this mapping is also in the next profile, we won't close the
4176 * pcm handle here, because it would get immediately reopened
4178 if (next
&& next
->output_mappings
&& pa_idxset_get_by_data(next
->output_mappings
, m
, NULL
))
4181 snd_pcm_close(m
->output_pcm
);
4182 m
->output_pcm
= NULL
;
4185 if (to_be_finalized
->input_mappings
)
4186 PA_IDXSET_FOREACH(m
, to_be_finalized
->input_mappings
, idx
) {
4191 if (to_be_finalized
->supported
)
4194 /* If this mapping is also in the next profile, we won't close the
4195 * pcm handle here, because it would get immediately reopened
4197 if (next
&& next
->input_mappings
&& pa_idxset_get_by_data(next
->input_mappings
, m
, NULL
))
4200 snd_pcm_close(m
->input_pcm
);
4201 m
->input_pcm
= NULL
;
4205 static snd_pcm_t
* mapping_open_pcm(pa_alsa_mapping
*m
,
4206 const pa_sample_spec
*ss
,
4209 unsigned default_n_fragments
,
4210 unsigned default_fragment_size_msec
) {
4212 pa_sample_spec try_ss
= *ss
;
4213 pa_channel_map try_map
= m
->channel_map
;
4214 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
4216 try_ss
.channels
= try_map
.channels
;
4219 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
4220 pa_frame_size(&try_ss
);
4221 try_buffer_size
= default_n_fragments
* try_period_size
;
4223 return pa_alsa_open_by_template(
4224 m
->device_strings
, dev_id
, NULL
, &try_ss
,
4225 &try_map
, mode
, &try_period_size
,
4226 &try_buffer_size
, 0, NULL
, NULL
, TRUE
);
4229 static void paths_drop_unsupported(pa_hashmap
* h
) {
4236 p
= pa_hashmap_iterate(h
, &state
, &key
);
4238 if (p
->supported
<= 0) {
4239 pa_hashmap_remove(h
, key
);
4240 pa_alsa_path_free(p
);
4242 p
= pa_hashmap_iterate(h
, &state
, &key
);
4246 void pa_alsa_profile_set_probe(
4247 pa_alsa_profile_set
*ps
,
4249 const pa_sample_spec
*ss
,
4250 unsigned default_n_fragments
,
4251 unsigned default_fragment_size_msec
) {
4254 pa_alsa_profile
*p
, *last
= NULL
;
4264 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
4267 /* Skip if this is already marked that it is supported (i.e. from the config file) */
4268 if (!p
->supported
) {
4270 pa_log_debug("Looking at profile %s", p
->name
);
4271 profile_finalize_probing(last
, p
);
4272 p
->supported
= TRUE
;
4274 /* Check if we can open all new ones */
4275 if (p
->output_mappings
)
4276 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4281 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
4282 if (!(m
->output_pcm
= mapping_open_pcm(m
, ss
, dev_id
,
4283 SND_PCM_STREAM_PLAYBACK
,
4284 default_n_fragments
,
4285 default_fragment_size_msec
))) {
4286 p
->supported
= FALSE
;
4291 if (p
->input_mappings
&& p
->supported
)
4292 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4297 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
4298 if (!(m
->input_pcm
= mapping_open_pcm(m
, ss
, dev_id
,
4299 SND_PCM_STREAM_CAPTURE
,
4300 default_n_fragments
,
4301 default_fragment_size_msec
))) {
4302 p
->supported
= FALSE
;
4313 pa_log_debug("Profile %s supported.", p
->name
);
4315 if (p
->output_mappings
)
4316 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
4318 mapping_paths_probe(m
, p
, PA_ALSA_DIRECTION_OUTPUT
);
4320 if (p
->input_mappings
)
4321 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
4323 mapping_paths_probe(m
, p
, PA_ALSA_DIRECTION_INPUT
);
4327 profile_finalize_probing(last
, NULL
);
4329 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4330 if (!p
->supported
) {
4331 pa_hashmap_remove(ps
->profiles
, p
->name
);
4335 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4336 if (m
->supported
<= 0) {
4337 pa_hashmap_remove(ps
->mappings
, m
->name
);
4341 paths_drop_unsupported(ps
->input_paths
);
4342 paths_drop_unsupported(ps
->output_paths
);
4347 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
4350 pa_alsa_decibel_fix
*db_fix
;
4355 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4358 pa_yes_no(ps
->auto_profiles
),
4359 pa_yes_no(ps
->probed
),
4360 pa_hashmap_size(ps
->mappings
),
4361 pa_hashmap_size(ps
->profiles
),
4362 pa_hashmap_size(ps
->decibel_fixes
));
4364 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4365 pa_alsa_mapping_dump(m
);
4367 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4368 pa_alsa_profile_dump(p
);
4370 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4371 pa_alsa_decibel_fix_dump(db_fix
);
4374 static pa_device_port
* device_port_alsa_init(pa_hashmap
*ports
,
4376 const char* description
,
4378 pa_alsa_setting
*setting
,
4379 pa_card_profile
*cp
,
4383 pa_device_port
* p
= pa_hashmap_get(ports
, name
);
4385 pa_alsa_port_data
*data
;
4387 p
= pa_device_port_new(core
, name
, description
, sizeof(pa_alsa_port_data
));
4389 pa_hashmap_put(ports
, p
->name
, p
);
4391 data
= PA_DEVICE_PORT_DATA(p
);
4393 data
->setting
= setting
;
4397 p
->is_input
|= path
->direction
== PA_ALSA_DIRECTION_ANY
|| path
->direction
== PA_ALSA_DIRECTION_INPUT
;
4398 p
->is_output
|= path
->direction
== PA_ALSA_DIRECTION_ANY
|| path
->direction
== PA_ALSA_DIRECTION_OUTPUT
;
4401 pa_hashmap_put(p
->profiles
, cp
->name
, cp
);
4404 pa_hashmap_put(extra
, p
->name
, p
);
4405 pa_device_port_ref(p
);
4411 void pa_alsa_path_set_add_ports(
4412 pa_alsa_path_set
*ps
,
4413 pa_card_profile
*cp
,
4426 PA_HASHMAP_FOREACH(path
, ps
->paths
, state
) {
4427 if (!path
->settings
|| !path
->settings
->next
) {
4428 /* If there is no or just one setting we only need a
4430 pa_device_port
*port
= device_port_alsa_init(ports
, path
->name
,
4431 path
->description
, path
, path
->settings
, cp
, extra
, core
);
4432 port
->priority
= path
->priority
* 100;
4436 PA_LLIST_FOREACH(s
, path
->settings
) {
4437 pa_device_port
*port
;
4440 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4442 if (s
->description
[0])
4443 d
= pa_sprintf_malloc("%s / %s", path
->description
, s
->description
);
4445 d
= pa_xstrdup(path
->description
);
4447 port
= device_port_alsa_init(ports
, n
, d
, path
, s
, cp
, extra
, core
);
4448 port
->priority
= path
->priority
* 100 + s
->priority
;
4457 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
, pa_card
*card
) {
4463 if (ps
->paths
&& pa_hashmap_size(ps
->paths
) > 0) {
4465 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4466 pa_alsa_path_set_add_ports(ps
, NULL
, card
->ports
, *p
, card
->core
);
4469 pa_log_debug("Added %u ports", *p
? pa_hashmap_size(*p
) : 0);