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][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(
1788 const char *filename
,
1790 const char *section
,
1796 pa_alsa_path
*p
= userdata
;
1801 if (!(e
= element_get(p
, section
, TRUE
))) {
1802 pa_log("[%s:%u] Switch makes no sense in '%s'", filename
, line
, section
);
1806 if (pa_streq(rvalue
, "ignore"))
1807 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1808 else if (pa_streq(rvalue
, "mute"))
1809 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1810 else if (pa_streq(rvalue
, "off"))
1811 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1812 else if (pa_streq(rvalue
, "on"))
1813 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1814 else if (pa_streq(rvalue
, "select"))
1815 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1817 pa_log("[%s:%u] Switch invalid of '%s'", filename
, line
, section
);
1824 static int element_parse_volume(
1825 const char *filename
,
1827 const char *section
,
1833 pa_alsa_path
*p
= userdata
;
1838 if (!(e
= element_get(p
, section
, TRUE
))) {
1839 pa_log("[%s:%u] Volume makes no sense in '%s'", filename
, line
, section
);
1843 if (pa_streq(rvalue
, "ignore"))
1844 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1845 else if (pa_streq(rvalue
, "merge"))
1846 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1847 else if (pa_streq(rvalue
, "off"))
1848 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1849 else if (pa_streq(rvalue
, "zero"))
1850 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1854 if (pa_atou(rvalue
, &constant
) >= 0) {
1855 e
->volume_use
= PA_ALSA_VOLUME_CONSTANT
;
1856 e
->constant_volume
= constant
;
1858 pa_log("[%s:%u] Volume invalid of '%s'", filename
, line
, section
);
1866 static int element_parse_enumeration(
1867 const char *filename
,
1869 const char *section
,
1875 pa_alsa_path
*p
= userdata
;
1880 if (!(e
= element_get(p
, section
, TRUE
))) {
1881 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename
, line
, section
);
1885 if (pa_streq(rvalue
, "ignore"))
1886 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1887 else if (pa_streq(rvalue
, "select"))
1888 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1890 pa_log("[%s:%u] Enumeration invalid of '%s'", filename
, line
, section
);
1897 static int option_parse_priority(
1898 const char *filename
,
1900 const char *section
,
1906 pa_alsa_path
*p
= userdata
;
1912 if (!(o
= option_get(p
, section
))) {
1913 pa_log("[%s:%u] Priority makes no sense in '%s'", filename
, line
, section
);
1917 if (pa_atou(rvalue
, &prio
) < 0) {
1918 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
1926 static int option_parse_name(
1927 const char *filename
,
1929 const char *section
,
1935 pa_alsa_path
*p
= userdata
;
1940 if (!(o
= option_get(p
, section
))) {
1941 pa_log("[%s:%u] Name makes no sense in '%s'", filename
, line
, section
);
1946 o
->name
= pa_xstrdup(rvalue
);
1951 static int element_parse_required(
1952 const char *filename
,
1954 const char *section
,
1960 pa_alsa_path
*p
= userdata
;
1964 pa_alsa_required_t req
;
1968 e
= element_get(p
, section
, TRUE
);
1969 o
= option_get(p
, section
);
1970 j
= jack_get(p
, section
);
1971 if (!e
&& !o
&& !j
) {
1972 pa_log("[%s:%u] Required makes no sense in '%s'", filename
, line
, section
);
1976 if (pa_streq(rvalue
, "ignore"))
1977 req
= PA_ALSA_REQUIRED_IGNORE
;
1978 else if (pa_streq(rvalue
, "switch") && e
)
1979 req
= PA_ALSA_REQUIRED_SWITCH
;
1980 else if (pa_streq(rvalue
, "volume") && e
)
1981 req
= PA_ALSA_REQUIRED_VOLUME
;
1982 else if (pa_streq(rvalue
, "enumeration"))
1983 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1984 else if (pa_streq(rvalue
, "any"))
1985 req
= PA_ALSA_REQUIRED_ANY
;
1987 pa_log("[%s:%u] Required invalid of '%s'", filename
, line
, section
);
1991 if (pa_streq(lvalue
, "required-absent")) {
1993 e
->required_absent
= req
;
1995 o
->required_absent
= req
;
1997 j
->required_absent
= req
;
1999 else if (pa_streq(lvalue
, "required-any")) {
2001 e
->required_any
= req
;
2002 e
->path
->has_req_any
= TRUE
;
2005 o
->required_any
= req
;
2006 o
->element
->path
->has_req_any
= TRUE
;
2009 j
->required_any
= req
;
2010 j
->path
->has_req_any
= TRUE
;
2026 static int element_parse_direction(
2027 const char *filename
,
2029 const char *section
,
2035 pa_alsa_path
*p
= userdata
;
2040 if (!(e
= element_get(p
, section
, TRUE
))) {
2041 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
2045 if (pa_streq(rvalue
, "playback"))
2046 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
2047 else if (pa_streq(rvalue
, "capture"))
2048 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
2050 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
2057 static int element_parse_direction_try_other(
2058 const char *filename
,
2060 const char *section
,
2066 pa_alsa_path
*p
= userdata
;
2070 if (!(e
= element_get(p
, section
, TRUE
))) {
2071 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
2075 if ((yes
= pa_parse_boolean(rvalue
)) < 0) {
2076 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
2080 e
->direction_try_other
= !!yes
;
2084 static int element_parse_volume_limit(
2085 const char *filename
,
2087 const char *section
,
2093 pa_alsa_path
*p
= userdata
;
2097 if (!(e
= element_get(p
, section
, TRUE
))) {
2098 pa_log("[%s:%u] volume-limit makes no sense in '%s'", filename
, line
, section
);
2102 if (pa_atol(rvalue
, &volume_limit
) < 0 || volume_limit
< 0) {
2103 pa_log("[%s:%u] Invalid value for volume-limit", filename
, line
);
2107 e
->volume_limit
= volume_limit
;
2111 static pa_channel_position_mask_t
parse_mask(const char *m
) {
2112 pa_channel_position_mask_t v
;
2114 if (pa_streq(m
, "all-left"))
2115 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
2116 else if (pa_streq(m
, "all-right"))
2117 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
2118 else if (pa_streq(m
, "all-center"))
2119 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
2120 else if (pa_streq(m
, "all-front"))
2121 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
2122 else if (pa_streq(m
, "all-rear"))
2123 v
= PA_CHANNEL_POSITION_MASK_REAR
;
2124 else if (pa_streq(m
, "all-side"))
2125 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
2126 else if (pa_streq(m
, "all-top"))
2127 v
= PA_CHANNEL_POSITION_MASK_TOP
;
2128 else if (pa_streq(m
, "all-no-lfe"))
2129 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
2130 else if (pa_streq(m
, "all"))
2131 v
= PA_CHANNEL_POSITION_MASK_ALL
;
2133 pa_channel_position_t p
;
2135 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
2138 v
= PA_CHANNEL_POSITION_MASK(p
);
2144 static int element_parse_override_map(
2145 const char *filename
,
2147 const char *section
,
2153 pa_alsa_path
*p
= userdata
;
2155 const char *state
= NULL
;
2159 if (!(e
= element_get(p
, section
, TRUE
))) {
2160 pa_log("[%s:%u] Override map makes no sense in '%s'", filename
, line
, section
);
2164 while ((n
= pa_split(rvalue
, ",", &state
))) {
2165 pa_channel_position_mask_t m
;
2170 if ((m
= parse_mask(n
)) == 0) {
2171 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename
, line
, n
, section
);
2177 if (pa_streq(lvalue
, "override-map.1"))
2178 e
->masks
[i
++][0] = m
;
2180 e
->masks
[i
++][1] = m
;
2182 /* Later on we might add override-map.3 and so on here ... */
2187 e
->override_map
= TRUE
;
2192 static int jack_parse_state(
2193 const char *filename
,
2195 const char *section
,
2201 pa_alsa_path
*p
= userdata
;
2203 pa_port_available_t pa
;
2205 if (!(j
= jack_get(p
, section
))) {
2206 pa_log("[%s:%u] state makes no sense in '%s'", filename
, line
, section
);
2210 if (!strcmp(rvalue
,"yes"))
2211 pa
= PA_PORT_AVAILABLE_YES
;
2212 else if (!strcmp(rvalue
,"no"))
2213 pa
= PA_PORT_AVAILABLE_NO
;
2214 else if (!strcmp(rvalue
,"unknown"))
2215 pa
= PA_PORT_AVAILABLE_UNKNOWN
;
2217 pa_log("[%s:%u] state must be 'yes','no' or 'unknown' in '%s'", filename
, line
, section
);
2221 if (!strcmp(lvalue
, "state.unplugged"))
2222 j
->state_unplugged
= pa
;
2224 j
->state_plugged
= pa
;
2225 pa_assert(!strcmp(lvalue
, "state.plugged"));
2231 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
2232 snd_mixer_selem_id_t
*sid
;
2233 snd_mixer_elem_t
*me
;
2239 SELEM_INIT(sid
, e
->alsa_name
);
2240 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2241 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2245 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
2247 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
2248 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
2250 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
2253 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2256 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
2258 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
2259 pa_log_warn("Failed to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2265 int pa_alsa_setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
2272 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
2273 element_set_option(o
->element
, m
, o
->alsa_idx
);
2278 static int option_verify(pa_alsa_option
*o
) {
2279 static const struct description_map well_known_descriptions
[] = {
2280 { "input", N_("Input") },
2281 { "input-docking", N_("Docking Station Input") },
2282 { "input-docking-microphone", N_("Docking Station Microphone") },
2283 { "input-docking-linein", N_("Docking Station Line In") },
2284 { "input-linein", N_("Line In") },
2285 { "input-microphone", N_("Microphone") },
2286 { "input-microphone-front", N_("Front Microphone") },
2287 { "input-microphone-rear", N_("Rear Microphone") },
2288 { "input-microphone-external", N_("External Microphone") },
2289 { "input-microphone-internal", N_("Internal Microphone") },
2290 { "input-radio", N_("Radio") },
2291 { "input-video", N_("Video") },
2292 { "input-agc-on", N_("Automatic Gain Control") },
2293 { "input-agc-off", N_("No Automatic Gain Control") },
2294 { "input-boost-on", N_("Boost") },
2295 { "input-boost-off", N_("No Boost") },
2296 { "output-amplifier-on", N_("Amplifier") },
2297 { "output-amplifier-off", N_("No Amplifier") },
2298 { "output-bass-boost-on", N_("Bass Boost") },
2299 { "output-bass-boost-off", N_("No Bass Boost") },
2300 { "output-speaker", N_("Speaker") },
2301 { "output-headphones", N_("Headphones") }
2307 pa_log("No name set for option %s", o
->alsa_name
);
2311 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
2312 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
2313 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
2317 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
2318 !pa_streq(o
->alsa_name
, "on") &&
2319 !pa_streq(o
->alsa_name
, "off")) {
2320 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
2324 if (!o
->description
)
2325 o
->description
= pa_xstrdup(lookup_description(o
->name
,
2326 well_known_descriptions
,
2327 PA_ELEMENTSOF(well_known_descriptions
)));
2328 if (!o
->description
)
2329 o
->description
= pa_xstrdup(o
->name
);
2334 static int element_verify(pa_alsa_element
*e
) {
2339 // 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);
2340 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
2341 (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required_any
== e
->required_absent
) ||
2342 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) ||
2343 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
2344 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
2348 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
2349 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
2353 PA_LLIST_FOREACH(o
, e
->options
)
2354 if (option_verify(o
) < 0)
2360 static int path_verify(pa_alsa_path
*p
) {
2361 static const struct description_map well_known_descriptions
[] = {
2362 { "analog-input", N_("Analog Input") },
2363 { "analog-input-microphone", N_("Microphone") },
2364 { "analog-input-microphone-front", N_("Front Microphone") },
2365 { "analog-input-microphone-rear", N_("Rear Microphone") },
2366 { "analog-input-microphone-dock", N_("Dock Microphone") },
2367 { "analog-input-microphone-internal", N_("Internal Microphone") },
2368 { "analog-input-linein", N_("Line In") },
2369 { "analog-input-radio", N_("Radio") },
2370 { "analog-input-video", N_("Video") },
2371 { "analog-output", N_("Analog Output") },
2372 { "analog-output-headphones", N_("Headphones") },
2373 { "analog-output-lfe-on-mono", N_("LFE on Separate Mono Output") },
2374 { "analog-output-lineout", N_("Line Out") },
2375 { "analog-output-mono", N_("Analog Mono Output") },
2376 { "analog-output-speaker", N_("Speakers") },
2377 { "hdmi-output", N_("HDMI / DisplayPort") },
2378 { "iec958-stereo-output", N_("Digital Output (S/PDIF)") },
2379 { "iec958-passthrough-output", N_("Digital Passthrough (S/PDIF)") }
2386 PA_LLIST_FOREACH(e
, p
->elements
)
2387 if (element_verify(e
) < 0)
2390 if (!p
->description
)
2391 p
->description
= pa_xstrdup(lookup_description(p
->name
,
2392 well_known_descriptions
,
2393 PA_ELEMENTSOF(well_known_descriptions
)));
2395 if (!p
->description
)
2396 p
->description
= pa_xstrdup(p
->name
);
2401 static const char *get_default_paths_dir(void) {
2402 if (pa_run_from_build_tree())
2403 return PA_BUILDDIR
"/modules/alsa/mixer/paths/";
2405 return PA_ALSA_PATHS_DIR
;
2408 pa_alsa_path
* pa_alsa_path_new(const char *paths_dir
, const char *fname
, pa_alsa_direction_t direction
) {
2414 pa_config_item items
[] = {
2416 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
2417 { "description", pa_config_parse_string
, NULL
, "General" },
2418 { "name", pa_config_parse_string
, NULL
, "General" },
2421 { "priority", option_parse_priority
, NULL
, NULL
},
2422 { "name", option_parse_name
, NULL
, NULL
},
2425 { "state.plugged", jack_parse_state
, NULL
, NULL
},
2426 { "state.unplugged", jack_parse_state
, NULL
, NULL
},
2429 { "switch", element_parse_switch
, NULL
, NULL
},
2430 { "volume", element_parse_volume
, NULL
, NULL
},
2431 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
2432 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
2433 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
2434 /* ... later on we might add override-map.3 and so on here ... */
2435 { "required", element_parse_required
, NULL
, NULL
},
2436 { "required-any", element_parse_required
, NULL
, NULL
},
2437 { "required-absent", element_parse_required
, NULL
, NULL
},
2438 { "direction", element_parse_direction
, NULL
, NULL
},
2439 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2440 { "volume-limit", element_parse_volume_limit
, NULL
, NULL
},
2441 { NULL
, NULL
, NULL
, NULL
}
2446 p
= pa_xnew0(pa_alsa_path
, 1);
2447 n
= pa_path_get_filename(fname
);
2448 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2449 p
->direction
= direction
;
2451 items
[0].data
= &p
->priority
;
2452 items
[1].data
= &p
->description
;
2453 items
[2].data
= &p
->name
;
2456 paths_dir
= get_default_paths_dir();
2458 fn
= pa_maybe_prefix_path(fname
, paths_dir
);
2460 r
= pa_config_parse(fn
, NULL
, items
, p
);
2466 if (path_verify(p
) < 0)
2472 pa_alsa_path_free(p
);
2476 pa_alsa_path
*pa_alsa_path_synthesize(const char *element
, pa_alsa_direction_t direction
) {
2482 p
= pa_xnew0(pa_alsa_path
, 1);
2483 p
->name
= pa_xstrdup(element
);
2484 p
->direction
= direction
;
2486 e
= pa_xnew0(pa_alsa_element
, 1);
2488 e
->alsa_name
= pa_xstrdup(element
);
2489 e
->direction
= direction
;
2490 e
->volume_limit
= -1;
2492 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2493 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2495 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2496 p
->last_element
= e
;
2500 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2501 pa_alsa_option
*o
, *n
;
2505 for (o
= e
->options
; o
; o
= n
) {
2508 if (o
->alsa_idx
< 0) {
2509 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2515 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2516 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2517 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2520 static void path_drop_unsupported(pa_alsa_path
*p
) {
2521 pa_alsa_element
*e
, *n
;
2525 for (e
= p
->elements
; e
; e
= n
) {
2528 if (!element_drop_unsupported(e
)) {
2529 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2535 static void path_make_options_unique(pa_alsa_path
*p
) {
2537 pa_alsa_option
*o
, *u
;
2539 PA_LLIST_FOREACH(e
, p
->elements
) {
2540 PA_LLIST_FOREACH(o
, e
->options
) {
2544 for (u
= o
->next
; u
; u
= u
->next
)
2545 if (pa_streq(u
->name
, o
->name
))
2551 m
= pa_xstrdup(o
->name
);
2553 /* OK, this name is not unique, hence let's rename */
2554 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2557 if (!pa_streq(u
->name
, m
))
2560 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2564 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2565 pa_xfree(u
->description
);
2566 u
->description
= nd
;
2576 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2579 for (; e
; e
= e
->next
)
2580 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2581 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2587 for (o
= e
->options
; o
; o
= o
->next
) {
2591 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2592 s
->options
= pa_idxset_copy(template->options
);
2593 s
->name
= pa_sprintf_malloc("%s+%s", template->name
, o
->name
);
2595 (template->description
[0] && o
->description
[0])
2596 ? pa_sprintf_malloc("%s / %s", template->description
, o
->description
)
2597 : (template->description
[0]
2598 ? pa_xstrdup(template->description
)
2599 : pa_xstrdup(o
->description
));
2601 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2603 s
= pa_xnew0(pa_alsa_setting
, 1);
2604 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2605 s
->name
= pa_xstrdup(o
->name
);
2606 s
->description
= pa_xstrdup(o
->description
);
2607 s
->priority
= o
->priority
;
2610 pa_idxset_put(s
->options
, o
, NULL
);
2612 if (element_create_settings(e
->next
, s
))
2613 /* This is not a leaf, so let's get rid of it */
2616 /* This is a leaf, so let's add it */
2617 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2619 e
->path
->last_setting
= s
;
2626 static void path_create_settings(pa_alsa_path
*p
) {
2629 element_create_settings(p
->elements
, NULL
);
2632 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_hctl_t
*hctl
, pa_bool_t ignore_dB
) {
2635 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2636 pa_channel_position_t t
;
2637 pa_channel_position_mask_t path_volume_channels
= 0;
2643 return p
->supported
? 0 : -1;
2649 pa_log_debug("Probing path '%s'", p
->name
);
2651 PA_LLIST_FOREACH(j
, p
->jacks
) {
2652 if (jack_probe(j
, hctl
) < 0) {
2653 p
->supported
= FALSE
;
2654 pa_log_debug("Probe of jack '%s' failed.", j
->alsa_name
);
2657 pa_log_debug("Probe of jack '%s' succeeded (%s)", j
->alsa_name
, j
->has_control
? "found!" : "not found");
2660 PA_LLIST_FOREACH(e
, p
->elements
) {
2661 if (element_probe(e
, m
) < 0) {
2662 p
->supported
= FALSE
;
2663 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2666 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
);
2671 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2673 if (!p
->has_volume
) {
2674 p
->min_volume
= e
->min_volume
;
2675 p
->max_volume
= e
->max_volume
;
2679 if (!p
->has_volume
) {
2680 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2681 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2682 min_dB
[t
] = e
->min_dB
;
2683 max_dB
[t
] = e
->max_dB
;
2684 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2691 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2692 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2693 min_dB
[t
] += e
->min_dB
;
2694 max_dB
[t
] += e
->max_dB
;
2695 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2698 /* Hmm, there's another element before us
2699 * which cannot do dB volumes, so we we need
2700 * to 'neutralize' this slider */
2701 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2702 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2705 } else if (p
->has_volume
) {
2706 /* We can't use this volume, so let's ignore it */
2707 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2708 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2710 p
->has_volume
= TRUE
;
2713 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2717 if (p
->has_req_any
&& !p
->req_any_present
) {
2718 p
->supported
= FALSE
;
2719 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2723 path_drop_unsupported(p
);
2724 path_make_options_unique(p
);
2725 path_create_settings(p
);
2727 p
->supported
= TRUE
;
2729 p
->min_dB
= INFINITY
;
2730 p
->max_dB
= -INFINITY
;
2732 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2733 if (path_volume_channels
& PA_CHANNEL_POSITION_MASK(t
)) {
2734 if (p
->min_dB
> min_dB
[t
])
2735 p
->min_dB
= min_dB
[t
];
2737 if (p
->max_dB
< max_dB
[t
])
2738 p
->max_dB
= max_dB
[t
];
2745 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2748 pa_log_debug("Setting %s (%s) priority=%u",
2750 pa_strnull(s
->description
),
2754 void pa_alsa_jack_dump(pa_alsa_jack
*j
) {
2757 pa_log_debug("Jack %s, alsa_name='%s', detection %s", j
->name
, j
->alsa_name
, j
->has_control
? "possible" : "unavailable");
2760 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2763 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2765 pa_strnull(o
->name
),
2766 pa_strnull(o
->description
),
2771 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2775 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",
2785 (long long unsigned) e
->merged_mask
,
2787 pa_yes_no(e
->override_map
));
2789 PA_LLIST_FOREACH(o
, e
->options
)
2790 pa_alsa_option_dump(o
);
2793 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2799 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2800 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2802 pa_strnull(p
->description
),
2805 pa_yes_no(p
->probed
),
2806 pa_yes_no(p
->supported
),
2807 pa_yes_no(p
->has_mute
),
2808 pa_yes_no(p
->has_volume
),
2809 pa_yes_no(p
->has_dB
),
2810 p
->min_volume
, p
->max_volume
,
2811 p
->min_dB
, p
->max_dB
);
2813 PA_LLIST_FOREACH(e
, p
->elements
)
2814 pa_alsa_element_dump(e
);
2816 PA_LLIST_FOREACH(j
, p
->jacks
)
2817 pa_alsa_jack_dump(j
);
2819 PA_LLIST_FOREACH(s
, p
->settings
)
2820 pa_alsa_setting_dump(s
);
2823 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2824 snd_mixer_selem_id_t
*sid
;
2825 snd_mixer_elem_t
*me
;
2831 SELEM_INIT(sid
, e
->alsa_name
);
2832 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2833 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2837 snd_mixer_elem_set_callback(me
, cb
);
2838 snd_mixer_elem_set_callback_private(me
, userdata
);
2841 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2848 PA_LLIST_FOREACH(e
, p
->elements
)
2849 element_set_callback(e
, m
, cb
, userdata
);
2852 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2860 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
2861 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2864 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
, const char *paths_dir
) {
2865 pa_alsa_path_set
*ps
;
2866 char **pn
= NULL
, **en
= NULL
, **ie
;
2867 pa_alsa_decibel_fix
*db_fix
;
2868 void *state
, *state2
;
2872 pa_assert(m
->profile_set
);
2873 pa_assert(m
->profile_set
->decibel_fixes
);
2874 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2876 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2879 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2880 ps
->direction
= direction
;
2881 ps
->paths
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2883 if (direction
== PA_ALSA_DIRECTION_OUTPUT
) {
2884 pn
= m
->output_path_names
;
2885 cache
= m
->profile_set
->output_paths
;
2887 else if (direction
== PA_ALSA_DIRECTION_INPUT
) {
2888 pn
= m
->input_path_names
;
2889 cache
= m
->profile_set
->input_paths
;
2895 for (in
= pn
; *in
; in
++) {
2896 pa_alsa_path
*p
= NULL
;
2897 pa_bool_t duplicate
= FALSE
;
2900 for (kn
= pn
; kn
< in
; kn
++)
2901 if (pa_streq(*kn
, *in
)) {
2909 p
= pa_hashmap_get(cache
, *in
);
2911 char *fn
= pa_sprintf_malloc("%s.conf", *in
);
2912 p
= pa_alsa_path_new(paths_dir
, fn
, direction
);
2915 pa_hashmap_put(cache
, *in
, p
);
2917 pa_assert(pa_hashmap_get(cache
, *in
) == p
);
2919 pa_hashmap_put(ps
->paths
, p
, p
);
2926 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2927 en
= m
->output_element
;
2928 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2929 en
= m
->input_element
;
2932 pa_alsa_path_set_free(ps
);
2936 for (ie
= en
; *ie
; ie
++) {
2940 p
= pa_alsa_path_synthesize(*ie
, direction
);
2942 /* Mark all other passed elements for require-absent */
2943 for (je
= en
; *je
; je
++) {
2949 e
= pa_xnew0(pa_alsa_element
, 1);
2951 e
->alsa_name
= pa_xstrdup(*je
);
2952 e
->direction
= direction
;
2953 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2954 e
->volume_limit
= -1;
2956 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2957 p
->last_element
= e
;
2960 pa_hashmap_put(ps
->paths
, *ie
, p
);
2964 /* Assign decibel fixes to elements. */
2965 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2968 PA_HASHMAP_FOREACH(p
, ps
->paths
, state2
) {
2971 PA_LLIST_FOREACH(e
, p
->elements
) {
2972 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2973 /* The profile set that contains the dB fix may be freed
2974 * before the element, so we have to copy the dB fix
2976 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2977 e
->db_fix
->profile_set
= NULL
;
2978 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2979 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
2988 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2993 pa_log_debug("Path Set %p, direction=%i",
2997 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
2998 pa_alsa_path_dump(p
);
3002 static pa_bool_t
options_have_option(pa_alsa_option
*options
, const char *alsa_name
) {
3006 pa_assert(alsa_name
);
3008 PA_LLIST_FOREACH(o
, options
) {
3009 if (pa_streq(o
->alsa_name
, alsa_name
))
3015 static pa_bool_t
enumeration_is_subset(pa_alsa_option
*a_options
, pa_alsa_option
*b_options
) {
3016 pa_alsa_option
*oa
, *ob
;
3018 if (!a_options
) return TRUE
;
3019 if (!b_options
) return FALSE
;
3021 /* If there is an option A offers that B does not, then A is not a subset of B. */
3022 PA_LLIST_FOREACH(oa
, a_options
) {
3023 pa_bool_t found
= FALSE
;
3024 PA_LLIST_FOREACH(ob
, b_options
) {
3025 if (pa_streq(oa
->alsa_name
, ob
->alsa_name
)) {
3037 * Compares two elements to see if a is a subset of b
3039 static pa_bool_t
element_is_subset(pa_alsa_element
*a
, pa_alsa_element
*b
, snd_mixer_t
*m
) {
3045 * Every state is a subset of itself (with caveats for volume_limits and options)
3046 * IGNORE is a subset of every other state */
3048 /* Check the volume_use */
3049 if (a
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
3051 /* "Constant" is subset of "Constant" only when their constant values are equal */
3052 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& b
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& a
->constant_volume
!= b
->constant_volume
)
3055 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
3056 if (a
->volume_use
!= b
->volume_use
&& b
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
3059 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
3060 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
3061 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
3062 if (b
->volume_use
== PA_ALSA_VOLUME_MERGE
&& b
->volume_limit
>= 0) {
3065 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
)
3066 a_limit
= a
->constant_volume
;
3067 else if (a
->volume_use
== PA_ALSA_VOLUME_ZERO
) {
3071 int rounding
= (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
? +1 : -1);
3072 a_limit
= decibel_fix_get_step(a
->db_fix
, &dB
, rounding
);
3074 snd_mixer_selem_id_t
*sid
;
3075 snd_mixer_elem_t
*me
;
3077 SELEM_INIT(sid
, a
->alsa_name
);
3078 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
3079 pa_log_warn("Element %s seems to have disappeared.", a
->alsa_name
);
3083 if (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3084 if (snd_mixer_selem_ask_playback_dB_vol(me
, dB
, +1, &a_limit
) < 0)
3087 if (snd_mixer_selem_ask_capture_dB_vol(me
, dB
, -1, &a_limit
) < 0)
3091 } else if (a
->volume_use
== PA_ALSA_VOLUME_OFF
)
3092 a_limit
= a
->min_volume
;
3093 else if (a
->volume_use
== PA_ALSA_VOLUME_MERGE
)
3094 a_limit
= a
->volume_limit
;
3096 /* This should never be reached */
3099 if (a_limit
> b
->volume_limit
)
3103 if (a
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
3105 /* If override-maps are different, they're not subsets */
3106 if (a
->n_channels
!= b
->n_channels
)
3108 for (s
= 0; s
< SND_MIXER_SCHN_LAST
; s
++)
3109 if (a
->masks
[s
][a
->n_channels
-1] != b
->masks
[s
][b
->n_channels
-1]) {
3110 pa_log_debug("Element %s is not a subset - mask a: 0x%lx, mask b: 0x%lx, at channel %d",
3111 a
->alsa_name
, a
->masks
[s
][a
->n_channels
-1], b
->masks
[s
][b
->n_channels
-1], s
);
3117 if (a
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
3118 /* "On" is a subset of "Mute".
3119 * "Off" is a subset of "Mute".
3120 * "On" is a subset of "Select", if there is an "Option:On" in B.
3121 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3122 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3124 if (a
->switch_use
!= b
->switch_use
) {
3126 if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
|| a
->switch_use
== PA_ALSA_SWITCH_MUTE
3127 || b
->switch_use
== PA_ALSA_SWITCH_OFF
|| b
->switch_use
== PA_ALSA_SWITCH_ON
)
3130 if (b
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3131 if (a
->switch_use
== PA_ALSA_SWITCH_ON
) {
3132 if (!options_have_option(b
->options
, "on"))
3134 } else if (a
->switch_use
== PA_ALSA_SWITCH_OFF
) {
3135 if (!options_have_option(b
->options
, "off"))
3139 } else if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3140 if (!enumeration_is_subset(a
->options
, b
->options
))
3145 if (a
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
) {
3146 if (b
->enumeration_use
== PA_ALSA_ENUMERATION_IGNORE
)
3148 if (!enumeration_is_subset(a
->options
, b
->options
))
3155 static void path_set_condense(pa_alsa_path_set
*ps
, snd_mixer_t
*m
) {
3162 /* If we only have one path, then don't bother */
3163 if (pa_hashmap_size(ps
->paths
) < 2)
3166 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3170 PA_HASHMAP_FOREACH(p2
, ps
->paths
, state2
) {
3171 pa_alsa_element
*ea
, *eb
;
3172 pa_alsa_jack
*ja
, *jb
;
3173 pa_bool_t is_subset
= TRUE
;
3178 /* If a has a jack that b does not have, a is not a subset */
3179 PA_LLIST_FOREACH(ja
, p
->jacks
) {
3180 pa_bool_t exists
= FALSE
;
3182 if (!ja
->has_control
)
3185 PA_LLIST_FOREACH(jb
, p2
->jacks
) {
3186 if (jb
->has_control
&& !strcmp(jb
->alsa_name
, ja
->alsa_name
)) {
3198 /* Compare the elements of each set... */
3199 pa_assert_se(ea
= p
->elements
);
3200 pa_assert_se(eb
= p2
->elements
);
3203 if (pa_streq(ea
->alsa_name
, eb
->alsa_name
)) {
3204 if (element_is_subset(ea
, eb
, m
)) {
3207 if ((ea
&& !eb
) || (!ea
&& eb
))
3209 else if (!ea
&& !eb
)
3219 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p
->name
, p2
->name
);
3220 pa_hashmap_remove(ps
->paths
, p
);
3227 static pa_alsa_path
* path_set_find_path_by_name(pa_alsa_path_set
*ps
, const char* name
, pa_alsa_path
*ignore
)
3232 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
3233 if (p
!= ignore
&& pa_streq(p
->name
, name
))
3238 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
3239 pa_alsa_path
*p
, *q
;
3240 void *state
, *state2
;
3242 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3246 q
= path_set_find_path_by_name(ps
, p
->name
, p
);
3251 m
= pa_xstrdup(p
->name
);
3253 /* OK, this name is not unique, hence let's rename */
3255 PA_HASHMAP_FOREACH(q
, ps
->paths
, state2
) {
3258 if (!pa_streq(q
->name
, m
))
3261 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
3265 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
3266 pa_xfree(q
->description
);
3267 q
->description
= nd
;
3276 static void mapping_free(pa_alsa_mapping
*m
) {
3280 pa_xfree(m
->description
);
3282 pa_xstrfreev(m
->device_strings
);
3283 pa_xstrfreev(m
->input_path_names
);
3284 pa_xstrfreev(m
->output_path_names
);
3285 pa_xstrfreev(m
->input_element
);
3286 pa_xstrfreev(m
->output_element
);
3287 if (m
->input_path_set
)
3288 pa_alsa_path_set_free(m
->input_path_set
);
3289 if (m
->output_path_set
)
3290 pa_alsa_path_set_free(m
->output_path_set
);
3292 pa_assert(!m
->input_pcm
);
3293 pa_assert(!m
->output_pcm
);
3298 static void profile_free(pa_alsa_profile
*p
) {
3302 pa_xfree(p
->description
);
3304 pa_xstrfreev(p
->input_mapping_names
);
3305 pa_xstrfreev(p
->output_mapping_names
);
3307 if (p
->input_mappings
)
3308 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
3310 if (p
->output_mappings
)
3311 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
3316 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
3319 if (ps
->input_paths
) {
3322 while ((p
= pa_hashmap_steal_first(ps
->input_paths
)))
3323 pa_alsa_path_free(p
);
3325 pa_hashmap_free(ps
->input_paths
, NULL
, NULL
);
3328 if (ps
->output_paths
) {
3331 while ((p
= pa_hashmap_steal_first(ps
->output_paths
)))
3332 pa_alsa_path_free(p
);
3334 pa_hashmap_free(ps
->output_paths
, NULL
, NULL
);
3340 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
3343 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
3349 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
3352 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
3355 if (ps
->decibel_fixes
) {
3356 pa_alsa_decibel_fix
*db_fix
;
3358 while ((db_fix
= pa_hashmap_steal_first(ps
->decibel_fixes
)))
3359 decibel_fix_free(db_fix
);
3361 pa_hashmap_free(ps
->decibel_fixes
, NULL
, NULL
);
3367 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
3370 if (!pa_startswith(name
, "Mapping "))
3375 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
3378 m
= pa_xnew0(pa_alsa_mapping
, 1);
3379 m
->profile_set
= ps
;
3380 m
->name
= pa_xstrdup(name
);
3381 pa_channel_map_init(&m
->channel_map
);
3383 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
3388 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
3391 if (!pa_startswith(name
, "Profile "))
3396 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
3399 p
= pa_xnew0(pa_alsa_profile
, 1);
3400 p
->profile_set
= ps
;
3401 p
->name
= pa_xstrdup(name
);
3403 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3408 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
3409 pa_alsa_decibel_fix
*db_fix
;
3411 if (!pa_startswith(name
, "DecibelFix "))
3416 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
3419 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
3420 db_fix
->profile_set
= ps
;
3421 db_fix
->name
= pa_xstrdup(name
);
3423 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
3428 static int mapping_parse_device_strings(
3429 const char *filename
,
3431 const char *section
,
3437 pa_alsa_profile_set
*ps
= userdata
;
3442 if (!(m
= mapping_get(ps
, section
))) {
3443 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3447 pa_xstrfreev(m
->device_strings
);
3448 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
3449 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
3456 static int mapping_parse_channel_map(
3457 const char *filename
,
3459 const char *section
,
3465 pa_alsa_profile_set
*ps
= userdata
;
3470 if (!(m
= mapping_get(ps
, section
))) {
3471 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3475 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
3476 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
3483 static int mapping_parse_paths(
3484 const char *filename
,
3486 const char *section
,
3492 pa_alsa_profile_set
*ps
= userdata
;
3497 if (!(m
= mapping_get(ps
, section
))) {
3498 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3502 if (pa_streq(lvalue
, "paths-input")) {
3503 pa_xstrfreev(m
->input_path_names
);
3504 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
3506 pa_xstrfreev(m
->output_path_names
);
3507 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
3513 static int mapping_parse_element(
3514 const char *filename
,
3516 const char *section
,
3522 pa_alsa_profile_set
*ps
= userdata
;
3527 if (!(m
= mapping_get(ps
, section
))) {
3528 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3532 if (pa_streq(lvalue
, "element-input")) {
3533 pa_xstrfreev(m
->input_element
);
3534 m
->input_element
= pa_split_spaces_strv(rvalue
);
3536 pa_xstrfreev(m
->output_element
);
3537 m
->output_element
= pa_split_spaces_strv(rvalue
);
3543 static int mapping_parse_direction(
3544 const char *filename
,
3546 const char *section
,
3552 pa_alsa_profile_set
*ps
= userdata
;
3557 if (!(m
= mapping_get(ps
, section
))) {
3558 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3562 if (pa_streq(rvalue
, "input"))
3563 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3564 else if (pa_streq(rvalue
, "output"))
3565 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3566 else if (pa_streq(rvalue
, "any"))
3567 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3569 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
3576 static int mapping_parse_description(
3577 const char *filename
,
3579 const char *section
,
3585 pa_alsa_profile_set
*ps
= userdata
;
3591 if ((m
= mapping_get(ps
, section
))) {
3592 pa_xfree(m
->description
);
3593 m
->description
= pa_xstrdup(rvalue
);
3594 } else if ((p
= profile_get(ps
, section
))) {
3595 pa_xfree(p
->description
);
3596 p
->description
= pa_xstrdup(rvalue
);
3598 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3605 static int mapping_parse_priority(
3606 const char *filename
,
3608 const char *section
,
3614 pa_alsa_profile_set
*ps
= userdata
;
3621 if (pa_atou(rvalue
, &prio
) < 0) {
3622 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
3626 if ((m
= mapping_get(ps
, section
)))
3628 else if ((p
= profile_get(ps
, section
)))
3631 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3638 static int profile_parse_mappings(
3639 const char *filename
,
3641 const char *section
,
3647 pa_alsa_profile_set
*ps
= userdata
;
3652 if (!(p
= profile_get(ps
, section
))) {
3653 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3657 if (pa_streq(lvalue
, "input-mappings")) {
3658 pa_xstrfreev(p
->input_mapping_names
);
3659 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
3661 pa_xstrfreev(p
->output_mapping_names
);
3662 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
3668 static int profile_parse_skip_probe(
3669 const char *filename
,
3671 const char *section
,
3677 pa_alsa_profile_set
*ps
= userdata
;
3683 if (!(p
= profile_get(ps
, section
))) {
3684 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3688 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
3689 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
3698 static int decibel_fix_parse_db_values(
3699 const char *filename
,
3701 const char *section
,
3707 pa_alsa_profile_set
*ps
= userdata
;
3708 pa_alsa_decibel_fix
*db_fix
;
3712 unsigned n
= 8; /* Current size of the db_values table. */
3713 unsigned min_step
= 0;
3714 unsigned max_step
= 0;
3715 unsigned i
= 0; /* Index to the items table. */
3716 unsigned prev_step
= 0;
3719 pa_assert(filename
);
3725 if (!(db_fix
= decibel_fix_get(ps
, section
))) {
3726 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3730 if (!(items
= pa_split_spaces_strv(rvalue
))) {
3731 pa_log("[%s:%u] Value missing", pa_strnull(filename
), line
);
3735 db_values
= pa_xnew(long, n
);
3737 while ((item
= items
[i
++])) {
3738 char *s
= item
; /* Step value string. */
3739 char *d
= item
; /* dB value string. */
3743 /* Move d forward until it points to a colon or to the end of the item. */
3744 for (; *d
&& *d
!= ':'; ++d
);
3747 /* item started with colon. */
3748 pa_log("[%s:%u] No step value found in %s", filename
, line
, item
);
3752 if (!*d
|| !*(d
+ 1)) {
3753 /* No colon found, or it was the last character in item. */
3754 pa_log("[%s:%u] No dB value found in %s", filename
, line
, item
);
3758 /* pa_atou() needs a null-terminating string. Let's replace the colon
3759 * with a zero byte. */
3762 if (pa_atou(s
, &step
) < 0) {
3763 pa_log("[%s:%u] Invalid step value: %s", filename
, line
, s
);
3767 if (pa_atod(d
, &db
) < 0) {
3768 pa_log("[%s:%u] Invalid dB value: %s", filename
, line
, d
);
3772 if (step
<= prev_step
&& i
!= 1) {
3773 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename
, line
, step
, prev_step
);
3777 if (db
< prev_db
&& i
!= 1) {
3778 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename
, line
, db
, prev_db
);
3784 db_values
[0] = (long) (db
* 100.0);
3788 /* Interpolate linearly. */
3789 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3791 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3793 /* Reallocate the db_values table if it's about to overflow. */
3794 if (prev_step
+ 1 - min_step
== n
) {
3796 db_values
= pa_xrenew(long, db_values
, n
);
3799 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3806 db_fix
->min_step
= min_step
;
3807 db_fix
->max_step
= max_step
;
3808 pa_xfree(db_fix
->db_values
);
3809 db_fix
->db_values
= db_values
;
3811 pa_xstrfreev(items
);
3816 pa_xstrfreev(items
);
3817 pa_xfree(db_values
);
3822 static void mapping_paths_probe(pa_alsa_mapping
*m
, pa_alsa_profile
*profile
,
3823 pa_alsa_direction_t direction
) {
3827 snd_pcm_t
*pcm_handle
;
3828 pa_alsa_path_set
*ps
;
3829 snd_mixer_t
*mixer_handle
;
3830 snd_hctl_t
*hctl_handle
;
3832 if (direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3833 if (m
->output_path_set
)
3834 return; /* Already probed */
3835 m
->output_path_set
= ps
= pa_alsa_path_set_new(m
, direction
, NULL
); /* FIXME: Handle paths_dir */
3836 pcm_handle
= m
->output_pcm
;
3838 if (m
->input_path_set
)
3839 return; /* Already probed */
3840 m
->input_path_set
= ps
= pa_alsa_path_set_new(m
, direction
, NULL
); /* FIXME: Handle paths_dir */
3841 pcm_handle
= m
->input_pcm
;
3845 return; /* No paths */
3847 pa_assert(pcm_handle
);
3849 mixer_handle
= pa_alsa_open_mixer_for_pcm(pcm_handle
, NULL
, &hctl_handle
);
3850 if (!mixer_handle
|| !hctl_handle
) {
3851 /* Cannot open mixer, remove all entries */
3852 while (pa_hashmap_steal_first(ps
->paths
));
3857 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3858 if (pa_alsa_path_probe(p
, mixer_handle
, hctl_handle
, m
->profile_set
->ignore_dB
) < 0) {
3859 pa_hashmap_remove(ps
->paths
, p
);
3863 path_set_condense(ps
, mixer_handle
);
3864 path_set_make_paths_unique(ps
);
3867 snd_mixer_close(mixer_handle
);
3869 pa_log_debug("Available mixer paths (after tidying):");
3870 pa_alsa_path_set_dump(ps
);
3873 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3875 static const struct description_map well_known_descriptions
[] = {
3876 { "analog-mono", N_("Analog Mono") },
3877 { "analog-stereo", N_("Analog Stereo") },
3878 { "analog-surround-21", N_("Analog Surround 2.1") },
3879 { "analog-surround-30", N_("Analog Surround 3.0") },
3880 { "analog-surround-31", N_("Analog Surround 3.1") },
3881 { "analog-surround-40", N_("Analog Surround 4.0") },
3882 { "analog-surround-41", N_("Analog Surround 4.1") },
3883 { "analog-surround-50", N_("Analog Surround 5.0") },
3884 { "analog-surround-51", N_("Analog Surround 5.1") },
3885 { "analog-surround-61", N_("Analog Surround 6.0") },
3886 { "analog-surround-61", N_("Analog Surround 6.1") },
3887 { "analog-surround-70", N_("Analog Surround 7.0") },
3888 { "analog-surround-71", N_("Analog Surround 7.1") },
3889 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3890 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3891 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3892 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3893 { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
3894 { "hdmi-stereo", N_("Digital Stereo (HDMI)") },
3895 { "hdmi-surround-51", N_("Digital Surround 5.1 (HDMI)") }
3900 if (!pa_channel_map_valid(&m
->channel_map
)) {
3901 pa_log("Mapping %s is missing channel map.", m
->name
);
3905 if (!m
->device_strings
) {
3906 pa_log("Mapping %s is missing device strings.", m
->name
);
3910 if ((m
->input_path_names
&& m
->input_element
) ||
3911 (m
->output_path_names
&& m
->output_element
)) {
3912 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3916 if (!m
->description
)
3917 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3918 well_known_descriptions
,
3919 PA_ELEMENTSOF(well_known_descriptions
)));
3921 if (!m
->description
)
3922 m
->description
= pa_xstrdup(m
->name
);
3925 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3927 else if (m
->channel_map
.channels
== bonus
->channels
)
3934 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3935 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3939 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3941 pa_strnull(m
->description
),
3943 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3944 pa_yes_no(m
->supported
),
3948 static void profile_set_add_auto_pair(
3949 pa_alsa_profile_set
*ps
,
3950 pa_alsa_mapping
*m
, /* output */
3951 pa_alsa_mapping
*n
/* input */) {
3959 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3962 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3966 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3968 name
= pa_sprintf_malloc("output:%s", m
->name
);
3970 name
= pa_sprintf_malloc("input:%s", n
->name
);
3972 if (pa_hashmap_get(ps
->profiles
, name
)) {
3977 p
= pa_xnew0(pa_alsa_profile
, 1);
3978 p
->profile_set
= ps
;
3982 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3983 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3984 p
->priority
+= m
->priority
* 100;
3988 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3989 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3990 p
->priority
+= n
->priority
;
3993 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3996 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3997 pa_alsa_mapping
*m
, *n
;
3998 void *m_state
, *n_state
;
4002 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
4003 profile_set_add_auto_pair(ps
, m
, NULL
);
4005 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
4006 profile_set_add_auto_pair(ps
, m
, n
);
4009 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
4010 profile_set_add_auto_pair(ps
, NULL
, n
);
4013 static int profile_verify(pa_alsa_profile
*p
) {
4015 static const struct description_map well_known_descriptions
[] = {
4016 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
4017 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
4018 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
4019 { "off", N_("Off") }
4024 /* Replace the output mapping names by the actual mappings */
4025 if (p
->output_mapping_names
) {
4028 pa_assert(!p
->output_mappings
);
4029 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
4031 for (name
= p
->output_mapping_names
; *name
; name
++) {
4034 pa_bool_t duplicate
= FALSE
;
4036 for (in
= name
+ 1; *in
; in
++)
4037 if (pa_streq(*name
, *in
)) {
4045 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
4046 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p
->name
, *name
);
4050 pa_idxset_put(p
->output_mappings
, m
, NULL
);
4056 pa_xstrfreev(p
->output_mapping_names
);
4057 p
->output_mapping_names
= NULL
;
4060 /* Replace the input mapping names by the actual mappings */
4061 if (p
->input_mapping_names
) {
4064 pa_assert(!p
->input_mappings
);
4065 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
4067 for (name
= p
->input_mapping_names
; *name
; name
++) {
4070 pa_bool_t duplicate
= FALSE
;
4072 for (in
= name
+ 1; *in
; in
++)
4073 if (pa_streq(*name
, *in
)) {
4081 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
4082 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p
->name
, *name
);
4086 pa_idxset_put(p
->input_mappings
, m
, NULL
);
4092 pa_xstrfreev(p
->input_mapping_names
);
4093 p
->input_mapping_names
= NULL
;
4096 if (!p
->input_mappings
&& !p
->output_mappings
) {
4097 pa_log("Profile '%s' lacks mappings.", p
->name
);
4101 if (!p
->description
)
4102 p
->description
= pa_xstrdup(lookup_description(p
->name
,
4103 well_known_descriptions
,
4104 PA_ELEMENTSOF(well_known_descriptions
)));
4106 if (!p
->description
) {
4111 sb
= pa_strbuf_new();
4113 if (p
->output_mappings
)
4114 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4115 if (!pa_strbuf_isempty(sb
))
4116 pa_strbuf_puts(sb
, " + ");
4118 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
4121 if (p
->input_mappings
)
4122 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4123 if (!pa_strbuf_isempty(sb
))
4124 pa_strbuf_puts(sb
, " + ");
4126 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
4129 p
->description
= pa_strbuf_tostring_free(sb
);
4135 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
4140 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4142 pa_strnull(p
->description
),
4144 pa_yes_no(p
->supported
),
4145 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
4146 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
4148 if (p
->input_mappings
)
4149 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
4150 pa_log_debug("Input %s", m
->name
);
4152 if (p
->output_mappings
)
4153 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
4154 pa_log_debug("Output %s", m
->name
);
4157 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
4160 /* Check that the dB mapping has been configured. Since "db-values" is
4161 * currently the only option in the DecibelFix section, and decibel fix
4162 * objects don't get created if a DecibelFix section is empty, this is
4163 * actually a redundant check. Having this may prevent future bugs,
4165 if (!db_fix
->db_values
) {
4166 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
4173 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
4174 char *db_values
= NULL
;
4178 if (db_fix
->db_values
) {
4180 unsigned long i
, nsteps
;
4182 pa_assert(db_fix
->min_step
<= db_fix
->max_step
);
4183 nsteps
= db_fix
->max_step
- db_fix
->min_step
+ 1;
4185 buf
= pa_strbuf_new();
4186 for (i
= 0; i
< nsteps
; ++i
)
4187 pa_strbuf_printf(buf
, "[%li]:%0.2f ", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
4189 db_values
= pa_strbuf_tostring_free(buf
);
4192 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4193 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
4195 pa_xfree(db_values
);
4198 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
4199 pa_alsa_profile_set
*ps
;
4202 pa_alsa_decibel_fix
*db_fix
;
4207 static pa_config_item items
[] = {
4209 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
4212 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
4213 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
4214 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
4215 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
4216 { "element-input", mapping_parse_element
, NULL
, NULL
},
4217 { "element-output", mapping_parse_element
, NULL
, NULL
},
4218 { "direction", mapping_parse_direction
, NULL
, NULL
},
4220 /* Shared by [Mapping ...] and [Profile ...] */
4221 { "description", mapping_parse_description
, NULL
, NULL
},
4222 { "priority", mapping_parse_priority
, NULL
, NULL
},
4225 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
4226 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
4227 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
4229 /* [DecibelFix ...] */
4230 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
4231 { NULL
, NULL
, NULL
, NULL
}
4234 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
4235 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4236 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4237 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4238 ps
->input_paths
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4239 ps
->output_paths
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4241 items
[0].data
= &ps
->auto_profiles
;
4244 fname
= "default.conf";
4246 fn
= pa_maybe_prefix_path(fname
,
4247 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
4248 PA_ALSA_PROFILE_SETS_DIR
);
4250 r
= pa_config_parse(fn
, NULL
, items
, ps
);
4256 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4257 if (mapping_verify(m
, bonus
) < 0)
4260 if (ps
->auto_profiles
)
4261 profile_set_add_auto(ps
);
4263 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4264 if (profile_verify(p
) < 0)
4267 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4268 if (decibel_fix_verify(db_fix
) < 0)
4274 pa_alsa_profile_set_free(ps
);
4278 static void profile_finalize_probing(pa_alsa_profile
*to_be_finalized
, pa_alsa_profile
*next
) {
4282 if (!to_be_finalized
)
4285 if (to_be_finalized
->output_mappings
)
4286 PA_IDXSET_FOREACH(m
, to_be_finalized
->output_mappings
, idx
) {
4291 if (to_be_finalized
->supported
)
4294 /* If this mapping is also in the next profile, we won't close the
4295 * pcm handle here, because it would get immediately reopened
4297 if (next
&& next
->output_mappings
&& pa_idxset_get_by_data(next
->output_mappings
, m
, NULL
))
4300 snd_pcm_close(m
->output_pcm
);
4301 m
->output_pcm
= NULL
;
4304 if (to_be_finalized
->input_mappings
)
4305 PA_IDXSET_FOREACH(m
, to_be_finalized
->input_mappings
, idx
) {
4310 if (to_be_finalized
->supported
)
4313 /* If this mapping is also in the next profile, we won't close the
4314 * pcm handle here, because it would get immediately reopened
4316 if (next
&& next
->input_mappings
&& pa_idxset_get_by_data(next
->input_mappings
, m
, NULL
))
4319 snd_pcm_close(m
->input_pcm
);
4320 m
->input_pcm
= NULL
;
4324 static snd_pcm_t
* mapping_open_pcm(pa_alsa_mapping
*m
,
4325 const pa_sample_spec
*ss
,
4328 unsigned default_n_fragments
,
4329 unsigned default_fragment_size_msec
) {
4331 pa_sample_spec try_ss
= *ss
;
4332 pa_channel_map try_map
= m
->channel_map
;
4333 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
4335 try_ss
.channels
= try_map
.channels
;
4338 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
4339 pa_frame_size(&try_ss
);
4340 try_buffer_size
= default_n_fragments
* try_period_size
;
4342 return pa_alsa_open_by_template(
4343 m
->device_strings
, dev_id
, NULL
, &try_ss
,
4344 &try_map
, mode
, &try_period_size
,
4345 &try_buffer_size
, 0, NULL
, NULL
, TRUE
);
4348 static void paths_drop_unsupported(pa_hashmap
* h
) {
4355 p
= pa_hashmap_iterate(h
, &state
, &key
);
4357 if (p
->supported
<= 0) {
4358 pa_hashmap_remove(h
, key
);
4359 pa_alsa_path_free(p
);
4361 p
= pa_hashmap_iterate(h
, &state
, &key
);
4365 void pa_alsa_profile_set_probe(
4366 pa_alsa_profile_set
*ps
,
4368 const pa_sample_spec
*ss
,
4369 unsigned default_n_fragments
,
4370 unsigned default_fragment_size_msec
) {
4373 pa_alsa_profile
*p
, *last
= NULL
;
4383 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
4386 /* Skip if this is already marked that it is supported (i.e. from the config file) */
4387 if (!p
->supported
) {
4389 pa_log_debug("Looking at profile %s", p
->name
);
4390 profile_finalize_probing(last
, p
);
4391 p
->supported
= TRUE
;
4393 /* Check if we can open all new ones */
4394 if (p
->output_mappings
)
4395 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4400 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
4401 if (!(m
->output_pcm
= mapping_open_pcm(m
, ss
, dev_id
,
4402 SND_PCM_STREAM_PLAYBACK
,
4403 default_n_fragments
,
4404 default_fragment_size_msec
))) {
4405 p
->supported
= FALSE
;
4410 if (p
->input_mappings
&& p
->supported
)
4411 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4416 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
4417 if (!(m
->input_pcm
= mapping_open_pcm(m
, ss
, dev_id
,
4418 SND_PCM_STREAM_CAPTURE
,
4419 default_n_fragments
,
4420 default_fragment_size_msec
))) {
4421 p
->supported
= FALSE
;
4432 pa_log_debug("Profile %s supported.", p
->name
);
4434 if (p
->output_mappings
)
4435 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
4437 mapping_paths_probe(m
, p
, PA_ALSA_DIRECTION_OUTPUT
);
4439 if (p
->input_mappings
)
4440 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
4442 mapping_paths_probe(m
, p
, PA_ALSA_DIRECTION_INPUT
);
4446 profile_finalize_probing(last
, NULL
);
4448 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4449 if (!p
->supported
) {
4450 pa_hashmap_remove(ps
->profiles
, p
->name
);
4454 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4455 if (m
->supported
<= 0) {
4456 pa_hashmap_remove(ps
->mappings
, m
->name
);
4460 paths_drop_unsupported(ps
->input_paths
);
4461 paths_drop_unsupported(ps
->output_paths
);
4466 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
4469 pa_alsa_decibel_fix
*db_fix
;
4474 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4477 pa_yes_no(ps
->auto_profiles
),
4478 pa_yes_no(ps
->probed
),
4479 pa_hashmap_size(ps
->mappings
),
4480 pa_hashmap_size(ps
->profiles
),
4481 pa_hashmap_size(ps
->decibel_fixes
));
4483 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4484 pa_alsa_mapping_dump(m
);
4486 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4487 pa_alsa_profile_dump(p
);
4489 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4490 pa_alsa_decibel_fix_dump(db_fix
);
4493 static pa_device_port
* device_port_alsa_init(pa_hashmap
*ports
,
4495 const char* description
,
4497 pa_alsa_setting
*setting
,
4498 pa_card_profile
*cp
,
4502 pa_device_port
* p
= pa_hashmap_get(ports
, name
);
4504 pa_alsa_port_data
*data
;
4506 p
= pa_device_port_new(core
, name
, description
, sizeof(pa_alsa_port_data
));
4508 pa_hashmap_put(ports
, p
->name
, p
);
4509 p
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4511 data
= PA_DEVICE_PORT_DATA(p
);
4513 data
->setting
= setting
;
4517 p
->is_input
|= path
->direction
== PA_ALSA_DIRECTION_ANY
|| path
->direction
== PA_ALSA_DIRECTION_INPUT
;
4518 p
->is_output
|= path
->direction
== PA_ALSA_DIRECTION_ANY
|| path
->direction
== PA_ALSA_DIRECTION_OUTPUT
;
4521 pa_hashmap_put(p
->profiles
, cp
->name
, cp
);
4524 pa_hashmap_put(extra
, p
->name
, p
);
4525 pa_device_port_ref(p
);
4531 void pa_alsa_path_set_add_ports(
4532 pa_alsa_path_set
*ps
,
4533 pa_card_profile
*cp
,
4546 PA_HASHMAP_FOREACH(path
, ps
->paths
, state
) {
4547 if (!path
->settings
|| !path
->settings
->next
) {
4548 /* If there is no or just one setting we only need a
4550 pa_device_port
*port
= device_port_alsa_init(ports
, path
->name
,
4551 path
->description
, path
, path
->settings
, cp
, extra
, core
);
4552 port
->priority
= path
->priority
* 100;
4556 PA_LLIST_FOREACH(s
, path
->settings
) {
4557 pa_device_port
*port
;
4560 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4562 if (s
->description
[0])
4563 d
= pa_sprintf_malloc("%s / %s", path
->description
, s
->description
);
4565 d
= pa_xstrdup(path
->description
);
4567 port
= device_port_alsa_init(ports
, n
, d
, path
, s
, cp
, extra
, core
);
4568 port
->priority
= path
->priority
* 100 + s
->priority
;
4577 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
, pa_card
*card
) {
4583 if (ps
->paths
&& pa_hashmap_size(ps
->paths
) > 0) {
4585 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4586 pa_alsa_path_set_add_ports(ps
, NULL
, card
->ports
, *p
, card
->core
);
4589 pa_log_debug("Added %u ports", *p
? pa_hashmap_size(*p
) : 0);