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>
29 #include <asoundlib.h>
31 #ifdef HAVE_VALGRIND_MEMCHECK_H
32 #include <valgrind/memcheck.h>
35 #include <pulse/sample.h>
36 #include <pulse/xmalloc.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/i18n.h>
40 #include <pulse/utf8.h>
42 #include <pulsecore/log.h>
43 #include <pulsecore/macro.h>
44 #include <pulsecore/core-util.h>
45 #include <pulsecore/atomic.h>
46 #include <pulsecore/core-error.h>
47 #include <pulsecore/once.h>
48 #include <pulsecore/thread.h>
49 #include <pulsecore/conf-parser.h>
50 #include <pulsecore/strbuf.h>
52 #include "alsa-mixer.h"
53 #include "alsa-util.h"
55 struct description_map
{
57 const char *description
;
60 static const char *lookup_description(const char *name
, const struct description_map dm
[], unsigned n
) {
63 for (i
= 0; i
< n
; i
++)
64 if (pa_streq(dm
[i
].name
, name
))
65 return _(dm
[i
].description
);
70 struct pa_alsa_fdlist
{
73 /* This is a temporary buffer used to avoid lots of mallocs */
74 struct pollfd
*work_fds
;
79 pa_defer_event
*defer
;
84 void (*cb
)(void *userdata
);
88 static void io_cb(pa_mainloop_api
*a
, pa_io_event
* e
, int fd
, pa_io_event_flags_t events
, void *userdata
) {
90 struct pa_alsa_fdlist
*fdl
= userdata
;
93 unsigned short revents
;
97 pa_assert(fdl
->mixer
);
99 pa_assert(fdl
->work_fds
);
106 memcpy(fdl
->work_fds
, fdl
->fds
, sizeof(struct pollfd
) * fdl
->num_fds
);
108 for (i
= 0; i
< fdl
->num_fds
; i
++) {
109 if (e
== fdl
->ios
[i
]) {
110 if (events
& PA_IO_EVENT_INPUT
)
111 fdl
->work_fds
[i
].revents
|= POLLIN
;
112 if (events
& PA_IO_EVENT_OUTPUT
)
113 fdl
->work_fds
[i
].revents
|= POLLOUT
;
114 if (events
& PA_IO_EVENT_ERROR
)
115 fdl
->work_fds
[i
].revents
|= POLLERR
;
116 if (events
& PA_IO_EVENT_HANGUP
)
117 fdl
->work_fds
[i
].revents
|= POLLHUP
;
122 pa_assert(i
!= fdl
->num_fds
);
124 if ((err
= snd_mixer_poll_descriptors_revents(fdl
->mixer
, fdl
->work_fds
, fdl
->num_fds
, &revents
)) < 0) {
125 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err
));
129 a
->defer_enable(fdl
->defer
, 1);
132 snd_mixer_handle_events(fdl
->mixer
);
135 static void defer_cb(pa_mainloop_api
*a
, pa_defer_event
* e
, void *userdata
) {
136 struct pa_alsa_fdlist
*fdl
= userdata
;
143 pa_assert(fdl
->mixer
);
145 a
->defer_enable(fdl
->defer
, 0);
147 if ((n
= snd_mixer_poll_descriptors_count(fdl
->mixer
)) < 0) {
148 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n
));
151 num_fds
= (unsigned) n
;
153 if (num_fds
!= fdl
->num_fds
) {
157 pa_xfree(fdl
->work_fds
);
158 fdl
->fds
= pa_xnew0(struct pollfd
, num_fds
);
159 fdl
->work_fds
= pa_xnew(struct pollfd
, num_fds
);
162 memset(fdl
->work_fds
, 0, sizeof(struct pollfd
) * num_fds
);
164 if ((err
= snd_mixer_poll_descriptors(fdl
->mixer
, fdl
->work_fds
, num_fds
)) < 0) {
165 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err
));
171 if (memcmp(fdl
->fds
, fdl
->work_fds
, sizeof(struct pollfd
) * num_fds
) == 0)
175 for (i
= 0; i
< fdl
->num_fds
; i
++)
176 a
->io_free(fdl
->ios
[i
]);
178 if (num_fds
!= fdl
->num_fds
) {
185 fdl
->ios
= pa_xnew(pa_io_event
*, num_fds
);
188 temp
= fdl
->work_fds
;
189 fdl
->work_fds
= fdl
->fds
;
192 fdl
->num_fds
= num_fds
;
194 for (i
= 0;i
< num_fds
;i
++)
195 fdl
->ios
[i
] = a
->io_new(a
, fdl
->fds
[i
].fd
,
196 ((fdl
->fds
[i
].events
& POLLIN
) ? PA_IO_EVENT_INPUT
: 0) |
197 ((fdl
->fds
[i
].events
& POLLOUT
) ? PA_IO_EVENT_OUTPUT
: 0),
201 struct pa_alsa_fdlist
*pa_alsa_fdlist_new(void) {
202 struct pa_alsa_fdlist
*fdl
;
204 fdl
= pa_xnew0(struct pa_alsa_fdlist
, 1);
209 void pa_alsa_fdlist_free(struct pa_alsa_fdlist
*fdl
) {
214 fdl
->m
->defer_free(fdl
->defer
);
220 for (i
= 0; i
< fdl
->num_fds
; i
++)
221 fdl
->m
->io_free(fdl
->ios
[i
]);
228 pa_xfree(fdl
->work_fds
);
233 int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist
*fdl
, snd_mixer_t
*mixer_handle
, pa_mainloop_api
* m
) {
235 pa_assert(mixer_handle
);
239 fdl
->mixer
= mixer_handle
;
241 fdl
->defer
= m
->defer_new(m
, defer_cb
, fdl
);
246 struct pa_alsa_mixer_pdata
{
248 pa_rtpoll_item
*poll_item
;
253 struct pa_alsa_mixer_pdata
*pa_alsa_mixer_pdata_new(void) {
254 struct pa_alsa_mixer_pdata
*pd
;
256 pd
= pa_xnew0(struct pa_alsa_mixer_pdata
, 1);
261 void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata
*pd
) {
265 pa_rtpoll_item_free(pd
->poll_item
);
271 static int rtpoll_work_cb(pa_rtpoll_item
*i
) {
272 struct pa_alsa_mixer_pdata
*pd
;
275 unsigned short revents
= 0;
278 pd
= pa_rtpoll_item_get_userdata(i
);
280 pa_assert_fp(i
== pd
->poll_item
);
282 p
= pa_rtpoll_item_get_pollfd(i
, &n_fds
);
284 if ((err
= snd_mixer_poll_descriptors_revents(pd
->mixer
, p
, n_fds
, &revents
)) < 0) {
285 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err
));
286 pa_rtpoll_item_free(i
);
291 snd_mixer_handle_events(pd
->mixer
);
292 pa_rtpoll_item_free(i
);
293 pa_alsa_set_mixer_rtpoll(pd
, pd
->mixer
, pd
->rtpoll
);
299 int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata
*pd
, snd_mixer_t
*mixer
, pa_rtpoll
*rtp
) {
308 if ((n
= snd_mixer_poll_descriptors_count(mixer
)) < 0) {
309 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n
));
313 i
= pa_rtpoll_item_new(rtp
, PA_RTPOLL_LATE
, (unsigned) n
);
315 p
= pa_rtpoll_item_get_pollfd(i
, NULL
);
317 memset(p
, 0, sizeof(struct pollfd
) * n
);
319 if ((err
= snd_mixer_poll_descriptors(mixer
, p
, (unsigned) n
)) < 0) {
320 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err
));
321 pa_rtpoll_item_free(i
);
329 pa_rtpoll_item_set_userdata(i
, pd
);
330 pa_rtpoll_item_set_work_callback(i
, rtpoll_work_cb
);
335 static int prepare_mixer(snd_mixer_t
*mixer
, const char *dev
) {
341 if ((err
= snd_mixer_attach(mixer
, dev
)) < 0) {
342 pa_log_info("Unable to attach to mixer %s: %s", dev
, pa_alsa_strerror(err
));
346 if ((err
= snd_mixer_selem_register(mixer
, NULL
, NULL
)) < 0) {
347 pa_log_warn("Unable to register mixer: %s", pa_alsa_strerror(err
));
351 if ((err
= snd_mixer_load(mixer
)) < 0) {
352 pa_log_warn("Unable to load mixer: %s", pa_alsa_strerror(err
));
356 pa_log_info("Successfully attached to mixer '%s'", dev
);
360 snd_mixer_t
*pa_alsa_open_mixer_for_pcm(snd_pcm_t
*pcm
, char **ctl_device
) {
364 snd_pcm_info_t
* info
;
365 snd_pcm_info_alloca(&info
);
369 if ((err
= snd_mixer_open(&m
, 0)) < 0) {
370 pa_log("Error opening mixer: %s", pa_alsa_strerror(err
));
374 /* First, try by name */
375 if ((dev
= snd_pcm_name(pcm
)))
376 if (prepare_mixer(m
, dev
) >= 0) {
378 *ctl_device
= pa_xstrdup(dev
);
383 /* Then, try by card index */
384 if (snd_pcm_info(pcm
, info
) >= 0) {
388 if ((card_idx
= snd_pcm_info_get_card(info
)) >= 0) {
390 md
= pa_sprintf_malloc("hw:%i", card_idx
);
392 if (!dev
|| !pa_streq(dev
, md
))
393 if (prepare_mixer(m
, md
) >= 0) {
411 static const snd_mixer_selem_channel_id_t alsa_channel_ids
[PA_CHANNEL_POSITION_MAX
] = {
412 [PA_CHANNEL_POSITION_MONO
] = SND_MIXER_SCHN_MONO
, /* The ALSA name is just an alias! */
414 [PA_CHANNEL_POSITION_FRONT_CENTER
] = SND_MIXER_SCHN_FRONT_CENTER
,
415 [PA_CHANNEL_POSITION_FRONT_LEFT
] = SND_MIXER_SCHN_FRONT_LEFT
,
416 [PA_CHANNEL_POSITION_FRONT_RIGHT
] = SND_MIXER_SCHN_FRONT_RIGHT
,
418 [PA_CHANNEL_POSITION_REAR_CENTER
] = SND_MIXER_SCHN_REAR_CENTER
,
419 [PA_CHANNEL_POSITION_REAR_LEFT
] = SND_MIXER_SCHN_REAR_LEFT
,
420 [PA_CHANNEL_POSITION_REAR_RIGHT
] = SND_MIXER_SCHN_REAR_RIGHT
,
422 [PA_CHANNEL_POSITION_LFE
] = SND_MIXER_SCHN_WOOFER
,
424 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
425 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
427 [PA_CHANNEL_POSITION_SIDE_LEFT
] = SND_MIXER_SCHN_SIDE_LEFT
,
428 [PA_CHANNEL_POSITION_SIDE_RIGHT
] = SND_MIXER_SCHN_SIDE_RIGHT
,
430 [PA_CHANNEL_POSITION_AUX0
] = SND_MIXER_SCHN_UNKNOWN
,
431 [PA_CHANNEL_POSITION_AUX1
] = SND_MIXER_SCHN_UNKNOWN
,
432 [PA_CHANNEL_POSITION_AUX2
] = SND_MIXER_SCHN_UNKNOWN
,
433 [PA_CHANNEL_POSITION_AUX3
] = SND_MIXER_SCHN_UNKNOWN
,
434 [PA_CHANNEL_POSITION_AUX4
] = SND_MIXER_SCHN_UNKNOWN
,
435 [PA_CHANNEL_POSITION_AUX5
] = SND_MIXER_SCHN_UNKNOWN
,
436 [PA_CHANNEL_POSITION_AUX6
] = SND_MIXER_SCHN_UNKNOWN
,
437 [PA_CHANNEL_POSITION_AUX7
] = SND_MIXER_SCHN_UNKNOWN
,
438 [PA_CHANNEL_POSITION_AUX8
] = SND_MIXER_SCHN_UNKNOWN
,
439 [PA_CHANNEL_POSITION_AUX9
] = SND_MIXER_SCHN_UNKNOWN
,
440 [PA_CHANNEL_POSITION_AUX10
] = SND_MIXER_SCHN_UNKNOWN
,
441 [PA_CHANNEL_POSITION_AUX11
] = SND_MIXER_SCHN_UNKNOWN
,
442 [PA_CHANNEL_POSITION_AUX12
] = SND_MIXER_SCHN_UNKNOWN
,
443 [PA_CHANNEL_POSITION_AUX13
] = SND_MIXER_SCHN_UNKNOWN
,
444 [PA_CHANNEL_POSITION_AUX14
] = SND_MIXER_SCHN_UNKNOWN
,
445 [PA_CHANNEL_POSITION_AUX15
] = SND_MIXER_SCHN_UNKNOWN
,
446 [PA_CHANNEL_POSITION_AUX16
] = SND_MIXER_SCHN_UNKNOWN
,
447 [PA_CHANNEL_POSITION_AUX17
] = SND_MIXER_SCHN_UNKNOWN
,
448 [PA_CHANNEL_POSITION_AUX18
] = SND_MIXER_SCHN_UNKNOWN
,
449 [PA_CHANNEL_POSITION_AUX19
] = SND_MIXER_SCHN_UNKNOWN
,
450 [PA_CHANNEL_POSITION_AUX20
] = SND_MIXER_SCHN_UNKNOWN
,
451 [PA_CHANNEL_POSITION_AUX21
] = SND_MIXER_SCHN_UNKNOWN
,
452 [PA_CHANNEL_POSITION_AUX22
] = SND_MIXER_SCHN_UNKNOWN
,
453 [PA_CHANNEL_POSITION_AUX23
] = SND_MIXER_SCHN_UNKNOWN
,
454 [PA_CHANNEL_POSITION_AUX24
] = SND_MIXER_SCHN_UNKNOWN
,
455 [PA_CHANNEL_POSITION_AUX25
] = SND_MIXER_SCHN_UNKNOWN
,
456 [PA_CHANNEL_POSITION_AUX26
] = SND_MIXER_SCHN_UNKNOWN
,
457 [PA_CHANNEL_POSITION_AUX27
] = SND_MIXER_SCHN_UNKNOWN
,
458 [PA_CHANNEL_POSITION_AUX28
] = SND_MIXER_SCHN_UNKNOWN
,
459 [PA_CHANNEL_POSITION_AUX29
] = SND_MIXER_SCHN_UNKNOWN
,
460 [PA_CHANNEL_POSITION_AUX30
] = SND_MIXER_SCHN_UNKNOWN
,
461 [PA_CHANNEL_POSITION_AUX31
] = SND_MIXER_SCHN_UNKNOWN
,
463 [PA_CHANNEL_POSITION_TOP_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
465 [PA_CHANNEL_POSITION_TOP_FRONT_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
466 [PA_CHANNEL_POSITION_TOP_FRONT_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
467 [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
,
469 [PA_CHANNEL_POSITION_TOP_REAR_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
470 [PA_CHANNEL_POSITION_TOP_REAR_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
471 [PA_CHANNEL_POSITION_TOP_REAR_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
474 static void setting_free(pa_alsa_setting
*s
) {
478 pa_idxset_free(s
->options
, NULL
, NULL
);
481 pa_xfree(s
->description
);
485 static void option_free(pa_alsa_option
*o
) {
488 pa_xfree(o
->alsa_name
);
490 pa_xfree(o
->description
);
494 static void element_free(pa_alsa_element
*e
) {
498 while ((o
= e
->options
)) {
499 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
503 pa_xfree(e
->alsa_name
);
507 void pa_alsa_path_free(pa_alsa_path
*p
) {
513 while ((e
= p
->elements
)) {
514 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
518 while ((s
= p
->settings
)) {
519 PA_LLIST_REMOVE(pa_alsa_setting
, p
->settings
, s
);
524 pa_xfree(p
->description
);
528 void pa_alsa_path_set_free(pa_alsa_path_set
*ps
) {
532 while ((p
= ps
->paths
)) {
533 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
534 pa_alsa_path_free(p
);
540 static long to_alsa_dB(pa_volume_t v
) {
541 return (long) (pa_sw_volume_to_dB(v
) * 100.0);
544 static pa_volume_t
from_alsa_dB(long v
) {
545 return pa_sw_volume_from_dB((double) v
/ 100.0);
548 static long to_alsa_volume(pa_volume_t v
, long min
, long max
) {
551 w
= (long) round(((double) v
* (double) (max
- min
)) / PA_VOLUME_NORM
) + min
;
552 return PA_CLAMP_UNLIKELY(w
, min
, max
);
555 static pa_volume_t
from_alsa_volume(long v
, long min
, long max
) {
556 return (pa_volume_t
) round(((double) (v
- min
) * PA_VOLUME_NORM
) / (double) (max
- min
));
559 #define SELEM_INIT(sid, name) \
561 snd_mixer_selem_id_alloca(&(sid)); \
562 snd_mixer_selem_id_set_name((sid), (name)); \
563 snd_mixer_selem_id_set_index((sid), 0); \
566 static int element_get_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
567 snd_mixer_selem_id_t
*sid
;
568 snd_mixer_elem_t
*me
;
569 snd_mixer_selem_channel_id_t c
;
570 pa_channel_position_mask_t mask
= 0;
578 SELEM_INIT(sid
, e
->alsa_name
);
579 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
580 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
584 pa_cvolume_mute(v
, cm
->channels
);
586 /* We take the highest volume of all channels that match */
588 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
595 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
596 if (snd_mixer_selem_has_playback_channel(me
, c
))
597 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
601 if (snd_mixer_selem_has_capture_channel(me
, c
))
602 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
610 #ifdef HAVE_VALGRIND_MEMCHECK_H
611 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
614 f
= from_alsa_dB(value
);
619 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
620 if (snd_mixer_selem_has_playback_channel(me
, c
))
621 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
625 if (snd_mixer_selem_has_capture_channel(me
, c
))
626 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
634 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
637 for (k
= 0; k
< cm
->channels
; k
++)
638 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
639 if (v
->values
[k
] < f
)
642 mask
|= e
->masks
[c
][e
->n_channels
-1];
645 for (k
= 0; k
< cm
->channels
; k
++)
646 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
647 v
->values
[k
] = PA_VOLUME_NORM
;
652 int pa_alsa_path_get_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
663 pa_cvolume_reset(v
, cm
->channels
);
665 PA_LLIST_FOREACH(e
, p
->elements
) {
668 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
671 pa_assert(!p
->has_dB
|| e
->has_dB
);
673 if (element_get_volume(e
, m
, cm
, &ev
) < 0)
676 /* If we have no dB information all we can do is take the first element and leave */
682 pa_sw_cvolume_multiply(v
, v
, &ev
);
688 static int element_get_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t
*b
) {
689 snd_mixer_selem_id_t
*sid
;
690 snd_mixer_elem_t
*me
;
691 snd_mixer_selem_channel_id_t c
;
697 SELEM_INIT(sid
, e
->alsa_name
);
698 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
699 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
703 /* We return muted if at least one channel is muted */
705 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
709 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
710 if (snd_mixer_selem_has_playback_channel(me
, c
))
711 r
= snd_mixer_selem_get_playback_switch(me
, c
, &value
);
715 if (snd_mixer_selem_has_capture_channel(me
, c
))
716 r
= snd_mixer_selem_get_capture_switch(me
, c
, &value
);
734 int pa_alsa_path_get_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t
*muted
) {
744 PA_LLIST_FOREACH(e
, p
->elements
) {
747 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
750 if (element_get_switch(e
, m
, &b
) < 0)
763 static int element_set_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
, pa_bool_t write_to_hw
) {
765 snd_mixer_selem_id_t
*sid
;
767 snd_mixer_elem_t
*me
;
768 snd_mixer_selem_channel_id_t c
;
769 pa_channel_position_mask_t mask
= 0;
776 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
778 SELEM_INIT(sid
, e
->alsa_name
);
779 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
780 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
784 pa_cvolume_mute(&rv
, cm
->channels
);
786 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
788 pa_volume_t f
= PA_VOLUME_MUTED
;
789 pa_bool_t found
= FALSE
;
791 for (k
= 0; k
< cm
->channels
; k
++)
792 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
])) {
794 if (v
->values
[k
] > f
)
799 /* Hmm, so this channel does not exist in the volume
800 * struct, so let's bind it to the overall max of the
802 f
= pa_cvolume_max(v
);
806 long value
= to_alsa_dB(f
);
807 int rounding
= value
> 0 ? -1 : +1;
809 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
810 /* If we call set_play_volume() without checking first
811 * if the channel is available, ALSA behaves ver
812 * strangely and doesn't fail the call */
813 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
815 if ((r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, rounding
)) >= 0)
816 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
819 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
820 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value
);
825 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
827 if ((r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, rounding
)) >= 0)
828 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
831 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
832 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value
);
841 #ifdef HAVE_VALGRIND_MEMCHECK_H
842 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
845 f
= from_alsa_dB(value
);
850 value
= to_alsa_volume(f
, e
->min_volume
, e
->max_volume
);
852 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
853 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
854 if ((r
= snd_mixer_selem_set_playback_volume(me
, c
, value
)) >= 0)
855 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
859 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
860 if ((r
= snd_mixer_selem_set_capture_volume(me
, c
, value
)) >= 0)
861 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
869 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
872 for (k
= 0; k
< cm
->channels
; k
++)
873 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
874 if (rv
.values
[k
] < f
)
877 mask
|= e
->masks
[c
][e
->n_channels
-1];
880 for (k
= 0; k
< cm
->channels
; k
++)
881 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
882 rv
.values
[k
] = PA_VOLUME_NORM
;
888 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 write_to_hw
) {
897 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
902 rv
= *v
; /* Remaining adjustment */
903 pa_cvolume_reset(v
, cm
->channels
); /* Adjustment done */
905 PA_LLIST_FOREACH(e
, p
->elements
) {
908 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
911 pa_assert(!p
->has_dB
|| e
->has_dB
);
914 if (element_set_volume(e
, m
, cm
, &ev
, write_to_hw
) < 0)
922 pa_sw_cvolume_multiply(v
, v
, &ev
);
923 pa_sw_cvolume_divide(&rv
, &rv
, &ev
);
929 static int element_set_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t b
) {
930 snd_mixer_elem_t
*me
;
931 snd_mixer_selem_id_t
*sid
;
937 SELEM_INIT(sid
, e
->alsa_name
);
938 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
939 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
943 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
944 r
= snd_mixer_selem_set_playback_switch_all(me
, b
);
946 r
= snd_mixer_selem_set_capture_switch_all(me
, b
);
949 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
954 int pa_alsa_path_set_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t muted
) {
963 PA_LLIST_FOREACH(e
, p
->elements
) {
965 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
968 if (element_set_switch(e
, m
, !muted
) < 0)
975 static int element_mute_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
976 snd_mixer_elem_t
*me
;
977 snd_mixer_selem_id_t
*sid
;
983 SELEM_INIT(sid
, e
->alsa_name
);
984 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
985 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
989 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
990 r
= snd_mixer_selem_set_playback_volume_all(me
, e
->min_volume
);
992 r
= snd_mixer_selem_set_capture_volume_all(me
, e
->min_volume
);
995 pa_log_warn("Failed to set volume to muted of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1000 /* The volume to 0dB */
1001 static int element_zero_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1002 snd_mixer_elem_t
*me
;
1003 snd_mixer_selem_id_t
*sid
;
1009 SELEM_INIT(sid
, e
->alsa_name
);
1010 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1011 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1015 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1016 r
= snd_mixer_selem_set_playback_dB_all(me
, 0, +1);
1018 r
= snd_mixer_selem_set_capture_dB_all(me
, 0, +1);
1021 pa_log_warn("Failed to set volume to 0dB of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1026 int pa_alsa_path_select(pa_alsa_path
*p
, snd_mixer_t
*m
) {
1033 pa_log_debug("Activating path %s", p
->name
);
1034 pa_alsa_path_dump(p
);
1036 PA_LLIST_FOREACH(e
, p
->elements
) {
1038 switch (e
->switch_use
) {
1039 case PA_ALSA_SWITCH_OFF
:
1040 r
= element_set_switch(e
, m
, FALSE
);
1043 case PA_ALSA_SWITCH_ON
:
1044 r
= element_set_switch(e
, m
, TRUE
);
1047 case PA_ALSA_SWITCH_MUTE
:
1048 case PA_ALSA_SWITCH_IGNORE
:
1049 case PA_ALSA_SWITCH_SELECT
:
1057 switch (e
->volume_use
) {
1058 case PA_ALSA_VOLUME_OFF
:
1059 r
= element_mute_volume(e
, m
);
1062 case PA_ALSA_VOLUME_ZERO
:
1063 r
= element_zero_volume(e
, m
);
1066 case PA_ALSA_VOLUME_MERGE
:
1067 case PA_ALSA_VOLUME_IGNORE
:
1079 static int check_required(pa_alsa_element
*e
, snd_mixer_elem_t
*me
) {
1080 pa_bool_t has_switch
;
1081 pa_bool_t has_enumeration
;
1082 pa_bool_t has_volume
;
1087 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1089 snd_mixer_selem_has_playback_switch(me
) ||
1090 (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
));
1093 snd_mixer_selem_has_capture_switch(me
) ||
1094 (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
));
1097 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1099 snd_mixer_selem_has_playback_volume(me
) ||
1100 (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
));
1103 snd_mixer_selem_has_capture_volume(me
) ||
1104 (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
));
1107 has_enumeration
= snd_mixer_selem_is_enumerated(me
);
1109 if ((e
->required
== PA_ALSA_REQUIRED_SWITCH
&& !has_switch
) ||
1110 (e
->required
== PA_ALSA_REQUIRED_VOLUME
&& !has_volume
) ||
1111 (e
->required
== PA_ALSA_REQUIRED_ENUMERATION
&& !has_enumeration
))
1114 if (e
->required
== PA_ALSA_REQUIRED_ANY
&& !(has_switch
|| has_volume
|| has_enumeration
))
1117 if ((e
->required_absent
== PA_ALSA_REQUIRED_SWITCH
&& has_switch
) ||
1118 (e
->required_absent
== PA_ALSA_REQUIRED_VOLUME
&& has_volume
) ||
1119 (e
->required_absent
== PA_ALSA_REQUIRED_ENUMERATION
&& has_enumeration
))
1122 if (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& (has_switch
|| has_volume
|| has_enumeration
))
1125 if (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) {
1126 switch (e
->required_any
) {
1127 case PA_ALSA_REQUIRED_VOLUME
:
1128 e
->path
->req_any_present
|= (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
);
1130 case PA_ALSA_REQUIRED_SWITCH
:
1131 e
->path
->req_any_present
|= (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
);
1133 case PA_ALSA_REQUIRED_ENUMERATION
:
1134 e
->path
->req_any_present
|= (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1136 case PA_ALSA_REQUIRED_ANY
:
1137 e
->path
->req_any_present
|=
1138 (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) ||
1139 (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) ||
1140 (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1145 if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1147 PA_LLIST_FOREACH(o
, e
->options
) {
1148 e
->path
->req_any_present
|= (o
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) &&
1150 if (o
->required
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
< 0)
1152 if (o
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
>= 0)
1160 static int element_probe(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1161 snd_mixer_selem_id_t
*sid
;
1162 snd_mixer_elem_t
*me
;
1167 SELEM_INIT(sid
, e
->alsa_name
);
1169 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1171 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1174 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1175 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1176 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1181 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1182 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1184 if (!snd_mixer_selem_has_playback_switch(me
)) {
1185 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1186 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1188 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1193 if (!snd_mixer_selem_has_capture_switch(me
)) {
1194 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1195 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1197 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1201 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1202 e
->direction_try_other
= FALSE
;
1205 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1207 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1209 if (!snd_mixer_selem_has_playback_volume(me
)) {
1210 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1211 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1213 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1218 if (!snd_mixer_selem_has_capture_volume(me
)) {
1219 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1220 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1222 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1226 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1227 long min_dB
= 0, max_dB
= 0;
1230 e
->direction_try_other
= FALSE
;
1232 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1233 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1235 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1238 #ifdef HAVE_VALGRIND_MEMCHECK_H
1239 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1240 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1243 e
->min_dB
= ((double) min_dB
) / 100.0;
1244 e
->max_dB
= ((double) max_dB
) / 100.0;
1246 if (min_dB
>= max_dB
) {
1247 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
);
1252 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1253 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1255 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1258 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1263 if (e
->min_volume
>= e
->max_volume
) {
1264 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
);
1265 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1269 pa_channel_position_t p
;
1271 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1272 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1274 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1279 if (!e
->override_map
) {
1280 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++)
1281 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1282 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1285 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1288 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1290 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1293 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1294 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1296 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1299 if (e
->n_channels
<= 0) {
1300 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1304 if (!e
->override_map
) {
1305 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1306 pa_bool_t has_channel
;
1308 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1311 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1312 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1314 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1316 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1321 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++)
1322 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1329 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1332 PA_LLIST_FOREACH(o
, e
->options
)
1333 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1334 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1338 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1339 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1343 PA_LLIST_FOREACH(o
, e
->options
) {
1346 for (i
= 0; i
< n
; i
++) {
1349 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1352 if (!pa_streq(buf
, o
->alsa_name
))
1360 if (check_required(e
, me
) < 0)
1366 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, pa_bool_t prefixed
) {
1373 if (!pa_startswith(section
, "Element "))
1379 /* This is not an element section, but an enum section? */
1380 if (strchr(section
, ':'))
1383 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1384 return p
->last_element
;
1386 PA_LLIST_FOREACH(e
, p
->elements
)
1387 if (pa_streq(e
->alsa_name
, section
))
1390 e
= pa_xnew0(pa_alsa_element
, 1);
1392 e
->alsa_name
= pa_xstrdup(section
);
1393 e
->direction
= p
->direction
;
1395 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1398 p
->last_element
= e
;
1402 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1408 if (!pa_startswith(section
, "Option "))
1413 /* This is not an enum section, but an element section? */
1414 if (!(on
= strchr(section
, ':')))
1417 en
= pa_xstrndup(section
, on
- section
);
1420 if (p
->last_option
&&
1421 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1422 pa_streq(p
->last_option
->alsa_name
, on
)) {
1424 return p
->last_option
;
1427 pa_assert_se(e
= element_get(p
, en
, FALSE
));
1430 PA_LLIST_FOREACH(o
, e
->options
)
1431 if (pa_streq(o
->alsa_name
, on
))
1434 o
= pa_xnew0(pa_alsa_option
, 1);
1436 o
->alsa_name
= pa_xstrdup(on
);
1439 if (p
->last_option
&& p
->last_option
->element
== e
)
1440 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1442 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1449 static int element_parse_switch(
1450 const char *filename
,
1452 const char *section
,
1458 pa_alsa_path
*p
= userdata
;
1463 if (!(e
= element_get(p
, section
, TRUE
))) {
1464 pa_log("[%s:%u] Switch makes no sense in '%s'", filename
, line
, section
);
1468 if (pa_streq(rvalue
, "ignore"))
1469 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1470 else if (pa_streq(rvalue
, "mute"))
1471 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1472 else if (pa_streq(rvalue
, "off"))
1473 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1474 else if (pa_streq(rvalue
, "on"))
1475 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1476 else if (pa_streq(rvalue
, "select"))
1477 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1479 pa_log("[%s:%u] Switch invalid of '%s'", filename
, line
, section
);
1486 static int element_parse_volume(
1487 const char *filename
,
1489 const char *section
,
1495 pa_alsa_path
*p
= userdata
;
1500 if (!(e
= element_get(p
, section
, TRUE
))) {
1501 pa_log("[%s:%u] Volume makes no sense in '%s'", filename
, line
, section
);
1505 if (pa_streq(rvalue
, "ignore"))
1506 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1507 else if (pa_streq(rvalue
, "merge"))
1508 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1509 else if (pa_streq(rvalue
, "off"))
1510 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1511 else if (pa_streq(rvalue
, "zero"))
1512 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1514 pa_log("[%s:%u] Volume invalid of '%s'", filename
, line
, section
);
1521 static int element_parse_enumeration(
1522 const char *filename
,
1524 const char *section
,
1530 pa_alsa_path
*p
= userdata
;
1535 if (!(e
= element_get(p
, section
, TRUE
))) {
1536 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename
, line
, section
);
1540 if (pa_streq(rvalue
, "ignore"))
1541 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1542 else if (pa_streq(rvalue
, "select"))
1543 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1545 pa_log("[%s:%u] Enumeration invalid of '%s'", filename
, line
, section
);
1552 static int option_parse_priority(
1553 const char *filename
,
1555 const char *section
,
1561 pa_alsa_path
*p
= userdata
;
1567 if (!(o
= option_get(p
, section
))) {
1568 pa_log("[%s:%u] Priority makes no sense in '%s'", filename
, line
, section
);
1572 if (pa_atou(rvalue
, &prio
) < 0) {
1573 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
1581 static int option_parse_name(
1582 const char *filename
,
1584 const char *section
,
1590 pa_alsa_path
*p
= userdata
;
1595 if (!(o
= option_get(p
, section
))) {
1596 pa_log("[%s:%u] Name makes no sense in '%s'", filename
, line
, section
);
1601 o
->name
= pa_xstrdup(rvalue
);
1606 static int element_parse_required(
1607 const char *filename
,
1609 const char *section
,
1615 pa_alsa_path
*p
= userdata
;
1618 pa_alsa_required_t req
;
1622 e
= element_get(p
, section
, TRUE
);
1623 o
= option_get(p
, section
);
1625 pa_log("[%s:%u] Required makes no sense in '%s'", filename
, line
, section
);
1629 if (pa_streq(rvalue
, "ignore"))
1630 req
= PA_ALSA_REQUIRED_IGNORE
;
1631 else if (pa_streq(rvalue
, "switch") && e
)
1632 req
= PA_ALSA_REQUIRED_SWITCH
;
1633 else if (pa_streq(rvalue
, "volume") && e
)
1634 req
= PA_ALSA_REQUIRED_VOLUME
;
1635 else if (pa_streq(rvalue
, "enumeration"))
1636 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1637 else if (pa_streq(rvalue
, "any"))
1638 req
= PA_ALSA_REQUIRED_ANY
;
1640 pa_log("[%s:%u] Required invalid of '%s'", filename
, line
, section
);
1644 if (pa_streq(lvalue
, "required-absent")) {
1646 e
->required_absent
= req
;
1648 o
->required_absent
= req
;
1650 else if (pa_streq(lvalue
, "required-any")) {
1652 e
->required_any
= req
;
1653 e
->path
->has_req_any
= TRUE
;
1656 o
->required_any
= req
;
1657 o
->element
->path
->has_req_any
= TRUE
;
1670 static int element_parse_direction(
1671 const char *filename
,
1673 const char *section
,
1679 pa_alsa_path
*p
= userdata
;
1684 if (!(e
= element_get(p
, section
, TRUE
))) {
1685 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1689 if (pa_streq(rvalue
, "playback"))
1690 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1691 else if (pa_streq(rvalue
, "capture"))
1692 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1694 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1701 static int element_parse_direction_try_other(
1702 const char *filename
,
1704 const char *section
,
1710 pa_alsa_path
*p
= userdata
;
1714 if (!(e
= element_get(p
, section
, TRUE
))) {
1715 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1719 if ((yes
= pa_parse_boolean(rvalue
)) < 0) {
1720 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1724 e
->direction_try_other
= !!yes
;
1728 static pa_channel_position_mask_t
parse_mask(const char *m
) {
1729 pa_channel_position_mask_t v
;
1731 if (pa_streq(m
, "all-left"))
1732 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
1733 else if (pa_streq(m
, "all-right"))
1734 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
1735 else if (pa_streq(m
, "all-center"))
1736 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
1737 else if (pa_streq(m
, "all-front"))
1738 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
1739 else if (pa_streq(m
, "all-rear"))
1740 v
= PA_CHANNEL_POSITION_MASK_REAR
;
1741 else if (pa_streq(m
, "all-side"))
1742 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
1743 else if (pa_streq(m
, "all-top"))
1744 v
= PA_CHANNEL_POSITION_MASK_TOP
;
1745 else if (pa_streq(m
, "all-no-lfe"))
1746 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
1747 else if (pa_streq(m
, "all"))
1748 v
= PA_CHANNEL_POSITION_MASK_ALL
;
1750 pa_channel_position_t p
;
1752 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
1755 v
= PA_CHANNEL_POSITION_MASK(p
);
1761 static int element_parse_override_map(
1762 const char *filename
,
1764 const char *section
,
1770 pa_alsa_path
*p
= userdata
;
1772 const char *state
= NULL
;
1776 if (!(e
= element_get(p
, section
, TRUE
))) {
1777 pa_log("[%s:%u] Override map makes no sense in '%s'", filename
, line
, section
);
1781 while ((n
= pa_split(rvalue
, ",", &state
))) {
1782 pa_channel_position_mask_t m
;
1787 if ((m
= parse_mask(n
)) == 0) {
1788 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename
, line
, n
, section
);
1794 if (pa_streq(lvalue
, "override-map.1"))
1795 e
->masks
[i
++][0] = m
;
1797 e
->masks
[i
++][1] = m
;
1799 /* Later on we might add override-map.3 and so on here ... */
1804 e
->override_map
= TRUE
;
1809 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
1810 snd_mixer_selem_id_t
*sid
;
1811 snd_mixer_elem_t
*me
;
1817 SELEM_INIT(sid
, e
->alsa_name
);
1818 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1819 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1823 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1825 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1826 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
1828 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
1831 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1834 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
1836 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
1837 pa_log_warn("Failed to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1843 int pa_alsa_setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
1850 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
1851 element_set_option(o
->element
, m
, o
->alsa_idx
);
1856 static int option_verify(pa_alsa_option
*o
) {
1857 static const struct description_map well_known_descriptions
[] = {
1858 { "input", N_("Input") },
1859 { "input-docking", N_("Docking Station Input") },
1860 { "input-docking-microphone", N_("Docking Station Microphone") },
1861 { "input-docking-linein", N_("Docking Station Line-In") },
1862 { "input-linein", N_("Line-In") },
1863 { "input-microphone", N_("Microphone") },
1864 { "input-microphone-front", N_("Front Microphone") },
1865 { "input-microphone-rear", N_("Rear Microphone") },
1866 { "input-microphone-external", N_("External Microphone") },
1867 { "input-microphone-internal", N_("Internal Microphone") },
1868 { "input-radio", N_("Radio") },
1869 { "input-video", N_("Video") },
1870 { "input-agc-on", N_("Automatic Gain Control") },
1871 { "input-agc-off", N_("No Automatic Gain Control") },
1872 { "input-boost-on", N_("Boost") },
1873 { "input-boost-off", N_("No Boost") },
1874 { "output-amplifier-on", N_("Amplifier") },
1875 { "output-amplifier-off", N_("No Amplifier") },
1876 { "output-bass-boost-on", N_("Bass Boost") },
1877 { "output-bass-boost-off", N_("No Bass Boost") },
1878 { "output-speaker", N_("Speaker") },
1879 { "output-headphones", N_("Headphones") }
1885 pa_log("No name set for option %s", o
->alsa_name
);
1889 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
1890 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
1891 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
1895 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
1896 !pa_streq(o
->alsa_name
, "on") &&
1897 !pa_streq(o
->alsa_name
, "off")) {
1898 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
1902 if (!o
->description
)
1903 o
->description
= pa_xstrdup(lookup_description(o
->name
,
1904 well_known_descriptions
,
1905 PA_ELEMENTSOF(well_known_descriptions
)));
1906 if (!o
->description
)
1907 o
->description
= pa_xstrdup(o
->name
);
1912 static int element_verify(pa_alsa_element
*e
) {
1917 // 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);
1918 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
1919 (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required_any
== e
->required_absent
) ||
1920 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) ||
1921 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
1922 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
1926 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1927 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
1931 PA_LLIST_FOREACH(o
, e
->options
)
1932 if (option_verify(o
) < 0)
1938 static int path_verify(pa_alsa_path
*p
) {
1939 static const struct description_map well_known_descriptions
[] = {
1940 { "analog-input", N_("Analog Input") },
1941 { "analog-input-microphone", N_("Analog Microphone") },
1942 { "analog-input-microphone-front", N_("Front Microphone") },
1943 { "analog-input-microphone-rear", N_("Rear Microphone") },
1944 { "analog-input-microphone-dock", N_("Docking Station Microphone") },
1945 { "analog-input-microphone-internal", N_("Internal Microphone") },
1946 { "analog-input-linein", N_("Analog Line-In") },
1947 { "analog-input-radio", N_("Analog Radio") },
1948 { "analog-input-video", N_("Analog Video") },
1949 { "analog-output", N_("Analog Output") },
1950 { "analog-output-headphones", N_("Analog Headphones") },
1951 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
1952 { "analog-output-mono", N_("Analog Mono Output") },
1953 { "analog-output-speaker", N_("Analog Speakers") },
1954 { "iec958-stereo-output", N_("Digital Output (IEC958)") },
1955 { "iec958-passthrough-output", N_("Digital Passthrough (IEC958)") }
1962 PA_LLIST_FOREACH(e
, p
->elements
)
1963 if (element_verify(e
) < 0)
1966 if (!p
->description
)
1967 p
->description
= pa_xstrdup(lookup_description(p
->name
,
1968 well_known_descriptions
,
1969 PA_ELEMENTSOF(well_known_descriptions
)));
1971 if (!p
->description
)
1972 p
->description
= pa_xstrdup(p
->name
);
1977 pa_alsa_path
* pa_alsa_path_new(const char *fname
, pa_alsa_direction_t direction
) {
1983 pa_config_item items
[] = {
1985 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
1986 { "description", pa_config_parse_string
, NULL
, "General" },
1987 { "name", pa_config_parse_string
, NULL
, "General" },
1990 { "priority", option_parse_priority
, NULL
, NULL
},
1991 { "name", option_parse_name
, NULL
, NULL
},
1994 { "switch", element_parse_switch
, NULL
, NULL
},
1995 { "volume", element_parse_volume
, NULL
, NULL
},
1996 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
1997 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
1998 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
1999 /* ... later on we might add override-map.3 and so on here ... */
2000 { "required", element_parse_required
, NULL
, NULL
},
2001 { "required-any", element_parse_required
, NULL
, NULL
},
2002 { "required-absent", element_parse_required
, NULL
, NULL
},
2003 { "direction", element_parse_direction
, NULL
, NULL
},
2004 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2005 { NULL
, NULL
, NULL
, NULL
}
2010 p
= pa_xnew0(pa_alsa_path
, 1);
2011 n
= pa_path_get_filename(fname
);
2012 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2013 p
->direction
= direction
;
2015 items
[0].data
= &p
->priority
;
2016 items
[1].data
= &p
->description
;
2017 items
[2].data
= &p
->name
;
2019 fn
= pa_maybe_prefix_path(fname
,
2020 #if defined(__linux__) && !defined(__OPTIMIZE__)
2021 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/paths/" :
2025 r
= pa_config_parse(fn
, NULL
, items
, p
);
2031 if (path_verify(p
) < 0)
2037 pa_alsa_path_free(p
);
2041 pa_alsa_path
* pa_alsa_path_synthesize(const char*element
, pa_alsa_direction_t direction
) {
2047 p
= pa_xnew0(pa_alsa_path
, 1);
2048 p
->name
= pa_xstrdup(element
);
2049 p
->direction
= direction
;
2051 e
= pa_xnew0(pa_alsa_element
, 1);
2053 e
->alsa_name
= pa_xstrdup(element
);
2054 e
->direction
= direction
;
2056 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2057 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2059 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2060 p
->last_element
= e
;
2064 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2065 pa_alsa_option
*o
, *n
;
2069 for (o
= e
->options
; o
; o
= n
) {
2072 if (o
->alsa_idx
< 0) {
2073 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2079 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2080 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2081 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2084 static void path_drop_unsupported(pa_alsa_path
*p
) {
2085 pa_alsa_element
*e
, *n
;
2089 for (e
= p
->elements
; e
; e
= n
) {
2092 if (!element_drop_unsupported(e
)) {
2093 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2099 static void path_make_options_unique(pa_alsa_path
*p
) {
2101 pa_alsa_option
*o
, *u
;
2103 PA_LLIST_FOREACH(e
, p
->elements
) {
2104 PA_LLIST_FOREACH(o
, e
->options
) {
2108 for (u
= o
->next
; u
; u
= u
->next
)
2109 if (pa_streq(u
->name
, o
->name
))
2115 m
= pa_xstrdup(o
->name
);
2117 /* OK, this name is not unique, hence let's rename */
2118 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2121 if (!pa_streq(u
->name
, m
))
2124 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2128 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2129 pa_xfree(u
->description
);
2130 u
->description
= nd
;
2140 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2143 for (; e
; e
= e
->next
)
2144 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2145 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2151 for (o
= e
->options
; o
; o
= o
->next
) {
2155 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2156 s
->options
= pa_idxset_copy(template->options
);
2157 s
->name
= pa_sprintf_malloc(_("%s+%s"), template->name
, o
->name
);
2159 (template->description
[0] && o
->description
[0])
2160 ? pa_sprintf_malloc(_("%s / %s"), template->description
, o
->description
)
2161 : (template->description
[0]
2162 ? pa_xstrdup(template->description
)
2163 : pa_xstrdup(o
->description
));
2165 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2167 s
= pa_xnew0(pa_alsa_setting
, 1);
2168 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2169 s
->name
= pa_xstrdup(o
->name
);
2170 s
->description
= pa_xstrdup(o
->description
);
2171 s
->priority
= o
->priority
;
2174 pa_idxset_put(s
->options
, o
, NULL
);
2176 if (element_create_settings(e
->next
, s
))
2177 /* This is not a leaf, so let's get rid of it */
2180 /* This is a leaf, so let's add it */
2181 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2183 e
->path
->last_setting
= s
;
2190 static void path_create_settings(pa_alsa_path
*p
) {
2193 element_create_settings(p
->elements
, NULL
);
2196 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2198 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2199 pa_channel_position_t t
;
2210 pa_log_debug("Probing path '%s'", p
->name
);
2212 PA_LLIST_FOREACH(e
, p
->elements
) {
2213 if (element_probe(e
, m
) < 0) {
2214 p
->supported
= FALSE
;
2215 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2218 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
);
2223 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2225 if (!p
->has_volume
) {
2226 p
->min_volume
= e
->min_volume
;
2227 p
->max_volume
= e
->max_volume
;
2231 if (!p
->has_volume
) {
2232 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2233 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2234 min_dB
[t
] = e
->min_dB
;
2235 max_dB
[t
] = e
->max_dB
;
2242 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2243 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2244 min_dB
[t
] += e
->min_dB
;
2245 max_dB
[t
] += e
->max_dB
;
2248 /* Hmm, there's another element before us
2249 * which cannot do dB volumes, so we we need
2250 * to 'neutralize' this slider */
2251 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2252 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2255 } else if (p
->has_volume
) {
2256 /* We can't use this volume, so let's ignore it */
2257 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2258 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2260 p
->has_volume
= TRUE
;
2263 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2267 if (p
->has_req_any
&& !p
->req_any_present
) {
2268 p
->supported
= FALSE
;
2269 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2273 path_drop_unsupported(p
);
2274 path_make_options_unique(p
);
2275 path_create_settings(p
);
2277 p
->supported
= TRUE
;
2280 p
->min_dB
= INFINITY
;
2281 p
->max_dB
= -INFINITY
;
2283 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2284 if (p
->min_dB
> min_dB
[t
])
2285 p
->min_dB
= min_dB
[t
];
2287 if (p
->max_dB
< max_dB
[t
])
2288 p
->max_dB
= max_dB
[t
];
2294 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2297 pa_log_debug("Setting %s (%s) priority=%u",
2299 pa_strnull(s
->description
),
2303 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2306 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2308 pa_strnull(o
->name
),
2309 pa_strnull(o
->description
),
2314 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2318 pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s",
2327 (long long unsigned) e
->merged_mask
,
2329 pa_yes_no(e
->override_map
));
2331 PA_LLIST_FOREACH(o
, e
->options
)
2332 pa_alsa_option_dump(o
);
2335 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2340 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2341 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2343 pa_strnull(p
->description
),
2346 pa_yes_no(p
->probed
),
2347 pa_yes_no(p
->supported
),
2348 pa_yes_no(p
->has_mute
),
2349 pa_yes_no(p
->has_volume
),
2350 pa_yes_no(p
->has_dB
),
2351 p
->min_volume
, p
->max_volume
,
2352 p
->min_dB
, p
->max_dB
);
2354 PA_LLIST_FOREACH(e
, p
->elements
)
2355 pa_alsa_element_dump(e
);
2357 PA_LLIST_FOREACH(s
, p
->settings
)
2358 pa_alsa_setting_dump(s
);
2361 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2362 snd_mixer_selem_id_t
*sid
;
2363 snd_mixer_elem_t
*me
;
2369 SELEM_INIT(sid
, e
->alsa_name
);
2370 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2371 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2375 snd_mixer_elem_set_callback(me
, cb
);
2376 snd_mixer_elem_set_callback_private(me
, userdata
);
2379 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2386 PA_LLIST_FOREACH(e
, p
->elements
)
2387 element_set_callback(e
, m
, cb
, userdata
);
2390 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2397 PA_LLIST_FOREACH(p
, ps
->paths
)
2398 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2401 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
) {
2402 pa_alsa_path_set
*ps
;
2403 char **pn
= NULL
, **en
= NULL
, **ie
;
2406 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2408 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2411 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2412 ps
->direction
= direction
;
2414 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2415 pn
= m
->output_path_names
;
2416 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2417 pn
= m
->input_path_names
;
2422 for (in
= pn
; *in
; in
++) {
2424 pa_bool_t duplicate
= FALSE
;
2427 for (kn
= pn
; kn
!= in
; kn
++)
2428 if (pa_streq(*kn
, *in
)) {
2436 fn
= pa_sprintf_malloc("%s.conf", *in
);
2438 if ((p
= pa_alsa_path_new(fn
, direction
))) {
2440 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2450 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2451 en
= m
->output_element
;
2452 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2453 en
= m
->input_element
;
2456 pa_alsa_path_set_free(ps
);
2460 for (ie
= en
; *ie
; ie
++) {
2464 p
= pa_alsa_path_synthesize(*ie
, direction
);
2467 /* Mark all other passed elements for require-absent */
2468 for (je
= en
; *je
; je
++) {
2474 e
= pa_xnew0(pa_alsa_element
, 1);
2476 e
->alsa_name
= pa_xstrdup(*je
);
2477 e
->direction
= direction
;
2478 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2480 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2481 p
->last_element
= e
;
2484 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2491 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2495 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2498 pa_yes_no(ps
->probed
));
2500 PA_LLIST_FOREACH(p
, ps
->paths
)
2501 pa_alsa_path_dump(p
);
2504 static void path_set_unify(pa_alsa_path_set
*ps
) {
2506 pa_bool_t has_dB
= TRUE
, has_volume
= TRUE
, has_mute
= TRUE
;
2509 /* We have issues dealing with paths that vary too wildly. That
2510 * means for now we have to have all paths support volume/mute/dB
2513 PA_LLIST_FOREACH(p
, ps
->paths
) {
2514 pa_assert(p
->probed
);
2518 else if (!p
->has_dB
)
2525 if (!has_volume
|| !has_dB
|| !has_mute
) {
2528 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2530 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2533 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2535 PA_LLIST_FOREACH(p
, ps
->paths
) {
2537 p
->has_volume
= FALSE
;
2542 p
->has_mute
= FALSE
;
2547 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
2548 pa_alsa_path
*p
, *q
;
2550 PA_LLIST_FOREACH(p
, ps
->paths
) {
2554 for (q
= p
->next
; q
; q
= q
->next
)
2555 if (pa_streq(q
->name
, p
->name
))
2561 m
= pa_xstrdup(p
->name
);
2563 /* OK, this name is not unique, hence let's rename */
2564 for (i
= 1, q
= p
; q
; q
= q
->next
) {
2567 if (!pa_streq(q
->name
, m
))
2570 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2574 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
2575 pa_xfree(q
->description
);
2576 q
->description
= nd
;
2585 void pa_alsa_path_set_probe(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2586 pa_alsa_path
*p
, *n
;
2593 for (p
= ps
->paths
; p
; p
= n
) {
2596 if (pa_alsa_path_probe(p
, m
, ignore_dB
) < 0) {
2597 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
2598 pa_alsa_path_free(p
);
2603 path_set_make_paths_unique(ps
);
2607 static void mapping_free(pa_alsa_mapping
*m
) {
2611 pa_xfree(m
->description
);
2613 pa_xstrfreev(m
->device_strings
);
2614 pa_xstrfreev(m
->input_path_names
);
2615 pa_xstrfreev(m
->output_path_names
);
2616 pa_xstrfreev(m
->input_element
);
2617 pa_xstrfreev(m
->output_element
);
2619 pa_assert(!m
->input_pcm
);
2620 pa_assert(!m
->output_pcm
);
2625 static void profile_free(pa_alsa_profile
*p
) {
2629 pa_xfree(p
->description
);
2631 pa_xstrfreev(p
->input_mapping_names
);
2632 pa_xstrfreev(p
->output_mapping_names
);
2634 if (p
->input_mappings
)
2635 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
2637 if (p
->output_mappings
)
2638 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
2643 static void decibel_fix_free(pa_alsa_decibel_fix
*db_fix
) {
2646 pa_xfree(db_fix
->name
);
2647 pa_xfree(db_fix
->db_values
);
2652 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
2658 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
2661 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
2667 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
2670 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
2673 if (ps
->decibel_fixes
) {
2674 pa_alsa_decibel_fix
*db_fix
;
2676 while ((db_fix
= pa_hashmap_steal_first(ps
->decibel_fixes
)))
2677 decibel_fix_free(db_fix
);
2679 pa_hashmap_free(ps
->decibel_fixes
, NULL
, NULL
);
2685 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
2688 if (!pa_startswith(name
, "Mapping "))
2693 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
2696 m
= pa_xnew0(pa_alsa_mapping
, 1);
2697 m
->profile_set
= ps
;
2698 m
->name
= pa_xstrdup(name
);
2699 pa_channel_map_init(&m
->channel_map
);
2701 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
2706 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
2709 if (!pa_startswith(name
, "Profile "))
2714 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
2717 p
= pa_xnew0(pa_alsa_profile
, 1);
2718 p
->profile_set
= ps
;
2719 p
->name
= pa_xstrdup(name
);
2721 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
2726 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
2727 pa_alsa_decibel_fix
*db_fix
;
2729 if (!pa_startswith(name
, "DecibelFix "))
2734 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
2737 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
2738 db_fix
->profile_set
= ps
;
2739 db_fix
->name
= pa_xstrdup(name
);
2741 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
2746 static int mapping_parse_device_strings(
2747 const char *filename
,
2749 const char *section
,
2755 pa_alsa_profile_set
*ps
= userdata
;
2760 if (!(m
= mapping_get(ps
, section
))) {
2761 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2765 pa_xstrfreev(m
->device_strings
);
2766 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
2767 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
2774 static int mapping_parse_channel_map(
2775 const char *filename
,
2777 const char *section
,
2783 pa_alsa_profile_set
*ps
= userdata
;
2788 if (!(m
= mapping_get(ps
, section
))) {
2789 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2793 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
2794 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
2801 static int mapping_parse_paths(
2802 const char *filename
,
2804 const char *section
,
2810 pa_alsa_profile_set
*ps
= userdata
;
2815 if (!(m
= mapping_get(ps
, section
))) {
2816 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2820 if (pa_streq(lvalue
, "paths-input")) {
2821 pa_xstrfreev(m
->input_path_names
);
2822 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
2824 pa_xstrfreev(m
->output_path_names
);
2825 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
2831 static int mapping_parse_element(
2832 const char *filename
,
2834 const char *section
,
2840 pa_alsa_profile_set
*ps
= userdata
;
2845 if (!(m
= mapping_get(ps
, section
))) {
2846 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2850 if (pa_streq(lvalue
, "element-input")) {
2851 pa_xstrfreev(m
->input_element
);
2852 m
->input_element
= pa_split_spaces_strv(rvalue
);
2854 pa_xstrfreev(m
->output_element
);
2855 m
->output_element
= pa_split_spaces_strv(rvalue
);
2861 static int mapping_parse_direction(
2862 const char *filename
,
2864 const char *section
,
2870 pa_alsa_profile_set
*ps
= userdata
;
2875 if (!(m
= mapping_get(ps
, section
))) {
2876 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
2880 if (pa_streq(rvalue
, "input"))
2881 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
2882 else if (pa_streq(rvalue
, "output"))
2883 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
2884 else if (pa_streq(rvalue
, "any"))
2885 m
->direction
= PA_ALSA_DIRECTION_ANY
;
2887 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
2894 static int mapping_parse_description(
2895 const char *filename
,
2897 const char *section
,
2903 pa_alsa_profile_set
*ps
= userdata
;
2909 if ((m
= mapping_get(ps
, section
))) {
2910 pa_xfree(m
->description
);
2911 m
->description
= pa_xstrdup(rvalue
);
2912 } else if ((p
= profile_get(ps
, section
))) {
2913 pa_xfree(p
->description
);
2914 p
->description
= pa_xstrdup(rvalue
);
2916 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
2923 static int mapping_parse_priority(
2924 const char *filename
,
2926 const char *section
,
2932 pa_alsa_profile_set
*ps
= userdata
;
2939 if (pa_atou(rvalue
, &prio
) < 0) {
2940 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
2944 if ((m
= mapping_get(ps
, section
)))
2946 else if ((p
= profile_get(ps
, section
)))
2949 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
2956 static int profile_parse_mappings(
2957 const char *filename
,
2959 const char *section
,
2965 pa_alsa_profile_set
*ps
= userdata
;
2970 if (!(p
= profile_get(ps
, section
))) {
2971 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2975 if (pa_streq(lvalue
, "input-mappings")) {
2976 pa_xstrfreev(p
->input_mapping_names
);
2977 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
2979 pa_xstrfreev(p
->output_mapping_names
);
2980 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
2986 static int profile_parse_skip_probe(
2987 const char *filename
,
2989 const char *section
,
2995 pa_alsa_profile_set
*ps
= userdata
;
3001 if (!(p
= profile_get(ps
, section
))) {
3002 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3006 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
3007 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
3016 static int decibel_fix_parse_db_values(
3017 const char *filename
,
3019 const char *section
,
3025 pa_alsa_profile_set
*ps
= userdata
;
3026 pa_alsa_decibel_fix
*db_fix
;
3030 unsigned n
= 8; /* Current size of the db_values table. */
3031 unsigned min_step
= 0;
3032 unsigned max_step
= 0;
3033 unsigned i
= 0; /* Index to the items table. */
3034 unsigned prev_step
= 0;
3037 pa_assert(filename
);
3043 if (!(db_fix
= decibel_fix_get(ps
, section
))) {
3044 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3048 if (!(items
= pa_split_spaces_strv(rvalue
))) {
3049 pa_log("[%s:%u] Value missing", pa_strnull(filename
), line
);
3053 db_values
= pa_xnew(long, n
);
3055 while ((item
= items
[i
++])) {
3056 char *s
= item
; /* Step value string. */
3057 char *d
= item
; /* dB value string. */
3061 /* Move d forward until it points to a colon or to the end of the item. */
3062 for (; *d
&& *d
!= ':'; ++d
);
3065 /* item started with colon. */
3066 pa_log("[%s:%u] No step value found in %s", filename
, line
, item
);
3070 if (!*d
|| !*(d
+ 1)) {
3071 /* No colon found, or it was the last character in item. */
3072 pa_log("[%s:%u] No dB value found in %s", filename
, line
, item
);
3076 /* pa_atou() needs a null-terminating string. Let's replace the colon
3077 * with a zero byte. */
3080 if (pa_atou(s
, &step
) < 0) {
3081 pa_log("[%s:%u] Invalid step value: %s", filename
, line
, s
);
3085 if (pa_atod(d
, &db
) < 0) {
3086 pa_log("[%s:%u] Invalid dB value: %s", filename
, line
, d
);
3090 if (step
<= prev_step
&& i
!= 1) {
3091 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename
, line
, step
, prev_step
);
3095 if (db
< prev_db
&& i
!= 1) {
3096 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename
, line
, db
, prev_db
);
3102 db_values
[0] = (long) (db
* 100.0);
3106 /* Interpolate linearly. */
3107 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3109 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3111 /* Reallocate the db_values table if it's about to overflow. */
3112 if (prev_step
+ 1 - min_step
== n
) {
3114 db_values
= pa_xrenew(long, db_values
, n
);
3117 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3124 db_fix
->min_step
= min_step
;
3125 db_fix
->max_step
= max_step
;
3126 pa_xfree(db_fix
->db_values
);
3127 db_fix
->db_values
= db_values
;
3129 pa_xstrfreev(items
);
3134 pa_xstrfreev(items
);
3135 pa_xfree(db_values
);
3140 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3142 static const struct description_map well_known_descriptions
[] = {
3143 { "analog-mono", N_("Analog Mono") },
3144 { "analog-stereo", N_("Analog Stereo") },
3145 { "analog-surround-21", N_("Analog Surround 2.1") },
3146 { "analog-surround-30", N_("Analog Surround 3.0") },
3147 { "analog-surround-31", N_("Analog Surround 3.1") },
3148 { "analog-surround-40", N_("Analog Surround 4.0") },
3149 { "analog-surround-41", N_("Analog Surround 4.1") },
3150 { "analog-surround-50", N_("Analog Surround 5.0") },
3151 { "analog-surround-51", N_("Analog Surround 5.1") },
3152 { "analog-surround-61", N_("Analog Surround 6.0") },
3153 { "analog-surround-61", N_("Analog Surround 6.1") },
3154 { "analog-surround-70", N_("Analog Surround 7.0") },
3155 { "analog-surround-71", N_("Analog Surround 7.1") },
3156 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3157 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3158 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3159 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3160 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3165 if (!pa_channel_map_valid(&m
->channel_map
)) {
3166 pa_log("Mapping %s is missing channel map.", m
->name
);
3170 if (!m
->device_strings
) {
3171 pa_log("Mapping %s is missing device strings.", m
->name
);
3175 if ((m
->input_path_names
&& m
->input_element
) ||
3176 (m
->output_path_names
&& m
->output_element
)) {
3177 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3181 if (!m
->description
)
3182 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3183 well_known_descriptions
,
3184 PA_ELEMENTSOF(well_known_descriptions
)));
3186 if (!m
->description
)
3187 m
->description
= pa_xstrdup(m
->name
);
3190 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3192 else if (m
->channel_map
.channels
== bonus
->channels
)
3199 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3200 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3204 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3206 pa_strnull(m
->description
),
3208 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3209 pa_yes_no(m
->supported
),
3213 static void profile_set_add_auto_pair(
3214 pa_alsa_profile_set
*ps
,
3215 pa_alsa_mapping
*m
, /* output */
3216 pa_alsa_mapping
*n
/* input */) {
3224 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3227 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3231 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3233 name
= pa_sprintf_malloc("output:%s", m
->name
);
3235 name
= pa_sprintf_malloc("input:%s", n
->name
);
3237 if (pa_hashmap_get(ps
->profiles
, name
)) {
3242 p
= pa_xnew0(pa_alsa_profile
, 1);
3243 p
->profile_set
= ps
;
3247 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3248 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3249 p
->priority
+= m
->priority
* 100;
3253 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3254 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3255 p
->priority
+= n
->priority
;
3258 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3261 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3262 pa_alsa_mapping
*m
, *n
;
3263 void *m_state
, *n_state
;
3267 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3268 profile_set_add_auto_pair(ps
, m
, NULL
);
3270 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3271 profile_set_add_auto_pair(ps
, m
, n
);
3274 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3275 profile_set_add_auto_pair(ps
, NULL
, n
);
3278 static int profile_verify(pa_alsa_profile
*p
) {
3280 static const struct description_map well_known_descriptions
[] = {
3281 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3282 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3283 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3284 { "off", N_("Off") }
3289 /* Replace the output mapping names by the actual mappings */
3290 if (p
->output_mapping_names
) {
3293 pa_assert(!p
->output_mappings
);
3294 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3296 for (name
= p
->output_mapping_names
; *name
; name
++) {
3299 pa_bool_t duplicate
= FALSE
;
3301 for (in
= name
+ 1; *in
; in
++)
3302 if (pa_streq(*name
, *in
)) {
3310 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3311 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3315 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3321 pa_xstrfreev(p
->output_mapping_names
);
3322 p
->output_mapping_names
= NULL
;
3325 /* Replace the input mapping names by the actual mappings */
3326 if (p
->input_mapping_names
) {
3329 pa_assert(!p
->input_mappings
);
3330 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3332 for (name
= p
->input_mapping_names
; *name
; name
++) {
3335 pa_bool_t duplicate
= FALSE
;
3337 for (in
= name
+ 1; *in
; in
++)
3338 if (pa_streq(*name
, *in
)) {
3346 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3347 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3351 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3357 pa_xstrfreev(p
->input_mapping_names
);
3358 p
->input_mapping_names
= NULL
;
3361 if (!p
->input_mappings
&& !p
->output_mappings
) {
3362 pa_log("Profile '%s' lacks mappings.", p
->name
);
3366 if (!p
->description
)
3367 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3368 well_known_descriptions
,
3369 PA_ELEMENTSOF(well_known_descriptions
)));
3371 if (!p
->description
) {
3376 sb
= pa_strbuf_new();
3378 if (p
->output_mappings
)
3379 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3380 if (!pa_strbuf_isempty(sb
))
3381 pa_strbuf_puts(sb
, " + ");
3383 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
3386 if (p
->input_mappings
)
3387 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3388 if (!pa_strbuf_isempty(sb
))
3389 pa_strbuf_puts(sb
, " + ");
3391 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
3394 p
->description
= pa_strbuf_tostring_free(sb
);
3400 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
3405 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3407 pa_strnull(p
->description
),
3409 pa_yes_no(p
->supported
),
3410 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
3411 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
3413 if (p
->input_mappings
)
3414 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
3415 pa_log_debug("Input %s", m
->name
);
3417 if (p
->output_mappings
)
3418 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
3419 pa_log_debug("Output %s", m
->name
);
3422 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
3425 /* Check that the dB mapping has been configured. Since "db-values" is
3426 * currently the only option in the DecibelFix section, and decibel fix
3427 * objects don't get created if a DecibelFix section is empty, this is
3428 * actually a redundant check. Having this may prevent future bugs,
3430 if (!db_fix
->db_values
) {
3431 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
3438 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
3439 char *db_values
= NULL
;
3443 if (db_fix
->db_values
) {
3446 long max_i
= db_fix
->max_step
- db_fix
->min_step
;
3448 buf
= pa_strbuf_new();
3449 pa_strbuf_printf(buf
, "[%li]:%0.2f", db_fix
->min_step
, db_fix
->db_values
[0] / 100.0);
3451 for (i
= 1; i
<= max_i
; ++i
)
3452 pa_strbuf_printf(buf
, " [%li]:%0.2f", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
3454 db_values
= pa_strbuf_tostring_free(buf
);
3457 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
3458 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
3460 pa_xfree(db_values
);
3463 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
3464 pa_alsa_profile_set
*ps
;
3467 pa_alsa_decibel_fix
*db_fix
;
3472 static pa_config_item items
[] = {
3474 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
3477 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
3478 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
3479 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
3480 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
3481 { "element-input", mapping_parse_element
, NULL
, NULL
},
3482 { "element-output", mapping_parse_element
, NULL
, NULL
},
3483 { "direction", mapping_parse_direction
, NULL
, NULL
},
3485 /* Shared by [Mapping ...] and [Profile ...] */
3486 { "description", mapping_parse_description
, NULL
, NULL
},
3487 { "priority", mapping_parse_priority
, NULL
, NULL
},
3490 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
3491 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
3492 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
3494 /* [DecibelFix ...] */
3495 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
3496 { NULL
, NULL
, NULL
, NULL
}
3499 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
3500 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3501 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3502 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3504 items
[0].data
= &ps
->auto_profiles
;
3507 fname
= "default.conf";
3509 fn
= pa_maybe_prefix_path(fname
,
3510 #if defined(__linux__) && !defined(__OPTIMIZE__)
3511 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
3513 PA_ALSA_PROFILE_SETS_DIR
);
3515 r
= pa_config_parse(fn
, NULL
, items
, ps
);
3521 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3522 if (mapping_verify(m
, bonus
) < 0)
3525 if (ps
->auto_profiles
)
3526 profile_set_add_auto(ps
);
3528 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3529 if (profile_verify(p
) < 0)
3532 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
3533 if (decibel_fix_verify(db_fix
) < 0)
3539 pa_alsa_profile_set_free(ps
);
3543 void pa_alsa_profile_set_probe(
3544 pa_alsa_profile_set
*ps
,
3546 const pa_sample_spec
*ss
,
3547 unsigned default_n_fragments
,
3548 unsigned default_fragment_size_msec
) {
3551 pa_alsa_profile
*p
, *last
= NULL
;
3561 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
3562 pa_sample_spec try_ss
;
3563 pa_channel_map try_map
;
3564 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
3567 /* Is this already marked that it is supported? (i.e. from the config file) */
3571 pa_log_debug("Looking at profile %s", p
->name
);
3573 /* Close PCMs from the last iteration we don't need anymore */
3574 if (last
&& last
->output_mappings
)
3575 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
) {
3580 if (last
->supported
)
3583 if (!p
->output_mappings
|| !pa_idxset_get_by_data(p
->output_mappings
, m
, NULL
)) {
3584 snd_pcm_close(m
->output_pcm
);
3585 m
->output_pcm
= NULL
;
3589 if (last
&& last
->input_mappings
)
3590 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
) {
3595 if (last
->supported
)
3598 if (!p
->input_mappings
|| !pa_idxset_get_by_data(p
->input_mappings
, m
, NULL
)) {
3599 snd_pcm_close(m
->input_pcm
);
3600 m
->input_pcm
= NULL
;
3604 p
->supported
= TRUE
;
3606 /* Check if we can open all new ones */
3607 if (p
->output_mappings
)
3608 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3613 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
3614 try_map
= m
->channel_map
;
3616 try_ss
.channels
= try_map
.channels
;
3619 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
3620 pa_frame_size(&try_ss
);
3621 try_buffer_size
= default_n_fragments
* try_period_size
;
3623 if (!(m
->output_pcm
= pa_alsa_open_by_template(
3628 SND_PCM_STREAM_PLAYBACK
,
3629 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3631 p
->supported
= FALSE
;
3636 if (p
->input_mappings
&& p
->supported
)
3637 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3642 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
3643 try_map
= m
->channel_map
;
3645 try_ss
.channels
= try_map
.channels
;
3648 pa_usec_to_bytes(default_fragment_size_msec
*PA_USEC_PER_MSEC
, &try_ss
) /
3649 pa_frame_size(&try_ss
);
3650 try_buffer_size
= default_n_fragments
* try_period_size
;
3652 if (!(m
->input_pcm
= pa_alsa_open_by_template(
3657 SND_PCM_STREAM_CAPTURE
,
3658 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3660 p
->supported
= FALSE
;
3668 pa_log_debug("Profile %s supported.", p
->name
);
3675 if (last
->output_mappings
)
3676 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
)
3677 if (m
->output_pcm
) {
3679 if (last
->supported
)
3682 snd_pcm_close(m
->output_pcm
);
3683 m
->output_pcm
= NULL
;
3686 if (last
->input_mappings
)
3687 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
)
3690 if (last
->supported
)
3693 snd_pcm_close(m
->input_pcm
);
3694 m
->input_pcm
= NULL
;
3698 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3699 if (!p
->supported
) {
3700 pa_hashmap_remove(ps
->profiles
, p
->name
);
3704 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3705 if (m
->supported
<= 0) {
3706 pa_hashmap_remove(ps
->mappings
, m
->name
);
3713 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
3716 pa_alsa_decibel_fix
*db_fix
;
3721 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
3724 pa_yes_no(ps
->auto_profiles
),
3725 pa_yes_no(ps
->probed
),
3726 pa_hashmap_size(ps
->mappings
),
3727 pa_hashmap_size(ps
->profiles
),
3728 pa_hashmap_size(ps
->decibel_fixes
));
3730 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3731 pa_alsa_mapping_dump(m
);
3733 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3734 pa_alsa_profile_dump(p
);
3736 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
3737 pa_alsa_decibel_fix_dump(db_fix
);
3740 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
) {
3747 /* if there is no path, we don't want a port list */
3751 if (!ps
->paths
->next
){
3754 /* If there is only one path, but no or only one setting, then
3755 * we want a port list either */
3756 if (!ps
->paths
->settings
|| !ps
->paths
->settings
->next
)
3759 /* Ok, there is only one path, however with multiple settings,
3760 * so let's create a port for each setting */
3761 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3763 PA_LLIST_FOREACH(s
, ps
->paths
->settings
) {
3764 pa_device_port
*port
;
3765 pa_alsa_port_data
*data
;
3767 port
= pa_device_port_new(s
->name
, s
->description
, sizeof(pa_alsa_port_data
));
3768 port
->priority
= s
->priority
;
3770 data
= PA_DEVICE_PORT_DATA(port
);
3771 data
->path
= ps
->paths
;
3774 pa_hashmap_put(*p
, port
->name
, port
);
3779 /* We have multiple paths, so let's create a port for each
3780 * one, and each of each settings */
3781 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3783 PA_LLIST_FOREACH(path
, ps
->paths
) {
3785 if (!path
->settings
|| !path
->settings
->next
) {
3786 pa_device_port
*port
;
3787 pa_alsa_port_data
*data
;
3789 /* If there is no or just one setting we only need a
3792 port
= pa_device_port_new(path
->name
, path
->description
, sizeof(pa_alsa_port_data
));
3793 port
->priority
= path
->priority
* 100;
3796 data
= PA_DEVICE_PORT_DATA(port
);
3798 data
->setting
= path
->settings
;
3800 pa_hashmap_put(*p
, port
->name
, port
);
3804 PA_LLIST_FOREACH(s
, path
->settings
) {
3805 pa_device_port
*port
;
3806 pa_alsa_port_data
*data
;
3809 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
3811 if (s
->description
[0])
3812 d
= pa_sprintf_malloc(_("%s / %s"), path
->description
, s
->description
);
3814 d
= pa_xstrdup(path
->description
);
3816 port
= pa_device_port_new(n
, d
, sizeof(pa_alsa_port_data
));
3817 port
->priority
= path
->priority
* 100 + s
->priority
;
3822 data
= PA_DEVICE_PORT_DATA(port
);
3826 pa_hashmap_put(*p
, port
->name
, port
);
3832 pa_log_debug("Added %u ports", pa_hashmap_size(*p
));