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)
1157 if (check_required(e
, me
) < 0)
1163 static int element_probe(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1164 snd_mixer_selem_id_t
*sid
;
1165 snd_mixer_elem_t
*me
;
1170 SELEM_INIT(sid
, e
->alsa_name
);
1172 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1174 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1177 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1178 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1179 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1184 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1185 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1187 if (!snd_mixer_selem_has_playback_switch(me
)) {
1188 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1189 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1191 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1196 if (!snd_mixer_selem_has_capture_switch(me
)) {
1197 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1198 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1200 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1204 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1205 e
->direction_try_other
= FALSE
;
1208 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1210 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1212 if (!snd_mixer_selem_has_playback_volume(me
)) {
1213 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1214 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1216 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1221 if (!snd_mixer_selem_has_capture_volume(me
)) {
1222 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1223 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1225 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1229 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1230 long min_dB
= 0, max_dB
= 0;
1233 e
->direction_try_other
= FALSE
;
1235 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1236 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1238 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1241 #ifdef HAVE_VALGRIND_MEMCHECK_H
1242 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1243 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1246 e
->min_dB
= ((double) min_dB
) / 100.0;
1247 e
->max_dB
= ((double) max_dB
) / 100.0;
1249 if (min_dB
>= max_dB
) {
1250 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
);
1255 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1256 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1258 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1261 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1266 if (e
->min_volume
>= e
->max_volume
) {
1267 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
);
1268 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1272 pa_channel_position_t p
;
1274 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1275 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1277 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1282 if (!e
->override_map
) {
1283 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++)
1284 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1285 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1288 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1291 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1293 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1296 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1297 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1299 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1302 if (e
->n_channels
<= 0) {
1303 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1307 if (!e
->override_map
) {
1308 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1309 pa_bool_t has_channel
;
1311 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1314 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1315 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1317 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1319 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1324 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++)
1325 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1332 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1335 PA_LLIST_FOREACH(o
, e
->options
)
1336 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1337 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1341 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1342 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1346 PA_LLIST_FOREACH(o
, e
->options
) {
1349 for (i
= 0; i
< n
; i
++) {
1352 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1355 if (!pa_streq(buf
, o
->alsa_name
))
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 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
2649 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
2652 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
2658 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
2661 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
2667 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
2670 if (!pa_startswith(name
, "Mapping "))
2675 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
2678 m
= pa_xnew0(pa_alsa_mapping
, 1);
2679 m
->profile_set
= ps
;
2680 m
->name
= pa_xstrdup(name
);
2681 pa_channel_map_init(&m
->channel_map
);
2683 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
2688 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
2691 if (!pa_startswith(name
, "Profile "))
2696 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
2699 p
= pa_xnew0(pa_alsa_profile
, 1);
2700 p
->profile_set
= ps
;
2701 p
->name
= pa_xstrdup(name
);
2703 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
2708 static int mapping_parse_device_strings(
2709 const char *filename
,
2711 const char *section
,
2717 pa_alsa_profile_set
*ps
= userdata
;
2722 if (!(m
= mapping_get(ps
, section
))) {
2723 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2727 pa_xstrfreev(m
->device_strings
);
2728 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
2729 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
2736 static int mapping_parse_channel_map(
2737 const char *filename
,
2739 const char *section
,
2745 pa_alsa_profile_set
*ps
= userdata
;
2750 if (!(m
= mapping_get(ps
, section
))) {
2751 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2755 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
2756 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
2763 static int mapping_parse_paths(
2764 const char *filename
,
2766 const char *section
,
2772 pa_alsa_profile_set
*ps
= userdata
;
2777 if (!(m
= mapping_get(ps
, section
))) {
2778 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2782 if (pa_streq(lvalue
, "paths-input")) {
2783 pa_xstrfreev(m
->input_path_names
);
2784 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
2786 pa_xstrfreev(m
->output_path_names
);
2787 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
2793 static int mapping_parse_element(
2794 const char *filename
,
2796 const char *section
,
2802 pa_alsa_profile_set
*ps
= userdata
;
2807 if (!(m
= mapping_get(ps
, section
))) {
2808 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2812 if (pa_streq(lvalue
, "element-input")) {
2813 pa_xstrfreev(m
->input_element
);
2814 m
->input_element
= pa_split_spaces_strv(rvalue
);
2816 pa_xstrfreev(m
->output_element
);
2817 m
->output_element
= pa_split_spaces_strv(rvalue
);
2823 static int mapping_parse_direction(
2824 const char *filename
,
2826 const char *section
,
2832 pa_alsa_profile_set
*ps
= userdata
;
2837 if (!(m
= mapping_get(ps
, section
))) {
2838 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
2842 if (pa_streq(rvalue
, "input"))
2843 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
2844 else if (pa_streq(rvalue
, "output"))
2845 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
2846 else if (pa_streq(rvalue
, "any"))
2847 m
->direction
= PA_ALSA_DIRECTION_ANY
;
2849 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
2856 static int mapping_parse_description(
2857 const char *filename
,
2859 const char *section
,
2865 pa_alsa_profile_set
*ps
= userdata
;
2871 if ((m
= mapping_get(ps
, section
))) {
2872 pa_xfree(m
->description
);
2873 m
->description
= pa_xstrdup(rvalue
);
2874 } else if ((p
= profile_get(ps
, section
))) {
2875 pa_xfree(p
->description
);
2876 p
->description
= pa_xstrdup(rvalue
);
2878 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
2885 static int mapping_parse_priority(
2886 const char *filename
,
2888 const char *section
,
2894 pa_alsa_profile_set
*ps
= userdata
;
2901 if (pa_atou(rvalue
, &prio
) < 0) {
2902 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
2906 if ((m
= mapping_get(ps
, section
)))
2908 else if ((p
= profile_get(ps
, section
)))
2911 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
2918 static int profile_parse_mappings(
2919 const char *filename
,
2921 const char *section
,
2927 pa_alsa_profile_set
*ps
= userdata
;
2932 if (!(p
= profile_get(ps
, section
))) {
2933 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2937 if (pa_streq(lvalue
, "input-mappings")) {
2938 pa_xstrfreev(p
->input_mapping_names
);
2939 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
2941 pa_xstrfreev(p
->output_mapping_names
);
2942 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
2948 static int profile_parse_skip_probe(
2949 const char *filename
,
2951 const char *section
,
2957 pa_alsa_profile_set
*ps
= userdata
;
2963 if (!(p
= profile_get(ps
, section
))) {
2964 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2968 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
2969 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
2978 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
2980 static const struct description_map well_known_descriptions
[] = {
2981 { "analog-mono", N_("Analog Mono") },
2982 { "analog-stereo", N_("Analog Stereo") },
2983 { "analog-surround-21", N_("Analog Surround 2.1") },
2984 { "analog-surround-30", N_("Analog Surround 3.0") },
2985 { "analog-surround-31", N_("Analog Surround 3.1") },
2986 { "analog-surround-40", N_("Analog Surround 4.0") },
2987 { "analog-surround-41", N_("Analog Surround 4.1") },
2988 { "analog-surround-50", N_("Analog Surround 5.0") },
2989 { "analog-surround-51", N_("Analog Surround 5.1") },
2990 { "analog-surround-61", N_("Analog Surround 6.0") },
2991 { "analog-surround-61", N_("Analog Surround 6.1") },
2992 { "analog-surround-70", N_("Analog Surround 7.0") },
2993 { "analog-surround-71", N_("Analog Surround 7.1") },
2994 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
2995 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
2996 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
2997 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
2998 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3003 if (!pa_channel_map_valid(&m
->channel_map
)) {
3004 pa_log("Mapping %s is missing channel map.", m
->name
);
3008 if (!m
->device_strings
) {
3009 pa_log("Mapping %s is missing device strings.", m
->name
);
3013 if ((m
->input_path_names
&& m
->input_element
) ||
3014 (m
->output_path_names
&& m
->output_element
)) {
3015 pa_log("Mapping %s must have either mixer path or mixer elment, not both.", m
->name
);
3019 if (!m
->description
)
3020 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3021 well_known_descriptions
,
3022 PA_ELEMENTSOF(well_known_descriptions
)));
3024 if (!m
->description
)
3025 m
->description
= pa_xstrdup(m
->name
);
3028 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3030 else if (m
->channel_map
.channels
== bonus
->channels
)
3037 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3038 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3042 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3044 pa_strnull(m
->description
),
3046 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3047 pa_yes_no(m
->supported
),
3051 static void profile_set_add_auto_pair(
3052 pa_alsa_profile_set
*ps
,
3053 pa_alsa_mapping
*m
, /* output */
3054 pa_alsa_mapping
*n
/* input */) {
3062 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3065 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3069 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3071 name
= pa_sprintf_malloc("output:%s", m
->name
);
3073 name
= pa_sprintf_malloc("input:%s", n
->name
);
3075 if (pa_hashmap_get(ps
->profiles
, name
)) {
3080 p
= pa_xnew0(pa_alsa_profile
, 1);
3081 p
->profile_set
= ps
;
3085 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3086 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3087 p
->priority
+= m
->priority
* 100;
3091 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3092 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3093 p
->priority
+= n
->priority
;
3096 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3099 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3100 pa_alsa_mapping
*m
, *n
;
3101 void *m_state
, *n_state
;
3105 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3106 profile_set_add_auto_pair(ps
, m
, NULL
);
3108 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3109 profile_set_add_auto_pair(ps
, m
, n
);
3112 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3113 profile_set_add_auto_pair(ps
, NULL
, n
);
3116 static int profile_verify(pa_alsa_profile
*p
) {
3118 static const struct description_map well_known_descriptions
[] = {
3119 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3120 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3121 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3122 { "off", N_("Off") }
3127 /* Replace the output mapping names by the actual mappings */
3128 if (p
->output_mapping_names
) {
3131 pa_assert(!p
->output_mappings
);
3132 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3134 for (name
= p
->output_mapping_names
; *name
; name
++) {
3137 pa_bool_t duplicate
= FALSE
;
3139 for (in
= name
+ 1; *in
; in
++)
3140 if (pa_streq(*name
, *in
)) {
3148 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3149 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3153 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3159 pa_xstrfreev(p
->output_mapping_names
);
3160 p
->output_mapping_names
= NULL
;
3163 /* Replace the input mapping names by the actual mappings */
3164 if (p
->input_mapping_names
) {
3167 pa_assert(!p
->input_mappings
);
3168 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3170 for (name
= p
->input_mapping_names
; *name
; name
++) {
3173 pa_bool_t duplicate
= FALSE
;
3175 for (in
= name
+ 1; *in
; in
++)
3176 if (pa_streq(*name
, *in
)) {
3184 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3185 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3189 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3195 pa_xstrfreev(p
->input_mapping_names
);
3196 p
->input_mapping_names
= NULL
;
3199 if (!p
->input_mappings
&& !p
->output_mappings
) {
3200 pa_log("Profile '%s' lacks mappings.", p
->name
);
3204 if (!p
->description
)
3205 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3206 well_known_descriptions
,
3207 PA_ELEMENTSOF(well_known_descriptions
)));
3209 if (!p
->description
) {
3214 sb
= pa_strbuf_new();
3216 if (p
->output_mappings
)
3217 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3218 if (!pa_strbuf_isempty(sb
))
3219 pa_strbuf_puts(sb
, " + ");
3221 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
3224 if (p
->input_mappings
)
3225 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3226 if (!pa_strbuf_isempty(sb
))
3227 pa_strbuf_puts(sb
, " + ");
3229 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
3232 p
->description
= pa_strbuf_tostring_free(sb
);
3238 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
3243 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3245 pa_strnull(p
->description
),
3247 pa_yes_no(p
->supported
),
3248 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
3249 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
3251 if (p
->input_mappings
)
3252 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
3253 pa_log_debug("Input %s", m
->name
);
3255 if (p
->output_mappings
)
3256 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
3257 pa_log_debug("Output %s", m
->name
);
3260 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
3261 pa_alsa_profile_set
*ps
;
3268 static pa_config_item items
[] = {
3270 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
3273 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
3274 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
3275 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
3276 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
3277 { "element-input", mapping_parse_element
, NULL
, NULL
},
3278 { "element-output", mapping_parse_element
, NULL
, NULL
},
3279 { "direction", mapping_parse_direction
, NULL
, NULL
},
3281 /* Shared by [Mapping ...] and [Profile ...] */
3282 { "description", mapping_parse_description
, NULL
, NULL
},
3283 { "priority", mapping_parse_priority
, NULL
, NULL
},
3286 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
3287 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
3288 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
3289 { NULL
, NULL
, NULL
, NULL
}
3292 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
3293 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3294 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3296 items
[0].data
= &ps
->auto_profiles
;
3299 fname
= "default.conf";
3301 fn
= pa_maybe_prefix_path(fname
,
3302 #if defined(__linux__) && !defined(__OPTIMIZE__)
3303 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
3305 PA_ALSA_PROFILE_SETS_DIR
);
3307 r
= pa_config_parse(fn
, NULL
, items
, ps
);
3313 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3314 if (mapping_verify(m
, bonus
) < 0)
3317 if (ps
->auto_profiles
)
3318 profile_set_add_auto(ps
);
3320 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3321 if (profile_verify(p
) < 0)
3327 pa_alsa_profile_set_free(ps
);
3331 void pa_alsa_profile_set_probe(
3332 pa_alsa_profile_set
*ps
,
3334 const pa_sample_spec
*ss
,
3335 unsigned default_n_fragments
,
3336 unsigned default_fragment_size_msec
) {
3339 pa_alsa_profile
*p
, *last
= NULL
;
3349 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
3350 pa_sample_spec try_ss
;
3351 pa_channel_map try_map
;
3352 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
3355 /* Is this already marked that it is supported? (i.e. from the config file) */
3359 pa_log_debug("Looking at profile %s", p
->name
);
3361 /* Close PCMs from the last iteration we don't need anymore */
3362 if (last
&& last
->output_mappings
)
3363 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
) {
3368 if (last
->supported
)
3371 if (!p
->output_mappings
|| !pa_idxset_get_by_data(p
->output_mappings
, m
, NULL
)) {
3372 snd_pcm_close(m
->output_pcm
);
3373 m
->output_pcm
= NULL
;
3377 if (last
&& last
->input_mappings
)
3378 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
) {
3383 if (last
->supported
)
3386 if (!p
->input_mappings
|| !pa_idxset_get_by_data(p
->input_mappings
, m
, NULL
)) {
3387 snd_pcm_close(m
->input_pcm
);
3388 m
->input_pcm
= NULL
;
3392 p
->supported
= TRUE
;
3394 /* Check if we can open all new ones */
3395 if (p
->output_mappings
)
3396 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3401 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
3402 try_map
= m
->channel_map
;
3404 try_ss
.channels
= try_map
.channels
;
3407 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
3408 pa_frame_size(&try_ss
);
3409 try_buffer_size
= default_n_fragments
* try_period_size
;
3411 if (!(m
->output_pcm
= pa_alsa_open_by_template(
3416 SND_PCM_STREAM_PLAYBACK
,
3417 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3419 p
->supported
= FALSE
;
3424 if (p
->input_mappings
&& p
->supported
)
3425 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3430 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
3431 try_map
= m
->channel_map
;
3433 try_ss
.channels
= try_map
.channels
;
3436 pa_usec_to_bytes(default_fragment_size_msec
*PA_USEC_PER_MSEC
, &try_ss
) /
3437 pa_frame_size(&try_ss
);
3438 try_buffer_size
= default_n_fragments
* try_period_size
;
3440 if (!(m
->input_pcm
= pa_alsa_open_by_template(
3445 SND_PCM_STREAM_CAPTURE
,
3446 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3448 p
->supported
= FALSE
;
3456 pa_log_debug("Profile %s supported.", p
->name
);
3463 if (last
->output_mappings
)
3464 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
)
3465 if (m
->output_pcm
) {
3467 if (last
->supported
)
3470 snd_pcm_close(m
->output_pcm
);
3471 m
->output_pcm
= NULL
;
3474 if (last
->input_mappings
)
3475 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
)
3478 if (last
->supported
)
3481 snd_pcm_close(m
->input_pcm
);
3482 m
->input_pcm
= NULL
;
3486 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3487 if (!p
->supported
) {
3488 pa_hashmap_remove(ps
->profiles
, p
->name
);
3492 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3493 if (m
->supported
<= 0) {
3494 pa_hashmap_remove(ps
->mappings
, m
->name
);
3501 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
3508 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u",
3511 pa_yes_no(ps
->auto_profiles
),
3512 pa_yes_no(ps
->probed
),
3513 pa_hashmap_size(ps
->mappings
),
3514 pa_hashmap_size(ps
->profiles
));
3516 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3517 pa_alsa_mapping_dump(m
);
3519 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3520 pa_alsa_profile_dump(p
);
3523 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
) {
3530 /* if there is no path, we don't want a port list */
3534 if (!ps
->paths
->next
){
3537 /* If there is only one path, but no or only one setting, then
3538 * we want a port list either */
3539 if (!ps
->paths
->settings
|| !ps
->paths
->settings
->next
)
3542 /* Ok, there is only one path, however with multiple settings,
3543 * so let's create a port for each setting */
3544 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3546 PA_LLIST_FOREACH(s
, ps
->paths
->settings
) {
3547 pa_device_port
*port
;
3548 pa_alsa_port_data
*data
;
3550 port
= pa_device_port_new(s
->name
, s
->description
, sizeof(pa_alsa_port_data
));
3551 port
->priority
= s
->priority
;
3553 data
= PA_DEVICE_PORT_DATA(port
);
3554 data
->path
= ps
->paths
;
3557 pa_hashmap_put(*p
, port
->name
, port
);
3562 /* We have multiple paths, so let's create a port for each
3563 * one, and each of each settings */
3564 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3566 PA_LLIST_FOREACH(path
, ps
->paths
) {
3568 if (!path
->settings
|| !path
->settings
->next
) {
3569 pa_device_port
*port
;
3570 pa_alsa_port_data
*data
;
3572 /* If there is no or just one setting we only need a
3575 port
= pa_device_port_new(path
->name
, path
->description
, sizeof(pa_alsa_port_data
));
3576 port
->priority
= path
->priority
* 100;
3579 data
= PA_DEVICE_PORT_DATA(port
);
3581 data
->setting
= path
->settings
;
3583 pa_hashmap_put(*p
, port
->name
, port
);
3587 PA_LLIST_FOREACH(s
, path
->settings
) {
3588 pa_device_port
*port
;
3589 pa_alsa_port_data
*data
;
3592 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
3594 if (s
->description
[0])
3595 d
= pa_sprintf_malloc(_("%s / %s"), path
->description
, s
->description
);
3597 d
= pa_xstrdup(path
->description
);
3599 port
= pa_device_port_new(n
, d
, sizeof(pa_alsa_port_data
));
3600 port
->priority
= path
->priority
* 100 + s
->priority
;
3605 data
= PA_DEVICE_PORT_DATA(port
);
3609 pa_hashmap_put(*p
, port
->name
, port
);
3615 pa_log_debug("Added %u ports", pa_hashmap_size(*p
));