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
);
808 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
809 /* If we call set_play_volume() without checking first
810 * if the channel is available, ALSA behaves ver
811 * strangely and doesn't fail the call */
812 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
814 if ((r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, +1)) >= 0)
815 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
818 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, value
, +1, &alsa_val
)) >= 0)
819 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value
);
824 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
826 if ((r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, +1)) >= 0)
827 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
830 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, value
, +1, &alsa_val
)) >= 0)
831 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value
);
840 #ifdef HAVE_VALGRIND_MEMCHECK_H
841 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
844 f
= from_alsa_dB(value
);
849 value
= to_alsa_volume(f
, e
->min_volume
, e
->max_volume
);
851 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
852 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
853 if ((r
= snd_mixer_selem_set_playback_volume(me
, c
, value
)) >= 0)
854 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
858 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
859 if ((r
= snd_mixer_selem_set_capture_volume(me
, c
, value
)) >= 0)
860 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
868 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
871 for (k
= 0; k
< cm
->channels
; k
++)
872 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
873 if (rv
.values
[k
] < f
)
876 mask
|= e
->masks
[c
][e
->n_channels
-1];
879 for (k
= 0; k
< cm
->channels
; k
++)
880 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
881 rv
.values
[k
] = PA_VOLUME_NORM
;
887 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
) {
896 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
901 rv
= *v
; /* Remaining adjustment */
902 pa_cvolume_reset(v
, cm
->channels
); /* Adjustment done */
904 PA_LLIST_FOREACH(e
, p
->elements
) {
907 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
910 pa_assert(!p
->has_dB
|| e
->has_dB
);
913 if (element_set_volume(e
, m
, cm
, &ev
, write_to_hw
) < 0)
921 pa_sw_cvolume_multiply(v
, v
, &ev
);
922 pa_sw_cvolume_divide(&rv
, &rv
, &ev
);
928 static int element_set_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t b
) {
929 snd_mixer_elem_t
*me
;
930 snd_mixer_selem_id_t
*sid
;
936 SELEM_INIT(sid
, e
->alsa_name
);
937 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
938 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
942 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
943 r
= snd_mixer_selem_set_playback_switch_all(me
, b
);
945 r
= snd_mixer_selem_set_capture_switch_all(me
, b
);
948 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
953 int pa_alsa_path_set_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t muted
) {
962 PA_LLIST_FOREACH(e
, p
->elements
) {
964 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
967 if (element_set_switch(e
, m
, !muted
) < 0)
974 static int element_mute_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
975 snd_mixer_elem_t
*me
;
976 snd_mixer_selem_id_t
*sid
;
982 SELEM_INIT(sid
, e
->alsa_name
);
983 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
984 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
988 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
989 r
= snd_mixer_selem_set_playback_volume_all(me
, e
->min_volume
);
991 r
= snd_mixer_selem_set_capture_volume_all(me
, e
->min_volume
);
994 pa_log_warn("Failed to set volume to muted of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
999 /* The volume to 0dB */
1000 static int element_zero_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1001 snd_mixer_elem_t
*me
;
1002 snd_mixer_selem_id_t
*sid
;
1008 SELEM_INIT(sid
, e
->alsa_name
);
1009 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1010 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1014 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1015 r
= snd_mixer_selem_set_playback_dB_all(me
, 0, +1);
1017 r
= snd_mixer_selem_set_capture_dB_all(me
, 0, +1);
1020 pa_log_warn("Failed to set volume to 0dB of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1025 int pa_alsa_path_select(pa_alsa_path
*p
, snd_mixer_t
*m
) {
1032 pa_log_debug("Activating path %s", p
->name
);
1033 pa_alsa_path_dump(p
);
1035 PA_LLIST_FOREACH(e
, p
->elements
) {
1037 switch (e
->switch_use
) {
1038 case PA_ALSA_SWITCH_OFF
:
1039 r
= element_set_switch(e
, m
, FALSE
);
1042 case PA_ALSA_SWITCH_ON
:
1043 r
= element_set_switch(e
, m
, TRUE
);
1046 case PA_ALSA_SWITCH_MUTE
:
1047 case PA_ALSA_SWITCH_IGNORE
:
1048 case PA_ALSA_SWITCH_SELECT
:
1056 switch (e
->volume_use
) {
1057 case PA_ALSA_VOLUME_OFF
:
1058 r
= element_mute_volume(e
, m
);
1061 case PA_ALSA_VOLUME_ZERO
:
1062 r
= element_zero_volume(e
, m
);
1065 case PA_ALSA_VOLUME_MERGE
:
1066 case PA_ALSA_VOLUME_IGNORE
:
1078 static int check_required(pa_alsa_element
*e
, snd_mixer_elem_t
*me
) {
1079 pa_bool_t has_switch
;
1080 pa_bool_t has_enumeration
;
1081 pa_bool_t has_volume
;
1086 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1088 snd_mixer_selem_has_playback_switch(me
) ||
1089 (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
));
1092 snd_mixer_selem_has_capture_switch(me
) ||
1093 (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
));
1096 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1098 snd_mixer_selem_has_playback_volume(me
) ||
1099 (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
));
1102 snd_mixer_selem_has_capture_volume(me
) ||
1103 (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
));
1106 has_enumeration
= snd_mixer_selem_is_enumerated(me
);
1108 if ((e
->required
== PA_ALSA_REQUIRED_SWITCH
&& !has_switch
) ||
1109 (e
->required
== PA_ALSA_REQUIRED_VOLUME
&& !has_volume
) ||
1110 (e
->required
== PA_ALSA_REQUIRED_ENUMERATION
&& !has_enumeration
))
1113 if (e
->required
== PA_ALSA_REQUIRED_ANY
&& !(has_switch
|| has_volume
|| has_enumeration
))
1116 if ((e
->required_absent
== PA_ALSA_REQUIRED_SWITCH
&& has_switch
) ||
1117 (e
->required_absent
== PA_ALSA_REQUIRED_VOLUME
&& has_volume
) ||
1118 (e
->required_absent
== PA_ALSA_REQUIRED_ENUMERATION
&& has_enumeration
))
1121 if (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& (has_switch
|| has_volume
|| has_enumeration
))
1124 if (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) {
1125 switch (e
->required_any
) {
1126 case PA_ALSA_REQUIRED_VOLUME
:
1127 e
->path
->req_any_present
|= (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
);
1129 case PA_ALSA_REQUIRED_SWITCH
:
1130 e
->path
->req_any_present
|= (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
);
1132 case PA_ALSA_REQUIRED_ENUMERATION
:
1133 e
->path
->req_any_present
|= (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1135 case PA_ALSA_REQUIRED_ANY
:
1136 e
->path
->req_any_present
|=
1137 (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) ||
1138 (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) ||
1139 (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1144 if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1146 PA_LLIST_FOREACH(o
, e
->options
) {
1147 e
->path
->req_any_present
|= (o
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) &&
1149 if (o
->required
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
< 0)
1151 if (o
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
>= 0)
1156 if (check_required(e
, me
) < 0)
1162 static int element_probe(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1163 snd_mixer_selem_id_t
*sid
;
1164 snd_mixer_elem_t
*me
;
1169 SELEM_INIT(sid
, e
->alsa_name
);
1171 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1173 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1176 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1177 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1178 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1183 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1184 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1186 if (!snd_mixer_selem_has_playback_switch(me
)) {
1187 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1188 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1190 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1195 if (!snd_mixer_selem_has_capture_switch(me
)) {
1196 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1197 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1199 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1203 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1204 e
->direction_try_other
= FALSE
;
1207 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1209 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1211 if (!snd_mixer_selem_has_playback_volume(me
)) {
1212 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1213 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1215 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1220 if (!snd_mixer_selem_has_capture_volume(me
)) {
1221 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1222 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1224 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1228 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1229 long min_dB
= 0, max_dB
= 0;
1232 e
->direction_try_other
= FALSE
;
1234 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1235 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1237 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1240 #ifdef HAVE_VALGRIND_MEMCHECK_H
1241 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1242 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1245 e
->min_dB
= ((double) min_dB
) / 100.0;
1246 e
->max_dB
= ((double) max_dB
) / 100.0;
1248 if (min_dB
>= max_dB
) {
1249 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
);
1254 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1255 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1257 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1260 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1265 if (e
->min_volume
>= e
->max_volume
) {
1266 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
);
1267 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1271 pa_channel_position_t p
;
1273 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1274 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1276 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1281 if (!e
->override_map
) {
1282 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++)
1283 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1284 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1287 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1290 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1292 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1295 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1296 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1298 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1301 if (e
->n_channels
<= 0) {
1302 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1306 if (!e
->override_map
) {
1307 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1308 pa_bool_t has_channel
;
1310 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1313 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1314 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1316 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1318 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1323 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++)
1324 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1331 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1334 PA_LLIST_FOREACH(o
, e
->options
)
1335 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1336 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1340 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1341 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1345 PA_LLIST_FOREACH(o
, e
->options
) {
1348 for (i
= 0; i
< n
; i
++) {
1351 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1354 if (!pa_streq(buf
, o
->alsa_name
))
1365 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, pa_bool_t prefixed
) {
1372 if (!pa_startswith(section
, "Element "))
1378 /* This is not an element section, but an enum section? */
1379 if (strchr(section
, ':'))
1382 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1383 return p
->last_element
;
1385 PA_LLIST_FOREACH(e
, p
->elements
)
1386 if (pa_streq(e
->alsa_name
, section
))
1389 e
= pa_xnew0(pa_alsa_element
, 1);
1391 e
->alsa_name
= pa_xstrdup(section
);
1392 e
->direction
= p
->direction
;
1394 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1397 p
->last_element
= e
;
1401 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1407 if (!pa_startswith(section
, "Option "))
1412 /* This is not an enum section, but an element section? */
1413 if (!(on
= strchr(section
, ':')))
1416 en
= pa_xstrndup(section
, on
- section
);
1419 if (p
->last_option
&&
1420 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1421 pa_streq(p
->last_option
->alsa_name
, on
)) {
1423 return p
->last_option
;
1426 pa_assert_se(e
= element_get(p
, en
, FALSE
));
1429 PA_LLIST_FOREACH(o
, e
->options
)
1430 if (pa_streq(o
->alsa_name
, on
))
1433 o
= pa_xnew0(pa_alsa_option
, 1);
1435 o
->alsa_name
= pa_xstrdup(on
);
1438 if (p
->last_option
&& p
->last_option
->element
== e
)
1439 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1441 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1448 static int element_parse_switch(
1449 const char *filename
,
1451 const char *section
,
1457 pa_alsa_path
*p
= userdata
;
1462 if (!(e
= element_get(p
, section
, TRUE
))) {
1463 pa_log("[%s:%u] Switch makes no sense in '%s'", filename
, line
, section
);
1467 if (pa_streq(rvalue
, "ignore"))
1468 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1469 else if (pa_streq(rvalue
, "mute"))
1470 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1471 else if (pa_streq(rvalue
, "off"))
1472 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1473 else if (pa_streq(rvalue
, "on"))
1474 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1475 else if (pa_streq(rvalue
, "select"))
1476 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1478 pa_log("[%s:%u] Switch invalid of '%s'", filename
, line
, section
);
1485 static int element_parse_volume(
1486 const char *filename
,
1488 const char *section
,
1494 pa_alsa_path
*p
= userdata
;
1499 if (!(e
= element_get(p
, section
, TRUE
))) {
1500 pa_log("[%s:%u] Volume makes no sense in '%s'", filename
, line
, section
);
1504 if (pa_streq(rvalue
, "ignore"))
1505 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1506 else if (pa_streq(rvalue
, "merge"))
1507 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1508 else if (pa_streq(rvalue
, "off"))
1509 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1510 else if (pa_streq(rvalue
, "zero"))
1511 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1513 pa_log("[%s:%u] Volume invalid of '%s'", filename
, line
, section
);
1520 static int element_parse_enumeration(
1521 const char *filename
,
1523 const char *section
,
1529 pa_alsa_path
*p
= userdata
;
1534 if (!(e
= element_get(p
, section
, TRUE
))) {
1535 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename
, line
, section
);
1539 if (pa_streq(rvalue
, "ignore"))
1540 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1541 else if (pa_streq(rvalue
, "select"))
1542 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1544 pa_log("[%s:%u] Enumeration invalid of '%s'", filename
, line
, section
);
1551 static int option_parse_priority(
1552 const char *filename
,
1554 const char *section
,
1560 pa_alsa_path
*p
= userdata
;
1566 if (!(o
= option_get(p
, section
))) {
1567 pa_log("[%s:%u] Priority makes no sense in '%s'", filename
, line
, section
);
1571 if (pa_atou(rvalue
, &prio
) < 0) {
1572 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
1580 static int option_parse_name(
1581 const char *filename
,
1583 const char *section
,
1589 pa_alsa_path
*p
= userdata
;
1594 if (!(o
= option_get(p
, section
))) {
1595 pa_log("[%s:%u] Name makes no sense in '%s'", filename
, line
, section
);
1600 o
->name
= pa_xstrdup(rvalue
);
1605 static int element_parse_required(
1606 const char *filename
,
1608 const char *section
,
1614 pa_alsa_path
*p
= userdata
;
1617 pa_alsa_required_t req
;
1621 e
= element_get(p
, section
, TRUE
);
1622 o
= option_get(p
, section
);
1624 pa_log("[%s:%u] Required makes no sense in '%s'", filename
, line
, section
);
1628 if (pa_streq(rvalue
, "ignore"))
1629 req
= PA_ALSA_REQUIRED_IGNORE
;
1630 else if (pa_streq(rvalue
, "switch") && e
)
1631 req
= PA_ALSA_REQUIRED_SWITCH
;
1632 else if (pa_streq(rvalue
, "volume") && e
)
1633 req
= PA_ALSA_REQUIRED_VOLUME
;
1634 else if (pa_streq(rvalue
, "enumeration"))
1635 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1636 else if (pa_streq(rvalue
, "any"))
1637 req
= PA_ALSA_REQUIRED_ANY
;
1639 pa_log("[%s:%u] Required invalid of '%s'", filename
, line
, section
);
1643 if (pa_streq(lvalue
, "required-absent")) {
1645 e
->required_absent
= req
;
1647 o
->required_absent
= req
;
1649 else if (pa_streq(lvalue
, "required-any")) {
1651 e
->required_any
= req
;
1652 e
->path
->has_req_any
= TRUE
;
1655 o
->required_any
= req
;
1656 o
->element
->path
->has_req_any
= TRUE
;
1669 static int element_parse_direction(
1670 const char *filename
,
1672 const char *section
,
1678 pa_alsa_path
*p
= userdata
;
1683 if (!(e
= element_get(p
, section
, TRUE
))) {
1684 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1688 if (pa_streq(rvalue
, "playback"))
1689 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1690 else if (pa_streq(rvalue
, "capture"))
1691 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1693 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1700 static int element_parse_direction_try_other(
1701 const char *filename
,
1703 const char *section
,
1709 pa_alsa_path
*p
= userdata
;
1713 if (!(e
= element_get(p
, section
, TRUE
))) {
1714 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1718 if ((yes
= pa_parse_boolean(rvalue
)) < 0) {
1719 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1723 e
->direction_try_other
= !!yes
;
1727 static pa_channel_position_mask_t
parse_mask(const char *m
) {
1728 pa_channel_position_mask_t v
;
1730 if (pa_streq(m
, "all-left"))
1731 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
1732 else if (pa_streq(m
, "all-right"))
1733 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
1734 else if (pa_streq(m
, "all-center"))
1735 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
1736 else if (pa_streq(m
, "all-front"))
1737 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
1738 else if (pa_streq(m
, "all-rear"))
1739 v
= PA_CHANNEL_POSITION_MASK_REAR
;
1740 else if (pa_streq(m
, "all-side"))
1741 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
1742 else if (pa_streq(m
, "all-top"))
1743 v
= PA_CHANNEL_POSITION_MASK_TOP
;
1744 else if (pa_streq(m
, "all-no-lfe"))
1745 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
1746 else if (pa_streq(m
, "all"))
1747 v
= PA_CHANNEL_POSITION_MASK_ALL
;
1749 pa_channel_position_t p
;
1751 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
1754 v
= PA_CHANNEL_POSITION_MASK(p
);
1760 static int element_parse_override_map(
1761 const char *filename
,
1763 const char *section
,
1769 pa_alsa_path
*p
= userdata
;
1771 const char *state
= NULL
;
1775 if (!(e
= element_get(p
, section
, TRUE
))) {
1776 pa_log("[%s:%u] Override map makes no sense in '%s'", filename
, line
, section
);
1780 while ((n
= pa_split(rvalue
, ",", &state
))) {
1781 pa_channel_position_mask_t m
;
1786 if ((m
= parse_mask(n
)) == 0) {
1787 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename
, line
, n
, section
);
1793 if (pa_streq(lvalue
, "override-map.1"))
1794 e
->masks
[i
++][0] = m
;
1796 e
->masks
[i
++][1] = m
;
1798 /* Later on we might add override-map.3 and so on here ... */
1803 e
->override_map
= TRUE
;
1808 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
1809 snd_mixer_selem_id_t
*sid
;
1810 snd_mixer_elem_t
*me
;
1816 SELEM_INIT(sid
, e
->alsa_name
);
1817 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1818 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1822 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1824 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1825 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
1827 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
1830 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1833 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
1835 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
1836 pa_log_warn("Failed to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1842 int pa_alsa_setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
1849 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
1850 element_set_option(o
->element
, m
, o
->alsa_idx
);
1855 static int option_verify(pa_alsa_option
*o
) {
1856 static const struct description_map well_known_descriptions
[] = {
1857 { "input", N_("Input") },
1858 { "input-docking", N_("Docking Station Input") },
1859 { "input-docking-microphone", N_("Docking Station Microphone") },
1860 { "input-docking-linein", N_("Docking Station Line-In") },
1861 { "input-linein", N_("Line-In") },
1862 { "input-microphone", N_("Microphone") },
1863 { "input-microphone-front", N_("Front Microphone") },
1864 { "input-microphone-rear", N_("Rear Microphone") },
1865 { "input-microphone-external", N_("External Microphone") },
1866 { "input-microphone-internal", N_("Internal Microphone") },
1867 { "input-radio", N_("Radio") },
1868 { "input-video", N_("Video") },
1869 { "input-agc-on", N_("Automatic Gain Control") },
1870 { "input-agc-off", N_("No Automatic Gain Control") },
1871 { "input-boost-on", N_("Boost") },
1872 { "input-boost-off", N_("No Boost") },
1873 { "output-amplifier-on", N_("Amplifier") },
1874 { "output-amplifier-off", N_("No Amplifier") },
1875 { "output-bass-boost-on", N_("Bass Boost") },
1876 { "output-bass-boost-off", N_("No Bass Boost") },
1877 { "output-speaker", N_("Speaker") },
1878 { "output-headphones", N_("Headphones") }
1884 pa_log("No name set for option %s", o
->alsa_name
);
1888 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
1889 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
1890 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
1894 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
1895 !pa_streq(o
->alsa_name
, "on") &&
1896 !pa_streq(o
->alsa_name
, "off")) {
1897 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
1901 if (!o
->description
)
1902 o
->description
= pa_xstrdup(lookup_description(o
->name
,
1903 well_known_descriptions
,
1904 PA_ELEMENTSOF(well_known_descriptions
)));
1905 if (!o
->description
)
1906 o
->description
= pa_xstrdup(o
->name
);
1911 static int element_verify(pa_alsa_element
*e
) {
1916 // 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);
1917 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
1918 (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required_any
== e
->required_absent
) ||
1919 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) ||
1920 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
1921 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
1925 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1926 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
1930 PA_LLIST_FOREACH(o
, e
->options
)
1931 if (option_verify(o
) < 0)
1937 static int path_verify(pa_alsa_path
*p
) {
1938 static const struct description_map well_known_descriptions
[] = {
1939 { "analog-input", N_("Analog Input") },
1940 { "analog-input-microphone", N_("Analog Microphone") },
1941 { "analog-input-microphone-front", N_("Front Microphone") },
1942 { "analog-input-microphone-rear", N_("Rear Microphone") },
1943 { "analog-input-microphone-dock", N_("Docking Station Microphone") },
1944 { "analog-input-microphone-internal", N_("Internal Microphone") },
1945 { "analog-input-linein", N_("Analog Line-In") },
1946 { "analog-input-radio", N_("Analog Radio") },
1947 { "analog-input-video", N_("Analog Video") },
1948 { "analog-output", N_("Analog Output") },
1949 { "analog-output-headphones", N_("Analog Headphones") },
1950 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
1951 { "analog-output-mono", N_("Analog Mono Output") },
1952 { "analog-output-speaker", N_("Analog Speakers") },
1953 { "iec958-stereo-output", N_("Digital Output (IEC958)") },
1954 { "iec958-passthrough-output", N_("Digital Passthrough (IEC958)") }
1961 PA_LLIST_FOREACH(e
, p
->elements
)
1962 if (element_verify(e
) < 0)
1965 if (!p
->description
)
1966 p
->description
= pa_xstrdup(lookup_description(p
->name
,
1967 well_known_descriptions
,
1968 PA_ELEMENTSOF(well_known_descriptions
)));
1970 if (!p
->description
)
1971 p
->description
= pa_xstrdup(p
->name
);
1976 pa_alsa_path
* pa_alsa_path_new(const char *fname
, pa_alsa_direction_t direction
) {
1982 pa_config_item items
[] = {
1984 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
1985 { "description", pa_config_parse_string
, NULL
, "General" },
1986 { "name", pa_config_parse_string
, NULL
, "General" },
1989 { "priority", option_parse_priority
, NULL
, NULL
},
1990 { "name", option_parse_name
, NULL
, NULL
},
1993 { "switch", element_parse_switch
, NULL
, NULL
},
1994 { "volume", element_parse_volume
, NULL
, NULL
},
1995 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
1996 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
1997 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
1998 /* ... later on we might add override-map.3 and so on here ... */
1999 { "required", element_parse_required
, NULL
, NULL
},
2000 { "required-any", element_parse_required
, NULL
, NULL
},
2001 { "required-absent", element_parse_required
, NULL
, NULL
},
2002 { "direction", element_parse_direction
, NULL
, NULL
},
2003 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2004 { NULL
, NULL
, NULL
, NULL
}
2009 p
= pa_xnew0(pa_alsa_path
, 1);
2010 n
= pa_path_get_filename(fname
);
2011 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2012 p
->direction
= direction
;
2014 items
[0].data
= &p
->priority
;
2015 items
[1].data
= &p
->description
;
2016 items
[2].data
= &p
->name
;
2018 fn
= pa_maybe_prefix_path(fname
,
2019 #if defined(__linux__) && !defined(__OPTIMIZE__)
2020 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/paths/" :
2024 r
= pa_config_parse(fn
, NULL
, items
, p
);
2030 if (path_verify(p
) < 0)
2036 pa_alsa_path_free(p
);
2040 pa_alsa_path
* pa_alsa_path_synthesize(const char*element
, pa_alsa_direction_t direction
) {
2046 p
= pa_xnew0(pa_alsa_path
, 1);
2047 p
->name
= pa_xstrdup(element
);
2048 p
->direction
= direction
;
2050 e
= pa_xnew0(pa_alsa_element
, 1);
2052 e
->alsa_name
= pa_xstrdup(element
);
2053 e
->direction
= direction
;
2055 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2056 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2058 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2059 p
->last_element
= e
;
2063 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2064 pa_alsa_option
*o
, *n
;
2068 for (o
= e
->options
; o
; o
= n
) {
2071 if (o
->alsa_idx
< 0) {
2072 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2078 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2079 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2080 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2083 static void path_drop_unsupported(pa_alsa_path
*p
) {
2084 pa_alsa_element
*e
, *n
;
2088 for (e
= p
->elements
; e
; e
= n
) {
2091 if (!element_drop_unsupported(e
)) {
2092 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2098 static void path_make_options_unique(pa_alsa_path
*p
) {
2100 pa_alsa_option
*o
, *u
;
2102 PA_LLIST_FOREACH(e
, p
->elements
) {
2103 PA_LLIST_FOREACH(o
, e
->options
) {
2107 for (u
= o
->next
; u
; u
= u
->next
)
2108 if (pa_streq(u
->name
, o
->name
))
2114 m
= pa_xstrdup(o
->name
);
2116 /* OK, this name is not unique, hence let's rename */
2117 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2120 if (!pa_streq(u
->name
, m
))
2123 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2127 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2128 pa_xfree(u
->description
);
2129 u
->description
= nd
;
2139 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2142 for (; e
; e
= e
->next
)
2143 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2144 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2150 for (o
= e
->options
; o
; o
= o
->next
) {
2154 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2155 s
->options
= pa_idxset_copy(template->options
);
2156 s
->name
= pa_sprintf_malloc(_("%s+%s"), template->name
, o
->name
);
2158 (template->description
[0] && o
->description
[0])
2159 ? pa_sprintf_malloc(_("%s / %s"), template->description
, o
->description
)
2160 : (template->description
[0]
2161 ? pa_xstrdup(template->description
)
2162 : pa_xstrdup(o
->description
));
2164 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2166 s
= pa_xnew0(pa_alsa_setting
, 1);
2167 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2168 s
->name
= pa_xstrdup(o
->name
);
2169 s
->description
= pa_xstrdup(o
->description
);
2170 s
->priority
= o
->priority
;
2173 pa_idxset_put(s
->options
, o
, NULL
);
2175 if (element_create_settings(e
->next
, s
))
2176 /* This is not a leaf, so let's get rid of it */
2179 /* This is a leaf, so let's add it */
2180 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2182 e
->path
->last_setting
= s
;
2189 static void path_create_settings(pa_alsa_path
*p
) {
2192 element_create_settings(p
->elements
, NULL
);
2195 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2197 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2198 pa_channel_position_t t
;
2209 pa_log_debug("Probing path '%s'", p
->name
);
2211 PA_LLIST_FOREACH(e
, p
->elements
) {
2212 if (element_probe(e
, m
) < 0) {
2213 p
->supported
= FALSE
;
2214 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2221 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2223 if (!p
->has_volume
) {
2224 p
->min_volume
= e
->min_volume
;
2225 p
->max_volume
= e
->max_volume
;
2229 if (!p
->has_volume
) {
2230 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2231 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2232 min_dB
[t
] = e
->min_dB
;
2233 max_dB
[t
] = e
->max_dB
;
2240 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2241 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2242 min_dB
[t
] += e
->min_dB
;
2243 max_dB
[t
] += e
->max_dB
;
2246 /* Hmm, there's another element before us
2247 * which cannot do dB volumes, so we we need
2248 * to 'neutralize' this slider */
2249 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2250 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2253 } else if (p
->has_volume
)
2254 /* We can't use this volume, so let's ignore it */
2255 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2257 p
->has_volume
= TRUE
;
2260 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2264 if (p
->has_req_any
&& !p
->req_any_present
) {
2265 p
->supported
= FALSE
;
2266 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2270 path_drop_unsupported(p
);
2271 path_make_options_unique(p
);
2272 path_create_settings(p
);
2274 p
->supported
= TRUE
;
2277 p
->min_dB
= INFINITY
;
2278 p
->max_dB
= -INFINITY
;
2280 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2281 if (p
->min_dB
> min_dB
[t
])
2282 p
->min_dB
= min_dB
[t
];
2284 if (p
->max_dB
< max_dB
[t
])
2285 p
->max_dB
= max_dB
[t
];
2291 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2294 pa_log_debug("Setting %s (%s) priority=%u",
2296 pa_strnull(s
->description
),
2300 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2303 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2305 pa_strnull(o
->name
),
2306 pa_strnull(o
->description
),
2311 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2315 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",
2324 (long long unsigned) e
->merged_mask
,
2326 pa_yes_no(e
->override_map
));
2328 PA_LLIST_FOREACH(o
, e
->options
)
2329 pa_alsa_option_dump(o
);
2332 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2337 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2338 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2340 pa_strnull(p
->description
),
2343 pa_yes_no(p
->probed
),
2344 pa_yes_no(p
->supported
),
2345 pa_yes_no(p
->has_mute
),
2346 pa_yes_no(p
->has_volume
),
2347 pa_yes_no(p
->has_dB
),
2348 p
->min_volume
, p
->max_volume
,
2349 p
->min_dB
, p
->max_dB
);
2351 PA_LLIST_FOREACH(e
, p
->elements
)
2352 pa_alsa_element_dump(e
);
2354 PA_LLIST_FOREACH(s
, p
->settings
)
2355 pa_alsa_setting_dump(s
);
2358 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2359 snd_mixer_selem_id_t
*sid
;
2360 snd_mixer_elem_t
*me
;
2366 SELEM_INIT(sid
, e
->alsa_name
);
2367 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2368 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2372 snd_mixer_elem_set_callback(me
, cb
);
2373 snd_mixer_elem_set_callback_private(me
, userdata
);
2376 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2383 PA_LLIST_FOREACH(e
, p
->elements
)
2384 element_set_callback(e
, m
, cb
, userdata
);
2387 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2394 PA_LLIST_FOREACH(p
, ps
->paths
)
2395 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2398 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
) {
2399 pa_alsa_path_set
*ps
;
2400 char **pn
= NULL
, **en
= NULL
, **ie
;
2403 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2405 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2408 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2409 ps
->direction
= direction
;
2411 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2412 pn
= m
->output_path_names
;
2413 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2414 pn
= m
->input_path_names
;
2419 for (in
= pn
; *in
; in
++) {
2421 pa_bool_t duplicate
= FALSE
;
2424 for (kn
= pn
; kn
!= in
; kn
++)
2425 if (pa_streq(*kn
, *in
)) {
2433 fn
= pa_sprintf_malloc("%s.conf", *in
);
2435 if ((p
= pa_alsa_path_new(fn
, direction
))) {
2437 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2447 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2448 en
= m
->output_element
;
2449 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2450 en
= m
->input_element
;
2453 pa_alsa_path_set_free(ps
);
2457 for (ie
= en
; *ie
; ie
++) {
2461 p
= pa_alsa_path_synthesize(*ie
, direction
);
2464 /* Mark all other passed elements for require-absent */
2465 for (je
= en
; *je
; je
++) {
2471 e
= pa_xnew0(pa_alsa_element
, 1);
2473 e
->alsa_name
= pa_xstrdup(*je
);
2474 e
->direction
= direction
;
2475 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2477 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2478 p
->last_element
= e
;
2481 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2488 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2492 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2495 pa_yes_no(ps
->probed
));
2497 PA_LLIST_FOREACH(p
, ps
->paths
)
2498 pa_alsa_path_dump(p
);
2501 static void path_set_unify(pa_alsa_path_set
*ps
) {
2503 pa_bool_t has_dB
= TRUE
, has_volume
= TRUE
, has_mute
= TRUE
;
2506 /* We have issues dealing with paths that vary too wildly. That
2507 * means for now we have to have all paths support volume/mute/dB
2510 PA_LLIST_FOREACH(p
, ps
->paths
) {
2511 pa_assert(p
->probed
);
2515 else if (!p
->has_dB
)
2522 if (!has_volume
|| !has_dB
|| !has_mute
) {
2525 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2527 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2530 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2532 PA_LLIST_FOREACH(p
, ps
->paths
) {
2534 p
->has_volume
= FALSE
;
2539 p
->has_mute
= FALSE
;
2544 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
2545 pa_alsa_path
*p
, *q
;
2547 PA_LLIST_FOREACH(p
, ps
->paths
) {
2551 for (q
= p
->next
; q
; q
= q
->next
)
2552 if (pa_streq(q
->name
, p
->name
))
2558 m
= pa_xstrdup(p
->name
);
2560 /* OK, this name is not unique, hence let's rename */
2561 for (i
= 1, q
= p
; q
; q
= q
->next
) {
2564 if (!pa_streq(q
->name
, m
))
2567 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2571 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
2572 pa_xfree(q
->description
);
2573 q
->description
= nd
;
2582 void pa_alsa_path_set_probe(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2583 pa_alsa_path
*p
, *n
;
2590 for (p
= ps
->paths
; p
; p
= n
) {
2593 if (pa_alsa_path_probe(p
, m
, ignore_dB
) < 0) {
2594 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
2595 pa_alsa_path_free(p
);
2600 path_set_make_paths_unique(ps
);
2604 static void mapping_free(pa_alsa_mapping
*m
) {
2608 pa_xfree(m
->description
);
2610 pa_xstrfreev(m
->device_strings
);
2611 pa_xstrfreev(m
->input_path_names
);
2612 pa_xstrfreev(m
->output_path_names
);
2613 pa_xstrfreev(m
->input_element
);
2614 pa_xstrfreev(m
->output_element
);
2616 pa_assert(!m
->input_pcm
);
2617 pa_assert(!m
->output_pcm
);
2622 static void profile_free(pa_alsa_profile
*p
) {
2626 pa_xfree(p
->description
);
2628 pa_xstrfreev(p
->input_mapping_names
);
2629 pa_xstrfreev(p
->output_mapping_names
);
2631 if (p
->input_mappings
)
2632 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
2634 if (p
->output_mappings
)
2635 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
2640 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
2646 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
2649 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
2655 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
2658 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
2664 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
2667 if (!pa_startswith(name
, "Mapping "))
2672 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
2675 m
= pa_xnew0(pa_alsa_mapping
, 1);
2676 m
->profile_set
= ps
;
2677 m
->name
= pa_xstrdup(name
);
2678 pa_channel_map_init(&m
->channel_map
);
2680 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
2685 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
2688 if (!pa_startswith(name
, "Profile "))
2693 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
2696 p
= pa_xnew0(pa_alsa_profile
, 1);
2697 p
->profile_set
= ps
;
2698 p
->name
= pa_xstrdup(name
);
2700 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
2705 static int mapping_parse_device_strings(
2706 const char *filename
,
2708 const char *section
,
2714 pa_alsa_profile_set
*ps
= userdata
;
2719 if (!(m
= mapping_get(ps
, section
))) {
2720 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2724 pa_xstrfreev(m
->device_strings
);
2725 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
2726 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
2733 static int mapping_parse_channel_map(
2734 const char *filename
,
2736 const char *section
,
2742 pa_alsa_profile_set
*ps
= userdata
;
2747 if (!(m
= mapping_get(ps
, section
))) {
2748 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2752 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
2753 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
2760 static int mapping_parse_paths(
2761 const char *filename
,
2763 const char *section
,
2769 pa_alsa_profile_set
*ps
= userdata
;
2774 if (!(m
= mapping_get(ps
, section
))) {
2775 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2779 if (pa_streq(lvalue
, "paths-input")) {
2780 pa_xstrfreev(m
->input_path_names
);
2781 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
2783 pa_xstrfreev(m
->output_path_names
);
2784 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
2790 static int mapping_parse_element(
2791 const char *filename
,
2793 const char *section
,
2799 pa_alsa_profile_set
*ps
= userdata
;
2804 if (!(m
= mapping_get(ps
, section
))) {
2805 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2809 if (pa_streq(lvalue
, "element-input")) {
2810 pa_xstrfreev(m
->input_element
);
2811 m
->input_element
= pa_split_spaces_strv(rvalue
);
2813 pa_xstrfreev(m
->output_element
);
2814 m
->output_element
= pa_split_spaces_strv(rvalue
);
2820 static int mapping_parse_direction(
2821 const char *filename
,
2823 const char *section
,
2829 pa_alsa_profile_set
*ps
= userdata
;
2834 if (!(m
= mapping_get(ps
, section
))) {
2835 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
2839 if (pa_streq(rvalue
, "input"))
2840 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
2841 else if (pa_streq(rvalue
, "output"))
2842 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
2843 else if (pa_streq(rvalue
, "any"))
2844 m
->direction
= PA_ALSA_DIRECTION_ANY
;
2846 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
2853 static int mapping_parse_description(
2854 const char *filename
,
2856 const char *section
,
2862 pa_alsa_profile_set
*ps
= userdata
;
2868 if ((m
= mapping_get(ps
, section
))) {
2869 pa_xfree(m
->description
);
2870 m
->description
= pa_xstrdup(rvalue
);
2871 } else if ((p
= profile_get(ps
, section
))) {
2872 pa_xfree(p
->description
);
2873 p
->description
= pa_xstrdup(rvalue
);
2875 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
2882 static int mapping_parse_priority(
2883 const char *filename
,
2885 const char *section
,
2891 pa_alsa_profile_set
*ps
= userdata
;
2898 if (pa_atou(rvalue
, &prio
) < 0) {
2899 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
2903 if ((m
= mapping_get(ps
, section
)))
2905 else if ((p
= profile_get(ps
, section
)))
2908 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
2915 static int profile_parse_mappings(
2916 const char *filename
,
2918 const char *section
,
2924 pa_alsa_profile_set
*ps
= userdata
;
2929 if (!(p
= profile_get(ps
, section
))) {
2930 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2934 if (pa_streq(lvalue
, "input-mappings")) {
2935 pa_xstrfreev(p
->input_mapping_names
);
2936 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
2938 pa_xstrfreev(p
->output_mapping_names
);
2939 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
2945 static int profile_parse_skip_probe(
2946 const char *filename
,
2948 const char *section
,
2954 pa_alsa_profile_set
*ps
= userdata
;
2960 if (!(p
= profile_get(ps
, section
))) {
2961 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2965 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
2966 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
2975 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
2977 static const struct description_map well_known_descriptions
[] = {
2978 { "analog-mono", N_("Analog Mono") },
2979 { "analog-stereo", N_("Analog Stereo") },
2980 { "analog-surround-21", N_("Analog Surround 2.1") },
2981 { "analog-surround-30", N_("Analog Surround 3.0") },
2982 { "analog-surround-31", N_("Analog Surround 3.1") },
2983 { "analog-surround-40", N_("Analog Surround 4.0") },
2984 { "analog-surround-41", N_("Analog Surround 4.1") },
2985 { "analog-surround-50", N_("Analog Surround 5.0") },
2986 { "analog-surround-51", N_("Analog Surround 5.1") },
2987 { "analog-surround-61", N_("Analog Surround 6.0") },
2988 { "analog-surround-61", N_("Analog Surround 6.1") },
2989 { "analog-surround-70", N_("Analog Surround 7.0") },
2990 { "analog-surround-71", N_("Analog Surround 7.1") },
2991 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
2992 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
2993 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
2994 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
2995 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3000 if (!pa_channel_map_valid(&m
->channel_map
)) {
3001 pa_log("Mapping %s is missing channel map.", m
->name
);
3005 if (!m
->device_strings
) {
3006 pa_log("Mapping %s is missing device strings.", m
->name
);
3010 if ((m
->input_path_names
&& m
->input_element
) ||
3011 (m
->output_path_names
&& m
->output_element
)) {
3012 pa_log("Mapping %s must have either mixer path or mixer elment, not both.", m
->name
);
3016 if (!m
->description
)
3017 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3018 well_known_descriptions
,
3019 PA_ELEMENTSOF(well_known_descriptions
)));
3021 if (!m
->description
)
3022 m
->description
= pa_xstrdup(m
->name
);
3025 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3027 else if (m
->channel_map
.channels
== bonus
->channels
)
3034 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3035 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3039 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3041 pa_strnull(m
->description
),
3043 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3044 pa_yes_no(m
->supported
),
3048 static void profile_set_add_auto_pair(
3049 pa_alsa_profile_set
*ps
,
3050 pa_alsa_mapping
*m
, /* output */
3051 pa_alsa_mapping
*n
/* input */) {
3059 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3062 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3066 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3068 name
= pa_sprintf_malloc("output:%s", m
->name
);
3070 name
= pa_sprintf_malloc("input:%s", n
->name
);
3072 if (pa_hashmap_get(ps
->profiles
, name
)) {
3077 p
= pa_xnew0(pa_alsa_profile
, 1);
3078 p
->profile_set
= ps
;
3082 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3083 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3084 p
->priority
+= m
->priority
* 100;
3088 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3089 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3090 p
->priority
+= n
->priority
;
3093 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3096 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3097 pa_alsa_mapping
*m
, *n
;
3098 void *m_state
, *n_state
;
3102 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3103 profile_set_add_auto_pair(ps
, m
, NULL
);
3105 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3106 profile_set_add_auto_pair(ps
, m
, n
);
3109 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3110 profile_set_add_auto_pair(ps
, NULL
, n
);
3113 static int profile_verify(pa_alsa_profile
*p
) {
3115 static const struct description_map well_known_descriptions
[] = {
3116 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3117 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3118 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3119 { "off", N_("Off") }
3124 /* Replace the output mapping names by the actual mappings */
3125 if (p
->output_mapping_names
) {
3128 pa_assert(!p
->output_mappings
);
3129 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3131 for (name
= p
->output_mapping_names
; *name
; name
++) {
3134 pa_bool_t duplicate
= FALSE
;
3136 for (in
= name
+ 1; *in
; in
++)
3137 if (pa_streq(*name
, *in
)) {
3145 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3146 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3150 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3156 pa_xstrfreev(p
->output_mapping_names
);
3157 p
->output_mapping_names
= NULL
;
3160 /* Replace the input mapping names by the actual mappings */
3161 if (p
->input_mapping_names
) {
3164 pa_assert(!p
->input_mappings
);
3165 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3167 for (name
= p
->input_mapping_names
; *name
; name
++) {
3170 pa_bool_t duplicate
= FALSE
;
3172 for (in
= name
+ 1; *in
; in
++)
3173 if (pa_streq(*name
, *in
)) {
3181 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3182 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3186 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3192 pa_xstrfreev(p
->input_mapping_names
);
3193 p
->input_mapping_names
= NULL
;
3196 if (!p
->input_mappings
&& !p
->output_mappings
) {
3197 pa_log("Profile '%s' lacks mappings.", p
->name
);
3201 if (!p
->description
)
3202 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3203 well_known_descriptions
,
3204 PA_ELEMENTSOF(well_known_descriptions
)));
3206 if (!p
->description
) {
3211 sb
= pa_strbuf_new();
3213 if (p
->output_mappings
)
3214 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3215 if (!pa_strbuf_isempty(sb
))
3216 pa_strbuf_puts(sb
, " + ");
3218 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
3221 if (p
->input_mappings
)
3222 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3223 if (!pa_strbuf_isempty(sb
))
3224 pa_strbuf_puts(sb
, " + ");
3226 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
3229 p
->description
= pa_strbuf_tostring_free(sb
);
3235 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
3240 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3242 pa_strnull(p
->description
),
3244 pa_yes_no(p
->supported
),
3245 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
3246 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
3248 if (p
->input_mappings
)
3249 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
3250 pa_log_debug("Input %s", m
->name
);
3252 if (p
->output_mappings
)
3253 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
3254 pa_log_debug("Output %s", m
->name
);
3257 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
3258 pa_alsa_profile_set
*ps
;
3265 static pa_config_item items
[] = {
3267 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
3270 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
3271 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
3272 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
3273 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
3274 { "element-input", mapping_parse_element
, NULL
, NULL
},
3275 { "element-output", mapping_parse_element
, NULL
, NULL
},
3276 { "direction", mapping_parse_direction
, NULL
, NULL
},
3278 /* Shared by [Mapping ...] and [Profile ...] */
3279 { "description", mapping_parse_description
, NULL
, NULL
},
3280 { "priority", mapping_parse_priority
, NULL
, NULL
},
3283 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
3284 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
3285 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
3286 { NULL
, NULL
, NULL
, NULL
}
3289 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
3290 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3291 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3293 items
[0].data
= &ps
->auto_profiles
;
3296 fname
= "default.conf";
3298 fn
= pa_maybe_prefix_path(fname
,
3299 #if defined(__linux__) && !defined(__OPTIMIZE__)
3300 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
3302 PA_ALSA_PROFILE_SETS_DIR
);
3304 r
= pa_config_parse(fn
, NULL
, items
, ps
);
3310 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3311 if (mapping_verify(m
, bonus
) < 0)
3314 if (ps
->auto_profiles
)
3315 profile_set_add_auto(ps
);
3317 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3318 if (profile_verify(p
) < 0)
3324 pa_alsa_profile_set_free(ps
);
3328 void pa_alsa_profile_set_probe(
3329 pa_alsa_profile_set
*ps
,
3331 const pa_sample_spec
*ss
,
3332 unsigned default_n_fragments
,
3333 unsigned default_fragment_size_msec
) {
3336 pa_alsa_profile
*p
, *last
= NULL
;
3346 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
3347 pa_sample_spec try_ss
;
3348 pa_channel_map try_map
;
3349 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
3352 /* Is this already marked that it is supported? (i.e. from the config file) */
3356 pa_log_debug("Looking at profile %s", p
->name
);
3358 /* Close PCMs from the last iteration we don't need anymore */
3359 if (last
&& last
->output_mappings
)
3360 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
) {
3365 if (last
->supported
)
3368 if (!p
->output_mappings
|| !pa_idxset_get_by_data(p
->output_mappings
, m
, NULL
)) {
3369 snd_pcm_close(m
->output_pcm
);
3370 m
->output_pcm
= NULL
;
3374 if (last
&& last
->input_mappings
)
3375 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
) {
3380 if (last
->supported
)
3383 if (!p
->input_mappings
|| !pa_idxset_get_by_data(p
->input_mappings
, m
, NULL
)) {
3384 snd_pcm_close(m
->input_pcm
);
3385 m
->input_pcm
= NULL
;
3389 p
->supported
= TRUE
;
3391 /* Check if we can open all new ones */
3392 if (p
->output_mappings
)
3393 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3398 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
3399 try_map
= m
->channel_map
;
3401 try_ss
.channels
= try_map
.channels
;
3404 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
3405 pa_frame_size(&try_ss
);
3406 try_buffer_size
= default_n_fragments
* try_period_size
;
3408 if (!(m
->output_pcm
= pa_alsa_open_by_template(
3413 SND_PCM_STREAM_PLAYBACK
,
3414 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3416 p
->supported
= FALSE
;
3421 if (p
->input_mappings
&& p
->supported
)
3422 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3427 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
3428 try_map
= m
->channel_map
;
3430 try_ss
.channels
= try_map
.channels
;
3433 pa_usec_to_bytes(default_fragment_size_msec
*PA_USEC_PER_MSEC
, &try_ss
) /
3434 pa_frame_size(&try_ss
);
3435 try_buffer_size
= default_n_fragments
* try_period_size
;
3437 if (!(m
->input_pcm
= pa_alsa_open_by_template(
3442 SND_PCM_STREAM_CAPTURE
,
3443 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3445 p
->supported
= FALSE
;
3453 pa_log_debug("Profile %s supported.", p
->name
);
3460 if (last
->output_mappings
)
3461 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
)
3462 if (m
->output_pcm
) {
3464 if (last
->supported
)
3467 snd_pcm_close(m
->output_pcm
);
3468 m
->output_pcm
= NULL
;
3471 if (last
->input_mappings
)
3472 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
)
3475 if (last
->supported
)
3478 snd_pcm_close(m
->input_pcm
);
3479 m
->input_pcm
= NULL
;
3483 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3484 if (!p
->supported
) {
3485 pa_hashmap_remove(ps
->profiles
, p
->name
);
3489 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3490 if (m
->supported
<= 0) {
3491 pa_hashmap_remove(ps
->mappings
, m
->name
);
3498 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
3505 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u",
3508 pa_yes_no(ps
->auto_profiles
),
3509 pa_yes_no(ps
->probed
),
3510 pa_hashmap_size(ps
->mappings
),
3511 pa_hashmap_size(ps
->profiles
));
3513 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3514 pa_alsa_mapping_dump(m
);
3516 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3517 pa_alsa_profile_dump(p
);
3520 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
) {
3527 /* if there is no path, we don't want a port list */
3531 if (!ps
->paths
->next
){
3534 /* If there is only one path, but no or only one setting, then
3535 * we want a port list either */
3536 if (!ps
->paths
->settings
|| !ps
->paths
->settings
->next
)
3539 /* Ok, there is only one path, however with multiple settings,
3540 * so let's create a port for each setting */
3541 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3543 PA_LLIST_FOREACH(s
, ps
->paths
->settings
) {
3544 pa_device_port
*port
;
3545 pa_alsa_port_data
*data
;
3547 port
= pa_device_port_new(s
->name
, s
->description
, sizeof(pa_alsa_port_data
));
3548 port
->priority
= s
->priority
;
3550 data
= PA_DEVICE_PORT_DATA(port
);
3551 data
->path
= ps
->paths
;
3554 pa_hashmap_put(*p
, port
->name
, port
);
3559 /* We have multiple paths, so let's create a port for each
3560 * one, and each of each settings */
3561 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3563 PA_LLIST_FOREACH(path
, ps
->paths
) {
3565 if (!path
->settings
|| !path
->settings
->next
) {
3566 pa_device_port
*port
;
3567 pa_alsa_port_data
*data
;
3569 /* If there is no or just one setting we only need a
3572 port
= pa_device_port_new(path
->name
, path
->description
, sizeof(pa_alsa_port_data
));
3573 port
->priority
= path
->priority
* 100;
3576 data
= PA_DEVICE_PORT_DATA(port
);
3578 data
->setting
= path
->settings
;
3580 pa_hashmap_put(*p
, port
->name
, port
);
3584 PA_LLIST_FOREACH(s
, path
->settings
) {
3585 pa_device_port
*port
;
3586 pa_alsa_port_data
*data
;
3589 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
3591 if (s
->description
[0])
3592 d
= pa_sprintf_malloc(_("%s / %s"), path
->description
, s
->description
);
3594 d
= pa_xstrdup(path
->description
);
3596 port
= pa_device_port_new(n
, d
, sizeof(pa_alsa_port_data
));
3597 port
->priority
= path
->priority
* 100 + s
->priority
;
3602 data
= PA_DEVICE_PORT_DATA(port
);
3606 pa_hashmap_put(*p
, port
->name
, port
);
3612 pa_log_debug("Added %u ports", pa_hashmap_size(*p
));