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
))
1127 static int element_probe(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1128 snd_mixer_selem_id_t
*sid
;
1129 snd_mixer_elem_t
*me
;
1134 SELEM_INIT(sid
, e
->alsa_name
);
1136 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1138 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1141 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1142 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1143 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1148 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1149 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1151 if (!snd_mixer_selem_has_playback_switch(me
)) {
1152 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1153 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1155 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1160 if (!snd_mixer_selem_has_capture_switch(me
)) {
1161 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1162 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1164 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1168 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1169 e
->direction_try_other
= FALSE
;
1172 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1174 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1176 if (!snd_mixer_selem_has_playback_volume(me
)) {
1177 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1178 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1180 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1185 if (!snd_mixer_selem_has_capture_volume(me
)) {
1186 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1187 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1189 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1193 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1194 long min_dB
= 0, max_dB
= 0;
1197 e
->direction_try_other
= FALSE
;
1199 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1200 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1202 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1205 #ifdef HAVE_VALGRIND_MEMCHECK_H
1206 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1207 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1210 e
->min_dB
= ((double) min_dB
) / 100.0;
1211 e
->max_dB
= ((double) max_dB
) / 100.0;
1213 if (min_dB
>= max_dB
) {
1214 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
);
1219 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1220 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1222 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1225 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1230 if (e
->min_volume
>= e
->max_volume
) {
1231 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
);
1232 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1236 pa_channel_position_t p
;
1238 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1239 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1241 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1246 if (!e
->override_map
) {
1247 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++)
1248 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1249 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1252 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1255 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1257 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1260 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1261 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1263 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1266 if (e
->n_channels
<= 0) {
1267 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1271 if (!e
->override_map
) {
1272 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1273 pa_bool_t has_channel
;
1275 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1278 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1279 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1281 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1283 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1288 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++)
1289 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1296 if (check_required(e
, me
) < 0)
1299 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1302 PA_LLIST_FOREACH(o
, e
->options
)
1303 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1304 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1308 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1309 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1313 PA_LLIST_FOREACH(o
, e
->options
) {
1316 for (i
= 0; i
< n
; i
++) {
1319 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1322 if (!pa_streq(buf
, o
->alsa_name
))
1333 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, pa_bool_t prefixed
) {
1340 if (!pa_startswith(section
, "Element "))
1346 /* This is not an element section, but an enum section? */
1347 if (strchr(section
, ':'))
1350 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1351 return p
->last_element
;
1353 PA_LLIST_FOREACH(e
, p
->elements
)
1354 if (pa_streq(e
->alsa_name
, section
))
1357 e
= pa_xnew0(pa_alsa_element
, 1);
1359 e
->alsa_name
= pa_xstrdup(section
);
1360 e
->direction
= p
->direction
;
1362 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1365 p
->last_element
= e
;
1369 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1375 if (!pa_startswith(section
, "Option "))
1380 /* This is not an enum section, but an element section? */
1381 if (!(on
= strchr(section
, ':')))
1384 en
= pa_xstrndup(section
, on
- section
);
1387 if (p
->last_option
&&
1388 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1389 pa_streq(p
->last_option
->alsa_name
, on
)) {
1391 return p
->last_option
;
1394 pa_assert_se(e
= element_get(p
, en
, FALSE
));
1397 PA_LLIST_FOREACH(o
, e
->options
)
1398 if (pa_streq(o
->alsa_name
, on
))
1401 o
= pa_xnew0(pa_alsa_option
, 1);
1403 o
->alsa_name
= pa_xstrdup(on
);
1406 if (p
->last_option
&& p
->last_option
->element
== e
)
1407 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1409 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1416 static int element_parse_switch(
1417 const char *filename
,
1419 const char *section
,
1425 pa_alsa_path
*p
= userdata
;
1430 if (!(e
= element_get(p
, section
, TRUE
))) {
1431 pa_log("[%s:%u] Switch makes no sense in '%s'", filename
, line
, section
);
1435 if (pa_streq(rvalue
, "ignore"))
1436 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1437 else if (pa_streq(rvalue
, "mute"))
1438 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1439 else if (pa_streq(rvalue
, "off"))
1440 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1441 else if (pa_streq(rvalue
, "on"))
1442 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1443 else if (pa_streq(rvalue
, "select"))
1444 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1446 pa_log("[%s:%u] Switch invalid of '%s'", filename
, line
, section
);
1453 static int element_parse_volume(
1454 const char *filename
,
1456 const char *section
,
1462 pa_alsa_path
*p
= userdata
;
1467 if (!(e
= element_get(p
, section
, TRUE
))) {
1468 pa_log("[%s:%u] Volume makes no sense in '%s'", filename
, line
, section
);
1472 if (pa_streq(rvalue
, "ignore"))
1473 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1474 else if (pa_streq(rvalue
, "merge"))
1475 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1476 else if (pa_streq(rvalue
, "off"))
1477 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1478 else if (pa_streq(rvalue
, "zero"))
1479 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1481 pa_log("[%s:%u] Volume invalid of '%s'", filename
, line
, section
);
1488 static int element_parse_enumeration(
1489 const char *filename
,
1491 const char *section
,
1497 pa_alsa_path
*p
= userdata
;
1502 if (!(e
= element_get(p
, section
, TRUE
))) {
1503 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename
, line
, section
);
1507 if (pa_streq(rvalue
, "ignore"))
1508 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1509 else if (pa_streq(rvalue
, "select"))
1510 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1512 pa_log("[%s:%u] Enumeration invalid of '%s'", filename
, line
, section
);
1519 static int option_parse_priority(
1520 const char *filename
,
1522 const char *section
,
1528 pa_alsa_path
*p
= userdata
;
1534 if (!(o
= option_get(p
, section
))) {
1535 pa_log("[%s:%u] Priority makes no sense in '%s'", filename
, line
, section
);
1539 if (pa_atou(rvalue
, &prio
) < 0) {
1540 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
1548 static int option_parse_name(
1549 const char *filename
,
1551 const char *section
,
1557 pa_alsa_path
*p
= userdata
;
1562 if (!(o
= option_get(p
, section
))) {
1563 pa_log("[%s:%u] Name makes no sense in '%s'", filename
, line
, section
);
1568 o
->name
= pa_xstrdup(rvalue
);
1573 static int element_parse_required(
1574 const char *filename
,
1576 const char *section
,
1582 pa_alsa_path
*p
= userdata
;
1584 pa_alsa_required_t req
;
1588 if (!(e
= element_get(p
, section
, TRUE
))) {
1589 pa_log("[%s:%u] Required makes no sense in '%s'", filename
, line
, section
);
1593 if (pa_streq(rvalue
, "ignore"))
1594 req
= PA_ALSA_REQUIRED_IGNORE
;
1595 else if (pa_streq(rvalue
, "switch"))
1596 req
= PA_ALSA_REQUIRED_SWITCH
;
1597 else if (pa_streq(rvalue
, "volume"))
1598 req
= PA_ALSA_REQUIRED_VOLUME
;
1599 else if (pa_streq(rvalue
, "enumeration"))
1600 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1601 else if (pa_streq(rvalue
, "any"))
1602 req
= PA_ALSA_REQUIRED_ANY
;
1604 pa_log("[%s:%u] Required invalid of '%s'", filename
, line
, section
);
1608 if (pa_streq(lvalue
, "required-absent"))
1609 e
->required_absent
= req
;
1616 static int element_parse_direction(
1617 const char *filename
,
1619 const char *section
,
1625 pa_alsa_path
*p
= userdata
;
1630 if (!(e
= element_get(p
, section
, TRUE
))) {
1631 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1635 if (pa_streq(rvalue
, "playback"))
1636 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1637 else if (pa_streq(rvalue
, "capture"))
1638 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1640 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1647 static int element_parse_direction_try_other(
1648 const char *filename
,
1650 const char *section
,
1656 pa_alsa_path
*p
= userdata
;
1660 if (!(e
= element_get(p
, section
, TRUE
))) {
1661 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1665 if ((yes
= pa_parse_boolean(rvalue
)) < 0) {
1666 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1670 e
->direction_try_other
= !!yes
;
1674 static pa_channel_position_mask_t
parse_mask(const char *m
) {
1675 pa_channel_position_mask_t v
;
1677 if (pa_streq(m
, "all-left"))
1678 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
1679 else if (pa_streq(m
, "all-right"))
1680 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
1681 else if (pa_streq(m
, "all-center"))
1682 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
1683 else if (pa_streq(m
, "all-front"))
1684 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
1685 else if (pa_streq(m
, "all-rear"))
1686 v
= PA_CHANNEL_POSITION_MASK_REAR
;
1687 else if (pa_streq(m
, "all-side"))
1688 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
1689 else if (pa_streq(m
, "all-top"))
1690 v
= PA_CHANNEL_POSITION_MASK_TOP
;
1691 else if (pa_streq(m
, "all-no-lfe"))
1692 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
1693 else if (pa_streq(m
, "all"))
1694 v
= PA_CHANNEL_POSITION_MASK_ALL
;
1696 pa_channel_position_t p
;
1698 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
1701 v
= PA_CHANNEL_POSITION_MASK(p
);
1707 static int element_parse_override_map(
1708 const char *filename
,
1710 const char *section
,
1716 pa_alsa_path
*p
= userdata
;
1718 const char *state
= NULL
;
1722 if (!(e
= element_get(p
, section
, TRUE
))) {
1723 pa_log("[%s:%u] Override map makes no sense in '%s'", filename
, line
, section
);
1727 while ((n
= pa_split(rvalue
, ",", &state
))) {
1728 pa_channel_position_mask_t m
;
1733 if ((m
= parse_mask(n
)) == 0) {
1734 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename
, line
, n
, section
);
1740 if (pa_streq(lvalue
, "override-map.1"))
1741 e
->masks
[i
++][0] = m
;
1743 e
->masks
[i
++][1] = m
;
1745 /* Later on we might add override-map.3 and so on here ... */
1750 e
->override_map
= TRUE
;
1755 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
1756 snd_mixer_selem_id_t
*sid
;
1757 snd_mixer_elem_t
*me
;
1763 SELEM_INIT(sid
, e
->alsa_name
);
1764 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1765 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1769 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1771 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1772 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
1774 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
1777 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1780 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
1782 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
1783 pa_log_warn("Failed to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1789 int pa_alsa_setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
1796 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
1797 element_set_option(o
->element
, m
, o
->alsa_idx
);
1802 static int option_verify(pa_alsa_option
*o
) {
1803 static const struct description_map well_known_descriptions
[] = {
1804 { "input", N_("Input") },
1805 { "input-docking", N_("Docking Station Input") },
1806 { "input-docking-microphone", N_("Docking Station Microphone") },
1807 { "input-linein", N_("Line-In") },
1808 { "input-microphone", N_("Microphone") },
1809 { "input-microphone-external", N_("External Microphone") },
1810 { "input-microphone-internal", N_("Internal Microphone") },
1811 { "input-radio", N_("Radio") },
1812 { "input-video", N_("Video") },
1813 { "input-agc-on", N_("Automatic Gain Control") },
1814 { "input-agc-off", N_("No Automatic Gain Control") },
1815 { "input-boost-on", N_("Boost") },
1816 { "input-boost-off", N_("No Boost") },
1817 { "output-amplifier-on", N_("Amplifier") },
1818 { "output-amplifier-off", N_("No Amplifier") },
1819 { "output-bass-boost-on", N_("Bass Boost") },
1820 { "output-bass-boost-off", N_("No Bass Boost") },
1821 { "output-speaker", N_("Speaker") },
1822 { "output-headphones", N_("Headphones") }
1828 pa_log("No name set for option %s", o
->alsa_name
);
1832 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
1833 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
1834 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
1838 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
1839 !pa_streq(o
->alsa_name
, "on") &&
1840 !pa_streq(o
->alsa_name
, "off")) {
1841 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
1845 if (!o
->description
)
1846 o
->description
= pa_xstrdup(lookup_description(o
->name
,
1847 well_known_descriptions
,
1848 PA_ELEMENTSOF(well_known_descriptions
)));
1849 if (!o
->description
)
1850 o
->description
= pa_xstrdup(o
->name
);
1855 static int element_verify(pa_alsa_element
*e
) {
1860 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
1861 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
1862 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
1866 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1867 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
1871 PA_LLIST_FOREACH(o
, e
->options
)
1872 if (option_verify(o
) < 0)
1878 static int path_verify(pa_alsa_path
*p
) {
1879 static const struct description_map well_known_descriptions
[] = {
1880 { "analog-input", N_("Analog Input") },
1881 { "analog-input-microphone", N_("Analog Microphone") },
1882 { "analog-input-linein", N_("Analog Line-In") },
1883 { "analog-input-radio", N_("Analog Radio") },
1884 { "analog-input-video", N_("Analog Video") },
1885 { "analog-output", N_("Analog Output") },
1886 { "analog-output-headphones", N_("Analog Headphones") },
1887 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
1888 { "analog-output-mono", N_("Analog Mono Output") },
1889 { "analog-output-speaker", N_("Analog Speakers") },
1890 { "iec958-stereo-output", N_("Digital Output (IEC958)") },
1891 { "iec958-passthrough-output", N_("Digital Passthrough (IEC958)") }
1898 PA_LLIST_FOREACH(e
, p
->elements
)
1899 if (element_verify(e
) < 0)
1902 if (!p
->description
)
1903 p
->description
= pa_xstrdup(lookup_description(p
->name
,
1904 well_known_descriptions
,
1905 PA_ELEMENTSOF(well_known_descriptions
)));
1907 if (!p
->description
)
1908 p
->description
= pa_xstrdup(p
->name
);
1913 pa_alsa_path
* pa_alsa_path_new(const char *fname
, pa_alsa_direction_t direction
) {
1919 pa_config_item items
[] = {
1921 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
1922 { "description", pa_config_parse_string
, NULL
, "General" },
1923 { "name", pa_config_parse_string
, NULL
, "General" },
1926 { "priority", option_parse_priority
, NULL
, NULL
},
1927 { "name", option_parse_name
, NULL
, NULL
},
1930 { "switch", element_parse_switch
, NULL
, NULL
},
1931 { "volume", element_parse_volume
, NULL
, NULL
},
1932 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
1933 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
1934 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
1935 /* ... later on we might add override-map.3 and so on here ... */
1936 { "required", element_parse_required
, NULL
, NULL
},
1937 { "required-absent", element_parse_required
, NULL
, NULL
},
1938 { "direction", element_parse_direction
, NULL
, NULL
},
1939 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
1940 { NULL
, NULL
, NULL
, NULL
}
1945 p
= pa_xnew0(pa_alsa_path
, 1);
1946 n
= pa_path_get_filename(fname
);
1947 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
1948 p
->direction
= direction
;
1950 items
[0].data
= &p
->priority
;
1951 items
[1].data
= &p
->description
;
1952 items
[2].data
= &p
->name
;
1954 fn
= pa_maybe_prefix_path(fname
,
1955 #if defined(__linux__) && !defined(__OPTIMIZE__)
1956 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/paths/" :
1960 r
= pa_config_parse(fn
, NULL
, items
, p
);
1966 if (path_verify(p
) < 0)
1972 pa_alsa_path_free(p
);
1976 pa_alsa_path
* pa_alsa_path_synthesize(const char*element
, pa_alsa_direction_t direction
) {
1982 p
= pa_xnew0(pa_alsa_path
, 1);
1983 p
->name
= pa_xstrdup(element
);
1984 p
->direction
= direction
;
1986 e
= pa_xnew0(pa_alsa_element
, 1);
1988 e
->alsa_name
= pa_xstrdup(element
);
1989 e
->direction
= direction
;
1991 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1992 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1994 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
1995 p
->last_element
= e
;
1999 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2000 pa_alsa_option
*o
, *n
;
2004 for (o
= e
->options
; o
; o
= n
) {
2007 if (o
->alsa_idx
< 0) {
2008 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2014 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2015 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2016 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2019 static void path_drop_unsupported(pa_alsa_path
*p
) {
2020 pa_alsa_element
*e
, *n
;
2024 for (e
= p
->elements
; e
; e
= n
) {
2027 if (!element_drop_unsupported(e
)) {
2028 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2034 static void path_make_options_unique(pa_alsa_path
*p
) {
2036 pa_alsa_option
*o
, *u
;
2038 PA_LLIST_FOREACH(e
, p
->elements
) {
2039 PA_LLIST_FOREACH(o
, e
->options
) {
2043 for (u
= o
->next
; u
; u
= u
->next
)
2044 if (pa_streq(u
->name
, o
->name
))
2050 m
= pa_xstrdup(o
->name
);
2052 /* OK, this name is not unique, hence let's rename */
2053 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2056 if (!pa_streq(u
->name
, m
))
2059 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2063 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2064 pa_xfree(u
->description
);
2065 u
->description
= nd
;
2075 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2078 for (; e
; e
= e
->next
)
2079 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2080 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2086 for (o
= e
->options
; o
; o
= o
->next
) {
2090 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2091 s
->options
= pa_idxset_copy(template->options
);
2092 s
->name
= pa_sprintf_malloc(_("%s+%s"), template->name
, o
->name
);
2094 (template->description
[0] && o
->description
[0])
2095 ? pa_sprintf_malloc(_("%s / %s"), template->description
, o
->description
)
2096 : (template->description
[0]
2097 ? pa_xstrdup(template->description
)
2098 : pa_xstrdup(o
->description
));
2100 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2102 s
= pa_xnew0(pa_alsa_setting
, 1);
2103 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2104 s
->name
= pa_xstrdup(o
->name
);
2105 s
->description
= pa_xstrdup(o
->description
);
2106 s
->priority
= o
->priority
;
2109 pa_idxset_put(s
->options
, o
, NULL
);
2111 if (element_create_settings(e
->next
, s
))
2112 /* This is not a leaf, so let's get rid of it */
2115 /* This is a leaf, so let's add it */
2116 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2118 e
->path
->last_setting
= s
;
2125 static void path_create_settings(pa_alsa_path
*p
) {
2128 element_create_settings(p
->elements
, NULL
);
2131 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2133 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2134 pa_channel_position_t t
;
2145 pa_log_debug("Probing path '%s'", p
->name
);
2147 PA_LLIST_FOREACH(e
, p
->elements
) {
2148 if (element_probe(e
, m
) < 0) {
2149 p
->supported
= FALSE
;
2150 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2157 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2159 if (!p
->has_volume
) {
2160 p
->min_volume
= e
->min_volume
;
2161 p
->max_volume
= e
->max_volume
;
2165 if (!p
->has_volume
) {
2166 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2167 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2168 min_dB
[t
] = e
->min_dB
;
2169 max_dB
[t
] = e
->max_dB
;
2176 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2177 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2178 min_dB
[t
] += e
->min_dB
;
2179 max_dB
[t
] += e
->max_dB
;
2182 /* Hmm, there's another element before us
2183 * which cannot do dB volumes, so we we need
2184 * to 'neutralize' this slider */
2185 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2187 } else if (p
->has_volume
)
2188 /* We can't use this volume, so let's ignore it */
2189 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2191 p
->has_volume
= TRUE
;
2194 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2198 path_drop_unsupported(p
);
2199 path_make_options_unique(p
);
2200 path_create_settings(p
);
2202 p
->supported
= TRUE
;
2205 p
->min_dB
= INFINITY
;
2206 p
->max_dB
= -INFINITY
;
2208 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2209 if (p
->min_dB
> min_dB
[t
])
2210 p
->min_dB
= min_dB
[t
];
2212 if (p
->max_dB
< max_dB
[t
])
2213 p
->max_dB
= max_dB
[t
];
2219 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2222 pa_log_debug("Setting %s (%s) priority=%u",
2224 pa_strnull(s
->description
),
2228 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2231 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2233 pa_strnull(o
->name
),
2234 pa_strnull(o
->description
),
2239 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2243 pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, enumeration=%i, required=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s",
2251 (long long unsigned) e
->merged_mask
,
2253 pa_yes_no(e
->override_map
));
2255 PA_LLIST_FOREACH(o
, e
->options
)
2256 pa_alsa_option_dump(o
);
2259 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2264 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2265 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2267 pa_strnull(p
->description
),
2270 pa_yes_no(p
->probed
),
2271 pa_yes_no(p
->supported
),
2272 pa_yes_no(p
->has_mute
),
2273 pa_yes_no(p
->has_volume
),
2274 pa_yes_no(p
->has_dB
),
2275 p
->min_volume
, p
->max_volume
,
2276 p
->min_dB
, p
->max_dB
);
2278 PA_LLIST_FOREACH(e
, p
->elements
)
2279 pa_alsa_element_dump(e
);
2281 PA_LLIST_FOREACH(s
, p
->settings
)
2282 pa_alsa_setting_dump(s
);
2285 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2286 snd_mixer_selem_id_t
*sid
;
2287 snd_mixer_elem_t
*me
;
2293 SELEM_INIT(sid
, e
->alsa_name
);
2294 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2295 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2299 snd_mixer_elem_set_callback(me
, cb
);
2300 snd_mixer_elem_set_callback_private(me
, userdata
);
2303 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2310 PA_LLIST_FOREACH(e
, p
->elements
)
2311 element_set_callback(e
, m
, cb
, userdata
);
2314 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2321 PA_LLIST_FOREACH(p
, ps
->paths
)
2322 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2325 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
) {
2326 pa_alsa_path_set
*ps
;
2327 char **pn
= NULL
, **en
= NULL
, **ie
;
2330 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2332 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2335 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2336 ps
->direction
= direction
;
2338 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2339 pn
= m
->output_path_names
;
2340 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2341 pn
= m
->input_path_names
;
2346 for (in
= pn
; *in
; in
++) {
2348 pa_bool_t duplicate
= FALSE
;
2351 for (kn
= pn
; kn
!= in
; kn
++)
2352 if (pa_streq(*kn
, *in
)) {
2360 fn
= pa_sprintf_malloc("%s.conf", *in
);
2362 if ((p
= pa_alsa_path_new(fn
, direction
))) {
2364 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2374 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2375 en
= m
->output_element
;
2376 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2377 en
= m
->input_element
;
2380 pa_alsa_path_set_free(ps
);
2384 for (ie
= en
; *ie
; ie
++) {
2388 p
= pa_alsa_path_synthesize(*ie
, direction
);
2391 /* Mark all other passed elements for require-absent */
2392 for (je
= en
; *je
; je
++) {
2398 e
= pa_xnew0(pa_alsa_element
, 1);
2400 e
->alsa_name
= pa_xstrdup(*je
);
2401 e
->direction
= direction
;
2402 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2404 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2405 p
->last_element
= e
;
2408 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2415 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2419 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2422 pa_yes_no(ps
->probed
));
2424 PA_LLIST_FOREACH(p
, ps
->paths
)
2425 pa_alsa_path_dump(p
);
2428 static void path_set_unify(pa_alsa_path_set
*ps
) {
2430 pa_bool_t has_dB
= TRUE
, has_volume
= TRUE
, has_mute
= TRUE
;
2433 /* We have issues dealing with paths that vary too wildly. That
2434 * means for now we have to have all paths support volume/mute/dB
2437 PA_LLIST_FOREACH(p
, ps
->paths
) {
2438 pa_assert(p
->probed
);
2442 else if (!p
->has_dB
)
2449 if (!has_volume
|| !has_dB
|| !has_mute
) {
2452 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2454 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2457 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2459 PA_LLIST_FOREACH(p
, ps
->paths
) {
2461 p
->has_volume
= FALSE
;
2466 p
->has_mute
= FALSE
;
2471 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
2472 pa_alsa_path
*p
, *q
;
2474 PA_LLIST_FOREACH(p
, ps
->paths
) {
2478 for (q
= p
->next
; q
; q
= q
->next
)
2479 if (pa_streq(q
->name
, p
->name
))
2485 m
= pa_xstrdup(p
->name
);
2487 /* OK, this name is not unique, hence let's rename */
2488 for (i
= 1, q
= p
; q
; q
= q
->next
) {
2491 if (!pa_streq(q
->name
, m
))
2494 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2498 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
2499 pa_xfree(q
->description
);
2500 q
->description
= nd
;
2509 void pa_alsa_path_set_probe(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2510 pa_alsa_path
*p
, *n
;
2517 for (p
= ps
->paths
; p
; p
= n
) {
2520 if (pa_alsa_path_probe(p
, m
, ignore_dB
) < 0) {
2521 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
2522 pa_alsa_path_free(p
);
2527 path_set_make_paths_unique(ps
);
2531 static void mapping_free(pa_alsa_mapping
*m
) {
2535 pa_xfree(m
->description
);
2537 pa_xstrfreev(m
->device_strings
);
2538 pa_xstrfreev(m
->input_path_names
);
2539 pa_xstrfreev(m
->output_path_names
);
2540 pa_xstrfreev(m
->input_element
);
2541 pa_xstrfreev(m
->output_element
);
2543 pa_assert(!m
->input_pcm
);
2544 pa_assert(!m
->output_pcm
);
2549 static void profile_free(pa_alsa_profile
*p
) {
2553 pa_xfree(p
->description
);
2555 pa_xstrfreev(p
->input_mapping_names
);
2556 pa_xstrfreev(p
->output_mapping_names
);
2558 if (p
->input_mappings
)
2559 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
2561 if (p
->output_mappings
)
2562 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
2567 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
2573 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
2576 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
2582 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
2585 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
2591 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
2594 if (!pa_startswith(name
, "Mapping "))
2599 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
2602 m
= pa_xnew0(pa_alsa_mapping
, 1);
2603 m
->profile_set
= ps
;
2604 m
->name
= pa_xstrdup(name
);
2605 pa_channel_map_init(&m
->channel_map
);
2607 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
2612 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
2615 if (!pa_startswith(name
, "Profile "))
2620 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
2623 p
= pa_xnew0(pa_alsa_profile
, 1);
2624 p
->profile_set
= ps
;
2625 p
->name
= pa_xstrdup(name
);
2627 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
2632 static int mapping_parse_device_strings(
2633 const char *filename
,
2635 const char *section
,
2641 pa_alsa_profile_set
*ps
= userdata
;
2646 if (!(m
= mapping_get(ps
, section
))) {
2647 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2651 pa_xstrfreev(m
->device_strings
);
2652 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
2653 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
2660 static int mapping_parse_channel_map(
2661 const char *filename
,
2663 const char *section
,
2669 pa_alsa_profile_set
*ps
= userdata
;
2674 if (!(m
= mapping_get(ps
, section
))) {
2675 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2679 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
2680 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
2687 static int mapping_parse_paths(
2688 const char *filename
,
2690 const char *section
,
2696 pa_alsa_profile_set
*ps
= userdata
;
2701 if (!(m
= mapping_get(ps
, section
))) {
2702 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2706 if (pa_streq(lvalue
, "paths-input")) {
2707 pa_xstrfreev(m
->input_path_names
);
2708 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
2710 pa_xstrfreev(m
->output_path_names
);
2711 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
2717 static int mapping_parse_element(
2718 const char *filename
,
2720 const char *section
,
2726 pa_alsa_profile_set
*ps
= userdata
;
2731 if (!(m
= mapping_get(ps
, section
))) {
2732 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2736 if (pa_streq(lvalue
, "element-input")) {
2737 pa_xstrfreev(m
->input_element
);
2738 m
->input_element
= pa_split_spaces_strv(rvalue
);
2740 pa_xstrfreev(m
->output_element
);
2741 m
->output_element
= pa_split_spaces_strv(rvalue
);
2747 static int mapping_parse_direction(
2748 const char *filename
,
2750 const char *section
,
2756 pa_alsa_profile_set
*ps
= userdata
;
2761 if (!(m
= mapping_get(ps
, section
))) {
2762 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
2766 if (pa_streq(rvalue
, "input"))
2767 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
2768 else if (pa_streq(rvalue
, "output"))
2769 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
2770 else if (pa_streq(rvalue
, "any"))
2771 m
->direction
= PA_ALSA_DIRECTION_ANY
;
2773 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
2780 static int mapping_parse_description(
2781 const char *filename
,
2783 const char *section
,
2789 pa_alsa_profile_set
*ps
= userdata
;
2795 if ((m
= mapping_get(ps
, section
))) {
2796 pa_xfree(m
->description
);
2797 m
->description
= pa_xstrdup(rvalue
);
2798 } else if ((p
= profile_get(ps
, section
))) {
2799 pa_xfree(p
->description
);
2800 p
->description
= pa_xstrdup(rvalue
);
2802 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
2809 static int mapping_parse_priority(
2810 const char *filename
,
2812 const char *section
,
2818 pa_alsa_profile_set
*ps
= userdata
;
2825 if (pa_atou(rvalue
, &prio
) < 0) {
2826 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
2830 if ((m
= mapping_get(ps
, section
)))
2832 else if ((p
= profile_get(ps
, section
)))
2835 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
2842 static int profile_parse_mappings(
2843 const char *filename
,
2845 const char *section
,
2851 pa_alsa_profile_set
*ps
= userdata
;
2856 if (!(p
= profile_get(ps
, section
))) {
2857 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2861 if (pa_streq(lvalue
, "input-mappings")) {
2862 pa_xstrfreev(p
->input_mapping_names
);
2863 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
2865 pa_xstrfreev(p
->output_mapping_names
);
2866 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
2872 static int profile_parse_skip_probe(
2873 const char *filename
,
2875 const char *section
,
2881 pa_alsa_profile_set
*ps
= userdata
;
2887 if (!(p
= profile_get(ps
, section
))) {
2888 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2892 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
2893 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
2902 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
2904 static const struct description_map well_known_descriptions
[] = {
2905 { "analog-mono", N_("Analog Mono") },
2906 { "analog-stereo", N_("Analog Stereo") },
2907 { "analog-surround-21", N_("Analog Surround 2.1") },
2908 { "analog-surround-30", N_("Analog Surround 3.0") },
2909 { "analog-surround-31", N_("Analog Surround 3.1") },
2910 { "analog-surround-40", N_("Analog Surround 4.0") },
2911 { "analog-surround-41", N_("Analog Surround 4.1") },
2912 { "analog-surround-50", N_("Analog Surround 5.0") },
2913 { "analog-surround-51", N_("Analog Surround 5.1") },
2914 { "analog-surround-61", N_("Analog Surround 6.0") },
2915 { "analog-surround-61", N_("Analog Surround 6.1") },
2916 { "analog-surround-70", N_("Analog Surround 7.0") },
2917 { "analog-surround-71", N_("Analog Surround 7.1") },
2918 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
2919 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
2920 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
2921 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
2922 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
2927 if (!pa_channel_map_valid(&m
->channel_map
)) {
2928 pa_log("Mapping %s is missing channel map.", m
->name
);
2932 if (!m
->device_strings
) {
2933 pa_log("Mapping %s is missing device strings.", m
->name
);
2937 if ((m
->input_path_names
&& m
->input_element
) ||
2938 (m
->output_path_names
&& m
->output_element
)) {
2939 pa_log("Mapping %s must have either mixer path or mixer elment, not both.", m
->name
);
2943 if (!m
->description
)
2944 m
->description
= pa_xstrdup(lookup_description(m
->name
,
2945 well_known_descriptions
,
2946 PA_ELEMENTSOF(well_known_descriptions
)));
2948 if (!m
->description
)
2949 m
->description
= pa_xstrdup(m
->name
);
2952 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
2954 else if (m
->channel_map
.channels
== bonus
->channels
)
2961 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
2962 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
2966 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
2968 pa_strnull(m
->description
),
2970 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
2971 pa_yes_no(m
->supported
),
2975 static void profile_set_add_auto_pair(
2976 pa_alsa_profile_set
*ps
,
2977 pa_alsa_mapping
*m
, /* output */
2978 pa_alsa_mapping
*n
/* input */) {
2986 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
2989 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
2993 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
2995 name
= pa_sprintf_malloc("output:%s", m
->name
);
2997 name
= pa_sprintf_malloc("input:%s", n
->name
);
2999 if (pa_hashmap_get(ps
->profiles
, name
)) {
3004 p
= pa_xnew0(pa_alsa_profile
, 1);
3005 p
->profile_set
= ps
;
3009 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3010 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3011 p
->priority
+= m
->priority
* 100;
3015 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3016 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3017 p
->priority
+= n
->priority
;
3020 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3023 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3024 pa_alsa_mapping
*m
, *n
;
3025 void *m_state
, *n_state
;
3029 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3030 profile_set_add_auto_pair(ps
, m
, NULL
);
3032 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3033 profile_set_add_auto_pair(ps
, m
, n
);
3036 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3037 profile_set_add_auto_pair(ps
, NULL
, n
);
3040 static int profile_verify(pa_alsa_profile
*p
) {
3042 static const struct description_map well_known_descriptions
[] = {
3043 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3044 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3045 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3046 { "off", N_("Off") }
3051 /* Replace the output mapping names by the actual mappings */
3052 if (p
->output_mapping_names
) {
3055 pa_assert(!p
->output_mappings
);
3056 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3058 for (name
= p
->output_mapping_names
; *name
; name
++) {
3061 pa_bool_t duplicate
= FALSE
;
3063 for (in
= name
+ 1; *in
; in
++)
3064 if (pa_streq(*name
, *in
)) {
3072 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3073 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3077 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3083 pa_xstrfreev(p
->output_mapping_names
);
3084 p
->output_mapping_names
= NULL
;
3087 /* Replace the input mapping names by the actual mappings */
3088 if (p
->input_mapping_names
) {
3091 pa_assert(!p
->input_mappings
);
3092 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3094 for (name
= p
->input_mapping_names
; *name
; name
++) {
3097 pa_bool_t duplicate
= FALSE
;
3099 for (in
= name
+ 1; *in
; in
++)
3100 if (pa_streq(*name
, *in
)) {
3108 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3109 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3113 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3119 pa_xstrfreev(p
->input_mapping_names
);
3120 p
->input_mapping_names
= NULL
;
3123 if (!p
->input_mappings
&& !p
->output_mappings
) {
3124 pa_log("Profile '%s' lacks mappings.", p
->name
);
3128 if (!p
->description
)
3129 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3130 well_known_descriptions
,
3131 PA_ELEMENTSOF(well_known_descriptions
)));
3133 if (!p
->description
) {
3138 sb
= pa_strbuf_new();
3140 if (p
->output_mappings
)
3141 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3142 if (!pa_strbuf_isempty(sb
))
3143 pa_strbuf_puts(sb
, " + ");
3145 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
3148 if (p
->input_mappings
)
3149 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3150 if (!pa_strbuf_isempty(sb
))
3151 pa_strbuf_puts(sb
, " + ");
3153 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
3156 p
->description
= pa_strbuf_tostring_free(sb
);
3162 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
3167 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3169 pa_strnull(p
->description
),
3171 pa_yes_no(p
->supported
),
3172 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
3173 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
3175 if (p
->input_mappings
)
3176 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
3177 pa_log_debug("Input %s", m
->name
);
3179 if (p
->output_mappings
)
3180 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
3181 pa_log_debug("Output %s", m
->name
);
3184 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
3185 pa_alsa_profile_set
*ps
;
3192 static pa_config_item items
[] = {
3194 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
3197 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
3198 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
3199 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
3200 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
3201 { "element-input", mapping_parse_element
, NULL
, NULL
},
3202 { "element-output", mapping_parse_element
, NULL
, NULL
},
3203 { "direction", mapping_parse_direction
, NULL
, NULL
},
3205 /* Shared by [Mapping ...] and [Profile ...] */
3206 { "description", mapping_parse_description
, NULL
, NULL
},
3207 { "priority", mapping_parse_priority
, NULL
, NULL
},
3210 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
3211 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
3212 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
3213 { NULL
, NULL
, NULL
, NULL
}
3216 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
3217 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3218 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3220 items
[0].data
= &ps
->auto_profiles
;
3223 fname
= "default.conf";
3225 fn
= pa_maybe_prefix_path(fname
,
3226 #if defined(__linux__) && !defined(__OPTIMIZE__)
3227 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
3229 PA_ALSA_PROFILE_SETS_DIR
);
3231 r
= pa_config_parse(fn
, NULL
, items
, ps
);
3237 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3238 if (mapping_verify(m
, bonus
) < 0)
3241 if (ps
->auto_profiles
)
3242 profile_set_add_auto(ps
);
3244 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3245 if (profile_verify(p
) < 0)
3251 pa_alsa_profile_set_free(ps
);
3255 void pa_alsa_profile_set_probe(
3256 pa_alsa_profile_set
*ps
,
3258 const pa_sample_spec
*ss
,
3259 unsigned default_n_fragments
,
3260 unsigned default_fragment_size_msec
) {
3263 pa_alsa_profile
*p
, *last
= NULL
;
3273 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
3274 pa_sample_spec try_ss
;
3275 pa_channel_map try_map
;
3276 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
3279 /* Is this already marked that it is supported? (i.e. from the config file) */
3283 pa_log_debug("Looking at profile %s", p
->name
);
3285 /* Close PCMs from the last iteration we don't need anymore */
3286 if (last
&& last
->output_mappings
)
3287 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
) {
3292 if (last
->supported
)
3295 if (!p
->output_mappings
|| !pa_idxset_get_by_data(p
->output_mappings
, m
, NULL
)) {
3296 snd_pcm_close(m
->output_pcm
);
3297 m
->output_pcm
= NULL
;
3301 if (last
&& last
->input_mappings
)
3302 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
) {
3307 if (last
->supported
)
3310 if (!p
->input_mappings
|| !pa_idxset_get_by_data(p
->input_mappings
, m
, NULL
)) {
3311 snd_pcm_close(m
->input_pcm
);
3312 m
->input_pcm
= NULL
;
3316 p
->supported
= TRUE
;
3318 /* Check if we can open all new ones */
3319 if (p
->output_mappings
)
3320 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3325 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
3326 try_map
= m
->channel_map
;
3328 try_ss
.channels
= try_map
.channels
;
3331 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
3332 pa_frame_size(&try_ss
);
3333 try_buffer_size
= default_n_fragments
* try_period_size
;
3335 if (!(m
->output_pcm
= pa_alsa_open_by_template(
3340 SND_PCM_STREAM_PLAYBACK
,
3341 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3343 p
->supported
= FALSE
;
3348 if (p
->input_mappings
&& p
->supported
)
3349 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3354 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
3355 try_map
= m
->channel_map
;
3357 try_ss
.channels
= try_map
.channels
;
3360 pa_usec_to_bytes(default_fragment_size_msec
*PA_USEC_PER_MSEC
, &try_ss
) /
3361 pa_frame_size(&try_ss
);
3362 try_buffer_size
= default_n_fragments
* try_period_size
;
3364 if (!(m
->input_pcm
= pa_alsa_open_by_template(
3369 SND_PCM_STREAM_CAPTURE
,
3370 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3372 p
->supported
= FALSE
;
3380 pa_log_debug("Profile %s supported.", p
->name
);
3387 if (last
->output_mappings
)
3388 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
)
3389 if (m
->output_pcm
) {
3391 if (last
->supported
)
3394 snd_pcm_close(m
->output_pcm
);
3395 m
->output_pcm
= NULL
;
3398 if (last
->input_mappings
)
3399 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
)
3402 if (last
->supported
)
3405 snd_pcm_close(m
->input_pcm
);
3406 m
->input_pcm
= NULL
;
3410 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3411 if (!p
->supported
) {
3412 pa_hashmap_remove(ps
->profiles
, p
->name
);
3416 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3417 if (m
->supported
<= 0) {
3418 pa_hashmap_remove(ps
->mappings
, m
->name
);
3425 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
3432 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u",
3435 pa_yes_no(ps
->auto_profiles
),
3436 pa_yes_no(ps
->probed
),
3437 pa_hashmap_size(ps
->mappings
),
3438 pa_hashmap_size(ps
->profiles
));
3440 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3441 pa_alsa_mapping_dump(m
);
3443 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3444 pa_alsa_profile_dump(p
);
3447 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
) {
3454 /* if there is no path, we don't want a port list */
3458 if (!ps
->paths
->next
){
3461 /* If there is only one path, but no or only one setting, then
3462 * we want a port list either */
3463 if (!ps
->paths
->settings
|| !ps
->paths
->settings
->next
)
3466 /* Ok, there is only one path, however with multiple settings,
3467 * so let's create a port for each setting */
3468 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3470 PA_LLIST_FOREACH(s
, ps
->paths
->settings
) {
3471 pa_device_port
*port
;
3472 pa_alsa_port_data
*data
;
3474 port
= pa_device_port_new(s
->name
, s
->description
, sizeof(pa_alsa_port_data
));
3475 port
->priority
= s
->priority
;
3477 data
= PA_DEVICE_PORT_DATA(port
);
3478 data
->path
= ps
->paths
;
3481 pa_hashmap_put(*p
, port
->name
, port
);
3486 /* We have multiple paths, so let's create a port for each
3487 * one, and each of each settings */
3488 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3490 PA_LLIST_FOREACH(path
, ps
->paths
) {
3492 if (!path
->settings
|| !path
->settings
->next
) {
3493 pa_device_port
*port
;
3494 pa_alsa_port_data
*data
;
3496 /* If there is no or just one setting we only need a
3499 port
= pa_device_port_new(path
->name
, path
->description
, sizeof(pa_alsa_port_data
));
3500 port
->priority
= path
->priority
* 100;
3503 data
= PA_DEVICE_PORT_DATA(port
);
3505 data
->setting
= path
->settings
;
3507 pa_hashmap_put(*p
, port
->name
, port
);
3511 PA_LLIST_FOREACH(s
, path
->settings
) {
3512 pa_device_port
*port
;
3513 pa_alsa_port_data
*data
;
3516 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
3518 if (s
->description
[0])
3519 d
= pa_sprintf_malloc(_("%s / %s"), path
->description
, s
->description
);
3521 d
= pa_xstrdup(path
->description
);
3523 port
= pa_device_port_new(n
, d
, sizeof(pa_alsa_port_data
));
3524 port
->priority
= path
->priority
* 100 + s
->priority
;
3529 data
= PA_DEVICE_PORT_DATA(port
);
3533 pa_hashmap_put(*p
, port
->name
, port
);
3539 pa_log_debug("Added %u ports", pa_hashmap_size(*p
));