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
);
521 pa_proplist_free(p
->proplist
);
523 pa_xfree(p
->description
);
527 void pa_alsa_path_set_free(pa_alsa_path_set
*ps
) {
531 pa_hashmap_free(ps
->paths
, NULL
, NULL
);
536 static long to_alsa_dB(pa_volume_t v
) {
537 return (long) (pa_sw_volume_to_dB(v
) * 100.0);
540 static pa_volume_t
from_alsa_dB(long v
) {
541 return pa_sw_volume_from_dB((double) v
/ 100.0);
544 static long to_alsa_volume(pa_volume_t v
, long min
, long max
) {
547 w
= (long) round(((double) v
* (double) (max
- min
)) / PA_VOLUME_NORM
) + min
;
548 return PA_CLAMP_UNLIKELY(w
, min
, max
);
551 static pa_volume_t
from_alsa_volume(long v
, long min
, long max
) {
552 return (pa_volume_t
) round(((double) (v
- min
) * PA_VOLUME_NORM
) / (double) (max
- min
));
555 #define SELEM_INIT(sid, name) \
557 snd_mixer_selem_id_alloca(&(sid)); \
558 snd_mixer_selem_id_set_name((sid), (name)); \
559 snd_mixer_selem_id_set_index((sid), 0); \
562 static int element_get_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
563 snd_mixer_selem_id_t
*sid
;
564 snd_mixer_elem_t
*me
;
565 snd_mixer_selem_channel_id_t c
;
566 pa_channel_position_mask_t mask
= 0;
574 SELEM_INIT(sid
, e
->alsa_name
);
575 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
576 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
580 pa_cvolume_mute(v
, cm
->channels
);
582 /* We take the highest volume of all channels that match */
584 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
591 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
592 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
594 if ((r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
)) >= 0) {
595 /* If the channel volume is outside the limits set
596 * by the dB fix, we clamp the hw volume to be
597 * within the limits. */
598 if (value
< e
->db_fix
->min_step
) {
599 value
= e
->db_fix
->min_step
;
600 snd_mixer_selem_set_playback_volume(me
, c
, value
);
601 pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
602 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
603 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
604 } else if (value
> e
->db_fix
->max_step
) {
605 value
= e
->db_fix
->max_step
;
606 snd_mixer_selem_set_playback_volume(me
, c
, value
);
607 pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
608 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
609 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
612 /* Volume step -> dB value conversion. */
613 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
616 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
620 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
622 if ((r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
)) >= 0) {
623 /* If the channel volume is outside the limits set
624 * by the dB fix, we clamp the hw volume to be
625 * within the limits. */
626 if (value
< e
->db_fix
->min_step
) {
627 value
= e
->db_fix
->min_step
;
628 snd_mixer_selem_set_capture_volume(me
, c
, value
);
629 pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
630 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
631 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
632 } else if (value
> e
->db_fix
->max_step
) {
633 value
= e
->db_fix
->max_step
;
634 snd_mixer_selem_set_capture_volume(me
, c
, value
);
635 pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
636 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
637 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
640 /* Volume step -> dB value conversion. */
641 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
644 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
652 #ifdef HAVE_VALGRIND_MEMCHECK_H
653 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
656 f
= from_alsa_dB(value
);
661 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
662 if (snd_mixer_selem_has_playback_channel(me
, c
))
663 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
667 if (snd_mixer_selem_has_capture_channel(me
, c
))
668 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
676 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
679 for (k
= 0; k
< cm
->channels
; k
++)
680 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
681 if (v
->values
[k
] < f
)
684 mask
|= e
->masks
[c
][e
->n_channels
-1];
687 for (k
= 0; k
< cm
->channels
; k
++)
688 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
689 v
->values
[k
] = PA_VOLUME_NORM
;
694 int pa_alsa_path_get_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
705 pa_cvolume_reset(v
, cm
->channels
);
707 PA_LLIST_FOREACH(e
, p
->elements
) {
710 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
713 pa_assert(!p
->has_dB
|| e
->has_dB
);
715 if (element_get_volume(e
, m
, cm
, &ev
) < 0)
718 /* If we have no dB information all we can do is take the first element and leave */
724 pa_sw_cvolume_multiply(v
, v
, &ev
);
730 static int element_get_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t
*b
) {
731 snd_mixer_selem_id_t
*sid
;
732 snd_mixer_elem_t
*me
;
733 snd_mixer_selem_channel_id_t c
;
739 SELEM_INIT(sid
, e
->alsa_name
);
740 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
741 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
745 /* We return muted if at least one channel is muted */
747 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
751 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
752 if (snd_mixer_selem_has_playback_channel(me
, c
))
753 r
= snd_mixer_selem_get_playback_switch(me
, c
, &value
);
757 if (snd_mixer_selem_has_capture_channel(me
, c
))
758 r
= snd_mixer_selem_get_capture_switch(me
, c
, &value
);
776 int pa_alsa_path_get_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t
*muted
) {
786 PA_LLIST_FOREACH(e
, p
->elements
) {
789 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
792 if (element_get_switch(e
, m
, &b
) < 0)
805 /* Finds the closest item in db_fix->db_values and returns the corresponding
806 * step. *db_value is replaced with the value from the db_values table.
807 * Rounding is done based on the rounding parameter: -1 means rounding down and
808 * +1 means rounding up. */
809 static long decibel_fix_get_step(pa_alsa_decibel_fix
*db_fix
, long *db_value
, int rounding
) {
815 pa_assert(rounding
!= 0);
817 max_i
= db_fix
->max_step
- db_fix
->min_step
;
820 for (i
= 0; i
< max_i
; i
++) {
821 if (db_fix
->db_values
[i
] >= *db_value
)
825 for (i
= 0; i
< max_i
; i
++) {
826 if (db_fix
->db_values
[i
+ 1] > *db_value
)
831 *db_value
= db_fix
->db_values
[i
];
833 return i
+ db_fix
->min_step
;
836 /* Alsa lib documentation says for snd_mixer_selem_set_playback_dB() direction argument,
837 * that "-1 = accurate or first below, 0 = accurate, 1 = accurate or first above".
838 * But even with accurate nearest dB volume step is not selected, so that is why we need
839 * this function. Returns 0 and nearest selectable volume in *value_dB on success or
840 * negative error code if fails. */
841 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
) {
851 if (d
== PA_ALSA_DIRECTION_OUTPUT
) {
852 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, *value_dB
, +1, &alsa_val
)) >= 0)
853 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value_high
);
858 if (value_high
== *value_dB
)
861 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, *value_dB
, -1, &alsa_val
)) >= 0)
862 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value_low
);
864 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, *value_dB
, +1, &alsa_val
)) >= 0)
865 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value_high
);
870 if (value_high
== *value_dB
)
873 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, *value_dB
, -1, &alsa_val
)) >= 0)
874 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value_low
);
880 if (labs(value_high
- *value_dB
) < labs(value_low
- *value_dB
))
881 *value_dB
= value_high
;
883 *value_dB
= value_low
;
888 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
) {
890 snd_mixer_selem_id_t
*sid
;
892 snd_mixer_elem_t
*me
;
893 snd_mixer_selem_channel_id_t c
;
894 pa_channel_position_mask_t mask
= 0;
901 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
903 SELEM_INIT(sid
, e
->alsa_name
);
904 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
905 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
909 pa_cvolume_mute(&rv
, cm
->channels
);
911 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
913 pa_volume_t f
= PA_VOLUME_MUTED
;
914 pa_bool_t found
= FALSE
;
916 for (k
= 0; k
< cm
->channels
; k
++)
917 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
])) {
919 if (v
->values
[k
] > f
)
924 /* Hmm, so this channel does not exist in the volume
925 * struct, so let's bind it to the overall max of the
927 f
= pa_cvolume_max(v
);
931 long value
= to_alsa_dB(f
);
934 if (e
->volume_limit
>= 0 && value
> (e
->max_dB
* 100))
935 value
= e
->max_dB
* 100;
937 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
938 /* If we call set_playback_volume() without checking first
939 * if the channel is available, ALSA behaves very
940 * strangely and doesn't fail the call */
941 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
945 r
= snd_mixer_selem_set_playback_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
947 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
953 if (deferred_volume
) {
954 if ((r
= element_get_nearest_alsa_dB(me
, c
, PA_ALSA_DIRECTION_OUTPUT
, &value
)) >= 0)
955 r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, 0);
957 if ((r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, rounding
)) >= 0)
958 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
962 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
963 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value
);
969 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
973 r
= snd_mixer_selem_set_capture_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
975 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
981 if (deferred_volume
) {
982 if ((r
= element_get_nearest_alsa_dB(me
, c
, PA_ALSA_DIRECTION_INPUT
, &value
)) >= 0)
983 r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, 0);
985 if ((r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, rounding
)) >= 0)
986 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
990 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
991 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value
);
1001 #ifdef HAVE_VALGRIND_MEMCHECK_H
1002 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
1005 f
= from_alsa_dB(value
);
1010 value
= to_alsa_volume(f
, e
->min_volume
, e
->max_volume
);
1012 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1013 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
1014 if ((r
= snd_mixer_selem_set_playback_volume(me
, c
, value
)) >= 0)
1015 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
1019 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
1020 if ((r
= snd_mixer_selem_set_capture_volume(me
, c
, value
)) >= 0)
1021 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
1029 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
1032 for (k
= 0; k
< cm
->channels
; k
++)
1033 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
1034 if (rv
.values
[k
] < f
)
1037 mask
|= e
->masks
[c
][e
->n_channels
-1];
1040 for (k
= 0; k
< cm
->channels
; k
++)
1041 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
1042 rv
.values
[k
] = PA_VOLUME_NORM
;
1048 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
) {
1057 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
1062 rv
= *v
; /* Remaining adjustment */
1063 pa_cvolume_reset(v
, cm
->channels
); /* Adjustment done */
1065 PA_LLIST_FOREACH(e
, p
->elements
) {
1068 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
1071 pa_assert(!p
->has_dB
|| e
->has_dB
);
1074 if (element_set_volume(e
, m
, cm
, &ev
, deferred_volume
, write_to_hw
) < 0)
1082 pa_sw_cvolume_multiply(v
, v
, &ev
);
1083 pa_sw_cvolume_divide(&rv
, &rv
, &ev
);
1089 static int element_set_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t b
) {
1090 snd_mixer_elem_t
*me
;
1091 snd_mixer_selem_id_t
*sid
;
1097 SELEM_INIT(sid
, e
->alsa_name
);
1098 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1099 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1103 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1104 r
= snd_mixer_selem_set_playback_switch_all(me
, b
);
1106 r
= snd_mixer_selem_set_capture_switch_all(me
, b
);
1109 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1114 int pa_alsa_path_set_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t muted
) {
1123 PA_LLIST_FOREACH(e
, p
->elements
) {
1125 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
1128 if (element_set_switch(e
, m
, !muted
) < 0)
1135 /* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
1136 * function sets all channels of the volume element to e->min_volume, 0 dB or
1137 * e->constant_volume. */
1138 static int element_set_constant_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1139 snd_mixer_elem_t
*me
= NULL
;
1140 snd_mixer_selem_id_t
*sid
= NULL
;
1143 pa_bool_t volume_set
= FALSE
;
1148 SELEM_INIT(sid
, e
->alsa_name
);
1149 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1150 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1154 switch (e
->volume_use
) {
1155 case PA_ALSA_VOLUME_OFF
:
1156 volume
= e
->min_volume
;
1160 case PA_ALSA_VOLUME_ZERO
:
1164 volume
= decibel_fix_get_step(e
->db_fix
, &dB
, (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
? +1 : -1));
1169 case PA_ALSA_VOLUME_CONSTANT
:
1170 volume
= e
->constant_volume
;
1175 pa_assert_not_reached();
1179 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1180 r
= snd_mixer_selem_set_playback_volume_all(me
, volume
);
1182 r
= snd_mixer_selem_set_capture_volume_all(me
, volume
);
1184 pa_assert(e
->volume_use
== PA_ALSA_VOLUME_ZERO
);
1185 pa_assert(!e
->db_fix
);
1187 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1188 r
= snd_mixer_selem_set_playback_dB_all(me
, 0, +1);
1190 r
= snd_mixer_selem_set_capture_dB_all(me
, 0, -1);
1194 pa_log_warn("Failed to set volume of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1199 int pa_alsa_path_select(pa_alsa_path
*p
, snd_mixer_t
*m
) {
1206 pa_log_debug("Activating path %s", p
->name
);
1207 pa_alsa_path_dump(p
);
1209 PA_LLIST_FOREACH(e
, p
->elements
) {
1211 switch (e
->switch_use
) {
1212 case PA_ALSA_SWITCH_OFF
:
1213 r
= element_set_switch(e
, m
, FALSE
);
1216 case PA_ALSA_SWITCH_ON
:
1217 r
= element_set_switch(e
, m
, TRUE
);
1220 case PA_ALSA_SWITCH_MUTE
:
1221 case PA_ALSA_SWITCH_IGNORE
:
1222 case PA_ALSA_SWITCH_SELECT
:
1230 switch (e
->volume_use
) {
1231 case PA_ALSA_VOLUME_OFF
:
1232 case PA_ALSA_VOLUME_ZERO
:
1233 case PA_ALSA_VOLUME_CONSTANT
:
1234 r
= element_set_constant_volume(e
, m
);
1237 case PA_ALSA_VOLUME_MERGE
:
1238 case PA_ALSA_VOLUME_IGNORE
:
1250 static int check_required(pa_alsa_element
*e
, snd_mixer_elem_t
*me
) {
1251 pa_bool_t has_switch
;
1252 pa_bool_t has_enumeration
;
1253 pa_bool_t has_volume
;
1258 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1260 snd_mixer_selem_has_playback_switch(me
) ||
1261 (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
));
1264 snd_mixer_selem_has_capture_switch(me
) ||
1265 (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
));
1268 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1270 snd_mixer_selem_has_playback_volume(me
) ||
1271 (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
));
1274 snd_mixer_selem_has_capture_volume(me
) ||
1275 (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
));
1278 has_enumeration
= snd_mixer_selem_is_enumerated(me
);
1280 if ((e
->required
== PA_ALSA_REQUIRED_SWITCH
&& !has_switch
) ||
1281 (e
->required
== PA_ALSA_REQUIRED_VOLUME
&& !has_volume
) ||
1282 (e
->required
== PA_ALSA_REQUIRED_ENUMERATION
&& !has_enumeration
))
1285 if (e
->required
== PA_ALSA_REQUIRED_ANY
&& !(has_switch
|| has_volume
|| has_enumeration
))
1288 if ((e
->required_absent
== PA_ALSA_REQUIRED_SWITCH
&& has_switch
) ||
1289 (e
->required_absent
== PA_ALSA_REQUIRED_VOLUME
&& has_volume
) ||
1290 (e
->required_absent
== PA_ALSA_REQUIRED_ENUMERATION
&& has_enumeration
))
1293 if (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& (has_switch
|| has_volume
|| has_enumeration
))
1296 if (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) {
1297 switch (e
->required_any
) {
1298 case PA_ALSA_REQUIRED_VOLUME
:
1299 e
->path
->req_any_present
|= (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
);
1301 case PA_ALSA_REQUIRED_SWITCH
:
1302 e
->path
->req_any_present
|= (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
);
1304 case PA_ALSA_REQUIRED_ENUMERATION
:
1305 e
->path
->req_any_present
|= (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1307 case PA_ALSA_REQUIRED_ANY
:
1308 e
->path
->req_any_present
|=
1309 (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) ||
1310 (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) ||
1311 (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1314 pa_assert_not_reached();
1318 if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1320 PA_LLIST_FOREACH(o
, e
->options
) {
1321 e
->path
->req_any_present
|= (o
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) &&
1323 if (o
->required
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
< 0)
1325 if (o
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
>= 0)
1333 static int element_probe(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1334 snd_mixer_selem_id_t
*sid
;
1335 snd_mixer_elem_t
*me
;
1341 SELEM_INIT(sid
, e
->alsa_name
);
1343 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1345 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1348 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1349 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1350 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1355 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1356 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1358 if (!snd_mixer_selem_has_playback_switch(me
)) {
1359 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1360 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1362 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1367 if (!snd_mixer_selem_has_capture_switch(me
)) {
1368 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1369 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1371 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1375 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1376 e
->direction_try_other
= FALSE
;
1379 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1381 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1383 if (!snd_mixer_selem_has_playback_volume(me
)) {
1384 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1385 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1387 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1392 if (!snd_mixer_selem_has_capture_volume(me
)) {
1393 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1394 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1396 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1400 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1401 long min_dB
= 0, max_dB
= 0;
1404 e
->direction_try_other
= FALSE
;
1406 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1407 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1409 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1412 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1416 if (e
->min_volume
>= e
->max_volume
) {
1417 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
);
1418 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1420 } else if (e
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&&
1421 (e
->min_volume
> e
->constant_volume
|| e
->max_volume
< e
->constant_volume
)) {
1422 pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
1423 e
->constant_volume
, e
->alsa_name
, e
->min_volume
, e
->max_volume
);
1424 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1428 pa_channel_position_t p
;
1431 ((e
->min_volume
> e
->db_fix
->min_step
) ||
1432 (e
->max_volume
< e
->db_fix
->max_step
))) {
1433 pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1434 "real hardware range (%li-%li). Disabling the decibel fix.", e
->alsa_name
,
1435 e
->db_fix
->min_step
, e
->db_fix
->max_step
,
1436 e
->min_volume
, e
->max_volume
);
1438 decibel_fix_free(e
->db_fix
);
1444 e
->min_volume
= e
->db_fix
->min_step
;
1445 e
->max_volume
= e
->db_fix
->max_step
;
1446 min_dB
= e
->db_fix
->db_values
[0];
1447 max_dB
= e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
];
1448 } else if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1449 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1451 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1453 /* Check that the kernel driver returns consistent limits with
1454 * both _get_*_dB_range() and _ask_*_vol_dB(). */
1455 if (e
->has_dB
&& !e
->db_fix
) {
1456 long min_dB_checked
= 0;
1457 long max_dB_checked
= 0;
1459 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1460 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1462 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1465 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->min_volume
);
1469 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1470 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1472 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1475 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->max_volume
);
1479 if (min_dB
!= min_dB_checked
|| max_dB
!= max_dB_checked
) {
1480 pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
1481 "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
1482 "%0.2f dB at level %li.",
1484 min_dB
/ 100.0, max_dB
/ 100.0,
1485 min_dB_checked
/ 100.0, e
->min_volume
, max_dB_checked
/ 100.0, e
->max_volume
);
1491 #ifdef HAVE_VALGRIND_MEMCHECK_H
1492 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1493 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1496 e
->min_dB
= ((double) min_dB
) / 100.0;
1497 e
->max_dB
= ((double) max_dB
) / 100.0;
1499 if (min_dB
>= max_dB
) {
1500 pa_assert(!e
->db_fix
);
1501 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
);
1506 if (e
->volume_limit
>= 0) {
1507 if (e
->volume_limit
<= e
->min_volume
|| e
->volume_limit
> e
->max_volume
)
1508 pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1509 "%li-%li. The volume limit is ignored.",
1510 e
->alsa_name
, e
->path
->name
, e
->volume_limit
, e
->min_volume
+ 1, e
->max_volume
);
1513 e
->max_volume
= e
->volume_limit
;
1517 e
->db_fix
->max_step
= e
->max_volume
;
1518 e
->max_dB
= ((double) e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
]) / 100.0;
1521 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1522 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB
);
1524 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB
);
1527 pa_log_warn("Failed to get dB value of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1530 e
->max_dB
= ((double) max_dB
) / 100.0;
1536 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1537 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1539 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1544 if (!e
->override_map
) {
1545 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1546 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1549 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1552 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1555 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1558 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1560 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1563 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1564 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1566 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1569 if (e
->n_channels
<= 0) {
1570 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1574 if (e
->n_channels
> 2) {
1575 /* FIXME: In some places code like this is used:
1577 * e->masks[alsa_channel_ids[p]][e->n_channels-1]
1579 * The definition of e->masks is
1581 * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][2];
1583 * Since the array size is fixed at 2, we obviously
1584 * don't support elements with more than two
1586 pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e
->alsa_name
, e
->n_channels
);
1590 if (!e
->override_map
) {
1591 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1592 pa_bool_t has_channel
;
1594 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1597 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1598 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1600 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1602 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1607 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1608 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1611 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1619 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1622 PA_LLIST_FOREACH(o
, e
->options
)
1623 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1624 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1628 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1629 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1633 PA_LLIST_FOREACH(o
, e
->options
) {
1636 for (i
= 0; i
< n
; i
++) {
1639 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1642 if (!pa_streq(buf
, o
->alsa_name
))
1650 if (check_required(e
, me
) < 0)
1656 static int jack_probe(pa_alsa_jack
*j
, snd_hctl_t
*h
) {
1661 j
->has_control
= pa_alsa_find_jack(h
, j
->alsa_name
) != NULL
;
1663 if (j
->has_control
) {
1664 if (j
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
)
1666 if (j
->required_any
!= PA_ALSA_REQUIRED_IGNORE
)
1667 j
->path
->req_any_present
= TRUE
;
1669 if (j
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1676 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, pa_bool_t prefixed
) {
1683 if (!pa_startswith(section
, "Element "))
1689 /* This is not an element section, but an enum section? */
1690 if (strchr(section
, ':'))
1693 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1694 return p
->last_element
;
1696 PA_LLIST_FOREACH(e
, p
->elements
)
1697 if (pa_streq(e
->alsa_name
, section
))
1700 e
= pa_xnew0(pa_alsa_element
, 1);
1702 e
->alsa_name
= pa_xstrdup(section
);
1703 e
->direction
= p
->direction
;
1704 e
->volume_limit
= -1;
1706 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1709 p
->last_element
= e
;
1713 static pa_alsa_jack
* jack_get(pa_alsa_path
*p
, const char *section
) {
1716 if (!pa_startswith(section
, "Jack "))
1720 if (p
->last_jack
&& pa_streq(p
->last_jack
->name
, section
))
1721 return p
->last_jack
;
1723 PA_LLIST_FOREACH(j
, p
->jacks
)
1724 if (pa_streq(j
->name
, section
))
1727 j
= pa_xnew0(pa_alsa_jack
, 1);
1728 j
->state_unplugged
= PA_PORT_AVAILABLE_NO
;
1729 j
->state_plugged
= PA_PORT_AVAILABLE_YES
;
1731 j
->name
= pa_xstrdup(section
);
1732 j
->alsa_name
= pa_sprintf_malloc("%s Jack", section
);
1733 PA_LLIST_INSERT_AFTER(pa_alsa_jack
, p
->jacks
, p
->last_jack
, j
);
1741 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1747 if (!pa_startswith(section
, "Option "))
1752 /* This is not an enum section, but an element section? */
1753 if (!(on
= strchr(section
, ':')))
1756 en
= pa_xstrndup(section
, on
- section
);
1759 if (p
->last_option
&&
1760 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1761 pa_streq(p
->last_option
->alsa_name
, on
)) {
1763 return p
->last_option
;
1766 pa_assert_se(e
= element_get(p
, en
, FALSE
));
1769 PA_LLIST_FOREACH(o
, e
->options
)
1770 if (pa_streq(o
->alsa_name
, on
))
1773 o
= pa_xnew0(pa_alsa_option
, 1);
1775 o
->alsa_name
= pa_xstrdup(on
);
1778 if (p
->last_option
&& p
->last_option
->element
== e
)
1779 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1781 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1788 static int element_parse_switch(pa_config_parser_state
*state
) {
1794 p
= state
->userdata
;
1796 if (!(e
= element_get(p
, state
->section
, TRUE
))) {
1797 pa_log("[%s:%u] Switch makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1801 if (pa_streq(state
->rvalue
, "ignore"))
1802 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1803 else if (pa_streq(state
->rvalue
, "mute"))
1804 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1805 else if (pa_streq(state
->rvalue
, "off"))
1806 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1807 else if (pa_streq(state
->rvalue
, "on"))
1808 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1809 else if (pa_streq(state
->rvalue
, "select"))
1810 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1812 pa_log("[%s:%u] Switch invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1819 static int element_parse_volume(pa_config_parser_state
*state
) {
1825 p
= state
->userdata
;
1827 if (!(e
= element_get(p
, state
->section
, TRUE
))) {
1828 pa_log("[%s:%u] Volume makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1832 if (pa_streq(state
->rvalue
, "ignore"))
1833 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1834 else if (pa_streq(state
->rvalue
, "merge"))
1835 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1836 else if (pa_streq(state
->rvalue
, "off"))
1837 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1838 else if (pa_streq(state
->rvalue
, "zero"))
1839 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1843 if (pa_atou(state
->rvalue
, &constant
) >= 0) {
1844 e
->volume_use
= PA_ALSA_VOLUME_CONSTANT
;
1845 e
->constant_volume
= constant
;
1847 pa_log("[%s:%u] Volume invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1855 static int element_parse_enumeration(pa_config_parser_state
*state
) {
1861 p
= state
->userdata
;
1863 if (!(e
= element_get(p
, state
->section
, TRUE
))) {
1864 pa_log("[%s:%u] Enumeration makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1868 if (pa_streq(state
->rvalue
, "ignore"))
1869 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1870 else if (pa_streq(state
->rvalue
, "select"))
1871 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1873 pa_log("[%s:%u] Enumeration invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1880 static int option_parse_priority(pa_config_parser_state
*state
) {
1887 p
= state
->userdata
;
1889 if (!(o
= option_get(p
, state
->section
))) {
1890 pa_log("[%s:%u] Priority makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1894 if (pa_atou(state
->rvalue
, &prio
) < 0) {
1895 pa_log("[%s:%u] Priority invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1903 static int option_parse_name(pa_config_parser_state
*state
) {
1909 p
= state
->userdata
;
1911 if (!(o
= option_get(p
, state
->section
))) {
1912 pa_log("[%s:%u] Name makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1917 o
->name
= pa_xstrdup(state
->rvalue
);
1922 static int element_parse_required(pa_config_parser_state
*state
) {
1927 pa_alsa_required_t req
;
1931 p
= state
->userdata
;
1933 e
= element_get(p
, state
->section
, TRUE
);
1934 o
= option_get(p
, state
->section
);
1935 j
= jack_get(p
, state
->section
);
1936 if (!e
&& !o
&& !j
) {
1937 pa_log("[%s:%u] Required makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1941 if (pa_streq(state
->rvalue
, "ignore"))
1942 req
= PA_ALSA_REQUIRED_IGNORE
;
1943 else if (pa_streq(state
->rvalue
, "switch") && e
)
1944 req
= PA_ALSA_REQUIRED_SWITCH
;
1945 else if (pa_streq(state
->rvalue
, "volume") && e
)
1946 req
= PA_ALSA_REQUIRED_VOLUME
;
1947 else if (pa_streq(state
->rvalue
, "enumeration"))
1948 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1949 else if (pa_streq(state
->rvalue
, "any"))
1950 req
= PA_ALSA_REQUIRED_ANY
;
1952 pa_log("[%s:%u] Required invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1956 if (pa_streq(state
->lvalue
, "required-absent")) {
1958 e
->required_absent
= req
;
1960 o
->required_absent
= req
;
1962 j
->required_absent
= req
;
1964 else if (pa_streq(state
->lvalue
, "required-any")) {
1966 e
->required_any
= req
;
1967 e
->path
->has_req_any
|= (req
!= PA_ALSA_REQUIRED_IGNORE
);
1970 o
->required_any
= req
;
1971 o
->element
->path
->has_req_any
|= (req
!= PA_ALSA_REQUIRED_IGNORE
);
1974 j
->required_any
= req
;
1975 j
->path
->has_req_any
|= (req
!= PA_ALSA_REQUIRED_IGNORE
);
1991 static int element_parse_direction(pa_config_parser_state
*state
) {
1997 p
= state
->userdata
;
1999 if (!(e
= element_get(p
, state
->section
, TRUE
))) {
2000 pa_log("[%s:%u] Direction makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2004 if (pa_streq(state
->rvalue
, "playback"))
2005 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
2006 else if (pa_streq(state
->rvalue
, "capture"))
2007 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
2009 pa_log("[%s:%u] Direction invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
2016 static int element_parse_direction_try_other(pa_config_parser_state
*state
) {
2023 p
= state
->userdata
;
2025 if (!(e
= element_get(p
, state
->section
, TRUE
))) {
2026 pa_log("[%s:%u] Direction makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2030 if ((yes
= pa_parse_boolean(state
->rvalue
)) < 0) {
2031 pa_log("[%s:%u] Direction invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
2035 e
->direction_try_other
= !!yes
;
2039 static int element_parse_volume_limit(pa_config_parser_state
*state
) {
2046 p
= state
->userdata
;
2048 if (!(e
= element_get(p
, state
->section
, TRUE
))) {
2049 pa_log("[%s:%u] volume-limit makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2053 if (pa_atol(state
->rvalue
, &volume_limit
) < 0 || volume_limit
< 0) {
2054 pa_log("[%s:%u] Invalid value for volume-limit", state
->filename
, state
->lineno
);
2058 e
->volume_limit
= volume_limit
;
2062 static pa_channel_position_mask_t
parse_mask(const char *m
) {
2063 pa_channel_position_mask_t v
;
2065 if (pa_streq(m
, "all-left"))
2066 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
2067 else if (pa_streq(m
, "all-right"))
2068 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
2069 else if (pa_streq(m
, "all-center"))
2070 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
2071 else if (pa_streq(m
, "all-front"))
2072 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
2073 else if (pa_streq(m
, "all-rear"))
2074 v
= PA_CHANNEL_POSITION_MASK_REAR
;
2075 else if (pa_streq(m
, "all-side"))
2076 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
2077 else if (pa_streq(m
, "all-top"))
2078 v
= PA_CHANNEL_POSITION_MASK_TOP
;
2079 else if (pa_streq(m
, "all-no-lfe"))
2080 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
2081 else if (pa_streq(m
, "all"))
2082 v
= PA_CHANNEL_POSITION_MASK_ALL
;
2084 pa_channel_position_t p
;
2086 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
2089 v
= PA_CHANNEL_POSITION_MASK(p
);
2095 static int element_parse_override_map(pa_config_parser_state
*state
) {
2098 const char *split_state
= NULL
;
2104 p
= state
->userdata
;
2106 if (!(e
= element_get(p
, state
->section
, TRUE
))) {
2107 pa_log("[%s:%u] Override map makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2111 while ((n
= pa_split(state
->rvalue
, ",", &split_state
))) {
2112 pa_channel_position_mask_t m
;
2117 if ((m
= parse_mask(n
)) == 0) {
2118 pa_log("[%s:%u] Override map '%s' invalid in '%s'", state
->filename
, state
->lineno
, n
, state
->section
);
2124 if (pa_streq(state
->lvalue
, "override-map.1"))
2125 e
->masks
[i
++][0] = m
;
2127 e
->masks
[i
++][1] = m
;
2129 /* Later on we might add override-map.3 and so on here ... */
2134 e
->override_map
= TRUE
;
2139 static int jack_parse_state(pa_config_parser_state
*state
) {
2142 pa_port_available_t pa
;
2146 p
= state
->userdata
;
2148 if (!(j
= jack_get(p
, state
->section
))) {
2149 pa_log("[%s:%u] state makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2153 if (pa_streq(state
->rvalue
, "yes"))
2154 pa
= PA_PORT_AVAILABLE_YES
;
2155 else if (pa_streq(state
->rvalue
, "no"))
2156 pa
= PA_PORT_AVAILABLE_NO
;
2157 else if (pa_streq(state
->rvalue
, "unknown"))
2158 pa
= PA_PORT_AVAILABLE_UNKNOWN
;
2160 pa_log("[%s:%u] state must be 'yes', 'no' or 'unknown' in '%s'", state
->filename
, state
->lineno
, state
->section
);
2164 if (pa_streq(state
->lvalue
, "state.unplugged"))
2165 j
->state_unplugged
= pa
;
2167 j
->state_plugged
= pa
;
2168 pa_assert(pa_streq(state
->lvalue
, "state.plugged"));
2174 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
2175 snd_mixer_selem_id_t
*sid
;
2176 snd_mixer_elem_t
*me
;
2182 SELEM_INIT(sid
, e
->alsa_name
);
2183 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2184 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2188 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
2190 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
2191 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
2193 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
2196 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2199 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
2201 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
2202 pa_log_warn("Failed to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2208 int pa_alsa_setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
2215 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
2216 element_set_option(o
->element
, m
, o
->alsa_idx
);
2221 static int option_verify(pa_alsa_option
*o
) {
2222 static const struct description_map well_known_descriptions
[] = {
2223 { "input", N_("Input") },
2224 { "input-docking", N_("Docking Station Input") },
2225 { "input-docking-microphone", N_("Docking Station Microphone") },
2226 { "input-docking-linein", N_("Docking Station Line In") },
2227 { "input-linein", N_("Line In") },
2228 { "input-microphone", N_("Microphone") },
2229 { "input-microphone-front", N_("Front Microphone") },
2230 { "input-microphone-rear", N_("Rear Microphone") },
2231 { "input-microphone-external", N_("External Microphone") },
2232 { "input-microphone-internal", N_("Internal Microphone") },
2233 { "input-radio", N_("Radio") },
2234 { "input-video", N_("Video") },
2235 { "input-agc-on", N_("Automatic Gain Control") },
2236 { "input-agc-off", N_("No Automatic Gain Control") },
2237 { "input-boost-on", N_("Boost") },
2238 { "input-boost-off", N_("No Boost") },
2239 { "output-amplifier-on", N_("Amplifier") },
2240 { "output-amplifier-off", N_("No Amplifier") },
2241 { "output-bass-boost-on", N_("Bass Boost") },
2242 { "output-bass-boost-off", N_("No Bass Boost") },
2243 { "output-speaker", N_("Speaker") },
2244 { "output-headphones", N_("Headphones") }
2250 pa_log("No name set for option %s", o
->alsa_name
);
2254 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
2255 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
2256 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
2260 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
2261 !pa_streq(o
->alsa_name
, "on") &&
2262 !pa_streq(o
->alsa_name
, "off")) {
2263 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
2267 if (!o
->description
)
2268 o
->description
= pa_xstrdup(lookup_description(o
->name
,
2269 well_known_descriptions
,
2270 PA_ELEMENTSOF(well_known_descriptions
)));
2271 if (!o
->description
)
2272 o
->description
= pa_xstrdup(o
->name
);
2277 static int element_verify(pa_alsa_element
*e
) {
2282 // 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);
2283 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
2284 (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required_any
== e
->required_absent
) ||
2285 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) ||
2286 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
2287 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
2291 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
2292 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
2296 PA_LLIST_FOREACH(o
, e
->options
)
2297 if (option_verify(o
) < 0)
2303 static int path_verify(pa_alsa_path
*p
) {
2304 static const struct description_map well_known_descriptions
[] = {
2305 { "analog-input", N_("Analog Input") },
2306 { "analog-input-microphone", N_("Microphone") },
2307 { "analog-input-microphone-front", N_("Front Microphone") },
2308 { "analog-input-microphone-rear", N_("Rear Microphone") },
2309 { "analog-input-microphone-dock", N_("Dock Microphone") },
2310 { "analog-input-microphone-internal", N_("Internal Microphone") },
2311 { "analog-input-linein", N_("Line In") },
2312 { "analog-input-radio", N_("Radio") },
2313 { "analog-input-video", N_("Video") },
2314 { "analog-output", N_("Analog Output") },
2315 { "analog-output-headphones", N_("Headphones") },
2316 { "analog-output-lfe-on-mono", N_("LFE on Separate Mono Output") },
2317 { "analog-output-lineout", N_("Line Out") },
2318 { "analog-output-mono", N_("Analog Mono Output") },
2319 { "analog-output-speaker", N_("Speakers") },
2320 { "hdmi-output", N_("HDMI / DisplayPort") },
2321 { "iec958-stereo-output", N_("Digital Output (S/PDIF)") },
2322 { "iec958-passthrough-output", N_("Digital Passthrough (S/PDIF)") }
2329 PA_LLIST_FOREACH(e
, p
->elements
)
2330 if (element_verify(e
) < 0)
2333 if (!p
->description
)
2334 p
->description
= pa_xstrdup(lookup_description(p
->name
,
2335 well_known_descriptions
,
2336 PA_ELEMENTSOF(well_known_descriptions
)));
2338 if (!p
->description
)
2339 p
->description
= pa_xstrdup(p
->name
);
2344 static const char *get_default_paths_dir(void) {
2345 if (pa_run_from_build_tree())
2346 return PA_BUILDDIR
"/modules/alsa/mixer/paths/";
2348 return PA_ALSA_PATHS_DIR
;
2351 pa_alsa_path
* pa_alsa_path_new(const char *paths_dir
, const char *fname
, pa_alsa_direction_t direction
) {
2357 pa_config_item items
[] = {
2359 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
2360 { "description", pa_config_parse_string
, NULL
, "General" },
2361 { "name", pa_config_parse_string
, NULL
, "General" },
2364 { "priority", option_parse_priority
, NULL
, NULL
},
2365 { "name", option_parse_name
, NULL
, NULL
},
2368 { "state.plugged", jack_parse_state
, NULL
, NULL
},
2369 { "state.unplugged", jack_parse_state
, NULL
, NULL
},
2372 { "switch", element_parse_switch
, NULL
, NULL
},
2373 { "volume", element_parse_volume
, NULL
, NULL
},
2374 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
2375 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
2376 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
2377 /* ... later on we might add override-map.3 and so on here ... */
2378 { "required", element_parse_required
, NULL
, NULL
},
2379 { "required-any", element_parse_required
, NULL
, NULL
},
2380 { "required-absent", element_parse_required
, NULL
, NULL
},
2381 { "direction", element_parse_direction
, NULL
, NULL
},
2382 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2383 { "volume-limit", element_parse_volume_limit
, NULL
, NULL
},
2384 { NULL
, NULL
, NULL
, NULL
}
2389 p
= pa_xnew0(pa_alsa_path
, 1);
2390 n
= pa_path_get_filename(fname
);
2391 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2392 p
->proplist
= pa_proplist_new();
2393 p
->direction
= direction
;
2395 items
[0].data
= &p
->priority
;
2396 items
[1].data
= &p
->description
;
2397 items
[2].data
= &p
->name
;
2400 paths_dir
= get_default_paths_dir();
2402 fn
= pa_maybe_prefix_path(fname
, paths_dir
);
2404 r
= pa_config_parse(fn
, NULL
, items
, p
->proplist
, p
);
2410 if (path_verify(p
) < 0)
2416 pa_alsa_path_free(p
);
2420 pa_alsa_path
*pa_alsa_path_synthesize(const char *element
, pa_alsa_direction_t direction
) {
2426 p
= pa_xnew0(pa_alsa_path
, 1);
2427 p
->name
= pa_xstrdup(element
);
2428 p
->direction
= direction
;
2430 e
= pa_xnew0(pa_alsa_element
, 1);
2432 e
->alsa_name
= pa_xstrdup(element
);
2433 e
->direction
= direction
;
2434 e
->volume_limit
= -1;
2436 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2437 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2439 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2440 p
->last_element
= e
;
2444 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2445 pa_alsa_option
*o
, *n
;
2449 for (o
= e
->options
; o
; o
= n
) {
2452 if (o
->alsa_idx
< 0) {
2453 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2459 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2460 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2461 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2464 static void path_drop_unsupported(pa_alsa_path
*p
) {
2465 pa_alsa_element
*e
, *n
;
2469 for (e
= p
->elements
; e
; e
= n
) {
2472 if (!element_drop_unsupported(e
)) {
2473 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2479 static void path_make_options_unique(pa_alsa_path
*p
) {
2481 pa_alsa_option
*o
, *u
;
2483 PA_LLIST_FOREACH(e
, p
->elements
) {
2484 PA_LLIST_FOREACH(o
, e
->options
) {
2488 for (u
= o
->next
; u
; u
= u
->next
)
2489 if (pa_streq(u
->name
, o
->name
))
2495 m
= pa_xstrdup(o
->name
);
2497 /* OK, this name is not unique, hence let's rename */
2498 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2501 if (!pa_streq(u
->name
, m
))
2504 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2508 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2509 pa_xfree(u
->description
);
2510 u
->description
= nd
;
2520 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2523 for (; e
; e
= e
->next
)
2524 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2525 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2531 for (o
= e
->options
; o
; o
= o
->next
) {
2535 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2536 s
->options
= pa_idxset_copy(template->options
);
2537 s
->name
= pa_sprintf_malloc("%s+%s", template->name
, o
->name
);
2539 (template->description
[0] && o
->description
[0])
2540 ? pa_sprintf_malloc("%s / %s", template->description
, o
->description
)
2541 : (template->description
[0]
2542 ? pa_xstrdup(template->description
)
2543 : pa_xstrdup(o
->description
));
2545 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2547 s
= pa_xnew0(pa_alsa_setting
, 1);
2548 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2549 s
->name
= pa_xstrdup(o
->name
);
2550 s
->description
= pa_xstrdup(o
->description
);
2551 s
->priority
= o
->priority
;
2554 pa_idxset_put(s
->options
, o
, NULL
);
2556 if (element_create_settings(e
->next
, s
))
2557 /* This is not a leaf, so let's get rid of it */
2560 /* This is a leaf, so let's add it */
2561 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2563 e
->path
->last_setting
= s
;
2570 static void path_create_settings(pa_alsa_path
*p
) {
2573 element_create_settings(p
->elements
, NULL
);
2576 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_hctl_t
*hctl
, pa_bool_t ignore_dB
) {
2579 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2580 pa_channel_position_t t
;
2581 pa_channel_position_mask_t path_volume_channels
= 0;
2587 return p
->supported
? 0 : -1;
2593 pa_log_debug("Probing path '%s'", p
->name
);
2595 PA_LLIST_FOREACH(j
, p
->jacks
) {
2596 if (jack_probe(j
, hctl
) < 0) {
2597 p
->supported
= FALSE
;
2598 pa_log_debug("Probe of jack '%s' failed.", j
->alsa_name
);
2601 pa_log_debug("Probe of jack '%s' succeeded (%s)", j
->alsa_name
, j
->has_control
? "found!" : "not found");
2604 PA_LLIST_FOREACH(e
, p
->elements
) {
2605 if (element_probe(e
, m
) < 0) {
2606 p
->supported
= FALSE
;
2607 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2610 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
);
2615 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2617 if (!p
->has_volume
) {
2618 p
->min_volume
= e
->min_volume
;
2619 p
->max_volume
= e
->max_volume
;
2623 if (!p
->has_volume
) {
2624 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2625 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2626 min_dB
[t
] = e
->min_dB
;
2627 max_dB
[t
] = e
->max_dB
;
2628 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
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
);
2642 /* Hmm, there's another element before us
2643 * which cannot do dB volumes, so we we need
2644 * to 'neutralize' this slider */
2645 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2646 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2649 } else if (p
->has_volume
) {
2650 /* We can't use this volume, so let's ignore it */
2651 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2652 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2654 p
->has_volume
= TRUE
;
2657 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2661 if (p
->has_req_any
&& !p
->req_any_present
) {
2662 p
->supported
= FALSE
;
2663 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2667 path_drop_unsupported(p
);
2668 path_make_options_unique(p
);
2669 path_create_settings(p
);
2671 p
->supported
= TRUE
;
2673 p
->min_dB
= INFINITY
;
2674 p
->max_dB
= -INFINITY
;
2676 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2677 if (path_volume_channels
& PA_CHANNEL_POSITION_MASK(t
)) {
2678 if (p
->min_dB
> min_dB
[t
])
2679 p
->min_dB
= min_dB
[t
];
2681 if (p
->max_dB
< max_dB
[t
])
2682 p
->max_dB
= max_dB
[t
];
2689 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2692 pa_log_debug("Setting %s (%s) priority=%u",
2694 pa_strnull(s
->description
),
2698 void pa_alsa_jack_dump(pa_alsa_jack
*j
) {
2701 pa_log_debug("Jack %s, alsa_name='%s', detection %s", j
->name
, j
->alsa_name
, j
->has_control
? "possible" : "unavailable");
2704 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2707 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2709 pa_strnull(o
->name
),
2710 pa_strnull(o
->description
),
2715 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2719 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",
2729 (long long unsigned) e
->merged_mask
,
2731 pa_yes_no(e
->override_map
));
2733 PA_LLIST_FOREACH(o
, e
->options
)
2734 pa_alsa_option_dump(o
);
2737 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2743 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2744 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2746 pa_strnull(p
->description
),
2749 pa_yes_no(p
->probed
),
2750 pa_yes_no(p
->supported
),
2751 pa_yes_no(p
->has_mute
),
2752 pa_yes_no(p
->has_volume
),
2753 pa_yes_no(p
->has_dB
),
2754 p
->min_volume
, p
->max_volume
,
2755 p
->min_dB
, p
->max_dB
);
2757 PA_LLIST_FOREACH(e
, p
->elements
)
2758 pa_alsa_element_dump(e
);
2760 PA_LLIST_FOREACH(j
, p
->jacks
)
2761 pa_alsa_jack_dump(j
);
2763 PA_LLIST_FOREACH(s
, p
->settings
)
2764 pa_alsa_setting_dump(s
);
2767 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2768 snd_mixer_selem_id_t
*sid
;
2769 snd_mixer_elem_t
*me
;
2775 SELEM_INIT(sid
, e
->alsa_name
);
2776 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2777 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2781 snd_mixer_elem_set_callback(me
, cb
);
2782 snd_mixer_elem_set_callback_private(me
, userdata
);
2785 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2792 PA_LLIST_FOREACH(e
, p
->elements
)
2793 element_set_callback(e
, m
, cb
, userdata
);
2796 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2804 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
2805 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2808 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
, const char *paths_dir
) {
2809 pa_alsa_path_set
*ps
;
2810 char **pn
= NULL
, **en
= NULL
, **ie
;
2811 pa_alsa_decibel_fix
*db_fix
;
2812 void *state
, *state2
;
2816 pa_assert(m
->profile_set
);
2817 pa_assert(m
->profile_set
->decibel_fixes
);
2818 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2820 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2823 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2824 ps
->direction
= direction
;
2825 ps
->paths
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2827 if (direction
== PA_ALSA_DIRECTION_OUTPUT
) {
2828 pn
= m
->output_path_names
;
2829 cache
= m
->profile_set
->output_paths
;
2831 else if (direction
== PA_ALSA_DIRECTION_INPUT
) {
2832 pn
= m
->input_path_names
;
2833 cache
= m
->profile_set
->input_paths
;
2839 for (in
= pn
; *in
; in
++) {
2840 pa_alsa_path
*p
= NULL
;
2841 pa_bool_t duplicate
= FALSE
;
2844 for (kn
= pn
; kn
< in
; kn
++)
2845 if (pa_streq(*kn
, *in
)) {
2853 p
= pa_hashmap_get(cache
, *in
);
2855 char *fn
= pa_sprintf_malloc("%s.conf", *in
);
2856 p
= pa_alsa_path_new(paths_dir
, fn
, direction
);
2859 pa_hashmap_put(cache
, *in
, p
);
2861 pa_assert(pa_hashmap_get(cache
, *in
) == p
);
2863 pa_hashmap_put(ps
->paths
, p
, p
);
2870 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2871 en
= m
->output_element
;
2872 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2873 en
= m
->input_element
;
2876 pa_alsa_path_set_free(ps
);
2880 for (ie
= en
; *ie
; ie
++) {
2884 p
= pa_alsa_path_synthesize(*ie
, direction
);
2886 /* Mark all other passed elements for require-absent */
2887 for (je
= en
; *je
; je
++) {
2893 e
= pa_xnew0(pa_alsa_element
, 1);
2895 e
->alsa_name
= pa_xstrdup(*je
);
2896 e
->direction
= direction
;
2897 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2898 e
->volume_limit
= -1;
2900 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2901 p
->last_element
= e
;
2904 pa_hashmap_put(ps
->paths
, *ie
, p
);
2908 /* Assign decibel fixes to elements. */
2909 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2912 PA_HASHMAP_FOREACH(p
, ps
->paths
, state2
) {
2915 PA_LLIST_FOREACH(e
, p
->elements
) {
2916 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2917 /* The profile set that contains the dB fix may be freed
2918 * before the element, so we have to copy the dB fix
2920 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2921 e
->db_fix
->profile_set
= NULL
;
2922 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2923 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
2932 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2937 pa_log_debug("Path Set %p, direction=%i",
2941 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
2942 pa_alsa_path_dump(p
);
2946 static pa_bool_t
options_have_option(pa_alsa_option
*options
, const char *alsa_name
) {
2950 pa_assert(alsa_name
);
2952 PA_LLIST_FOREACH(o
, options
) {
2953 if (pa_streq(o
->alsa_name
, alsa_name
))
2959 static pa_bool_t
enumeration_is_subset(pa_alsa_option
*a_options
, pa_alsa_option
*b_options
) {
2960 pa_alsa_option
*oa
, *ob
;
2962 if (!a_options
) return TRUE
;
2963 if (!b_options
) return FALSE
;
2965 /* If there is an option A offers that B does not, then A is not a subset of B. */
2966 PA_LLIST_FOREACH(oa
, a_options
) {
2967 pa_bool_t found
= FALSE
;
2968 PA_LLIST_FOREACH(ob
, b_options
) {
2969 if (pa_streq(oa
->alsa_name
, ob
->alsa_name
)) {
2981 * Compares two elements to see if a is a subset of b
2983 static pa_bool_t
element_is_subset(pa_alsa_element
*a
, pa_alsa_element
*b
, snd_mixer_t
*m
) {
2989 * Every state is a subset of itself (with caveats for volume_limits and options)
2990 * IGNORE is a subset of every other state */
2992 /* Check the volume_use */
2993 if (a
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
2995 /* "Constant" is subset of "Constant" only when their constant values are equal */
2996 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& b
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& a
->constant_volume
!= b
->constant_volume
)
2999 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
3000 if (a
->volume_use
!= b
->volume_use
&& b
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
3003 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
3004 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
3005 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
3006 if (b
->volume_use
== PA_ALSA_VOLUME_MERGE
&& b
->volume_limit
>= 0) {
3009 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
)
3010 a_limit
= a
->constant_volume
;
3011 else if (a
->volume_use
== PA_ALSA_VOLUME_ZERO
) {
3015 int rounding
= (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
? +1 : -1);
3016 a_limit
= decibel_fix_get_step(a
->db_fix
, &dB
, rounding
);
3018 snd_mixer_selem_id_t
*sid
;
3019 snd_mixer_elem_t
*me
;
3021 SELEM_INIT(sid
, a
->alsa_name
);
3022 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
3023 pa_log_warn("Element %s seems to have disappeared.", a
->alsa_name
);
3027 if (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3028 if (snd_mixer_selem_ask_playback_dB_vol(me
, dB
, +1, &a_limit
) < 0)
3031 if (snd_mixer_selem_ask_capture_dB_vol(me
, dB
, -1, &a_limit
) < 0)
3035 } else if (a
->volume_use
== PA_ALSA_VOLUME_OFF
)
3036 a_limit
= a
->min_volume
;
3037 else if (a
->volume_use
== PA_ALSA_VOLUME_MERGE
)
3038 a_limit
= a
->volume_limit
;
3040 /* This should never be reached */
3043 if (a_limit
> b
->volume_limit
)
3047 if (a
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
3049 /* If override-maps are different, they're not subsets */
3050 if (a
->n_channels
!= b
->n_channels
)
3052 for (s
= 0; s
<= SND_MIXER_SCHN_LAST
; s
++)
3053 if (a
->masks
[s
][a
->n_channels
-1] != b
->masks
[s
][b
->n_channels
-1]) {
3054 pa_log_debug("Element %s is not a subset - mask a: 0x%" PRIx64
", mask b: 0x%" PRIx64
", at channel %d",
3055 a
->alsa_name
, a
->masks
[s
][a
->n_channels
-1], b
->masks
[s
][b
->n_channels
-1], s
);
3061 if (a
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
3062 /* "On" is a subset of "Mute".
3063 * "Off" is a subset of "Mute".
3064 * "On" is a subset of "Select", if there is an "Option:On" in B.
3065 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3066 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3068 if (a
->switch_use
!= b
->switch_use
) {
3070 if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
|| a
->switch_use
== PA_ALSA_SWITCH_MUTE
3071 || b
->switch_use
== PA_ALSA_SWITCH_OFF
|| b
->switch_use
== PA_ALSA_SWITCH_ON
)
3074 if (b
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3075 if (a
->switch_use
== PA_ALSA_SWITCH_ON
) {
3076 if (!options_have_option(b
->options
, "on"))
3078 } else if (a
->switch_use
== PA_ALSA_SWITCH_OFF
) {
3079 if (!options_have_option(b
->options
, "off"))
3083 } else if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3084 if (!enumeration_is_subset(a
->options
, b
->options
))
3089 if (a
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
) {
3090 if (b
->enumeration_use
== PA_ALSA_ENUMERATION_IGNORE
)
3092 if (!enumeration_is_subset(a
->options
, b
->options
))
3099 static void path_set_condense(pa_alsa_path_set
*ps
, snd_mixer_t
*m
) {
3106 /* If we only have one path, then don't bother */
3107 if (pa_hashmap_size(ps
->paths
) < 2)
3110 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3114 PA_HASHMAP_FOREACH(p2
, ps
->paths
, state2
) {
3115 pa_alsa_element
*ea
, *eb
;
3116 pa_alsa_jack
*ja
, *jb
;
3117 pa_bool_t is_subset
= TRUE
;
3122 /* If a has a jack that b does not have, a is not a subset */
3123 PA_LLIST_FOREACH(ja
, p
->jacks
) {
3124 pa_bool_t exists
= FALSE
;
3126 if (!ja
->has_control
)
3129 PA_LLIST_FOREACH(jb
, p2
->jacks
) {
3130 if (jb
->has_control
&& pa_streq(jb
->alsa_name
, ja
->alsa_name
) &&
3131 (ja
->state_plugged
== jb
->state_plugged
) &&
3132 (ja
->state_unplugged
== jb
->state_unplugged
)) {
3144 /* Compare the elements of each set... */
3145 pa_assert_se(ea
= p
->elements
);
3146 pa_assert_se(eb
= p2
->elements
);
3149 if (pa_streq(ea
->alsa_name
, eb
->alsa_name
)) {
3150 if (element_is_subset(ea
, eb
, m
)) {
3153 if ((ea
&& !eb
) || (!ea
&& eb
))
3155 else if (!ea
&& !eb
)
3165 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p
->name
, p2
->name
);
3166 pa_hashmap_remove(ps
->paths
, p
);
3173 static pa_alsa_path
* path_set_find_path_by_name(pa_alsa_path_set
*ps
, const char* name
, pa_alsa_path
*ignore
)
3178 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
3179 if (p
!= ignore
&& pa_streq(p
->name
, name
))
3184 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
3185 pa_alsa_path
*p
, *q
;
3186 void *state
, *state2
;
3188 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3192 q
= path_set_find_path_by_name(ps
, p
->name
, p
);
3197 m
= pa_xstrdup(p
->name
);
3199 /* OK, this name is not unique, hence let's rename */
3201 PA_HASHMAP_FOREACH(q
, ps
->paths
, state2
) {
3204 if (!pa_streq(q
->name
, m
))
3207 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
3211 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
3212 pa_xfree(q
->description
);
3213 q
->description
= nd
;
3222 static void mapping_free(pa_alsa_mapping
*m
) {
3226 pa_xfree(m
->description
);
3228 pa_xstrfreev(m
->device_strings
);
3229 pa_xstrfreev(m
->input_path_names
);
3230 pa_xstrfreev(m
->output_path_names
);
3231 pa_xstrfreev(m
->input_element
);
3232 pa_xstrfreev(m
->output_element
);
3233 if (m
->input_path_set
)
3234 pa_alsa_path_set_free(m
->input_path_set
);
3235 if (m
->output_path_set
)
3236 pa_alsa_path_set_free(m
->output_path_set
);
3238 pa_assert(!m
->input_pcm
);
3239 pa_assert(!m
->output_pcm
);
3244 static void profile_free(pa_alsa_profile
*p
) {
3248 pa_xfree(p
->description
);
3250 pa_xstrfreev(p
->input_mapping_names
);
3251 pa_xstrfreev(p
->output_mapping_names
);
3253 if (p
->input_mappings
)
3254 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
3256 if (p
->output_mappings
)
3257 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
3262 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
3265 if (ps
->input_paths
) {
3268 while ((p
= pa_hashmap_steal_first(ps
->input_paths
)))
3269 pa_alsa_path_free(p
);
3271 pa_hashmap_free(ps
->input_paths
, NULL
, NULL
);
3274 if (ps
->output_paths
) {
3277 while ((p
= pa_hashmap_steal_first(ps
->output_paths
)))
3278 pa_alsa_path_free(p
);
3280 pa_hashmap_free(ps
->output_paths
, NULL
, NULL
);
3286 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
3289 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
3295 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
3298 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
3301 if (ps
->decibel_fixes
) {
3302 pa_alsa_decibel_fix
*db_fix
;
3304 while ((db_fix
= pa_hashmap_steal_first(ps
->decibel_fixes
)))
3305 decibel_fix_free(db_fix
);
3307 pa_hashmap_free(ps
->decibel_fixes
, NULL
, NULL
);
3313 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
3316 if (!pa_startswith(name
, "Mapping "))
3321 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
3324 m
= pa_xnew0(pa_alsa_mapping
, 1);
3325 m
->profile_set
= ps
;
3326 m
->name
= pa_xstrdup(name
);
3327 pa_channel_map_init(&m
->channel_map
);
3329 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
3334 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
3337 if (!pa_startswith(name
, "Profile "))
3342 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
3345 p
= pa_xnew0(pa_alsa_profile
, 1);
3346 p
->profile_set
= ps
;
3347 p
->name
= pa_xstrdup(name
);
3349 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3354 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
3355 pa_alsa_decibel_fix
*db_fix
;
3357 if (!pa_startswith(name
, "DecibelFix "))
3362 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
3365 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
3366 db_fix
->profile_set
= ps
;
3367 db_fix
->name
= pa_xstrdup(name
);
3369 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
3374 static int mapping_parse_device_strings(pa_config_parser_state
*state
) {
3375 pa_alsa_profile_set
*ps
;
3380 ps
= state
->userdata
;
3382 if (!(m
= mapping_get(ps
, state
->section
))) {
3383 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3387 pa_xstrfreev(m
->device_strings
);
3388 if (!(m
->device_strings
= pa_split_spaces_strv(state
->rvalue
))) {
3389 pa_log("[%s:%u] Device string list empty of '%s'", state
->filename
, state
->lineno
, state
->section
);
3396 static int mapping_parse_channel_map(pa_config_parser_state
*state
) {
3397 pa_alsa_profile_set
*ps
;
3402 ps
= state
->userdata
;
3404 if (!(m
= mapping_get(ps
, state
->section
))) {
3405 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3409 if (!(pa_channel_map_parse(&m
->channel_map
, state
->rvalue
))) {
3410 pa_log("[%s:%u] Channel map invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
3417 static int mapping_parse_paths(pa_config_parser_state
*state
) {
3418 pa_alsa_profile_set
*ps
;
3423 ps
= state
->userdata
;
3425 if (!(m
= mapping_get(ps
, state
->section
))) {
3426 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3430 if (pa_streq(state
->lvalue
, "paths-input")) {
3431 pa_xstrfreev(m
->input_path_names
);
3432 m
->input_path_names
= pa_split_spaces_strv(state
->rvalue
);
3434 pa_xstrfreev(m
->output_path_names
);
3435 m
->output_path_names
= pa_split_spaces_strv(state
->rvalue
);
3441 static int mapping_parse_element(pa_config_parser_state
*state
) {
3442 pa_alsa_profile_set
*ps
;
3447 ps
= state
->userdata
;
3449 if (!(m
= mapping_get(ps
, state
->section
))) {
3450 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3454 if (pa_streq(state
->lvalue
, "element-input")) {
3455 pa_xstrfreev(m
->input_element
);
3456 m
->input_element
= pa_split_spaces_strv(state
->rvalue
);
3458 pa_xstrfreev(m
->output_element
);
3459 m
->output_element
= pa_split_spaces_strv(state
->rvalue
);
3465 static int mapping_parse_direction(pa_config_parser_state
*state
) {
3466 pa_alsa_profile_set
*ps
;
3471 ps
= state
->userdata
;
3473 if (!(m
= mapping_get(ps
, state
->section
))) {
3474 pa_log("[%s:%u] Section name %s invalid.", state
->filename
, state
->lineno
, state
->section
);
3478 if (pa_streq(state
->rvalue
, "input"))
3479 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3480 else if (pa_streq(state
->rvalue
, "output"))
3481 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3482 else if (pa_streq(state
->rvalue
, "any"))
3483 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3485 pa_log("[%s:%u] Direction %s invalid.", state
->filename
, state
->lineno
, state
->rvalue
);
3492 static int mapping_parse_description(pa_config_parser_state
*state
) {
3493 pa_alsa_profile_set
*ps
;
3499 ps
= state
->userdata
;
3501 if ((m
= mapping_get(ps
, state
->section
))) {
3502 pa_xfree(m
->description
);
3503 m
->description
= pa_xstrdup(state
->rvalue
);
3504 } else if ((p
= profile_get(ps
, state
->section
))) {
3505 pa_xfree(p
->description
);
3506 p
->description
= pa_xstrdup(state
->rvalue
);
3508 pa_log("[%s:%u] Section name %s invalid.", state
->filename
, state
->lineno
, state
->section
);
3515 static int mapping_parse_priority(pa_config_parser_state
*state
) {
3516 pa_alsa_profile_set
*ps
;
3523 ps
= state
->userdata
;
3525 if (pa_atou(state
->rvalue
, &prio
) < 0) {
3526 pa_log("[%s:%u] Priority invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
3530 if ((m
= mapping_get(ps
, state
->section
)))
3532 else if ((p
= profile_get(ps
, state
->section
)))
3535 pa_log("[%s:%u] Section name %s invalid.", state
->filename
, state
->lineno
, state
->section
);
3542 static int profile_parse_mappings(pa_config_parser_state
*state
) {
3543 pa_alsa_profile_set
*ps
;
3548 ps
= state
->userdata
;
3550 if (!(p
= profile_get(ps
, state
->section
))) {
3551 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3555 if (pa_streq(state
->lvalue
, "input-mappings")) {
3556 pa_xstrfreev(p
->input_mapping_names
);
3557 p
->input_mapping_names
= pa_split_spaces_strv(state
->rvalue
);
3559 pa_xstrfreev(p
->output_mapping_names
);
3560 p
->output_mapping_names
= pa_split_spaces_strv(state
->rvalue
);
3566 static int profile_parse_skip_probe(pa_config_parser_state
*state
) {
3567 pa_alsa_profile_set
*ps
;
3573 ps
= state
->userdata
;
3575 if (!(p
= profile_get(ps
, state
->section
))) {
3576 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3580 if ((b
= pa_parse_boolean(state
->rvalue
)) < 0) {
3581 pa_log("[%s:%u] Skip probe invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
3590 static int decibel_fix_parse_db_values(pa_config_parser_state
*state
) {
3591 pa_alsa_profile_set
*ps
;
3592 pa_alsa_decibel_fix
*db_fix
;
3596 unsigned n
= 8; /* Current size of the db_values table. */
3597 unsigned min_step
= 0;
3598 unsigned max_step
= 0;
3599 unsigned i
= 0; /* Index to the items table. */
3600 unsigned prev_step
= 0;
3605 ps
= state
->userdata
;
3607 if (!(db_fix
= decibel_fix_get(ps
, state
->section
))) {
3608 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3612 if (!(items
= pa_split_spaces_strv(state
->rvalue
))) {
3613 pa_log("[%s:%u] Value missing", state
->filename
, state
->lineno
);
3617 db_values
= pa_xnew(long, n
);
3619 while ((item
= items
[i
++])) {
3620 char *s
= item
; /* Step value string. */
3621 char *d
= item
; /* dB value string. */
3625 /* Move d forward until it points to a colon or to the end of the item. */
3626 for (; *d
&& *d
!= ':'; ++d
);
3629 /* item started with colon. */
3630 pa_log("[%s:%u] No step value found in %s", state
->filename
, state
->lineno
, item
);
3634 if (!*d
|| !*(d
+ 1)) {
3635 /* No colon found, or it was the last character in item. */
3636 pa_log("[%s:%u] No dB value found in %s", state
->filename
, state
->lineno
, item
);
3640 /* pa_atou() needs a null-terminating string. Let's replace the colon
3641 * with a zero byte. */
3644 if (pa_atou(s
, &step
) < 0) {
3645 pa_log("[%s:%u] Invalid step value: %s", state
->filename
, state
->lineno
, s
);
3649 if (pa_atod(d
, &db
) < 0) {
3650 pa_log("[%s:%u] Invalid dB value: %s", state
->filename
, state
->lineno
, d
);
3654 if (step
<= prev_step
&& i
!= 1) {
3655 pa_log("[%s:%u] Step value %u not greater than the previous value %u", state
->filename
, state
->lineno
, step
, prev_step
);
3659 if (db
< prev_db
&& i
!= 1) {
3660 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", state
->filename
, state
->lineno
, db
, prev_db
);
3666 db_values
[0] = (long) (db
* 100.0);
3670 /* Interpolate linearly. */
3671 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3673 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3675 /* Reallocate the db_values table if it's about to overflow. */
3676 if (prev_step
+ 1 - min_step
== n
) {
3678 db_values
= pa_xrenew(long, db_values
, n
);
3681 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3688 db_fix
->min_step
= min_step
;
3689 db_fix
->max_step
= max_step
;
3690 pa_xfree(db_fix
->db_values
);
3691 db_fix
->db_values
= db_values
;
3693 pa_xstrfreev(items
);
3698 pa_xstrfreev(items
);
3699 pa_xfree(db_values
);
3704 static void mapping_paths_probe(pa_alsa_mapping
*m
, pa_alsa_profile
*profile
,
3705 pa_alsa_direction_t direction
) {
3709 snd_pcm_t
*pcm_handle
;
3710 pa_alsa_path_set
*ps
;
3711 snd_mixer_t
*mixer_handle
;
3712 snd_hctl_t
*hctl_handle
;
3714 if (direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3715 if (m
->output_path_set
)
3716 return; /* Already probed */
3717 m
->output_path_set
= ps
= pa_alsa_path_set_new(m
, direction
, NULL
); /* FIXME: Handle paths_dir */
3718 pcm_handle
= m
->output_pcm
;
3720 if (m
->input_path_set
)
3721 return; /* Already probed */
3722 m
->input_path_set
= ps
= pa_alsa_path_set_new(m
, direction
, NULL
); /* FIXME: Handle paths_dir */
3723 pcm_handle
= m
->input_pcm
;
3727 return; /* No paths */
3729 pa_assert(pcm_handle
);
3731 mixer_handle
= pa_alsa_open_mixer_for_pcm(pcm_handle
, NULL
, &hctl_handle
);
3732 if (!mixer_handle
|| !hctl_handle
) {
3733 /* Cannot open mixer, remove all entries */
3734 while (pa_hashmap_steal_first(ps
->paths
));
3739 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3740 if (pa_alsa_path_probe(p
, mixer_handle
, hctl_handle
, m
->profile_set
->ignore_dB
) < 0) {
3741 pa_hashmap_remove(ps
->paths
, p
);
3745 path_set_condense(ps
, mixer_handle
);
3746 path_set_make_paths_unique(ps
);
3749 snd_mixer_close(mixer_handle
);
3751 pa_log_debug("Available mixer paths (after tidying):");
3752 pa_alsa_path_set_dump(ps
);
3755 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3757 static const struct description_map well_known_descriptions
[] = {
3758 { "analog-mono", N_("Analog Mono") },
3759 { "analog-stereo", N_("Analog Stereo") },
3760 { "analog-surround-21", N_("Analog Surround 2.1") },
3761 { "analog-surround-30", N_("Analog Surround 3.0") },
3762 { "analog-surround-31", N_("Analog Surround 3.1") },
3763 { "analog-surround-40", N_("Analog Surround 4.0") },
3764 { "analog-surround-41", N_("Analog Surround 4.1") },
3765 { "analog-surround-50", N_("Analog Surround 5.0") },
3766 { "analog-surround-51", N_("Analog Surround 5.1") },
3767 { "analog-surround-61", N_("Analog Surround 6.0") },
3768 { "analog-surround-61", N_("Analog Surround 6.1") },
3769 { "analog-surround-70", N_("Analog Surround 7.0") },
3770 { "analog-surround-71", N_("Analog Surround 7.1") },
3771 { "analog-4-channel-input", N_("Analog 4-channel Input") },
3772 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3773 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3774 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3775 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3776 { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
3777 { "hdmi-stereo", N_("Digital Stereo (HDMI)") },
3778 { "hdmi-surround-51", N_("Digital Surround 5.1 (HDMI)") }
3783 if (!pa_channel_map_valid(&m
->channel_map
)) {
3784 pa_log("Mapping %s is missing channel map.", m
->name
);
3788 if (!m
->device_strings
) {
3789 pa_log("Mapping %s is missing device strings.", m
->name
);
3793 if ((m
->input_path_names
&& m
->input_element
) ||
3794 (m
->output_path_names
&& m
->output_element
)) {
3795 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3799 if (!m
->description
)
3800 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3801 well_known_descriptions
,
3802 PA_ELEMENTSOF(well_known_descriptions
)));
3804 if (!m
->description
)
3805 m
->description
= pa_xstrdup(m
->name
);
3808 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3810 else if (m
->channel_map
.channels
== bonus
->channels
)
3817 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3818 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3822 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3824 pa_strnull(m
->description
),
3826 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3827 pa_yes_no(m
->supported
),
3831 static void profile_set_add_auto_pair(
3832 pa_alsa_profile_set
*ps
,
3833 pa_alsa_mapping
*m
, /* output */
3834 pa_alsa_mapping
*n
/* input */) {
3842 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3845 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3849 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3851 name
= pa_sprintf_malloc("output:%s", m
->name
);
3853 name
= pa_sprintf_malloc("input:%s", n
->name
);
3855 if (pa_hashmap_get(ps
->profiles
, name
)) {
3860 p
= pa_xnew0(pa_alsa_profile
, 1);
3861 p
->profile_set
= ps
;
3865 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3866 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3867 p
->priority
+= m
->priority
* 100;
3871 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3872 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3873 p
->priority
+= n
->priority
;
3876 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3879 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3880 pa_alsa_mapping
*m
, *n
;
3881 void *m_state
, *n_state
;
3885 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3886 profile_set_add_auto_pair(ps
, m
, NULL
);
3888 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3889 profile_set_add_auto_pair(ps
, m
, n
);
3892 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3893 profile_set_add_auto_pair(ps
, NULL
, n
);
3896 static int profile_verify(pa_alsa_profile
*p
) {
3898 static const struct description_map well_known_descriptions
[] = {
3899 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3900 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3901 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3902 { "off", N_("Off") }
3907 /* Replace the output mapping names by the actual mappings */
3908 if (p
->output_mapping_names
) {
3911 pa_assert(!p
->output_mappings
);
3912 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3914 for (name
= p
->output_mapping_names
; *name
; name
++) {
3917 pa_bool_t duplicate
= FALSE
;
3919 for (in
= name
+ 1; *in
; in
++)
3920 if (pa_streq(*name
, *in
)) {
3928 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3929 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p
->name
, *name
);
3933 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3939 pa_xstrfreev(p
->output_mapping_names
);
3940 p
->output_mapping_names
= NULL
;
3943 /* Replace the input mapping names by the actual mappings */
3944 if (p
->input_mapping_names
) {
3947 pa_assert(!p
->input_mappings
);
3948 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3950 for (name
= p
->input_mapping_names
; *name
; name
++) {
3953 pa_bool_t duplicate
= FALSE
;
3955 for (in
= name
+ 1; *in
; in
++)
3956 if (pa_streq(*name
, *in
)) {
3964 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3965 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p
->name
, *name
);
3969 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3975 pa_xstrfreev(p
->input_mapping_names
);
3976 p
->input_mapping_names
= NULL
;
3979 if (!p
->input_mappings
&& !p
->output_mappings
) {
3980 pa_log("Profile '%s' lacks mappings.", p
->name
);
3984 if (!p
->description
)
3985 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3986 well_known_descriptions
,
3987 PA_ELEMENTSOF(well_known_descriptions
)));
3989 if (!p
->description
) {
3994 sb
= pa_strbuf_new();
3996 if (p
->output_mappings
)
3997 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3998 if (!pa_strbuf_isempty(sb
))
3999 pa_strbuf_puts(sb
, " + ");
4001 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
4004 if (p
->input_mappings
)
4005 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4006 if (!pa_strbuf_isempty(sb
))
4007 pa_strbuf_puts(sb
, " + ");
4009 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
4012 p
->description
= pa_strbuf_tostring_free(sb
);
4018 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
4023 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4025 pa_strnull(p
->description
),
4027 pa_yes_no(p
->supported
),
4028 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
4029 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
4031 if (p
->input_mappings
)
4032 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
4033 pa_log_debug("Input %s", m
->name
);
4035 if (p
->output_mappings
)
4036 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
4037 pa_log_debug("Output %s", m
->name
);
4040 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
4043 /* Check that the dB mapping has been configured. Since "db-values" is
4044 * currently the only option in the DecibelFix section, and decibel fix
4045 * objects don't get created if a DecibelFix section is empty, this is
4046 * actually a redundant check. Having this may prevent future bugs,
4048 if (!db_fix
->db_values
) {
4049 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
4056 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
4057 char *db_values
= NULL
;
4061 if (db_fix
->db_values
) {
4063 unsigned long i
, nsteps
;
4065 pa_assert(db_fix
->min_step
<= db_fix
->max_step
);
4066 nsteps
= db_fix
->max_step
- db_fix
->min_step
+ 1;
4068 buf
= pa_strbuf_new();
4069 for (i
= 0; i
< nsteps
; ++i
)
4070 pa_strbuf_printf(buf
, "[%li]:%0.2f ", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
4072 db_values
= pa_strbuf_tostring_free(buf
);
4075 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4076 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
4078 pa_xfree(db_values
);
4081 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
4082 pa_alsa_profile_set
*ps
;
4085 pa_alsa_decibel_fix
*db_fix
;
4090 static pa_config_item items
[] = {
4092 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
4095 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
4096 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
4097 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
4098 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
4099 { "element-input", mapping_parse_element
, NULL
, NULL
},
4100 { "element-output", mapping_parse_element
, NULL
, NULL
},
4101 { "direction", mapping_parse_direction
, NULL
, NULL
},
4103 /* Shared by [Mapping ...] and [Profile ...] */
4104 { "description", mapping_parse_description
, NULL
, NULL
},
4105 { "priority", mapping_parse_priority
, NULL
, NULL
},
4108 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
4109 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
4110 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
4112 /* [DecibelFix ...] */
4113 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
4114 { NULL
, NULL
, NULL
, NULL
}
4117 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
4118 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4119 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4120 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4121 ps
->input_paths
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4122 ps
->output_paths
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4124 items
[0].data
= &ps
->auto_profiles
;
4127 fname
= "default.conf";
4129 fn
= pa_maybe_prefix_path(fname
,
4130 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
4131 PA_ALSA_PROFILE_SETS_DIR
);
4133 r
= pa_config_parse(fn
, NULL
, items
, NULL
, ps
);
4139 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4140 if (mapping_verify(m
, bonus
) < 0)
4143 if (ps
->auto_profiles
)
4144 profile_set_add_auto(ps
);
4146 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4147 if (profile_verify(p
) < 0)
4150 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4151 if (decibel_fix_verify(db_fix
) < 0)
4157 pa_alsa_profile_set_free(ps
);
4161 static void profile_finalize_probing(pa_alsa_profile
*to_be_finalized
, pa_alsa_profile
*next
) {
4165 if (!to_be_finalized
)
4168 if (to_be_finalized
->output_mappings
)
4169 PA_IDXSET_FOREACH(m
, to_be_finalized
->output_mappings
, idx
) {
4174 if (to_be_finalized
->supported
)
4177 /* If this mapping is also in the next profile, we won't close the
4178 * pcm handle here, because it would get immediately reopened
4180 if (next
&& next
->output_mappings
&& pa_idxset_get_by_data(next
->output_mappings
, m
, NULL
))
4183 snd_pcm_close(m
->output_pcm
);
4184 m
->output_pcm
= NULL
;
4187 if (to_be_finalized
->input_mappings
)
4188 PA_IDXSET_FOREACH(m
, to_be_finalized
->input_mappings
, idx
) {
4193 if (to_be_finalized
->supported
)
4196 /* If this mapping is also in the next profile, we won't close the
4197 * pcm handle here, because it would get immediately reopened
4199 if (next
&& next
->input_mappings
&& pa_idxset_get_by_data(next
->input_mappings
, m
, NULL
))
4202 snd_pcm_close(m
->input_pcm
);
4203 m
->input_pcm
= NULL
;
4207 static snd_pcm_t
* mapping_open_pcm(pa_alsa_mapping
*m
,
4208 const pa_sample_spec
*ss
,
4211 unsigned default_n_fragments
,
4212 unsigned default_fragment_size_msec
) {
4214 pa_sample_spec try_ss
= *ss
;
4215 pa_channel_map try_map
= m
->channel_map
;
4216 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
4218 try_ss
.channels
= try_map
.channels
;
4221 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
4222 pa_frame_size(&try_ss
);
4223 try_buffer_size
= default_n_fragments
* try_period_size
;
4225 return pa_alsa_open_by_template(
4226 m
->device_strings
, dev_id
, NULL
, &try_ss
,
4227 &try_map
, mode
, &try_period_size
,
4228 &try_buffer_size
, 0, NULL
, NULL
, TRUE
);
4231 static void paths_drop_unsupported(pa_hashmap
* h
) {
4238 p
= pa_hashmap_iterate(h
, &state
, &key
);
4240 if (p
->supported
<= 0) {
4241 pa_hashmap_remove(h
, key
);
4242 pa_alsa_path_free(p
);
4244 p
= pa_hashmap_iterate(h
, &state
, &key
);
4248 void pa_alsa_profile_set_probe(
4249 pa_alsa_profile_set
*ps
,
4251 const pa_sample_spec
*ss
,
4252 unsigned default_n_fragments
,
4253 unsigned default_fragment_size_msec
) {
4256 pa_alsa_profile
*p
, *last
= NULL
;
4266 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
4269 /* Skip if this is already marked that it is supported (i.e. from the config file) */
4270 if (!p
->supported
) {
4272 pa_log_debug("Looking at profile %s", p
->name
);
4273 profile_finalize_probing(last
, p
);
4274 p
->supported
= TRUE
;
4276 /* Check if we can open all new ones */
4277 if (p
->output_mappings
)
4278 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4283 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
4284 if (!(m
->output_pcm
= mapping_open_pcm(m
, ss
, dev_id
,
4285 SND_PCM_STREAM_PLAYBACK
,
4286 default_n_fragments
,
4287 default_fragment_size_msec
))) {
4288 p
->supported
= FALSE
;
4293 if (p
->input_mappings
&& p
->supported
)
4294 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4299 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
4300 if (!(m
->input_pcm
= mapping_open_pcm(m
, ss
, dev_id
,
4301 SND_PCM_STREAM_CAPTURE
,
4302 default_n_fragments
,
4303 default_fragment_size_msec
))) {
4304 p
->supported
= FALSE
;
4315 pa_log_debug("Profile %s supported.", p
->name
);
4317 if (p
->output_mappings
)
4318 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
4320 mapping_paths_probe(m
, p
, PA_ALSA_DIRECTION_OUTPUT
);
4322 if (p
->input_mappings
)
4323 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
4325 mapping_paths_probe(m
, p
, PA_ALSA_DIRECTION_INPUT
);
4329 profile_finalize_probing(last
, NULL
);
4331 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4332 if (!p
->supported
) {
4333 pa_hashmap_remove(ps
->profiles
, p
->name
);
4337 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4338 if (m
->supported
<= 0) {
4339 pa_hashmap_remove(ps
->mappings
, m
->name
);
4343 paths_drop_unsupported(ps
->input_paths
);
4344 paths_drop_unsupported(ps
->output_paths
);
4349 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
4352 pa_alsa_decibel_fix
*db_fix
;
4357 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4360 pa_yes_no(ps
->auto_profiles
),
4361 pa_yes_no(ps
->probed
),
4362 pa_hashmap_size(ps
->mappings
),
4363 pa_hashmap_size(ps
->profiles
),
4364 pa_hashmap_size(ps
->decibel_fixes
));
4366 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4367 pa_alsa_mapping_dump(m
);
4369 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4370 pa_alsa_profile_dump(p
);
4372 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4373 pa_alsa_decibel_fix_dump(db_fix
);
4376 static pa_device_port
* device_port_alsa_init(pa_hashmap
*ports
,
4378 const char* description
,
4380 pa_alsa_setting
*setting
,
4381 pa_card_profile
*cp
,
4389 p
= pa_hashmap_get(ports
, name
);
4392 pa_alsa_port_data
*data
;
4394 p
= pa_device_port_new(core
, name
, description
, sizeof(pa_alsa_port_data
));
4396 pa_hashmap_put(ports
, p
->name
, p
);
4397 pa_proplist_update(p
->proplist
, PA_UPDATE_REPLACE
, path
->proplist
);
4399 data
= PA_DEVICE_PORT_DATA(p
);
4401 data
->setting
= setting
;
4405 p
->is_input
|= path
->direction
== PA_ALSA_DIRECTION_ANY
|| path
->direction
== PA_ALSA_DIRECTION_INPUT
;
4406 p
->is_output
|= path
->direction
== PA_ALSA_DIRECTION_ANY
|| path
->direction
== PA_ALSA_DIRECTION_OUTPUT
;
4409 pa_hashmap_put(p
->profiles
, cp
->name
, cp
);
4412 pa_hashmap_put(extra
, p
->name
, p
);
4413 pa_device_port_ref(p
);
4419 void pa_alsa_path_set_add_ports(
4420 pa_alsa_path_set
*ps
,
4421 pa_card_profile
*cp
,
4434 PA_HASHMAP_FOREACH(path
, ps
->paths
, state
) {
4435 if (!path
->settings
|| !path
->settings
->next
) {
4436 /* If there is no or just one setting we only need a
4438 pa_device_port
*port
= device_port_alsa_init(ports
, path
->name
,
4439 path
->description
, path
, path
->settings
, cp
, extra
, core
);
4440 port
->priority
= path
->priority
* 100;
4444 PA_LLIST_FOREACH(s
, path
->settings
) {
4445 pa_device_port
*port
;
4448 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4450 if (s
->description
[0])
4451 d
= pa_sprintf_malloc("%s / %s", path
->description
, s
->description
);
4453 d
= pa_xstrdup(path
->description
);
4455 port
= device_port_alsa_init(ports
, n
, d
, path
, s
, cp
, extra
, core
);
4456 port
->priority
= path
->priority
* 100 + s
->priority
;
4465 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
, pa_card
*card
) {
4471 if (ps
->paths
&& pa_hashmap_size(ps
->paths
) > 0) {
4473 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4474 pa_alsa_path_set_add_ports(ps
, NULL
, card
->ports
, *p
, card
->core
);
4477 pa_log_debug("Added %u ports", *p
? pa_hashmap_size(*p
) : 0);