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);
1728 j
->name
= pa_xstrdup(section
);
1729 j
->alsa_name
= pa_sprintf_malloc("%s Jack", section
);
1730 PA_LLIST_INSERT_AFTER(pa_alsa_jack
, p
->jacks
, p
->last_jack
, j
);
1738 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1744 if (!pa_startswith(section
, "Option "))
1749 /* This is not an enum section, but an element section? */
1750 if (!(on
= strchr(section
, ':')))
1753 en
= pa_xstrndup(section
, on
- section
);
1756 if (p
->last_option
&&
1757 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1758 pa_streq(p
->last_option
->alsa_name
, on
)) {
1760 return p
->last_option
;
1763 pa_assert_se(e
= element_get(p
, en
, FALSE
));
1766 PA_LLIST_FOREACH(o
, e
->options
)
1767 if (pa_streq(o
->alsa_name
, on
))
1770 o
= pa_xnew0(pa_alsa_option
, 1);
1772 o
->alsa_name
= pa_xstrdup(on
);
1775 if (p
->last_option
&& p
->last_option
->element
== e
)
1776 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1778 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1785 static int element_parse_switch(
1786 const char *filename
,
1788 const char *section
,
1794 pa_alsa_path
*p
= userdata
;
1799 if (!(e
= element_get(p
, section
, TRUE
))) {
1800 pa_log("[%s:%u] Switch makes no sense in '%s'", filename
, line
, section
);
1804 if (pa_streq(rvalue
, "ignore"))
1805 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1806 else if (pa_streq(rvalue
, "mute"))
1807 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1808 else if (pa_streq(rvalue
, "off"))
1809 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1810 else if (pa_streq(rvalue
, "on"))
1811 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1812 else if (pa_streq(rvalue
, "select"))
1813 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1815 pa_log("[%s:%u] Switch invalid of '%s'", filename
, line
, section
);
1822 static int element_parse_volume(
1823 const char *filename
,
1825 const char *section
,
1831 pa_alsa_path
*p
= userdata
;
1836 if (!(e
= element_get(p
, section
, TRUE
))) {
1837 pa_log("[%s:%u] Volume makes no sense in '%s'", filename
, line
, section
);
1841 if (pa_streq(rvalue
, "ignore"))
1842 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1843 else if (pa_streq(rvalue
, "merge"))
1844 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1845 else if (pa_streq(rvalue
, "off"))
1846 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1847 else if (pa_streq(rvalue
, "zero"))
1848 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1852 if (pa_atou(rvalue
, &constant
) >= 0) {
1853 e
->volume_use
= PA_ALSA_VOLUME_CONSTANT
;
1854 e
->constant_volume
= constant
;
1856 pa_log("[%s:%u] Volume invalid of '%s'", filename
, line
, section
);
1864 static int element_parse_enumeration(
1865 const char *filename
,
1867 const char *section
,
1873 pa_alsa_path
*p
= userdata
;
1878 if (!(e
= element_get(p
, section
, TRUE
))) {
1879 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename
, line
, section
);
1883 if (pa_streq(rvalue
, "ignore"))
1884 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1885 else if (pa_streq(rvalue
, "select"))
1886 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1888 pa_log("[%s:%u] Enumeration invalid of '%s'", filename
, line
, section
);
1895 static int option_parse_priority(
1896 const char *filename
,
1898 const char *section
,
1904 pa_alsa_path
*p
= userdata
;
1910 if (!(o
= option_get(p
, section
))) {
1911 pa_log("[%s:%u] Priority makes no sense in '%s'", filename
, line
, section
);
1915 if (pa_atou(rvalue
, &prio
) < 0) {
1916 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
1924 static int option_parse_name(
1925 const char *filename
,
1927 const char *section
,
1933 pa_alsa_path
*p
= userdata
;
1938 if (!(o
= option_get(p
, section
))) {
1939 pa_log("[%s:%u] Name makes no sense in '%s'", filename
, line
, section
);
1944 o
->name
= pa_xstrdup(rvalue
);
1949 static int element_parse_required(
1950 const char *filename
,
1952 const char *section
,
1958 pa_alsa_path
*p
= userdata
;
1962 pa_alsa_required_t req
;
1966 e
= element_get(p
, section
, TRUE
);
1967 o
= option_get(p
, section
);
1968 j
= jack_get(p
, section
);
1969 if (!e
&& !o
&& !j
) {
1970 pa_log("[%s:%u] Required makes no sense in '%s'", filename
, line
, section
);
1974 if (pa_streq(rvalue
, "ignore"))
1975 req
= PA_ALSA_REQUIRED_IGNORE
;
1976 else if (pa_streq(rvalue
, "switch") && e
)
1977 req
= PA_ALSA_REQUIRED_SWITCH
;
1978 else if (pa_streq(rvalue
, "volume") && e
)
1979 req
= PA_ALSA_REQUIRED_VOLUME
;
1980 else if (pa_streq(rvalue
, "enumeration"))
1981 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1982 else if (pa_streq(rvalue
, "any"))
1983 req
= PA_ALSA_REQUIRED_ANY
;
1985 pa_log("[%s:%u] Required invalid of '%s'", filename
, line
, section
);
1989 if (pa_streq(lvalue
, "required-absent")) {
1991 e
->required_absent
= req
;
1993 o
->required_absent
= req
;
1995 j
->required_absent
= req
;
1997 else if (pa_streq(lvalue
, "required-any")) {
1999 e
->required_any
= req
;
2000 e
->path
->has_req_any
= TRUE
;
2003 o
->required_any
= req
;
2004 o
->element
->path
->has_req_any
= TRUE
;
2007 j
->required_any
= req
;
2008 j
->path
->has_req_any
= TRUE
;
2024 static int element_parse_direction(
2025 const char *filename
,
2027 const char *section
,
2033 pa_alsa_path
*p
= userdata
;
2038 if (!(e
= element_get(p
, section
, TRUE
))) {
2039 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
2043 if (pa_streq(rvalue
, "playback"))
2044 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
2045 else if (pa_streq(rvalue
, "capture"))
2046 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
2048 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
2055 static int element_parse_direction_try_other(
2056 const char *filename
,
2058 const char *section
,
2064 pa_alsa_path
*p
= userdata
;
2068 if (!(e
= element_get(p
, section
, TRUE
))) {
2069 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
2073 if ((yes
= pa_parse_boolean(rvalue
)) < 0) {
2074 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
2078 e
->direction_try_other
= !!yes
;
2082 static int element_parse_volume_limit(
2083 const char *filename
,
2085 const char *section
,
2091 pa_alsa_path
*p
= userdata
;
2095 if (!(e
= element_get(p
, section
, TRUE
))) {
2096 pa_log("[%s:%u] volume-limit makes no sense in '%s'", filename
, line
, section
);
2100 if (pa_atol(rvalue
, &volume_limit
) < 0 || volume_limit
< 0) {
2101 pa_log("[%s:%u] Invalid value for volume-limit", filename
, line
);
2105 e
->volume_limit
= volume_limit
;
2109 static pa_channel_position_mask_t
parse_mask(const char *m
) {
2110 pa_channel_position_mask_t v
;
2112 if (pa_streq(m
, "all-left"))
2113 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
2114 else if (pa_streq(m
, "all-right"))
2115 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
2116 else if (pa_streq(m
, "all-center"))
2117 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
2118 else if (pa_streq(m
, "all-front"))
2119 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
2120 else if (pa_streq(m
, "all-rear"))
2121 v
= PA_CHANNEL_POSITION_MASK_REAR
;
2122 else if (pa_streq(m
, "all-side"))
2123 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
2124 else if (pa_streq(m
, "all-top"))
2125 v
= PA_CHANNEL_POSITION_MASK_TOP
;
2126 else if (pa_streq(m
, "all-no-lfe"))
2127 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
2128 else if (pa_streq(m
, "all"))
2129 v
= PA_CHANNEL_POSITION_MASK_ALL
;
2131 pa_channel_position_t p
;
2133 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
2136 v
= PA_CHANNEL_POSITION_MASK(p
);
2142 static int element_parse_override_map(
2143 const char *filename
,
2145 const char *section
,
2151 pa_alsa_path
*p
= userdata
;
2153 const char *state
= NULL
;
2157 if (!(e
= element_get(p
, section
, TRUE
))) {
2158 pa_log("[%s:%u] Override map makes no sense in '%s'", filename
, line
, section
);
2162 while ((n
= pa_split(rvalue
, ",", &state
))) {
2163 pa_channel_position_mask_t m
;
2168 if ((m
= parse_mask(n
)) == 0) {
2169 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename
, line
, n
, section
);
2175 if (pa_streq(lvalue
, "override-map.1"))
2176 e
->masks
[i
++][0] = m
;
2178 e
->masks
[i
++][1] = m
;
2180 /* Later on we might add override-map.3 and so on here ... */
2185 e
->override_map
= TRUE
;
2190 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
2191 snd_mixer_selem_id_t
*sid
;
2192 snd_mixer_elem_t
*me
;
2198 SELEM_INIT(sid
, e
->alsa_name
);
2199 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2200 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2204 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
2206 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
2207 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
2209 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
2212 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2215 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
2217 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
2218 pa_log_warn("Failed to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2224 int pa_alsa_setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
2231 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
2232 element_set_option(o
->element
, m
, o
->alsa_idx
);
2237 static int option_verify(pa_alsa_option
*o
) {
2238 static const struct description_map well_known_descriptions
[] = {
2239 { "input", N_("Input") },
2240 { "input-docking", N_("Docking Station Input") },
2241 { "input-docking-microphone", N_("Docking Station Microphone") },
2242 { "input-docking-linein", N_("Docking Station Line In") },
2243 { "input-linein", N_("Line In") },
2244 { "input-microphone", N_("Microphone") },
2245 { "input-microphone-front", N_("Front Microphone") },
2246 { "input-microphone-rear", N_("Rear Microphone") },
2247 { "input-microphone-external", N_("External Microphone") },
2248 { "input-microphone-internal", N_("Internal Microphone") },
2249 { "input-radio", N_("Radio") },
2250 { "input-video", N_("Video") },
2251 { "input-agc-on", N_("Automatic Gain Control") },
2252 { "input-agc-off", N_("No Automatic Gain Control") },
2253 { "input-boost-on", N_("Boost") },
2254 { "input-boost-off", N_("No Boost") },
2255 { "output-amplifier-on", N_("Amplifier") },
2256 { "output-amplifier-off", N_("No Amplifier") },
2257 { "output-bass-boost-on", N_("Bass Boost") },
2258 { "output-bass-boost-off", N_("No Bass Boost") },
2259 { "output-speaker", N_("Speaker") },
2260 { "output-headphones", N_("Headphones") }
2266 pa_log("No name set for option %s", o
->alsa_name
);
2270 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
2271 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
2272 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
2276 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
2277 !pa_streq(o
->alsa_name
, "on") &&
2278 !pa_streq(o
->alsa_name
, "off")) {
2279 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
2283 if (!o
->description
)
2284 o
->description
= pa_xstrdup(lookup_description(o
->name
,
2285 well_known_descriptions
,
2286 PA_ELEMENTSOF(well_known_descriptions
)));
2287 if (!o
->description
)
2288 o
->description
= pa_xstrdup(o
->name
);
2293 static int element_verify(pa_alsa_element
*e
) {
2298 // 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);
2299 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
2300 (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required_any
== e
->required_absent
) ||
2301 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) ||
2302 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
2303 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
2307 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
2308 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
2312 PA_LLIST_FOREACH(o
, e
->options
)
2313 if (option_verify(o
) < 0)
2319 static int path_verify(pa_alsa_path
*p
) {
2320 static const struct description_map well_known_descriptions
[] = {
2321 { "analog-input", N_("Analog Input") },
2322 { "analog-input-microphone", N_("Microphone") },
2323 { "analog-input-microphone-front", N_("Front Microphone") },
2324 { "analog-input-microphone-rear", N_("Rear Microphone") },
2325 { "analog-input-microphone-dock", N_("Dock Microphone") },
2326 { "analog-input-microphone-internal", N_("Internal Microphone") },
2327 { "analog-input-linein", N_("Line In") },
2328 { "analog-input-radio", N_("Radio") },
2329 { "analog-input-video", N_("Video") },
2330 { "analog-output", N_("Analog Output") },
2331 { "analog-output-headphones", N_("Headphones") },
2332 { "analog-output-lfe-on-mono", N_("LFE on Separate Mono Output") },
2333 { "analog-output-lineout", N_("Line Out") },
2334 { "analog-output-mono", N_("Analog Mono Output") },
2335 { "analog-output-speaker", N_("Speakers") },
2336 { "hdmi-output", N_("HDMI / DisplayPort") },
2337 { "iec958-stereo-output", N_("Digital Output (S/PDIF)") },
2338 { "iec958-passthrough-output", N_("Digital Passthrough (S/PDIF)") }
2345 PA_LLIST_FOREACH(e
, p
->elements
)
2346 if (element_verify(e
) < 0)
2349 if (!p
->description
)
2350 p
->description
= pa_xstrdup(lookup_description(p
->name
,
2351 well_known_descriptions
,
2352 PA_ELEMENTSOF(well_known_descriptions
)));
2354 if (!p
->description
)
2355 p
->description
= pa_xstrdup(p
->name
);
2360 static const char *get_default_paths_dir(void) {
2361 if (pa_run_from_build_tree())
2362 return PA_BUILDDIR
"/modules/alsa/mixer/paths/";
2364 return PA_ALSA_PATHS_DIR
;
2367 pa_alsa_path
* pa_alsa_path_new(const char *paths_dir
, const char *fname
, pa_alsa_direction_t direction
) {
2373 pa_config_item items
[] = {
2375 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
2376 { "description", pa_config_parse_string
, NULL
, "General" },
2377 { "name", pa_config_parse_string
, NULL
, "General" },
2380 { "priority", option_parse_priority
, NULL
, NULL
},
2381 { "name", option_parse_name
, NULL
, NULL
},
2384 { "switch", element_parse_switch
, NULL
, NULL
},
2385 { "volume", element_parse_volume
, NULL
, NULL
},
2386 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
2387 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
2388 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
2389 /* ... later on we might add override-map.3 and so on here ... */
2390 { "required", element_parse_required
, NULL
, NULL
},
2391 { "required-any", element_parse_required
, NULL
, NULL
},
2392 { "required-absent", element_parse_required
, NULL
, NULL
},
2393 { "direction", element_parse_direction
, NULL
, NULL
},
2394 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2395 { "volume-limit", element_parse_volume_limit
, NULL
, NULL
},
2396 { NULL
, NULL
, NULL
, NULL
}
2401 p
= pa_xnew0(pa_alsa_path
, 1);
2402 n
= pa_path_get_filename(fname
);
2403 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2404 p
->direction
= direction
;
2406 items
[0].data
= &p
->priority
;
2407 items
[1].data
= &p
->description
;
2408 items
[2].data
= &p
->name
;
2411 paths_dir
= get_default_paths_dir();
2413 fn
= pa_maybe_prefix_path(fname
, paths_dir
);
2415 r
= pa_config_parse(fn
, NULL
, items
, p
);
2421 if (path_verify(p
) < 0)
2427 pa_alsa_path_free(p
);
2431 pa_alsa_path
*pa_alsa_path_synthesize(const char *element
, pa_alsa_direction_t direction
) {
2437 p
= pa_xnew0(pa_alsa_path
, 1);
2438 p
->name
= pa_xstrdup(element
);
2439 p
->direction
= direction
;
2441 e
= pa_xnew0(pa_alsa_element
, 1);
2443 e
->alsa_name
= pa_xstrdup(element
);
2444 e
->direction
= direction
;
2445 e
->volume_limit
= -1;
2447 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2448 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2450 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2451 p
->last_element
= e
;
2455 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2456 pa_alsa_option
*o
, *n
;
2460 for (o
= e
->options
; o
; o
= n
) {
2463 if (o
->alsa_idx
< 0) {
2464 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2470 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2471 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2472 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2475 static void path_drop_unsupported(pa_alsa_path
*p
) {
2476 pa_alsa_element
*e
, *n
;
2480 for (e
= p
->elements
; e
; e
= n
) {
2483 if (!element_drop_unsupported(e
)) {
2484 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2490 static void path_make_options_unique(pa_alsa_path
*p
) {
2492 pa_alsa_option
*o
, *u
;
2494 PA_LLIST_FOREACH(e
, p
->elements
) {
2495 PA_LLIST_FOREACH(o
, e
->options
) {
2499 for (u
= o
->next
; u
; u
= u
->next
)
2500 if (pa_streq(u
->name
, o
->name
))
2506 m
= pa_xstrdup(o
->name
);
2508 /* OK, this name is not unique, hence let's rename */
2509 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2512 if (!pa_streq(u
->name
, m
))
2515 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2519 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2520 pa_xfree(u
->description
);
2521 u
->description
= nd
;
2531 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2534 for (; e
; e
= e
->next
)
2535 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2536 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2542 for (o
= e
->options
; o
; o
= o
->next
) {
2546 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2547 s
->options
= pa_idxset_copy(template->options
);
2548 s
->name
= pa_sprintf_malloc("%s+%s", template->name
, o
->name
);
2550 (template->description
[0] && o
->description
[0])
2551 ? pa_sprintf_malloc("%s / %s", template->description
, o
->description
)
2552 : (template->description
[0]
2553 ? pa_xstrdup(template->description
)
2554 : pa_xstrdup(o
->description
));
2556 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2558 s
= pa_xnew0(pa_alsa_setting
, 1);
2559 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2560 s
->name
= pa_xstrdup(o
->name
);
2561 s
->description
= pa_xstrdup(o
->description
);
2562 s
->priority
= o
->priority
;
2565 pa_idxset_put(s
->options
, o
, NULL
);
2567 if (element_create_settings(e
->next
, s
))
2568 /* This is not a leaf, so let's get rid of it */
2571 /* This is a leaf, so let's add it */
2572 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2574 e
->path
->last_setting
= s
;
2581 static void path_create_settings(pa_alsa_path
*p
) {
2584 element_create_settings(p
->elements
, NULL
);
2587 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_hctl_t
*hctl
, pa_bool_t ignore_dB
) {
2590 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2591 pa_channel_position_t t
;
2592 pa_channel_position_mask_t path_volume_channels
= 0;
2598 return p
->supported
? 0 : -1;
2604 pa_log_debug("Probing path '%s'", p
->name
);
2606 PA_LLIST_FOREACH(j
, p
->jacks
) {
2607 if (jack_probe(j
, hctl
) < 0) {
2608 p
->supported
= FALSE
;
2609 pa_log_debug("Probe of jack '%s' failed.", j
->alsa_name
);
2612 pa_log_debug("Probe of jack '%s' succeeded (%s)", j
->alsa_name
, j
->has_control
? "found!" : "not found");
2615 PA_LLIST_FOREACH(e
, p
->elements
) {
2616 if (element_probe(e
, m
) < 0) {
2617 p
->supported
= FALSE
;
2618 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2621 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
);
2626 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2628 if (!p
->has_volume
) {
2629 p
->min_volume
= e
->min_volume
;
2630 p
->max_volume
= e
->max_volume
;
2634 if (!p
->has_volume
) {
2635 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2636 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2637 min_dB
[t
] = e
->min_dB
;
2638 max_dB
[t
] = e
->max_dB
;
2639 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2646 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2647 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2648 min_dB
[t
] += e
->min_dB
;
2649 max_dB
[t
] += e
->max_dB
;
2650 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2653 /* Hmm, there's another element before us
2654 * which cannot do dB volumes, so we we need
2655 * to 'neutralize' this slider */
2656 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2657 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2660 } else if (p
->has_volume
) {
2661 /* We can't use this volume, so let's ignore it */
2662 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2663 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2665 p
->has_volume
= TRUE
;
2668 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2672 if (p
->has_req_any
&& !p
->req_any_present
) {
2673 p
->supported
= FALSE
;
2674 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2678 path_drop_unsupported(p
);
2679 path_make_options_unique(p
);
2680 path_create_settings(p
);
2682 p
->supported
= TRUE
;
2684 p
->min_dB
= INFINITY
;
2685 p
->max_dB
= -INFINITY
;
2687 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2688 if (path_volume_channels
& PA_CHANNEL_POSITION_MASK(t
)) {
2689 if (p
->min_dB
> min_dB
[t
])
2690 p
->min_dB
= min_dB
[t
];
2692 if (p
->max_dB
< max_dB
[t
])
2693 p
->max_dB
= max_dB
[t
];
2700 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2703 pa_log_debug("Setting %s (%s) priority=%u",
2705 pa_strnull(s
->description
),
2709 void pa_alsa_jack_dump(pa_alsa_jack
*j
) {
2712 pa_log_debug("Jack %s, alsa_name='%s', detection %s", j
->name
, j
->alsa_name
, j
->has_control
? "possible" : "unavailable");
2715 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2718 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2720 pa_strnull(o
->name
),
2721 pa_strnull(o
->description
),
2726 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2730 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",
2740 (long long unsigned) e
->merged_mask
,
2742 pa_yes_no(e
->override_map
));
2744 PA_LLIST_FOREACH(o
, e
->options
)
2745 pa_alsa_option_dump(o
);
2748 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2754 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2755 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2757 pa_strnull(p
->description
),
2760 pa_yes_no(p
->probed
),
2761 pa_yes_no(p
->supported
),
2762 pa_yes_no(p
->has_mute
),
2763 pa_yes_no(p
->has_volume
),
2764 pa_yes_no(p
->has_dB
),
2765 p
->min_volume
, p
->max_volume
,
2766 p
->min_dB
, p
->max_dB
);
2768 PA_LLIST_FOREACH(e
, p
->elements
)
2769 pa_alsa_element_dump(e
);
2771 PA_LLIST_FOREACH(j
, p
->jacks
)
2772 pa_alsa_jack_dump(j
);
2774 PA_LLIST_FOREACH(s
, p
->settings
)
2775 pa_alsa_setting_dump(s
);
2778 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2779 snd_mixer_selem_id_t
*sid
;
2780 snd_mixer_elem_t
*me
;
2786 SELEM_INIT(sid
, e
->alsa_name
);
2787 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2788 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2792 snd_mixer_elem_set_callback(me
, cb
);
2793 snd_mixer_elem_set_callback_private(me
, userdata
);
2796 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2803 PA_LLIST_FOREACH(e
, p
->elements
)
2804 element_set_callback(e
, m
, cb
, userdata
);
2807 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2815 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
2816 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2819 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
, const char *paths_dir
) {
2820 pa_alsa_path_set
*ps
;
2821 char **pn
= NULL
, **en
= NULL
, **ie
;
2822 pa_alsa_decibel_fix
*db_fix
;
2823 void *state
, *state2
;
2827 pa_assert(m
->profile_set
);
2828 pa_assert(m
->profile_set
->decibel_fixes
);
2829 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2831 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2834 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2835 ps
->direction
= direction
;
2836 ps
->paths
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2838 if (direction
== PA_ALSA_DIRECTION_OUTPUT
) {
2839 pn
= m
->output_path_names
;
2840 cache
= m
->profile_set
->output_paths
;
2842 else if (direction
== PA_ALSA_DIRECTION_INPUT
) {
2843 pn
= m
->input_path_names
;
2844 cache
= m
->profile_set
->input_paths
;
2850 for (in
= pn
; *in
; in
++) {
2851 pa_alsa_path
*p
= NULL
;
2852 pa_bool_t duplicate
= FALSE
;
2855 for (kn
= pn
; kn
< in
; kn
++)
2856 if (pa_streq(*kn
, *in
)) {
2864 p
= pa_hashmap_get(cache
, *in
);
2866 char *fn
= pa_sprintf_malloc("%s.conf", *in
);
2867 p
= pa_alsa_path_new(paths_dir
, fn
, direction
);
2870 pa_hashmap_put(cache
, *in
, p
);
2872 pa_assert(pa_hashmap_get(cache
, *in
) == p
);
2874 pa_hashmap_put(ps
->paths
, p
, p
);
2881 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2882 en
= m
->output_element
;
2883 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2884 en
= m
->input_element
;
2887 pa_alsa_path_set_free(ps
);
2891 for (ie
= en
; *ie
; ie
++) {
2895 p
= pa_alsa_path_synthesize(*ie
, direction
);
2897 /* Mark all other passed elements for require-absent */
2898 for (je
= en
; *je
; je
++) {
2904 e
= pa_xnew0(pa_alsa_element
, 1);
2906 e
->alsa_name
= pa_xstrdup(*je
);
2907 e
->direction
= direction
;
2908 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2909 e
->volume_limit
= -1;
2911 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2912 p
->last_element
= e
;
2915 pa_hashmap_put(ps
->paths
, *ie
, p
);
2919 /* Assign decibel fixes to elements. */
2920 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2923 PA_HASHMAP_FOREACH(p
, ps
->paths
, state2
) {
2926 PA_LLIST_FOREACH(e
, p
->elements
) {
2927 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2928 /* The profile set that contains the dB fix may be freed
2929 * before the element, so we have to copy the dB fix
2931 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2932 e
->db_fix
->profile_set
= NULL
;
2933 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2934 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
2943 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2948 pa_log_debug("Path Set %p, direction=%i",
2952 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
2953 pa_alsa_path_dump(p
);
2957 static pa_bool_t
options_have_option(pa_alsa_option
*options
, const char *alsa_name
) {
2961 pa_assert(alsa_name
);
2963 PA_LLIST_FOREACH(o
, options
) {
2964 if (pa_streq(o
->alsa_name
, alsa_name
))
2970 static pa_bool_t
enumeration_is_subset(pa_alsa_option
*a_options
, pa_alsa_option
*b_options
) {
2971 pa_alsa_option
*oa
, *ob
;
2973 if (!a_options
) return TRUE
;
2974 if (!b_options
) return FALSE
;
2976 /* If there is an option A offers that B does not, then A is not a subset of B. */
2977 PA_LLIST_FOREACH(oa
, a_options
) {
2978 pa_bool_t found
= FALSE
;
2979 PA_LLIST_FOREACH(ob
, b_options
) {
2980 if (pa_streq(oa
->alsa_name
, ob
->alsa_name
)) {
2992 * Compares two elements to see if a is a subset of b
2994 static pa_bool_t
element_is_subset(pa_alsa_element
*a
, pa_alsa_element
*b
, snd_mixer_t
*m
) {
3000 * Every state is a subset of itself (with caveats for volume_limits and options)
3001 * IGNORE is a subset of every other state */
3003 /* Check the volume_use */
3004 if (a
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
3006 /* "Constant" is subset of "Constant" only when their constant values are equal */
3007 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& b
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& a
->constant_volume
!= b
->constant_volume
)
3010 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
3011 if (a
->volume_use
!= b
->volume_use
&& b
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
3014 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
3015 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
3016 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
3017 if (b
->volume_use
== PA_ALSA_VOLUME_MERGE
&& b
->volume_limit
>= 0) {
3020 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
)
3021 a_limit
= a
->constant_volume
;
3022 else if (a
->volume_use
== PA_ALSA_VOLUME_ZERO
) {
3026 int rounding
= (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
? +1 : -1);
3027 a_limit
= decibel_fix_get_step(a
->db_fix
, &dB
, rounding
);
3029 snd_mixer_selem_id_t
*sid
;
3030 snd_mixer_elem_t
*me
;
3032 SELEM_INIT(sid
, a
->alsa_name
);
3033 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
3034 pa_log_warn("Element %s seems to have disappeared.", a
->alsa_name
);
3038 if (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3039 if (snd_mixer_selem_ask_playback_dB_vol(me
, dB
, +1, &a_limit
) < 0)
3042 if (snd_mixer_selem_ask_capture_dB_vol(me
, dB
, -1, &a_limit
) < 0)
3046 } else if (a
->volume_use
== PA_ALSA_VOLUME_OFF
)
3047 a_limit
= a
->min_volume
;
3048 else if (a
->volume_use
== PA_ALSA_VOLUME_MERGE
)
3049 a_limit
= a
->volume_limit
;
3051 /* This should never be reached */
3054 if (a_limit
> b
->volume_limit
)
3058 if (a
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
3060 /* If override-maps are different, they're not subsets */
3061 if (a
->n_channels
!= b
->n_channels
)
3063 for (s
= 0; s
< SND_MIXER_SCHN_LAST
; s
++)
3064 if (a
->masks
[s
][a
->n_channels
-1] != b
->masks
[s
][b
->n_channels
-1]) {
3065 pa_log_debug("Element %s is not a subset - mask a: 0x%lx, mask b: 0x%lx, at channel %d",
3066 a
->alsa_name
, a
->masks
[s
][a
->n_channels
-1], b
->masks
[s
][b
->n_channels
-1], s
);
3072 if (a
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
3073 /* "On" is a subset of "Mute".
3074 * "Off" is a subset of "Mute".
3075 * "On" is a subset of "Select", if there is an "Option:On" in B.
3076 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3077 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3079 if (a
->switch_use
!= b
->switch_use
) {
3081 if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
|| a
->switch_use
== PA_ALSA_SWITCH_MUTE
3082 || b
->switch_use
== PA_ALSA_SWITCH_OFF
|| b
->switch_use
== PA_ALSA_SWITCH_ON
)
3085 if (b
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3086 if (a
->switch_use
== PA_ALSA_SWITCH_ON
) {
3087 if (!options_have_option(b
->options
, "on"))
3089 } else if (a
->switch_use
== PA_ALSA_SWITCH_OFF
) {
3090 if (!options_have_option(b
->options
, "off"))
3094 } else if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3095 if (!enumeration_is_subset(a
->options
, b
->options
))
3100 if (a
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
) {
3101 if (b
->enumeration_use
== PA_ALSA_ENUMERATION_IGNORE
)
3103 if (!enumeration_is_subset(a
->options
, b
->options
))
3110 static void path_set_condense(pa_alsa_path_set
*ps
, snd_mixer_t
*m
) {
3117 /* If we only have one path, then don't bother */
3118 if (pa_hashmap_size(ps
->paths
) < 2)
3121 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3125 PA_HASHMAP_FOREACH(p2
, ps
->paths
, state2
) {
3126 pa_alsa_element
*ea
, *eb
;
3127 pa_alsa_jack
*ja
, *jb
;
3128 pa_bool_t is_subset
= TRUE
;
3133 /* If a has a jack that b does not have, a is not a subset */
3134 PA_LLIST_FOREACH(ja
, p
->jacks
) {
3135 pa_bool_t exists
= FALSE
;
3137 if (!ja
->has_control
)
3140 PA_LLIST_FOREACH(jb
, p2
->jacks
) {
3141 if (jb
->has_control
&& !strcmp(jb
->alsa_name
, ja
->alsa_name
)) {
3153 /* Compare the elements of each set... */
3154 pa_assert_se(ea
= p
->elements
);
3155 pa_assert_se(eb
= p2
->elements
);
3158 if (pa_streq(ea
->alsa_name
, eb
->alsa_name
)) {
3159 if (element_is_subset(ea
, eb
, m
)) {
3162 if ((ea
&& !eb
) || (!ea
&& eb
))
3164 else if (!ea
&& !eb
)
3174 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p
->name
, p2
->name
);
3175 pa_hashmap_remove(ps
->paths
, p
);
3182 static pa_alsa_path
* path_set_find_path_by_name(pa_alsa_path_set
*ps
, const char* name
, pa_alsa_path
*ignore
)
3187 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
3188 if (p
!= ignore
&& pa_streq(p
->name
, name
))
3193 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
3194 pa_alsa_path
*p
, *q
;
3195 void *state
, *state2
;
3197 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3201 q
= path_set_find_path_by_name(ps
, p
->name
, p
);
3206 m
= pa_xstrdup(p
->name
);
3208 /* OK, this name is not unique, hence let's rename */
3210 PA_HASHMAP_FOREACH(q
, ps
->paths
, state2
) {
3213 if (!pa_streq(q
->name
, m
))
3216 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
3220 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
3221 pa_xfree(q
->description
);
3222 q
->description
= nd
;
3231 static void mapping_free(pa_alsa_mapping
*m
) {
3235 pa_xfree(m
->description
);
3237 pa_xstrfreev(m
->device_strings
);
3238 pa_xstrfreev(m
->input_path_names
);
3239 pa_xstrfreev(m
->output_path_names
);
3240 pa_xstrfreev(m
->input_element
);
3241 pa_xstrfreev(m
->output_element
);
3242 if (m
->input_path_set
)
3243 pa_alsa_path_set_free(m
->input_path_set
);
3244 if (m
->output_path_set
)
3245 pa_alsa_path_set_free(m
->output_path_set
);
3247 pa_assert(!m
->input_pcm
);
3248 pa_assert(!m
->output_pcm
);
3253 static void profile_free(pa_alsa_profile
*p
) {
3257 pa_xfree(p
->description
);
3259 pa_xstrfreev(p
->input_mapping_names
);
3260 pa_xstrfreev(p
->output_mapping_names
);
3262 if (p
->input_mappings
)
3263 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
3265 if (p
->output_mappings
)
3266 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
3271 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
3274 if (ps
->input_paths
) {
3277 while ((p
= pa_hashmap_steal_first(ps
->input_paths
)))
3278 pa_alsa_path_free(p
);
3280 pa_hashmap_free(ps
->input_paths
, NULL
, NULL
);
3283 if (ps
->output_paths
) {
3286 while ((p
= pa_hashmap_steal_first(ps
->output_paths
)))
3287 pa_alsa_path_free(p
);
3289 pa_hashmap_free(ps
->output_paths
, NULL
, NULL
);
3295 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
3298 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
3304 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
3307 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
3310 if (ps
->decibel_fixes
) {
3311 pa_alsa_decibel_fix
*db_fix
;
3313 while ((db_fix
= pa_hashmap_steal_first(ps
->decibel_fixes
)))
3314 decibel_fix_free(db_fix
);
3316 pa_hashmap_free(ps
->decibel_fixes
, NULL
, NULL
);
3322 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
3325 if (!pa_startswith(name
, "Mapping "))
3330 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
3333 m
= pa_xnew0(pa_alsa_mapping
, 1);
3334 m
->profile_set
= ps
;
3335 m
->name
= pa_xstrdup(name
);
3336 pa_channel_map_init(&m
->channel_map
);
3338 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
3343 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
3346 if (!pa_startswith(name
, "Profile "))
3351 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
3354 p
= pa_xnew0(pa_alsa_profile
, 1);
3355 p
->profile_set
= ps
;
3356 p
->name
= pa_xstrdup(name
);
3358 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3363 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
3364 pa_alsa_decibel_fix
*db_fix
;
3366 if (!pa_startswith(name
, "DecibelFix "))
3371 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
3374 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
3375 db_fix
->profile_set
= ps
;
3376 db_fix
->name
= pa_xstrdup(name
);
3378 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
3383 static int mapping_parse_device_strings(
3384 const char *filename
,
3386 const char *section
,
3392 pa_alsa_profile_set
*ps
= userdata
;
3397 if (!(m
= mapping_get(ps
, section
))) {
3398 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3402 pa_xstrfreev(m
->device_strings
);
3403 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
3404 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
3411 static int mapping_parse_channel_map(
3412 const char *filename
,
3414 const char *section
,
3420 pa_alsa_profile_set
*ps
= userdata
;
3425 if (!(m
= mapping_get(ps
, section
))) {
3426 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3430 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
3431 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
3438 static int mapping_parse_paths(
3439 const char *filename
,
3441 const char *section
,
3447 pa_alsa_profile_set
*ps
= userdata
;
3452 if (!(m
= mapping_get(ps
, section
))) {
3453 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3457 if (pa_streq(lvalue
, "paths-input")) {
3458 pa_xstrfreev(m
->input_path_names
);
3459 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
3461 pa_xstrfreev(m
->output_path_names
);
3462 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
3468 static int mapping_parse_element(
3469 const char *filename
,
3471 const char *section
,
3477 pa_alsa_profile_set
*ps
= userdata
;
3482 if (!(m
= mapping_get(ps
, section
))) {
3483 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3487 if (pa_streq(lvalue
, "element-input")) {
3488 pa_xstrfreev(m
->input_element
);
3489 m
->input_element
= pa_split_spaces_strv(rvalue
);
3491 pa_xstrfreev(m
->output_element
);
3492 m
->output_element
= pa_split_spaces_strv(rvalue
);
3498 static int mapping_parse_direction(
3499 const char *filename
,
3501 const char *section
,
3507 pa_alsa_profile_set
*ps
= userdata
;
3512 if (!(m
= mapping_get(ps
, section
))) {
3513 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3517 if (pa_streq(rvalue
, "input"))
3518 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3519 else if (pa_streq(rvalue
, "output"))
3520 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3521 else if (pa_streq(rvalue
, "any"))
3522 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3524 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
3531 static int mapping_parse_description(
3532 const char *filename
,
3534 const char *section
,
3540 pa_alsa_profile_set
*ps
= userdata
;
3546 if ((m
= mapping_get(ps
, section
))) {
3547 pa_xfree(m
->description
);
3548 m
->description
= pa_xstrdup(rvalue
);
3549 } else if ((p
= profile_get(ps
, section
))) {
3550 pa_xfree(p
->description
);
3551 p
->description
= pa_xstrdup(rvalue
);
3553 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3560 static int mapping_parse_priority(
3561 const char *filename
,
3563 const char *section
,
3569 pa_alsa_profile_set
*ps
= userdata
;
3576 if (pa_atou(rvalue
, &prio
) < 0) {
3577 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
3581 if ((m
= mapping_get(ps
, section
)))
3583 else if ((p
= profile_get(ps
, section
)))
3586 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3593 static int profile_parse_mappings(
3594 const char *filename
,
3596 const char *section
,
3602 pa_alsa_profile_set
*ps
= userdata
;
3607 if (!(p
= profile_get(ps
, section
))) {
3608 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3612 if (pa_streq(lvalue
, "input-mappings")) {
3613 pa_xstrfreev(p
->input_mapping_names
);
3614 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
3616 pa_xstrfreev(p
->output_mapping_names
);
3617 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
3623 static int profile_parse_skip_probe(
3624 const char *filename
,
3626 const char *section
,
3632 pa_alsa_profile_set
*ps
= userdata
;
3638 if (!(p
= profile_get(ps
, section
))) {
3639 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3643 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
3644 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
3653 static int decibel_fix_parse_db_values(
3654 const char *filename
,
3656 const char *section
,
3662 pa_alsa_profile_set
*ps
= userdata
;
3663 pa_alsa_decibel_fix
*db_fix
;
3667 unsigned n
= 8; /* Current size of the db_values table. */
3668 unsigned min_step
= 0;
3669 unsigned max_step
= 0;
3670 unsigned i
= 0; /* Index to the items table. */
3671 unsigned prev_step
= 0;
3674 pa_assert(filename
);
3680 if (!(db_fix
= decibel_fix_get(ps
, section
))) {
3681 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3685 if (!(items
= pa_split_spaces_strv(rvalue
))) {
3686 pa_log("[%s:%u] Value missing", pa_strnull(filename
), line
);
3690 db_values
= pa_xnew(long, n
);
3692 while ((item
= items
[i
++])) {
3693 char *s
= item
; /* Step value string. */
3694 char *d
= item
; /* dB value string. */
3698 /* Move d forward until it points to a colon or to the end of the item. */
3699 for (; *d
&& *d
!= ':'; ++d
);
3702 /* item started with colon. */
3703 pa_log("[%s:%u] No step value found in %s", filename
, line
, item
);
3707 if (!*d
|| !*(d
+ 1)) {
3708 /* No colon found, or it was the last character in item. */
3709 pa_log("[%s:%u] No dB value found in %s", filename
, line
, item
);
3713 /* pa_atou() needs a null-terminating string. Let's replace the colon
3714 * with a zero byte. */
3717 if (pa_atou(s
, &step
) < 0) {
3718 pa_log("[%s:%u] Invalid step value: %s", filename
, line
, s
);
3722 if (pa_atod(d
, &db
) < 0) {
3723 pa_log("[%s:%u] Invalid dB value: %s", filename
, line
, d
);
3727 if (step
<= prev_step
&& i
!= 1) {
3728 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename
, line
, step
, prev_step
);
3732 if (db
< prev_db
&& i
!= 1) {
3733 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename
, line
, db
, prev_db
);
3739 db_values
[0] = (long) (db
* 100.0);
3743 /* Interpolate linearly. */
3744 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3746 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3748 /* Reallocate the db_values table if it's about to overflow. */
3749 if (prev_step
+ 1 - min_step
== n
) {
3751 db_values
= pa_xrenew(long, db_values
, n
);
3754 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3761 db_fix
->min_step
= min_step
;
3762 db_fix
->max_step
= max_step
;
3763 pa_xfree(db_fix
->db_values
);
3764 db_fix
->db_values
= db_values
;
3766 pa_xstrfreev(items
);
3771 pa_xstrfreev(items
);
3772 pa_xfree(db_values
);
3777 static void mapping_paths_probe(pa_alsa_mapping
*m
, pa_alsa_profile
*profile
,
3778 pa_alsa_direction_t direction
) {
3782 snd_pcm_t
*pcm_handle
;
3783 pa_alsa_path_set
*ps
;
3784 snd_mixer_t
*mixer_handle
;
3785 snd_hctl_t
*hctl_handle
;
3787 if (direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3788 if (m
->output_path_set
)
3789 return; /* Already probed */
3790 m
->output_path_set
= ps
= pa_alsa_path_set_new(m
, direction
, NULL
); /* FIXME: Handle paths_dir */
3791 pcm_handle
= m
->output_pcm
;
3793 if (m
->input_path_set
)
3794 return; /* Already probed */
3795 m
->input_path_set
= ps
= pa_alsa_path_set_new(m
, direction
, NULL
); /* FIXME: Handle paths_dir */
3796 pcm_handle
= m
->input_pcm
;
3800 return; /* No paths */
3802 pa_assert(pcm_handle
);
3804 mixer_handle
= pa_alsa_open_mixer_for_pcm(pcm_handle
, NULL
, &hctl_handle
);
3805 if (!mixer_handle
|| !hctl_handle
) {
3806 /* Cannot open mixer, remove all entries */
3807 while (pa_hashmap_steal_first(ps
->paths
));
3812 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3813 if (pa_alsa_path_probe(p
, mixer_handle
, hctl_handle
, m
->profile_set
->ignore_dB
) < 0) {
3814 pa_hashmap_remove(ps
->paths
, p
);
3818 path_set_condense(ps
, mixer_handle
);
3819 path_set_make_paths_unique(ps
);
3822 snd_mixer_close(mixer_handle
);
3824 pa_log_debug("Available mixer paths (after tidying):");
3825 pa_alsa_path_set_dump(ps
);
3828 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3830 static const struct description_map well_known_descriptions
[] = {
3831 { "analog-mono", N_("Analog Mono") },
3832 { "analog-stereo", N_("Analog Stereo") },
3833 { "analog-surround-21", N_("Analog Surround 2.1") },
3834 { "analog-surround-30", N_("Analog Surround 3.0") },
3835 { "analog-surround-31", N_("Analog Surround 3.1") },
3836 { "analog-surround-40", N_("Analog Surround 4.0") },
3837 { "analog-surround-41", N_("Analog Surround 4.1") },
3838 { "analog-surround-50", N_("Analog Surround 5.0") },
3839 { "analog-surround-51", N_("Analog Surround 5.1") },
3840 { "analog-surround-61", N_("Analog Surround 6.0") },
3841 { "analog-surround-61", N_("Analog Surround 6.1") },
3842 { "analog-surround-70", N_("Analog Surround 7.0") },
3843 { "analog-surround-71", N_("Analog Surround 7.1") },
3844 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3845 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3846 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3847 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3848 { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
3849 { "hdmi-stereo", N_("Digital Stereo (HDMI)") },
3850 { "hdmi-surround-51", N_("Digital Surround 5.1 (HDMI)") }
3855 if (!pa_channel_map_valid(&m
->channel_map
)) {
3856 pa_log("Mapping %s is missing channel map.", m
->name
);
3860 if (!m
->device_strings
) {
3861 pa_log("Mapping %s is missing device strings.", m
->name
);
3865 if ((m
->input_path_names
&& m
->input_element
) ||
3866 (m
->output_path_names
&& m
->output_element
)) {
3867 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3871 if (!m
->description
)
3872 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3873 well_known_descriptions
,
3874 PA_ELEMENTSOF(well_known_descriptions
)));
3876 if (!m
->description
)
3877 m
->description
= pa_xstrdup(m
->name
);
3880 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3882 else if (m
->channel_map
.channels
== bonus
->channels
)
3889 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3890 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3894 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3896 pa_strnull(m
->description
),
3898 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3899 pa_yes_no(m
->supported
),
3903 static void profile_set_add_auto_pair(
3904 pa_alsa_profile_set
*ps
,
3905 pa_alsa_mapping
*m
, /* output */
3906 pa_alsa_mapping
*n
/* input */) {
3914 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3917 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3921 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3923 name
= pa_sprintf_malloc("output:%s", m
->name
);
3925 name
= pa_sprintf_malloc("input:%s", n
->name
);
3927 if (pa_hashmap_get(ps
->profiles
, name
)) {
3932 p
= pa_xnew0(pa_alsa_profile
, 1);
3933 p
->profile_set
= ps
;
3937 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3938 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3939 p
->priority
+= m
->priority
* 100;
3943 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3944 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3945 p
->priority
+= n
->priority
;
3948 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3951 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3952 pa_alsa_mapping
*m
, *n
;
3953 void *m_state
, *n_state
;
3957 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3958 profile_set_add_auto_pair(ps
, m
, NULL
);
3960 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3961 profile_set_add_auto_pair(ps
, m
, n
);
3964 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3965 profile_set_add_auto_pair(ps
, NULL
, n
);
3968 static int profile_verify(pa_alsa_profile
*p
) {
3970 static const struct description_map well_known_descriptions
[] = {
3971 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3972 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3973 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3974 { "off", N_("Off") }
3979 /* Replace the output mapping names by the actual mappings */
3980 if (p
->output_mapping_names
) {
3983 pa_assert(!p
->output_mappings
);
3984 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3986 for (name
= p
->output_mapping_names
; *name
; name
++) {
3989 pa_bool_t duplicate
= FALSE
;
3991 for (in
= name
+ 1; *in
; in
++)
3992 if (pa_streq(*name
, *in
)) {
4000 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
4001 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p
->name
, *name
);
4005 pa_idxset_put(p
->output_mappings
, m
, NULL
);
4011 pa_xstrfreev(p
->output_mapping_names
);
4012 p
->output_mapping_names
= NULL
;
4015 /* Replace the input mapping names by the actual mappings */
4016 if (p
->input_mapping_names
) {
4019 pa_assert(!p
->input_mappings
);
4020 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
4022 for (name
= p
->input_mapping_names
; *name
; name
++) {
4025 pa_bool_t duplicate
= FALSE
;
4027 for (in
= name
+ 1; *in
; in
++)
4028 if (pa_streq(*name
, *in
)) {
4036 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
4037 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p
->name
, *name
);
4041 pa_idxset_put(p
->input_mappings
, m
, NULL
);
4047 pa_xstrfreev(p
->input_mapping_names
);
4048 p
->input_mapping_names
= NULL
;
4051 if (!p
->input_mappings
&& !p
->output_mappings
) {
4052 pa_log("Profile '%s' lacks mappings.", p
->name
);
4056 if (!p
->description
)
4057 p
->description
= pa_xstrdup(lookup_description(p
->name
,
4058 well_known_descriptions
,
4059 PA_ELEMENTSOF(well_known_descriptions
)));
4061 if (!p
->description
) {
4066 sb
= pa_strbuf_new();
4068 if (p
->output_mappings
)
4069 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4070 if (!pa_strbuf_isempty(sb
))
4071 pa_strbuf_puts(sb
, " + ");
4073 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
4076 if (p
->input_mappings
)
4077 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4078 if (!pa_strbuf_isempty(sb
))
4079 pa_strbuf_puts(sb
, " + ");
4081 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
4084 p
->description
= pa_strbuf_tostring_free(sb
);
4090 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
4095 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4097 pa_strnull(p
->description
),
4099 pa_yes_no(p
->supported
),
4100 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
4101 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
4103 if (p
->input_mappings
)
4104 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
4105 pa_log_debug("Input %s", m
->name
);
4107 if (p
->output_mappings
)
4108 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
4109 pa_log_debug("Output %s", m
->name
);
4112 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
4115 /* Check that the dB mapping has been configured. Since "db-values" is
4116 * currently the only option in the DecibelFix section, and decibel fix
4117 * objects don't get created if a DecibelFix section is empty, this is
4118 * actually a redundant check. Having this may prevent future bugs,
4120 if (!db_fix
->db_values
) {
4121 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
4128 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
4129 char *db_values
= NULL
;
4133 if (db_fix
->db_values
) {
4135 unsigned long i
, nsteps
;
4137 pa_assert(db_fix
->min_step
<= db_fix
->max_step
);
4138 nsteps
= db_fix
->max_step
- db_fix
->min_step
+ 1;
4140 buf
= pa_strbuf_new();
4141 for (i
= 0; i
< nsteps
; ++i
)
4142 pa_strbuf_printf(buf
, "[%li]:%0.2f ", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
4144 db_values
= pa_strbuf_tostring_free(buf
);
4147 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4148 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
4150 pa_xfree(db_values
);
4153 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
4154 pa_alsa_profile_set
*ps
;
4157 pa_alsa_decibel_fix
*db_fix
;
4162 static pa_config_item items
[] = {
4164 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
4167 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
4168 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
4169 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
4170 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
4171 { "element-input", mapping_parse_element
, NULL
, NULL
},
4172 { "element-output", mapping_parse_element
, NULL
, NULL
},
4173 { "direction", mapping_parse_direction
, NULL
, NULL
},
4175 /* Shared by [Mapping ...] and [Profile ...] */
4176 { "description", mapping_parse_description
, NULL
, NULL
},
4177 { "priority", mapping_parse_priority
, NULL
, NULL
},
4180 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
4181 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
4182 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
4184 /* [DecibelFix ...] */
4185 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
4186 { NULL
, NULL
, NULL
, NULL
}
4189 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
4190 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4191 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4192 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4193 ps
->input_paths
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4194 ps
->output_paths
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4196 items
[0].data
= &ps
->auto_profiles
;
4199 fname
= "default.conf";
4201 fn
= pa_maybe_prefix_path(fname
,
4202 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
4203 PA_ALSA_PROFILE_SETS_DIR
);
4205 r
= pa_config_parse(fn
, NULL
, items
, ps
);
4211 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4212 if (mapping_verify(m
, bonus
) < 0)
4215 if (ps
->auto_profiles
)
4216 profile_set_add_auto(ps
);
4218 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4219 if (profile_verify(p
) < 0)
4222 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4223 if (decibel_fix_verify(db_fix
) < 0)
4229 pa_alsa_profile_set_free(ps
);
4233 static void profile_finalize_probing(pa_alsa_profile
*to_be_finalized
, pa_alsa_profile
*next
) {
4237 if (!to_be_finalized
)
4240 if (to_be_finalized
->output_mappings
)
4241 PA_IDXSET_FOREACH(m
, to_be_finalized
->output_mappings
, idx
) {
4246 if (to_be_finalized
->supported
)
4249 /* If this mapping is also in the next profile, we won't close the
4250 * pcm handle here, because it would get immediately reopened
4252 if (next
&& next
->output_mappings
&& pa_idxset_get_by_data(next
->output_mappings
, m
, NULL
))
4255 snd_pcm_close(m
->output_pcm
);
4256 m
->output_pcm
= NULL
;
4259 if (to_be_finalized
->input_mappings
)
4260 PA_IDXSET_FOREACH(m
, to_be_finalized
->input_mappings
, idx
) {
4265 if (to_be_finalized
->supported
)
4268 /* If this mapping is also in the next profile, we won't close the
4269 * pcm handle here, because it would get immediately reopened
4271 if (next
&& next
->input_mappings
&& pa_idxset_get_by_data(next
->input_mappings
, m
, NULL
))
4274 snd_pcm_close(m
->input_pcm
);
4275 m
->input_pcm
= NULL
;
4279 static snd_pcm_t
* mapping_open_pcm(pa_alsa_mapping
*m
,
4280 const pa_sample_spec
*ss
,
4283 unsigned default_n_fragments
,
4284 unsigned default_fragment_size_msec
) {
4286 pa_sample_spec try_ss
= *ss
;
4287 pa_channel_map try_map
= m
->channel_map
;
4288 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
4290 try_ss
.channels
= try_map
.channels
;
4293 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
4294 pa_frame_size(&try_ss
);
4295 try_buffer_size
= default_n_fragments
* try_period_size
;
4297 return pa_alsa_open_by_template(
4298 m
->device_strings
, dev_id
, NULL
, &try_ss
,
4299 &try_map
, mode
, &try_period_size
,
4300 &try_buffer_size
, 0, NULL
, NULL
, TRUE
);
4303 static void paths_drop_unsupported(pa_hashmap
* h
) {
4310 p
= pa_hashmap_iterate(h
, &state
, &key
);
4312 if (p
->supported
<= 0) {
4313 pa_hashmap_remove(h
, key
);
4314 pa_alsa_path_free(p
);
4316 p
= pa_hashmap_iterate(h
, &state
, &key
);
4320 void pa_alsa_profile_set_probe(
4321 pa_alsa_profile_set
*ps
,
4323 const pa_sample_spec
*ss
,
4324 unsigned default_n_fragments
,
4325 unsigned default_fragment_size_msec
) {
4328 pa_alsa_profile
*p
, *last
= NULL
;
4338 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
4341 /* Skip if this is already marked that it is supported (i.e. from the config file) */
4342 if (!p
->supported
) {
4344 pa_log_debug("Looking at profile %s", p
->name
);
4345 profile_finalize_probing(last
, p
);
4346 p
->supported
= TRUE
;
4348 /* Check if we can open all new ones */
4349 if (p
->output_mappings
)
4350 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4355 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
4356 if (!(m
->output_pcm
= mapping_open_pcm(m
, ss
, dev_id
,
4357 SND_PCM_STREAM_PLAYBACK
,
4358 default_n_fragments
,
4359 default_fragment_size_msec
))) {
4360 p
->supported
= FALSE
;
4365 if (p
->input_mappings
&& p
->supported
)
4366 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4371 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
4372 if (!(m
->input_pcm
= mapping_open_pcm(m
, ss
, dev_id
,
4373 SND_PCM_STREAM_CAPTURE
,
4374 default_n_fragments
,
4375 default_fragment_size_msec
))) {
4376 p
->supported
= FALSE
;
4387 pa_log_debug("Profile %s supported.", p
->name
);
4389 if (p
->output_mappings
)
4390 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
4392 mapping_paths_probe(m
, p
, PA_ALSA_DIRECTION_OUTPUT
);
4394 if (p
->input_mappings
)
4395 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
4397 mapping_paths_probe(m
, p
, PA_ALSA_DIRECTION_INPUT
);
4401 profile_finalize_probing(last
, NULL
);
4403 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4404 if (!p
->supported
) {
4405 pa_hashmap_remove(ps
->profiles
, p
->name
);
4409 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4410 if (m
->supported
<= 0) {
4411 pa_hashmap_remove(ps
->mappings
, m
->name
);
4415 paths_drop_unsupported(ps
->input_paths
);
4416 paths_drop_unsupported(ps
->output_paths
);
4421 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
4424 pa_alsa_decibel_fix
*db_fix
;
4429 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4432 pa_yes_no(ps
->auto_profiles
),
4433 pa_yes_no(ps
->probed
),
4434 pa_hashmap_size(ps
->mappings
),
4435 pa_hashmap_size(ps
->profiles
),
4436 pa_hashmap_size(ps
->decibel_fixes
));
4438 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4439 pa_alsa_mapping_dump(m
);
4441 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4442 pa_alsa_profile_dump(p
);
4444 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4445 pa_alsa_decibel_fix_dump(db_fix
);
4448 static pa_device_port
* device_port_alsa_init(pa_hashmap
*ports
,
4450 const char* description
,
4452 pa_alsa_setting
*setting
,
4453 pa_card_profile
*cp
,
4457 pa_device_port
* p
= pa_hashmap_get(ports
, name
);
4459 pa_alsa_port_data
*data
;
4461 p
= pa_device_port_new(core
, name
, description
, sizeof(pa_alsa_port_data
));
4463 pa_hashmap_put(ports
, p
->name
, p
);
4464 p
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4466 data
= PA_DEVICE_PORT_DATA(p
);
4468 data
->setting
= setting
;
4472 p
->is_input
|= path
->direction
== PA_ALSA_DIRECTION_ANY
|| path
->direction
== PA_ALSA_DIRECTION_INPUT
;
4473 p
->is_output
|= path
->direction
== PA_ALSA_DIRECTION_ANY
|| path
->direction
== PA_ALSA_DIRECTION_OUTPUT
;
4476 pa_hashmap_put(p
->profiles
, cp
->name
, cp
);
4479 pa_hashmap_put(extra
, p
->name
, p
);
4480 pa_device_port_ref(p
);
4486 void pa_alsa_path_set_add_ports(
4487 pa_alsa_path_set
*ps
,
4488 pa_card_profile
*cp
,
4501 PA_HASHMAP_FOREACH(path
, ps
->paths
, state
) {
4502 if (!path
->settings
|| !path
->settings
->next
) {
4503 /* If there is no or just one setting we only need a
4505 pa_device_port
*port
= device_port_alsa_init(ports
, path
->name
,
4506 path
->description
, path
, path
->settings
, cp
, extra
, core
);
4507 port
->priority
= path
->priority
* 100;
4511 PA_LLIST_FOREACH(s
, path
->settings
) {
4512 pa_device_port
*port
;
4515 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4517 if (s
->description
[0])
4518 d
= pa_sprintf_malloc("%s / %s", path
->description
, s
->description
);
4520 d
= pa_xstrdup(path
->description
);
4522 port
= device_port_alsa_init(ports
, n
, d
, path
, s
, cp
, extra
, core
);
4523 port
->priority
= path
->priority
* 100 + s
->priority
;
4532 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
, pa_card
*card
) {
4538 if (ps
->paths
&& pa_hashmap_size(ps
->paths
) > 0) {
4540 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4541 pa_alsa_path_set_add_ports(ps
, NULL
, card
->ports
, *p
, card
->core
);
4544 pa_log_debug("Added %u ports", *p
? pa_hashmap_size(*p
) : 0);