2 This file is part of PulseAudio.
4 Copyright 2004-2009 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 #include <sys/types.h>
28 #include <asoundlib.h>
31 #ifdef HAVE_VALGRIND_MEMCHECK_H
32 #include <valgrind/memcheck.h>
35 #include <pulse/mainloop-api.h>
36 #include <pulse/sample.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/volume.h>
40 #include <pulse/xmalloc.h>
41 #include <pulse/utf8.h>
43 #include <pulsecore/i18n.h>
44 #include <pulsecore/log.h>
45 #include <pulsecore/macro.h>
46 #include <pulsecore/core-util.h>
47 #include <pulsecore/conf-parser.h>
48 #include <pulsecore/strbuf.h>
50 #include "alsa-mixer.h"
51 #include "alsa-util.h"
53 static int setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
);
55 struct description_map
{
57 const char *description
;
60 static const char *lookup_description(const char *key
, const struct description_map dm
[], unsigned n
) {
66 for (i
= 0; i
< n
; i
++)
67 if (pa_streq(dm
[i
].key
, key
))
68 return _(dm
[i
].description
);
73 struct pa_alsa_fdlist
{
76 /* This is a temporary buffer used to avoid lots of mallocs */
77 struct pollfd
*work_fds
;
83 pa_defer_event
*defer
;
88 void (*cb
)(void *userdata
);
92 static void io_cb(pa_mainloop_api
*a
, pa_io_event
*e
, int fd
, pa_io_event_flags_t events
, void *userdata
) {
94 struct pa_alsa_fdlist
*fdl
= userdata
;
97 unsigned short revents
;
101 pa_assert(fdl
->mixer
|| fdl
->hctl
);
103 pa_assert(fdl
->work_fds
);
110 memcpy(fdl
->work_fds
, fdl
->fds
, sizeof(struct pollfd
) * fdl
->num_fds
);
112 for (i
= 0; i
< fdl
->num_fds
; i
++) {
113 if (e
== fdl
->ios
[i
]) {
114 if (events
& PA_IO_EVENT_INPUT
)
115 fdl
->work_fds
[i
].revents
|= POLLIN
;
116 if (events
& PA_IO_EVENT_OUTPUT
)
117 fdl
->work_fds
[i
].revents
|= POLLOUT
;
118 if (events
& PA_IO_EVENT_ERROR
)
119 fdl
->work_fds
[i
].revents
|= POLLERR
;
120 if (events
& PA_IO_EVENT_HANGUP
)
121 fdl
->work_fds
[i
].revents
|= POLLHUP
;
126 pa_assert(i
!= fdl
->num_fds
);
129 err
= snd_hctl_poll_descriptors_revents(fdl
->hctl
, fdl
->work_fds
, fdl
->num_fds
, &revents
);
131 err
= snd_mixer_poll_descriptors_revents(fdl
->mixer
, fdl
->work_fds
, fdl
->num_fds
, &revents
);
134 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err
));
138 a
->defer_enable(fdl
->defer
, 1);
142 snd_hctl_handle_events(fdl
->hctl
);
144 snd_mixer_handle_events(fdl
->mixer
);
148 static void defer_cb(pa_mainloop_api
*a
, pa_defer_event
*e
, void *userdata
) {
149 struct pa_alsa_fdlist
*fdl
= userdata
;
156 pa_assert(fdl
->mixer
|| fdl
->hctl
);
158 a
->defer_enable(fdl
->defer
, 0);
161 n
= snd_hctl_poll_descriptors_count(fdl
->hctl
);
163 n
= snd_mixer_poll_descriptors_count(fdl
->mixer
);
166 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n
));
169 num_fds
= (unsigned) n
;
171 if (num_fds
!= fdl
->num_fds
) {
175 pa_xfree(fdl
->work_fds
);
176 fdl
->fds
= pa_xnew0(struct pollfd
, num_fds
);
177 fdl
->work_fds
= pa_xnew(struct pollfd
, num_fds
);
180 memset(fdl
->work_fds
, 0, sizeof(struct pollfd
) * num_fds
);
183 err
= snd_hctl_poll_descriptors(fdl
->hctl
, fdl
->work_fds
, num_fds
);
185 err
= snd_mixer_poll_descriptors(fdl
->mixer
, fdl
->work_fds
, num_fds
);
188 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err
));
194 if (memcmp(fdl
->fds
, fdl
->work_fds
, sizeof(struct pollfd
) * num_fds
) == 0)
198 for (i
= 0; i
< fdl
->num_fds
; i
++)
199 a
->io_free(fdl
->ios
[i
]);
201 if (num_fds
!= fdl
->num_fds
) {
208 fdl
->ios
= pa_xnew(pa_io_event
*, num_fds
);
211 temp
= fdl
->work_fds
;
212 fdl
->work_fds
= fdl
->fds
;
215 fdl
->num_fds
= num_fds
;
217 for (i
= 0;i
< num_fds
;i
++)
218 fdl
->ios
[i
] = a
->io_new(a
, fdl
->fds
[i
].fd
,
219 ((fdl
->fds
[i
].events
& POLLIN
) ? PA_IO_EVENT_INPUT
: 0) |
220 ((fdl
->fds
[i
].events
& POLLOUT
) ? PA_IO_EVENT_OUTPUT
: 0),
224 struct pa_alsa_fdlist
*pa_alsa_fdlist_new(void) {
225 struct pa_alsa_fdlist
*fdl
;
227 fdl
= pa_xnew0(struct pa_alsa_fdlist
, 1);
232 void pa_alsa_fdlist_free(struct pa_alsa_fdlist
*fdl
) {
237 fdl
->m
->defer_free(fdl
->defer
);
243 for (i
= 0; i
< fdl
->num_fds
; i
++)
244 fdl
->m
->io_free(fdl
->ios
[i
]);
251 pa_xfree(fdl
->work_fds
);
256 /* We can listen to either a snd_hctl_t or a snd_mixer_t, but not both */
257 int pa_alsa_fdlist_set_handle(struct pa_alsa_fdlist
*fdl
, snd_mixer_t
*mixer_handle
, snd_hctl_t
*hctl_handle
, pa_mainloop_api
*m
) {
259 pa_assert(hctl_handle
|| mixer_handle
);
260 pa_assert(!(hctl_handle
&& mixer_handle
));
264 fdl
->hctl
= hctl_handle
;
265 fdl
->mixer
= mixer_handle
;
267 fdl
->defer
= m
->defer_new(m
, defer_cb
, fdl
);
272 struct pa_alsa_mixer_pdata
{
274 pa_rtpoll_item
*poll_item
;
278 struct pa_alsa_mixer_pdata
*pa_alsa_mixer_pdata_new(void) {
279 struct pa_alsa_mixer_pdata
*pd
;
281 pd
= pa_xnew0(struct pa_alsa_mixer_pdata
, 1);
286 void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata
*pd
) {
290 pa_rtpoll_item_free(pd
->poll_item
);
296 static int rtpoll_work_cb(pa_rtpoll_item
*i
) {
297 struct pa_alsa_mixer_pdata
*pd
;
300 unsigned short revents
= 0;
303 pd
= pa_rtpoll_item_get_userdata(i
);
305 pa_assert_fp(i
== pd
->poll_item
);
307 p
= pa_rtpoll_item_get_pollfd(i
, &n_fds
);
309 if ((err
= snd_mixer_poll_descriptors_revents(pd
->mixer
, p
, n_fds
, &revents
)) < 0) {
310 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err
));
316 if (revents
& (POLLNVAL
| POLLERR
)) {
317 pa_log_debug("Device disconnected, stopping poll on mixer");
319 } else if (revents
& POLLERR
) {
320 /* This shouldn't happen. */
321 pa_log_error("Got a POLLERR (revents = %04x), stopping poll on mixer", revents
);
325 err
= snd_mixer_handle_events(pd
->mixer
);
327 if (PA_LIKELY(err
>= 0)) {
328 pa_rtpoll_item_free(i
);
329 pa_alsa_set_mixer_rtpoll(pd
, pd
->mixer
, pd
->rtpoll
);
331 pa_log_error("Error handling mixer event: %s", pa_alsa_strerror(err
));
340 pa_rtpoll_item_free(i
);
342 pd
->poll_item
= NULL
;
349 int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata
*pd
, snd_mixer_t
*mixer
, pa_rtpoll
*rtp
) {
358 if ((n
= snd_mixer_poll_descriptors_count(mixer
)) < 0) {
359 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n
));
363 i
= pa_rtpoll_item_new(rtp
, PA_RTPOLL_LATE
, (unsigned) n
);
365 p
= pa_rtpoll_item_get_pollfd(i
, NULL
);
367 memset(p
, 0, sizeof(struct pollfd
) * n
);
369 if ((err
= snd_mixer_poll_descriptors(mixer
, p
, (unsigned) n
)) < 0) {
370 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err
));
371 pa_rtpoll_item_free(i
);
379 pa_rtpoll_item_set_userdata(i
, pd
);
380 pa_rtpoll_item_set_work_callback(i
, rtpoll_work_cb
);
385 static const snd_mixer_selem_channel_id_t alsa_channel_ids
[PA_CHANNEL_POSITION_MAX
] = {
386 [PA_CHANNEL_POSITION_MONO
] = SND_MIXER_SCHN_MONO
, /* The ALSA name is just an alias! */
388 [PA_CHANNEL_POSITION_FRONT_CENTER
] = SND_MIXER_SCHN_FRONT_CENTER
,
389 [PA_CHANNEL_POSITION_FRONT_LEFT
] = SND_MIXER_SCHN_FRONT_LEFT
,
390 [PA_CHANNEL_POSITION_FRONT_RIGHT
] = SND_MIXER_SCHN_FRONT_RIGHT
,
392 [PA_CHANNEL_POSITION_REAR_CENTER
] = SND_MIXER_SCHN_REAR_CENTER
,
393 [PA_CHANNEL_POSITION_REAR_LEFT
] = SND_MIXER_SCHN_REAR_LEFT
,
394 [PA_CHANNEL_POSITION_REAR_RIGHT
] = SND_MIXER_SCHN_REAR_RIGHT
,
396 [PA_CHANNEL_POSITION_LFE
] = SND_MIXER_SCHN_WOOFER
,
398 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
399 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
401 [PA_CHANNEL_POSITION_SIDE_LEFT
] = SND_MIXER_SCHN_SIDE_LEFT
,
402 [PA_CHANNEL_POSITION_SIDE_RIGHT
] = SND_MIXER_SCHN_SIDE_RIGHT
,
404 [PA_CHANNEL_POSITION_AUX0
] = SND_MIXER_SCHN_UNKNOWN
,
405 [PA_CHANNEL_POSITION_AUX1
] = SND_MIXER_SCHN_UNKNOWN
,
406 [PA_CHANNEL_POSITION_AUX2
] = SND_MIXER_SCHN_UNKNOWN
,
407 [PA_CHANNEL_POSITION_AUX3
] = SND_MIXER_SCHN_UNKNOWN
,
408 [PA_CHANNEL_POSITION_AUX4
] = SND_MIXER_SCHN_UNKNOWN
,
409 [PA_CHANNEL_POSITION_AUX5
] = SND_MIXER_SCHN_UNKNOWN
,
410 [PA_CHANNEL_POSITION_AUX6
] = SND_MIXER_SCHN_UNKNOWN
,
411 [PA_CHANNEL_POSITION_AUX7
] = SND_MIXER_SCHN_UNKNOWN
,
412 [PA_CHANNEL_POSITION_AUX8
] = SND_MIXER_SCHN_UNKNOWN
,
413 [PA_CHANNEL_POSITION_AUX9
] = SND_MIXER_SCHN_UNKNOWN
,
414 [PA_CHANNEL_POSITION_AUX10
] = SND_MIXER_SCHN_UNKNOWN
,
415 [PA_CHANNEL_POSITION_AUX11
] = SND_MIXER_SCHN_UNKNOWN
,
416 [PA_CHANNEL_POSITION_AUX12
] = SND_MIXER_SCHN_UNKNOWN
,
417 [PA_CHANNEL_POSITION_AUX13
] = SND_MIXER_SCHN_UNKNOWN
,
418 [PA_CHANNEL_POSITION_AUX14
] = SND_MIXER_SCHN_UNKNOWN
,
419 [PA_CHANNEL_POSITION_AUX15
] = SND_MIXER_SCHN_UNKNOWN
,
420 [PA_CHANNEL_POSITION_AUX16
] = SND_MIXER_SCHN_UNKNOWN
,
421 [PA_CHANNEL_POSITION_AUX17
] = SND_MIXER_SCHN_UNKNOWN
,
422 [PA_CHANNEL_POSITION_AUX18
] = SND_MIXER_SCHN_UNKNOWN
,
423 [PA_CHANNEL_POSITION_AUX19
] = SND_MIXER_SCHN_UNKNOWN
,
424 [PA_CHANNEL_POSITION_AUX20
] = SND_MIXER_SCHN_UNKNOWN
,
425 [PA_CHANNEL_POSITION_AUX21
] = SND_MIXER_SCHN_UNKNOWN
,
426 [PA_CHANNEL_POSITION_AUX22
] = SND_MIXER_SCHN_UNKNOWN
,
427 [PA_CHANNEL_POSITION_AUX23
] = SND_MIXER_SCHN_UNKNOWN
,
428 [PA_CHANNEL_POSITION_AUX24
] = SND_MIXER_SCHN_UNKNOWN
,
429 [PA_CHANNEL_POSITION_AUX25
] = SND_MIXER_SCHN_UNKNOWN
,
430 [PA_CHANNEL_POSITION_AUX26
] = SND_MIXER_SCHN_UNKNOWN
,
431 [PA_CHANNEL_POSITION_AUX27
] = SND_MIXER_SCHN_UNKNOWN
,
432 [PA_CHANNEL_POSITION_AUX28
] = SND_MIXER_SCHN_UNKNOWN
,
433 [PA_CHANNEL_POSITION_AUX29
] = SND_MIXER_SCHN_UNKNOWN
,
434 [PA_CHANNEL_POSITION_AUX30
] = SND_MIXER_SCHN_UNKNOWN
,
435 [PA_CHANNEL_POSITION_AUX31
] = SND_MIXER_SCHN_UNKNOWN
,
437 [PA_CHANNEL_POSITION_TOP_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
439 [PA_CHANNEL_POSITION_TOP_FRONT_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
440 [PA_CHANNEL_POSITION_TOP_FRONT_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
441 [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
,
443 [PA_CHANNEL_POSITION_TOP_REAR_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
444 [PA_CHANNEL_POSITION_TOP_REAR_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
445 [PA_CHANNEL_POSITION_TOP_REAR_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
448 static void setting_free(pa_alsa_setting
*s
) {
452 pa_idxset_free(s
->options
, NULL
);
455 pa_xfree(s
->description
);
459 static void option_free(pa_alsa_option
*o
) {
462 pa_xfree(o
->alsa_name
);
464 pa_xfree(o
->description
);
468 static void decibel_fix_free(pa_alsa_decibel_fix
*db_fix
) {
471 pa_xfree(db_fix
->name
);
472 pa_xfree(db_fix
->db_values
);
477 static void jack_free(pa_alsa_jack
*j
) {
480 pa_xfree(j
->alsa_name
);
485 static void element_free(pa_alsa_element
*e
) {
489 while ((o
= e
->options
)) {
490 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
495 decibel_fix_free(e
->db_fix
);
497 pa_xfree(e
->alsa_name
);
501 void pa_alsa_path_free(pa_alsa_path
*p
) {
508 while ((j
= p
->jacks
)) {
509 PA_LLIST_REMOVE(pa_alsa_jack
, p
->jacks
, j
);
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
);
523 pa_proplist_free(p
->proplist
);
525 pa_xfree(p
->description
);
529 void pa_alsa_path_set_free(pa_alsa_path_set
*ps
) {
533 pa_hashmap_free(ps
->paths
);
538 static long to_alsa_dB(pa_volume_t v
) {
539 return (long) (pa_sw_volume_to_dB(v
) * 100.0);
542 static pa_volume_t
from_alsa_dB(long v
) {
543 return pa_sw_volume_from_dB((double) v
/ 100.0);
546 static long to_alsa_volume(pa_volume_t v
, long min
, long max
) {
549 w
= (long) round(((double) v
* (double) (max
- min
)) / PA_VOLUME_NORM
) + min
;
550 return PA_CLAMP_UNLIKELY(w
, min
, max
);
553 static pa_volume_t
from_alsa_volume(long v
, long min
, long max
) {
554 return (pa_volume_t
) round(((double) (v
- min
) * PA_VOLUME_NORM
) / (double) (max
- min
));
557 #define SELEM_INIT(sid, name) \
559 snd_mixer_selem_id_alloca(&(sid)); \
560 snd_mixer_selem_id_set_name((sid), (name)); \
561 snd_mixer_selem_id_set_index((sid), 0); \
564 static int element_get_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
565 snd_mixer_selem_id_t
*sid
;
566 snd_mixer_elem_t
*me
;
567 snd_mixer_selem_channel_id_t c
;
568 pa_channel_position_mask_t mask
= 0;
576 SELEM_INIT(sid
, e
->alsa_name
);
577 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
578 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
582 pa_cvolume_mute(v
, cm
->channels
);
584 /* We take the highest volume of all channels that match */
586 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
593 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
594 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
596 if ((r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
)) >= 0) {
597 /* If the channel volume is outside the limits set
598 * by the dB fix, we clamp the hw volume to be
599 * within the limits. */
600 if (value
< e
->db_fix
->min_step
) {
601 value
= e
->db_fix
->min_step
;
602 snd_mixer_selem_set_playback_volume(me
, c
, value
);
603 pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
604 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
605 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
606 } else if (value
> e
->db_fix
->max_step
) {
607 value
= e
->db_fix
->max_step
;
608 snd_mixer_selem_set_playback_volume(me
, c
, value
);
609 pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
610 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
611 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
614 /* Volume step -> dB value conversion. */
615 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
618 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
622 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
624 if ((r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
)) >= 0) {
625 /* If the channel volume is outside the limits set
626 * by the dB fix, we clamp the hw volume to be
627 * within the limits. */
628 if (value
< e
->db_fix
->min_step
) {
629 value
= e
->db_fix
->min_step
;
630 snd_mixer_selem_set_capture_volume(me
, c
, value
);
631 pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
632 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
633 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
634 } else if (value
> e
->db_fix
->max_step
) {
635 value
= e
->db_fix
->max_step
;
636 snd_mixer_selem_set_capture_volume(me
, c
, value
);
637 pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
638 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
639 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
642 /* Volume step -> dB value conversion. */
643 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
646 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
654 #ifdef HAVE_VALGRIND_MEMCHECK_H
655 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
658 f
= from_alsa_dB(value
);
663 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
664 if (snd_mixer_selem_has_playback_channel(me
, c
))
665 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
669 if (snd_mixer_selem_has_capture_channel(me
, c
))
670 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
678 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
681 for (k
= 0; k
< cm
->channels
; k
++)
682 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
683 if (v
->values
[k
] < f
)
686 mask
|= e
->masks
[c
][e
->n_channels
-1];
689 for (k
= 0; k
< cm
->channels
; k
++)
690 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
691 v
->values
[k
] = PA_VOLUME_NORM
;
696 int pa_alsa_path_get_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
707 pa_cvolume_reset(v
, cm
->channels
);
709 PA_LLIST_FOREACH(e
, p
->elements
) {
712 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
715 pa_assert(!p
->has_dB
|| e
->has_dB
);
717 if (element_get_volume(e
, m
, cm
, &ev
) < 0)
720 /* If we have no dB information all we can do is take the first element and leave */
726 pa_sw_cvolume_multiply(v
, v
, &ev
);
732 static int element_get_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, bool *b
) {
733 snd_mixer_selem_id_t
*sid
;
734 snd_mixer_elem_t
*me
;
735 snd_mixer_selem_channel_id_t c
;
741 SELEM_INIT(sid
, e
->alsa_name
);
742 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
743 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
747 /* We return muted if at least one channel is muted */
749 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
753 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
754 if (snd_mixer_selem_has_playback_channel(me
, c
))
755 r
= snd_mixer_selem_get_playback_switch(me
, c
, &value
);
759 if (snd_mixer_selem_has_capture_channel(me
, c
))
760 r
= snd_mixer_selem_get_capture_switch(me
, c
, &value
);
778 int pa_alsa_path_get_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, bool *muted
) {
788 PA_LLIST_FOREACH(e
, p
->elements
) {
791 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
794 if (element_get_switch(e
, m
, &b
) < 0)
807 /* Finds the closest item in db_fix->db_values and returns the corresponding
808 * step. *db_value is replaced with the value from the db_values table.
809 * Rounding is done based on the rounding parameter: -1 means rounding down and
810 * +1 means rounding up. */
811 static long decibel_fix_get_step(pa_alsa_decibel_fix
*db_fix
, long *db_value
, int rounding
) {
817 pa_assert(rounding
!= 0);
819 max_i
= db_fix
->max_step
- db_fix
->min_step
;
822 for (i
= 0; i
< max_i
; i
++) {
823 if (db_fix
->db_values
[i
] >= *db_value
)
827 for (i
= 0; i
< max_i
; i
++) {
828 if (db_fix
->db_values
[i
+ 1] > *db_value
)
833 *db_value
= db_fix
->db_values
[i
];
835 return i
+ db_fix
->min_step
;
838 /* Alsa lib documentation says for snd_mixer_selem_set_playback_dB() direction argument,
839 * that "-1 = accurate or first below, 0 = accurate, 1 = accurate or first above".
840 * But even with accurate nearest dB volume step is not selected, so that is why we need
841 * this function. Returns 0 and nearest selectable volume in *value_dB on success or
842 * negative error code if fails. */
843 static int element_get_nearest_alsa_dB(snd_mixer_elem_t
*me
, snd_mixer_selem_channel_id_t c
, pa_alsa_direction_t d
, long *value_dB
) {
853 if (d
== PA_ALSA_DIRECTION_OUTPUT
) {
854 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, *value_dB
, +1, &alsa_val
)) >= 0)
855 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value_high
);
860 if (value_high
== *value_dB
)
863 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, *value_dB
, -1, &alsa_val
)) >= 0)
864 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value_low
);
866 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, *value_dB
, +1, &alsa_val
)) >= 0)
867 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value_high
);
872 if (value_high
== *value_dB
)
875 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, *value_dB
, -1, &alsa_val
)) >= 0)
876 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value_low
);
882 if (labs(value_high
- *value_dB
) < labs(value_low
- *value_dB
))
883 *value_dB
= value_high
;
885 *value_dB
= value_low
;
890 static int element_set_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
, bool deferred_volume
, bool write_to_hw
) {
892 snd_mixer_selem_id_t
*sid
;
894 snd_mixer_elem_t
*me
;
895 snd_mixer_selem_channel_id_t c
;
896 pa_channel_position_mask_t mask
= 0;
903 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
905 SELEM_INIT(sid
, e
->alsa_name
);
906 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
907 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
911 pa_cvolume_mute(&rv
, cm
->channels
);
913 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
915 pa_volume_t f
= PA_VOLUME_MUTED
;
918 for (k
= 0; k
< cm
->channels
; k
++)
919 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
])) {
921 if (v
->values
[k
] > f
)
926 /* Hmm, so this channel does not exist in the volume
927 * struct, so let's bind it to the overall max of the
929 f
= pa_cvolume_max(v
);
933 long value
= to_alsa_dB(f
);
936 if (e
->volume_limit
>= 0 && value
> (e
->max_dB
* 100))
937 value
= e
->max_dB
* 100;
939 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
940 /* If we call set_playback_volume() without checking first
941 * if the channel is available, ALSA behaves very
942 * strangely and doesn't fail the call */
943 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
947 r
= snd_mixer_selem_set_playback_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
949 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
955 if (deferred_volume
) {
956 if ((r
= element_get_nearest_alsa_dB(me
, c
, PA_ALSA_DIRECTION_OUTPUT
, &value
)) >= 0)
957 r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, 0);
959 if ((r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, rounding
)) >= 0)
960 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
964 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
965 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value
);
971 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
975 r
= snd_mixer_selem_set_capture_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
977 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
983 if (deferred_volume
) {
984 if ((r
= element_get_nearest_alsa_dB(me
, c
, PA_ALSA_DIRECTION_INPUT
, &value
)) >= 0)
985 r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, 0);
987 if ((r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, rounding
)) >= 0)
988 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
992 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
993 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value
);
1003 #ifdef HAVE_VALGRIND_MEMCHECK_H
1004 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
1007 f
= from_alsa_dB(value
);
1012 value
= to_alsa_volume(f
, e
->min_volume
, e
->max_volume
);
1014 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1015 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
1016 if ((r
= snd_mixer_selem_set_playback_volume(me
, c
, value
)) >= 0)
1017 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
1021 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
1022 if ((r
= snd_mixer_selem_set_capture_volume(me
, c
, value
)) >= 0)
1023 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
1031 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
1034 for (k
= 0; k
< cm
->channels
; k
++)
1035 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
1036 if (rv
.values
[k
] < f
)
1039 mask
|= e
->masks
[c
][e
->n_channels
-1];
1042 for (k
= 0; k
< cm
->channels
; k
++)
1043 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
1044 rv
.values
[k
] = PA_VOLUME_NORM
;
1050 int pa_alsa_path_set_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
, bool deferred_volume
, bool write_to_hw
) {
1059 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
1064 rv
= *v
; /* Remaining adjustment */
1065 pa_cvolume_reset(v
, cm
->channels
); /* Adjustment done */
1067 PA_LLIST_FOREACH(e
, p
->elements
) {
1070 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
1073 pa_assert(!p
->has_dB
|| e
->has_dB
);
1076 if (element_set_volume(e
, m
, cm
, &ev
, deferred_volume
, write_to_hw
) < 0)
1084 pa_sw_cvolume_multiply(v
, v
, &ev
);
1085 pa_sw_cvolume_divide(&rv
, &rv
, &ev
);
1091 static int element_set_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, bool b
) {
1092 snd_mixer_elem_t
*me
;
1093 snd_mixer_selem_id_t
*sid
;
1099 SELEM_INIT(sid
, e
->alsa_name
);
1100 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1101 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1105 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1106 r
= snd_mixer_selem_set_playback_switch_all(me
, b
);
1108 r
= snd_mixer_selem_set_capture_switch_all(me
, b
);
1111 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1116 int pa_alsa_path_set_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, bool muted
) {
1125 PA_LLIST_FOREACH(e
, p
->elements
) {
1127 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
1130 if (element_set_switch(e
, m
, !muted
) < 0)
1137 /* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
1138 * function sets all channels of the volume element to e->min_volume, 0 dB or
1139 * e->constant_volume. */
1140 static int element_set_constant_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1141 snd_mixer_elem_t
*me
= NULL
;
1142 snd_mixer_selem_id_t
*sid
= NULL
;
1145 bool volume_set
= false;
1150 SELEM_INIT(sid
, e
->alsa_name
);
1151 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1152 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1156 switch (e
->volume_use
) {
1157 case PA_ALSA_VOLUME_OFF
:
1158 volume
= e
->min_volume
;
1162 case PA_ALSA_VOLUME_ZERO
:
1166 volume
= decibel_fix_get_step(e
->db_fix
, &dB
, (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
? +1 : -1));
1171 case PA_ALSA_VOLUME_CONSTANT
:
1172 volume
= e
->constant_volume
;
1177 pa_assert_not_reached();
1181 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1182 r
= snd_mixer_selem_set_playback_volume_all(me
, volume
);
1184 r
= snd_mixer_selem_set_capture_volume_all(me
, volume
);
1186 pa_assert(e
->volume_use
== PA_ALSA_VOLUME_ZERO
);
1187 pa_assert(!e
->db_fix
);
1189 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1190 r
= snd_mixer_selem_set_playback_dB_all(me
, 0, +1);
1192 r
= snd_mixer_selem_set_capture_dB_all(me
, 0, -1);
1196 pa_log_warn("Failed to set volume of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1201 int pa_alsa_path_select(pa_alsa_path
*p
, pa_alsa_setting
*s
, snd_mixer_t
*m
, bool device_is_muted
) {
1208 pa_log_debug("Activating path %s", p
->name
);
1209 pa_alsa_path_dump(p
);
1211 /* First turn on hw mute if available, to avoid noise
1212 * when setting the mixer controls. */
1213 if (p
->mute_during_activation
) {
1214 PA_LLIST_FOREACH(e
, p
->elements
) {
1215 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
1216 /* If the muting fails here, that's not a critical problem for
1217 * selecting a path, so we ignore the return value.
1218 * element_set_switch() will print a warning anyway, so this
1219 * won't be a silent failure either. */
1220 (void) element_set_switch(e
, m
, false);
1224 PA_LLIST_FOREACH(e
, p
->elements
) {
1226 switch (e
->switch_use
) {
1227 case PA_ALSA_SWITCH_OFF
:
1228 r
= element_set_switch(e
, m
, false);
1231 case PA_ALSA_SWITCH_ON
:
1232 r
= element_set_switch(e
, m
, true);
1235 case PA_ALSA_SWITCH_MUTE
:
1236 case PA_ALSA_SWITCH_IGNORE
:
1237 case PA_ALSA_SWITCH_SELECT
:
1245 switch (e
->volume_use
) {
1246 case PA_ALSA_VOLUME_OFF
:
1247 case PA_ALSA_VOLUME_ZERO
:
1248 case PA_ALSA_VOLUME_CONSTANT
:
1249 r
= element_set_constant_volume(e
, m
);
1252 case PA_ALSA_VOLUME_MERGE
:
1253 case PA_ALSA_VOLUME_IGNORE
:
1263 setting_select(s
, m
);
1265 /* Finally restore hw mute to the device mute status. */
1266 if (p
->mute_during_activation
) {
1267 PA_LLIST_FOREACH(e
, p
->elements
) {
1268 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
) {
1269 if (element_set_switch(e
, m
, !device_is_muted
) < 0)
1278 static int check_required(pa_alsa_element
*e
, snd_mixer_elem_t
*me
) {
1280 bool has_enumeration
;
1286 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1288 snd_mixer_selem_has_playback_switch(me
) ||
1289 (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
));
1292 snd_mixer_selem_has_capture_switch(me
) ||
1293 (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
));
1296 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1298 snd_mixer_selem_has_playback_volume(me
) ||
1299 (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
));
1302 snd_mixer_selem_has_capture_volume(me
) ||
1303 (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
));
1306 has_enumeration
= snd_mixer_selem_is_enumerated(me
);
1308 if ((e
->required
== PA_ALSA_REQUIRED_SWITCH
&& !has_switch
) ||
1309 (e
->required
== PA_ALSA_REQUIRED_VOLUME
&& !has_volume
) ||
1310 (e
->required
== PA_ALSA_REQUIRED_ENUMERATION
&& !has_enumeration
))
1313 if (e
->required
== PA_ALSA_REQUIRED_ANY
&& !(has_switch
|| has_volume
|| has_enumeration
))
1316 if ((e
->required_absent
== PA_ALSA_REQUIRED_SWITCH
&& has_switch
) ||
1317 (e
->required_absent
== PA_ALSA_REQUIRED_VOLUME
&& has_volume
) ||
1318 (e
->required_absent
== PA_ALSA_REQUIRED_ENUMERATION
&& has_enumeration
))
1321 if (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& (has_switch
|| has_volume
|| has_enumeration
))
1324 if (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) {
1325 switch (e
->required_any
) {
1326 case PA_ALSA_REQUIRED_VOLUME
:
1327 e
->path
->req_any_present
|= (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
);
1329 case PA_ALSA_REQUIRED_SWITCH
:
1330 e
->path
->req_any_present
|= (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
);
1332 case PA_ALSA_REQUIRED_ENUMERATION
:
1333 e
->path
->req_any_present
|= (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1335 case PA_ALSA_REQUIRED_ANY
:
1336 e
->path
->req_any_present
|=
1337 (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) ||
1338 (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) ||
1339 (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1342 pa_assert_not_reached();
1346 if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1348 PA_LLIST_FOREACH(o
, e
->options
) {
1349 e
->path
->req_any_present
|= (o
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) &&
1351 if (o
->required
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
< 0)
1353 if (o
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
>= 0)
1361 static int element_probe(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1362 snd_mixer_selem_id_t
*sid
;
1363 snd_mixer_elem_t
*me
;
1369 SELEM_INIT(sid
, e
->alsa_name
);
1371 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1373 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1376 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1377 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1378 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1383 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1384 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1386 if (!snd_mixer_selem_has_playback_switch(me
)) {
1387 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1388 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1390 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1395 if (!snd_mixer_selem_has_capture_switch(me
)) {
1396 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1397 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1399 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1403 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1404 e
->direction_try_other
= false;
1407 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1409 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1411 if (!snd_mixer_selem_has_playback_volume(me
)) {
1412 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1413 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1415 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1420 if (!snd_mixer_selem_has_capture_volume(me
)) {
1421 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1422 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1424 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1428 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1429 long min_dB
= 0, max_dB
= 0;
1432 e
->direction_try_other
= false;
1434 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1435 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1437 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1440 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1444 if (e
->min_volume
>= e
->max_volume
) {
1445 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
);
1446 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1448 } else if (e
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&&
1449 (e
->min_volume
> e
->constant_volume
|| e
->max_volume
< e
->constant_volume
)) {
1450 pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
1451 e
->constant_volume
, e
->alsa_name
, e
->min_volume
, e
->max_volume
);
1452 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1456 pa_channel_position_t p
;
1459 ((e
->min_volume
> e
->db_fix
->min_step
) ||
1460 (e
->max_volume
< e
->db_fix
->max_step
))) {
1461 pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1462 "real hardware range (%li-%li). Disabling the decibel fix.", e
->alsa_name
,
1463 e
->db_fix
->min_step
, e
->db_fix
->max_step
,
1464 e
->min_volume
, e
->max_volume
);
1466 decibel_fix_free(e
->db_fix
);
1472 e
->min_volume
= e
->db_fix
->min_step
;
1473 e
->max_volume
= e
->db_fix
->max_step
;
1474 min_dB
= e
->db_fix
->db_values
[0];
1475 max_dB
= e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
];
1476 } else if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1477 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1479 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1481 /* Check that the kernel driver returns consistent limits with
1482 * both _get_*_dB_range() and _ask_*_vol_dB(). */
1483 if (e
->has_dB
&& !e
->db_fix
) {
1484 long min_dB_checked
= 0;
1485 long max_dB_checked
= 0;
1487 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1488 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1490 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1493 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->min_volume
);
1497 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1498 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1500 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1503 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->max_volume
);
1507 if (min_dB
!= min_dB_checked
|| max_dB
!= max_dB_checked
) {
1508 pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
1509 "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
1510 "%0.2f dB at level %li.",
1512 min_dB
/ 100.0, max_dB
/ 100.0,
1513 min_dB_checked
/ 100.0, e
->min_volume
, max_dB_checked
/ 100.0, e
->max_volume
);
1519 #ifdef HAVE_VALGRIND_MEMCHECK_H
1520 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1521 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1524 e
->min_dB
= ((double) min_dB
) / 100.0;
1525 e
->max_dB
= ((double) max_dB
) / 100.0;
1527 if (min_dB
>= max_dB
) {
1528 pa_assert(!e
->db_fix
);
1529 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
);
1534 if (e
->volume_limit
>= 0) {
1535 if (e
->volume_limit
<= e
->min_volume
|| e
->volume_limit
> e
->max_volume
)
1536 pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1537 "%li-%li. The volume limit is ignored.",
1538 e
->alsa_name
, e
->path
->name
, e
->volume_limit
, e
->min_volume
+ 1, e
->max_volume
);
1541 e
->max_volume
= e
->volume_limit
;
1545 e
->db_fix
->max_step
= e
->max_volume
;
1546 e
->max_dB
= ((double) e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
]) / 100.0;
1549 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1550 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB
);
1552 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB
);
1555 pa_log_warn("Failed to get dB value of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1558 e
->max_dB
= ((double) max_dB
) / 100.0;
1564 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1565 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1567 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1572 if (!e
->override_map
) {
1573 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1574 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1577 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1580 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1583 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1586 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1588 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1591 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1592 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1594 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1597 if (e
->n_channels
<= 0) {
1598 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1602 if (e
->n_channels
> 2) {
1603 /* FIXME: In some places code like this is used:
1605 * e->masks[alsa_channel_ids[p]][e->n_channels-1]
1607 * The definition of e->masks is
1609 * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][2];
1611 * Since the array size is fixed at 2, we obviously
1612 * don't support elements with more than two
1614 pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e
->alsa_name
, e
->n_channels
);
1618 if (!e
->override_map
) {
1619 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1622 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1625 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1626 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1628 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1630 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1635 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1636 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1639 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1647 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1650 PA_LLIST_FOREACH(o
, e
->options
)
1651 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1652 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1656 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1657 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1661 PA_LLIST_FOREACH(o
, e
->options
) {
1664 for (i
= 0; i
< n
; i
++) {
1667 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1670 if (!pa_streq(buf
, o
->alsa_name
))
1678 if (check_required(e
, me
) < 0)
1684 static int jack_probe(pa_alsa_jack
*j
, snd_hctl_t
*h
) {
1689 j
->has_control
= pa_alsa_find_jack(h
, j
->alsa_name
) != NULL
;
1691 if (j
->has_control
) {
1692 if (j
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
)
1694 if (j
->required_any
!= PA_ALSA_REQUIRED_IGNORE
)
1695 j
->path
->req_any_present
= true;
1697 if (j
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1704 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, bool prefixed
) {
1711 if (!pa_startswith(section
, "Element "))
1717 /* This is not an element section, but an enum section? */
1718 if (strchr(section
, ':'))
1721 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1722 return p
->last_element
;
1724 PA_LLIST_FOREACH(e
, p
->elements
)
1725 if (pa_streq(e
->alsa_name
, section
))
1728 e
= pa_xnew0(pa_alsa_element
, 1);
1730 e
->alsa_name
= pa_xstrdup(section
);
1731 e
->direction
= p
->direction
;
1732 e
->volume_limit
= -1;
1734 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1737 p
->last_element
= e
;
1741 static pa_alsa_jack
* jack_get(pa_alsa_path
*p
, const char *section
) {
1744 if (!pa_startswith(section
, "Jack "))
1748 if (p
->last_jack
&& pa_streq(p
->last_jack
->name
, section
))
1749 return p
->last_jack
;
1751 PA_LLIST_FOREACH(j
, p
->jacks
)
1752 if (pa_streq(j
->name
, section
))
1755 j
= pa_xnew0(pa_alsa_jack
, 1);
1756 j
->state_unplugged
= PA_AVAILABLE_NO
;
1757 j
->state_plugged
= PA_AVAILABLE_YES
;
1759 j
->name
= pa_xstrdup(section
);
1760 j
->alsa_name
= pa_sprintf_malloc("%s Jack", section
);
1761 PA_LLIST_INSERT_AFTER(pa_alsa_jack
, p
->jacks
, p
->last_jack
, j
);
1768 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1774 if (!pa_startswith(section
, "Option "))
1779 /* This is not an enum section, but an element section? */
1780 if (!(on
= strchr(section
, ':')))
1783 en
= pa_xstrndup(section
, on
- section
);
1786 if (p
->last_option
&&
1787 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1788 pa_streq(p
->last_option
->alsa_name
, on
)) {
1790 return p
->last_option
;
1793 pa_assert_se(e
= element_get(p
, en
, false));
1796 PA_LLIST_FOREACH(o
, e
->options
)
1797 if (pa_streq(o
->alsa_name
, on
))
1800 o
= pa_xnew0(pa_alsa_option
, 1);
1802 o
->alsa_name
= pa_xstrdup(on
);
1805 if (p
->last_option
&& p
->last_option
->element
== e
)
1806 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1808 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1815 static int element_parse_switch(pa_config_parser_state
*state
) {
1821 p
= state
->userdata
;
1823 if (!(e
= element_get(p
, state
->section
, true))) {
1824 pa_log("[%s:%u] Switch makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1828 if (pa_streq(state
->rvalue
, "ignore"))
1829 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1830 else if (pa_streq(state
->rvalue
, "mute"))
1831 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1832 else if (pa_streq(state
->rvalue
, "off"))
1833 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1834 else if (pa_streq(state
->rvalue
, "on"))
1835 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1836 else if (pa_streq(state
->rvalue
, "select"))
1837 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1839 pa_log("[%s:%u] Switch invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1846 static int element_parse_volume(pa_config_parser_state
*state
) {
1852 p
= state
->userdata
;
1854 if (!(e
= element_get(p
, state
->section
, true))) {
1855 pa_log("[%s:%u] Volume makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1859 if (pa_streq(state
->rvalue
, "ignore"))
1860 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1861 else if (pa_streq(state
->rvalue
, "merge"))
1862 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1863 else if (pa_streq(state
->rvalue
, "off"))
1864 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1865 else if (pa_streq(state
->rvalue
, "zero"))
1866 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1870 if (pa_atou(state
->rvalue
, &constant
) >= 0) {
1871 e
->volume_use
= PA_ALSA_VOLUME_CONSTANT
;
1872 e
->constant_volume
= constant
;
1874 pa_log("[%s:%u] Volume invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1882 static int element_parse_enumeration(pa_config_parser_state
*state
) {
1888 p
= state
->userdata
;
1890 if (!(e
= element_get(p
, state
->section
, true))) {
1891 pa_log("[%s:%u] Enumeration makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1895 if (pa_streq(state
->rvalue
, "ignore"))
1896 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1897 else if (pa_streq(state
->rvalue
, "select"))
1898 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1900 pa_log("[%s:%u] Enumeration invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1907 static int option_parse_priority(pa_config_parser_state
*state
) {
1914 p
= state
->userdata
;
1916 if (!(o
= option_get(p
, state
->section
))) {
1917 pa_log("[%s:%u] Priority makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1921 if (pa_atou(state
->rvalue
, &prio
) < 0) {
1922 pa_log("[%s:%u] Priority invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1930 static int option_parse_name(pa_config_parser_state
*state
) {
1936 p
= state
->userdata
;
1938 if (!(o
= option_get(p
, state
->section
))) {
1939 pa_log("[%s:%u] Name makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1944 o
->name
= pa_xstrdup(state
->rvalue
);
1949 static int element_parse_required(pa_config_parser_state
*state
) {
1954 pa_alsa_required_t req
;
1958 p
= state
->userdata
;
1960 e
= element_get(p
, state
->section
, true);
1961 o
= option_get(p
, state
->section
);
1962 j
= jack_get(p
, state
->section
);
1963 if (!e
&& !o
&& !j
) {
1964 pa_log("[%s:%u] Required makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1968 if (pa_streq(state
->rvalue
, "ignore"))
1969 req
= PA_ALSA_REQUIRED_IGNORE
;
1970 else if (pa_streq(state
->rvalue
, "switch") && e
)
1971 req
= PA_ALSA_REQUIRED_SWITCH
;
1972 else if (pa_streq(state
->rvalue
, "volume") && e
)
1973 req
= PA_ALSA_REQUIRED_VOLUME
;
1974 else if (pa_streq(state
->rvalue
, "enumeration"))
1975 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1976 else if (pa_streq(state
->rvalue
, "any"))
1977 req
= PA_ALSA_REQUIRED_ANY
;
1979 pa_log("[%s:%u] Required invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1983 if (pa_streq(state
->lvalue
, "required-absent")) {
1985 e
->required_absent
= req
;
1987 o
->required_absent
= req
;
1989 j
->required_absent
= req
;
1991 else if (pa_streq(state
->lvalue
, "required-any")) {
1993 e
->required_any
= req
;
1994 e
->path
->has_req_any
|= (req
!= PA_ALSA_REQUIRED_IGNORE
);
1997 o
->required_any
= req
;
1998 o
->element
->path
->has_req_any
|= (req
!= PA_ALSA_REQUIRED_IGNORE
);
2001 j
->required_any
= req
;
2002 j
->path
->has_req_any
|= (req
!= PA_ALSA_REQUIRED_IGNORE
);
2018 static int element_parse_direction(pa_config_parser_state
*state
) {
2024 p
= state
->userdata
;
2026 if (!(e
= element_get(p
, state
->section
, true))) {
2027 pa_log("[%s:%u] Direction makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2031 if (pa_streq(state
->rvalue
, "playback"))
2032 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
2033 else if (pa_streq(state
->rvalue
, "capture"))
2034 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
2036 pa_log("[%s:%u] Direction invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
2043 static int element_parse_direction_try_other(pa_config_parser_state
*state
) {
2050 p
= state
->userdata
;
2052 if (!(e
= element_get(p
, state
->section
, true))) {
2053 pa_log("[%s:%u] Direction makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2057 if ((yes
= pa_parse_boolean(state
->rvalue
)) < 0) {
2058 pa_log("[%s:%u] Direction invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
2062 e
->direction_try_other
= !!yes
;
2066 static int element_parse_volume_limit(pa_config_parser_state
*state
) {
2073 p
= state
->userdata
;
2075 if (!(e
= element_get(p
, state
->section
, true))) {
2076 pa_log("[%s:%u] volume-limit makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2080 if (pa_atol(state
->rvalue
, &volume_limit
) < 0 || volume_limit
< 0) {
2081 pa_log("[%s:%u] Invalid value for volume-limit", state
->filename
, state
->lineno
);
2085 e
->volume_limit
= volume_limit
;
2089 static pa_channel_position_mask_t
parse_mask(const char *m
) {
2090 pa_channel_position_mask_t v
;
2092 if (pa_streq(m
, "all-left"))
2093 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
2094 else if (pa_streq(m
, "all-right"))
2095 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
2096 else if (pa_streq(m
, "all-center"))
2097 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
2098 else if (pa_streq(m
, "all-front"))
2099 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
2100 else if (pa_streq(m
, "all-rear"))
2101 v
= PA_CHANNEL_POSITION_MASK_REAR
;
2102 else if (pa_streq(m
, "all-side"))
2103 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
2104 else if (pa_streq(m
, "all-top"))
2105 v
= PA_CHANNEL_POSITION_MASK_TOP
;
2106 else if (pa_streq(m
, "all-no-lfe"))
2107 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
2108 else if (pa_streq(m
, "all"))
2109 v
= PA_CHANNEL_POSITION_MASK_ALL
;
2111 pa_channel_position_t p
;
2113 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
2116 v
= PA_CHANNEL_POSITION_MASK(p
);
2122 static int element_parse_override_map(pa_config_parser_state
*state
) {
2125 const char *split_state
= NULL
;
2131 p
= state
->userdata
;
2133 if (!(e
= element_get(p
, state
->section
, true))) {
2134 pa_log("[%s:%u] Override map makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2138 while ((n
= pa_split(state
->rvalue
, ",", &split_state
))) {
2139 pa_channel_position_mask_t m
;
2144 if ((m
= parse_mask(n
)) == 0) {
2145 pa_log("[%s:%u] Override map '%s' invalid in '%s'", state
->filename
, state
->lineno
, n
, state
->section
);
2151 if (pa_streq(state
->lvalue
, "override-map.1"))
2152 e
->masks
[i
++][0] = m
;
2154 e
->masks
[i
++][1] = m
;
2156 /* Later on we might add override-map.3 and so on here ... */
2161 e
->override_map
= true;
2166 static int jack_parse_state(pa_config_parser_state
*state
) {
2173 p
= state
->userdata
;
2175 if (!(j
= jack_get(p
, state
->section
))) {
2176 pa_log("[%s:%u] state makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2180 if (pa_streq(state
->rvalue
, "yes"))
2181 pa
= PA_AVAILABLE_YES
;
2182 else if (pa_streq(state
->rvalue
, "no"))
2183 pa
= PA_AVAILABLE_NO
;
2184 else if (pa_streq(state
->rvalue
, "unknown"))
2185 pa
= PA_AVAILABLE_UNKNOWN
;
2187 pa_log("[%s:%u] state must be 'yes', 'no' or 'unknown' in '%s'", state
->filename
, state
->lineno
, state
->section
);
2191 if (pa_streq(state
->lvalue
, "state.unplugged"))
2192 j
->state_unplugged
= pa
;
2194 j
->state_plugged
= pa
;
2195 pa_assert(pa_streq(state
->lvalue
, "state.plugged"));
2201 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
2202 snd_mixer_selem_id_t
*sid
;
2203 snd_mixer_elem_t
*me
;
2209 SELEM_INIT(sid
, e
->alsa_name
);
2210 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2211 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2215 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
2217 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
2218 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
2220 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
2223 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2226 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
2228 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
2229 pa_log_warn("Failed to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2235 static int setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
2242 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
2243 element_set_option(o
->element
, m
, o
->alsa_idx
);
2248 static int option_verify(pa_alsa_option
*o
) {
2249 static const struct description_map well_known_descriptions
[] = {
2250 { "input", N_("Input") },
2251 { "input-docking", N_("Docking Station Input") },
2252 { "input-docking-microphone", N_("Docking Station Microphone") },
2253 { "input-docking-linein", N_("Docking Station Line In") },
2254 { "input-linein", N_("Line In") },
2255 { "input-microphone", N_("Microphone") },
2256 { "input-microphone-front", N_("Front Microphone") },
2257 { "input-microphone-rear", N_("Rear Microphone") },
2258 { "input-microphone-external", N_("External Microphone") },
2259 { "input-microphone-internal", N_("Internal Microphone") },
2260 { "input-radio", N_("Radio") },
2261 { "input-video", N_("Video") },
2262 { "input-agc-on", N_("Automatic Gain Control") },
2263 { "input-agc-off", N_("No Automatic Gain Control") },
2264 { "input-boost-on", N_("Boost") },
2265 { "input-boost-off", N_("No Boost") },
2266 { "output-amplifier-on", N_("Amplifier") },
2267 { "output-amplifier-off", N_("No Amplifier") },
2268 { "output-bass-boost-on", N_("Bass Boost") },
2269 { "output-bass-boost-off", N_("No Bass Boost") },
2270 { "output-speaker", N_("Speaker") },
2271 { "output-headphones", N_("Headphones") }
2277 pa_log("No name set for option %s", o
->alsa_name
);
2281 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
2282 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
2283 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
2287 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
2288 !pa_streq(o
->alsa_name
, "on") &&
2289 !pa_streq(o
->alsa_name
, "off")) {
2290 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
2294 if (!o
->description
)
2295 o
->description
= pa_xstrdup(lookup_description(o
->name
,
2296 well_known_descriptions
,
2297 PA_ELEMENTSOF(well_known_descriptions
)));
2298 if (!o
->description
)
2299 o
->description
= pa_xstrdup(o
->name
);
2304 static int element_verify(pa_alsa_element
*e
) {
2309 // 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);
2310 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
2311 (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required_any
== e
->required_absent
) ||
2312 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) ||
2313 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
2314 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
2318 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
2319 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
2323 PA_LLIST_FOREACH(o
, e
->options
)
2324 if (option_verify(o
) < 0)
2330 static int path_verify(pa_alsa_path
*p
) {
2331 static const struct description_map well_known_descriptions
[] = {
2332 { "analog-input", N_("Analog Input") },
2333 { "analog-input-microphone", N_("Microphone") },
2334 { "analog-input-microphone-front", N_("Front Microphone") },
2335 { "analog-input-microphone-rear", N_("Rear Microphone") },
2336 { "analog-input-microphone-dock", N_("Dock Microphone") },
2337 { "analog-input-microphone-internal", N_("Internal Microphone") },
2338 { "analog-input-microphone-headset", N_("Headset Microphone") },
2339 { "analog-input-linein", N_("Line In") },
2340 { "analog-input-radio", N_("Radio") },
2341 { "analog-input-video", N_("Video") },
2342 { "analog-output", N_("Analog Output") },
2343 { "analog-output-headphones", N_("Headphones") },
2344 { "analog-output-lfe-on-mono", N_("LFE on Separate Mono Output") },
2345 { "analog-output-lineout", N_("Line Out") },
2346 { "analog-output-mono", N_("Analog Mono Output") },
2347 { "analog-output-speaker", N_("Speakers") },
2348 { "hdmi-output", N_("HDMI / DisplayPort") },
2349 { "iec958-stereo-output", N_("Digital Output (S/PDIF)") },
2350 { "iec958-stereo-input", N_("Digital Input (S/PDIF)") },
2351 { "iec958-passthrough-output", N_("Digital Passthrough (S/PDIF)") }
2358 PA_LLIST_FOREACH(e
, p
->elements
)
2359 if (element_verify(e
) < 0)
2362 if (!p
->description
)
2363 p
->description
= pa_xstrdup(lookup_description(p
->description_key
? p
->description_key
: p
->name
,
2364 well_known_descriptions
,
2365 PA_ELEMENTSOF(well_known_descriptions
)));
2367 if (!p
->description
) {
2368 if (p
->description_key
)
2369 pa_log_warn("Path %s: Unrecognized description key: %s", p
->name
, p
->description_key
);
2371 p
->description
= pa_xstrdup(p
->name
);
2377 static const char *get_default_paths_dir(void) {
2378 if (pa_run_from_build_tree())
2379 return PA_SRCDIR
"/modules/alsa/mixer/paths/";
2381 return PA_ALSA_PATHS_DIR
;
2384 pa_alsa_path
* pa_alsa_path_new(const char *paths_dir
, const char *fname
, pa_alsa_direction_t direction
) {
2389 bool mute_during_activation
= false;
2391 pa_config_item items
[] = {
2393 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
2394 { "description-key", pa_config_parse_string
, NULL
, "General" },
2395 { "description", pa_config_parse_string
, NULL
, "General" },
2396 { "mute-during-activation", pa_config_parse_bool
, NULL
, "General" },
2397 { "eld-device", pa_config_parse_int
, NULL
, "General" },
2400 { "priority", option_parse_priority
, NULL
, NULL
},
2401 { "name", option_parse_name
, NULL
, NULL
},
2404 { "state.plugged", jack_parse_state
, NULL
, NULL
},
2405 { "state.unplugged", jack_parse_state
, NULL
, NULL
},
2408 { "switch", element_parse_switch
, NULL
, NULL
},
2409 { "volume", element_parse_volume
, NULL
, NULL
},
2410 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
2411 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
2412 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
2413 /* ... later on we might add override-map.3 and so on here ... */
2414 { "required", element_parse_required
, NULL
, NULL
},
2415 { "required-any", element_parse_required
, NULL
, NULL
},
2416 { "required-absent", element_parse_required
, NULL
, NULL
},
2417 { "direction", element_parse_direction
, NULL
, NULL
},
2418 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2419 { "volume-limit", element_parse_volume_limit
, NULL
, NULL
},
2420 { NULL
, NULL
, NULL
, NULL
}
2425 p
= pa_xnew0(pa_alsa_path
, 1);
2426 n
= pa_path_get_filename(fname
);
2427 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2428 p
->proplist
= pa_proplist_new();
2429 p
->direction
= direction
;
2432 items
[0].data
= &p
->priority
;
2433 items
[1].data
= &p
->description_key
;
2434 items
[2].data
= &p
->description
;
2435 items
[3].data
= &mute_during_activation
;
2436 items
[4].data
= &p
->eld_device
;
2439 paths_dir
= get_default_paths_dir();
2441 fn
= pa_maybe_prefix_path(fname
, paths_dir
);
2443 r
= pa_config_parse(fn
, NULL
, items
, p
->proplist
, p
);
2449 p
->mute_during_activation
= mute_during_activation
;
2451 if (path_verify(p
) < 0)
2457 pa_alsa_path_free(p
);
2461 pa_alsa_path
*pa_alsa_path_synthesize(const char *element
, pa_alsa_direction_t direction
) {
2467 p
= pa_xnew0(pa_alsa_path
, 1);
2468 p
->name
= pa_xstrdup(element
);
2469 p
->direction
= direction
;
2471 e
= pa_xnew0(pa_alsa_element
, 1);
2473 e
->alsa_name
= pa_xstrdup(element
);
2474 e
->direction
= direction
;
2475 e
->volume_limit
= -1;
2477 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2478 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2480 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2481 p
->last_element
= e
;
2485 static bool element_drop_unsupported(pa_alsa_element
*e
) {
2486 pa_alsa_option
*o
, *n
;
2490 for (o
= e
->options
; o
; o
= n
) {
2493 if (o
->alsa_idx
< 0) {
2494 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2500 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2501 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2502 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2505 static void path_drop_unsupported(pa_alsa_path
*p
) {
2506 pa_alsa_element
*e
, *n
;
2510 for (e
= p
->elements
; e
; e
= n
) {
2513 if (!element_drop_unsupported(e
)) {
2514 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2520 static void path_make_options_unique(pa_alsa_path
*p
) {
2522 pa_alsa_option
*o
, *u
;
2524 PA_LLIST_FOREACH(e
, p
->elements
) {
2525 PA_LLIST_FOREACH(o
, e
->options
) {
2529 for (u
= o
->next
; u
; u
= u
->next
)
2530 if (pa_streq(u
->name
, o
->name
))
2536 m
= pa_xstrdup(o
->name
);
2538 /* OK, this name is not unique, hence let's rename */
2539 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2542 if (!pa_streq(u
->name
, m
))
2545 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2549 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2550 pa_xfree(u
->description
);
2551 u
->description
= nd
;
2561 static bool element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2564 for (; e
; e
= e
->next
)
2565 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2566 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2572 for (o
= e
->options
; o
; o
= o
->next
) {
2576 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2577 s
->options
= pa_idxset_copy(template->options
);
2578 s
->name
= pa_sprintf_malloc("%s+%s", template->name
, o
->name
);
2580 (template->description
[0] && o
->description
[0])
2581 ? pa_sprintf_malloc("%s / %s", template->description
, o
->description
)
2582 : (template->description
[0]
2583 ? pa_xstrdup(template->description
)
2584 : pa_xstrdup(o
->description
));
2586 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2588 s
= pa_xnew0(pa_alsa_setting
, 1);
2589 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2590 s
->name
= pa_xstrdup(o
->name
);
2591 s
->description
= pa_xstrdup(o
->description
);
2592 s
->priority
= o
->priority
;
2595 pa_idxset_put(s
->options
, o
, NULL
);
2597 if (element_create_settings(e
->next
, s
))
2598 /* This is not a leaf, so let's get rid of it */
2601 /* This is a leaf, so let's add it */
2602 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2604 e
->path
->last_setting
= s
;
2611 static void path_create_settings(pa_alsa_path
*p
) {
2614 element_create_settings(p
->elements
, NULL
);
2617 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_hctl_t
*hctl
, bool ignore_dB
) {
2620 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2621 pa_channel_position_t t
;
2622 pa_channel_position_mask_t path_volume_channels
= 0;
2628 return p
->supported
? 0 : -1;
2634 pa_log_debug("Probing path '%s'", p
->name
);
2636 PA_LLIST_FOREACH(j
, p
->jacks
) {
2637 if (jack_probe(j
, hctl
) < 0) {
2638 p
->supported
= false;
2639 pa_log_debug("Probe of jack '%s' failed.", j
->alsa_name
);
2642 pa_log_debug("Probe of jack '%s' succeeded (%s)", j
->alsa_name
, j
->has_control
? "found!" : "not found");
2645 PA_LLIST_FOREACH(e
, p
->elements
) {
2646 if (element_probe(e
, m
) < 0) {
2647 p
->supported
= false;
2648 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2651 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
);
2656 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2658 if (!p
->has_volume
) {
2659 p
->min_volume
= e
->min_volume
;
2660 p
->max_volume
= e
->max_volume
;
2664 if (!p
->has_volume
) {
2665 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2666 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2667 min_dB
[t
] = e
->min_dB
;
2668 max_dB
[t
] = e
->max_dB
;
2669 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2676 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2677 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2678 min_dB
[t
] += e
->min_dB
;
2679 max_dB
[t
] += e
->max_dB
;
2680 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2683 /* Hmm, there's another element before us
2684 * which cannot do dB volumes, so we we need
2685 * to 'neutralize' this slider */
2686 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2687 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2690 } else if (p
->has_volume
) {
2691 /* We can't use this volume, so let's ignore it */
2692 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2693 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2695 p
->has_volume
= true;
2698 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2702 if (p
->has_req_any
&& !p
->req_any_present
) {
2703 p
->supported
= false;
2704 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2708 path_drop_unsupported(p
);
2709 path_make_options_unique(p
);
2710 path_create_settings(p
);
2712 p
->supported
= true;
2714 p
->min_dB
= INFINITY
;
2715 p
->max_dB
= -INFINITY
;
2717 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2718 if (path_volume_channels
& PA_CHANNEL_POSITION_MASK(t
)) {
2719 if (p
->min_dB
> min_dB
[t
])
2720 p
->min_dB
= min_dB
[t
];
2722 if (p
->max_dB
< max_dB
[t
])
2723 p
->max_dB
= max_dB
[t
];
2730 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2733 pa_log_debug("Setting %s (%s) priority=%u",
2735 pa_strnull(s
->description
),
2739 void pa_alsa_jack_dump(pa_alsa_jack
*j
) {
2742 pa_log_debug("Jack %s, alsa_name='%s', detection %s", j
->name
, j
->alsa_name
, j
->has_control
? "possible" : "unavailable");
2745 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2748 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2750 pa_strnull(o
->name
),
2751 pa_strnull(o
->description
),
2756 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2760 pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, volume_limit=%li, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s",
2770 (long long unsigned) e
->merged_mask
,
2772 pa_yes_no(e
->override_map
));
2774 PA_LLIST_FOREACH(o
, e
->options
)
2775 pa_alsa_option_dump(o
);
2778 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2784 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2785 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2787 pa_strnull(p
->description
),
2790 pa_yes_no(p
->probed
),
2791 pa_yes_no(p
->supported
),
2792 pa_yes_no(p
->has_mute
),
2793 pa_yes_no(p
->has_volume
),
2794 pa_yes_no(p
->has_dB
),
2795 p
->min_volume
, p
->max_volume
,
2796 p
->min_dB
, p
->max_dB
);
2798 PA_LLIST_FOREACH(e
, p
->elements
)
2799 pa_alsa_element_dump(e
);
2801 PA_LLIST_FOREACH(j
, p
->jacks
)
2802 pa_alsa_jack_dump(j
);
2804 PA_LLIST_FOREACH(s
, p
->settings
)
2805 pa_alsa_setting_dump(s
);
2808 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2809 snd_mixer_selem_id_t
*sid
;
2810 snd_mixer_elem_t
*me
;
2816 SELEM_INIT(sid
, e
->alsa_name
);
2817 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2818 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2822 snd_mixer_elem_set_callback(me
, cb
);
2823 snd_mixer_elem_set_callback_private(me
, userdata
);
2826 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2833 PA_LLIST_FOREACH(e
, p
->elements
)
2834 element_set_callback(e
, m
, cb
, userdata
);
2837 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2845 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
2846 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2849 static pa_alsa_path
*profile_set_get_path(pa_alsa_profile_set
*ps
, const char *path_name
) {
2853 pa_assert(path_name
);
2855 if ((path
= pa_hashmap_get(ps
->output_paths
, path_name
)))
2858 return pa_hashmap_get(ps
->input_paths
, path_name
);
2861 static void profile_set_add_path(pa_alsa_profile_set
*ps
, pa_alsa_path
*path
) {
2865 switch (path
->direction
) {
2866 case PA_ALSA_DIRECTION_OUTPUT
:
2867 pa_assert_se(pa_hashmap_put(ps
->output_paths
, path
->name
, path
) >= 0);
2870 case PA_ALSA_DIRECTION_INPUT
:
2871 pa_assert_se(pa_hashmap_put(ps
->input_paths
, path
->name
, path
) >= 0);
2875 pa_assert_not_reached();
2879 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
, const char *paths_dir
) {
2880 pa_alsa_path_set
*ps
;
2881 char **pn
= NULL
, **en
= NULL
, **ie
;
2882 pa_alsa_decibel_fix
*db_fix
;
2883 void *state
, *state2
;
2886 pa_assert(m
->profile_set
);
2887 pa_assert(m
->profile_set
->decibel_fixes
);
2888 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2890 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2893 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2894 ps
->direction
= direction
;
2895 ps
->paths
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2897 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2898 pn
= m
->output_path_names
;
2900 pn
= m
->input_path_names
;
2905 for (in
= pn
; *in
; in
++) {
2906 pa_alsa_path
*p
= NULL
;
2907 bool duplicate
= false;
2910 for (kn
= pn
; kn
< in
; kn
++)
2911 if (pa_streq(*kn
, *in
)) {
2919 p
= profile_set_get_path(m
->profile_set
, *in
);
2921 if (p
&& p
->direction
!= direction
) {
2922 pa_log("Configuration error: Path %s is used both as an input and as an output path.", p
->name
);
2927 char *fn
= pa_sprintf_malloc("%s.conf", *in
);
2928 p
= pa_alsa_path_new(paths_dir
, fn
, direction
);
2931 profile_set_add_path(m
->profile_set
, p
);
2935 pa_hashmap_put(ps
->paths
, p
, p
);
2942 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2943 en
= m
->output_element
;
2945 en
= m
->input_element
;
2950 for (ie
= en
; *ie
; ie
++) {
2954 p
= pa_alsa_path_synthesize(*ie
, direction
);
2956 /* Mark all other passed elements for require-absent */
2957 for (je
= en
; *je
; je
++) {
2963 e
= pa_xnew0(pa_alsa_element
, 1);
2965 e
->alsa_name
= pa_xstrdup(*je
);
2966 e
->direction
= direction
;
2967 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2968 e
->volume_limit
= -1;
2970 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2971 p
->last_element
= e
;
2974 pa_hashmap_put(ps
->paths
, *ie
, p
);
2978 /* Assign decibel fixes to elements. */
2979 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2982 PA_HASHMAP_FOREACH(p
, ps
->paths
, state2
) {
2985 PA_LLIST_FOREACH(e
, p
->elements
) {
2986 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2987 /* The profile set that contains the dB fix may be freed
2988 * before the element, so we have to copy the dB fix
2990 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2991 e
->db_fix
->profile_set
= NULL
;
2992 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2993 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
3003 pa_alsa_path_set_free(ps
);
3008 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
3013 pa_log_debug("Path Set %p, direction=%i",
3017 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
3018 pa_alsa_path_dump(p
);
3021 static bool options_have_option(pa_alsa_option
*options
, const char *alsa_name
) {
3025 pa_assert(alsa_name
);
3027 PA_LLIST_FOREACH(o
, options
) {
3028 if (pa_streq(o
->alsa_name
, alsa_name
))
3034 static bool enumeration_is_subset(pa_alsa_option
*a_options
, pa_alsa_option
*b_options
) {
3035 pa_alsa_option
*oa
, *ob
;
3037 if (!a_options
) return true;
3038 if (!b_options
) return false;
3040 /* If there is an option A offers that B does not, then A is not a subset of B. */
3041 PA_LLIST_FOREACH(oa
, a_options
) {
3043 PA_LLIST_FOREACH(ob
, b_options
) {
3044 if (pa_streq(oa
->alsa_name
, ob
->alsa_name
)) {
3056 * Compares two elements to see if a is a subset of b
3058 static bool element_is_subset(pa_alsa_element
*a
, pa_alsa_element
*b
, snd_mixer_t
*m
) {
3064 * Every state is a subset of itself (with caveats for volume_limits and options)
3065 * IGNORE is a subset of every other state */
3067 /* Check the volume_use */
3068 if (a
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
3070 /* "Constant" is subset of "Constant" only when their constant values are equal */
3071 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& b
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& a
->constant_volume
!= b
->constant_volume
)
3074 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
3075 if (a
->volume_use
!= b
->volume_use
&& b
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
3078 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
3079 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
3080 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
3081 if (b
->volume_use
== PA_ALSA_VOLUME_MERGE
&& b
->volume_limit
>= 0) {
3084 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
)
3085 a_limit
= a
->constant_volume
;
3086 else if (a
->volume_use
== PA_ALSA_VOLUME_ZERO
) {
3090 int rounding
= (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
? +1 : -1);
3091 a_limit
= decibel_fix_get_step(a
->db_fix
, &dB
, rounding
);
3093 snd_mixer_selem_id_t
*sid
;
3094 snd_mixer_elem_t
*me
;
3096 SELEM_INIT(sid
, a
->alsa_name
);
3097 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
3098 pa_log_warn("Element %s seems to have disappeared.", a
->alsa_name
);
3102 if (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3103 if (snd_mixer_selem_ask_playback_dB_vol(me
, dB
, +1, &a_limit
) < 0)
3106 if (snd_mixer_selem_ask_capture_dB_vol(me
, dB
, -1, &a_limit
) < 0)
3110 } else if (a
->volume_use
== PA_ALSA_VOLUME_OFF
)
3111 a_limit
= a
->min_volume
;
3112 else if (a
->volume_use
== PA_ALSA_VOLUME_MERGE
)
3113 a_limit
= a
->volume_limit
;
3115 /* This should never be reached */
3118 if (a_limit
> b
->volume_limit
)
3122 if (a
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
3124 /* If override-maps are different, they're not subsets */
3125 if (a
->n_channels
!= b
->n_channels
)
3127 for (s
= 0; s
<= SND_MIXER_SCHN_LAST
; s
++)
3128 if (a
->masks
[s
][a
->n_channels
-1] != b
->masks
[s
][b
->n_channels
-1]) {
3129 pa_log_debug("Element %s is not a subset - mask a: 0x%" PRIx64
", mask b: 0x%" PRIx64
", at channel %d",
3130 a
->alsa_name
, a
->masks
[s
][a
->n_channels
-1], b
->masks
[s
][b
->n_channels
-1], s
);
3136 if (a
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
3137 /* "On" is a subset of "Mute".
3138 * "Off" is a subset of "Mute".
3139 * "On" is a subset of "Select", if there is an "Option:On" in B.
3140 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3141 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3143 if (a
->switch_use
!= b
->switch_use
) {
3145 if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
|| a
->switch_use
== PA_ALSA_SWITCH_MUTE
3146 || b
->switch_use
== PA_ALSA_SWITCH_OFF
|| b
->switch_use
== PA_ALSA_SWITCH_ON
)
3149 if (b
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3150 if (a
->switch_use
== PA_ALSA_SWITCH_ON
) {
3151 if (!options_have_option(b
->options
, "on"))
3153 } else if (a
->switch_use
== PA_ALSA_SWITCH_OFF
) {
3154 if (!options_have_option(b
->options
, "off"))
3158 } else if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3159 if (!enumeration_is_subset(a
->options
, b
->options
))
3164 if (a
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
) {
3165 if (b
->enumeration_use
== PA_ALSA_ENUMERATION_IGNORE
)
3167 if (!enumeration_is_subset(a
->options
, b
->options
))
3174 static void path_set_condense(pa_alsa_path_set
*ps
, snd_mixer_t
*m
) {
3181 /* If we only have one path, then don't bother */
3182 if (pa_hashmap_size(ps
->paths
) < 2)
3185 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3189 PA_HASHMAP_FOREACH(p2
, ps
->paths
, state2
) {
3190 pa_alsa_element
*ea
, *eb
;
3191 pa_alsa_jack
*ja
, *jb
;
3192 bool is_subset
= true;
3197 /* If a has a jack that b does not have, a is not a subset */
3198 PA_LLIST_FOREACH(ja
, p
->jacks
) {
3199 bool exists
= false;
3201 if (!ja
->has_control
)
3204 PA_LLIST_FOREACH(jb
, p2
->jacks
) {
3205 if (jb
->has_control
&& pa_streq(jb
->alsa_name
, ja
->alsa_name
) &&
3206 (ja
->state_plugged
== jb
->state_plugged
) &&
3207 (ja
->state_unplugged
== jb
->state_unplugged
)) {
3219 /* Compare the elements of each set... */
3226 else if ((ea
&& !eb
) || (!ea
&& eb
))
3228 else if (pa_streq(ea
->alsa_name
, eb
->alsa_name
)) {
3229 if (element_is_subset(ea
, eb
, m
)) {
3239 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p
->name
, p2
->name
);
3240 pa_hashmap_remove(ps
->paths
, p
);
3247 static pa_alsa_path
* path_set_find_path_by_description(pa_alsa_path_set
*ps
, const char* description
, pa_alsa_path
*ignore
) {
3251 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
3252 if (p
!= ignore
&& pa_streq(p
->description
, description
))
3258 static void path_set_make_path_descriptions_unique(pa_alsa_path_set
*ps
) {
3259 pa_alsa_path
*p
, *q
;
3260 void *state
, *state2
;
3262 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3264 char *old_description
;
3266 q
= path_set_find_path_by_description(ps
, p
->description
, p
);
3271 old_description
= pa_xstrdup(p
->description
);
3273 /* OK, this description is not unique, hence let's rename */
3275 PA_HASHMAP_FOREACH(q
, ps
->paths
, state2
) {
3276 char *new_description
;
3278 if (!pa_streq(q
->description
, old_description
))
3281 new_description
= pa_sprintf_malloc("%s %u", q
->description
, i
);
3282 pa_xfree(q
->description
);
3283 q
->description
= new_description
;
3288 pa_xfree(old_description
);
3292 static void mapping_free(pa_alsa_mapping
*m
) {
3296 pa_xfree(m
->description
);
3298 pa_proplist_free(m
->proplist
);
3300 pa_xstrfreev(m
->device_strings
);
3301 pa_xstrfreev(m
->input_path_names
);
3302 pa_xstrfreev(m
->output_path_names
);
3303 pa_xstrfreev(m
->input_element
);
3304 pa_xstrfreev(m
->output_element
);
3305 if (m
->input_path_set
)
3306 pa_alsa_path_set_free(m
->input_path_set
);
3307 if (m
->output_path_set
)
3308 pa_alsa_path_set_free(m
->output_path_set
);
3310 pa_assert(!m
->input_pcm
);
3311 pa_assert(!m
->output_pcm
);
3313 pa_alsa_ucm_mapping_context_free(&m
->ucm_context
);
3318 static void profile_free(pa_alsa_profile
*p
) {
3322 pa_xfree(p
->description
);
3324 pa_xstrfreev(p
->input_mapping_names
);
3325 pa_xstrfreev(p
->output_mapping_names
);
3327 if (p
->input_mappings
)
3328 pa_idxset_free(p
->input_mappings
, NULL
);
3330 if (p
->output_mappings
)
3331 pa_idxset_free(p
->output_mappings
, NULL
);
3336 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
3339 if (ps
->input_paths
)
3340 pa_hashmap_free(ps
->input_paths
);
3342 if (ps
->output_paths
)
3343 pa_hashmap_free(ps
->output_paths
);
3346 pa_hashmap_free(ps
->profiles
);
3349 pa_hashmap_free(ps
->mappings
);
3351 if (ps
->decibel_fixes
)
3352 pa_hashmap_free(ps
->decibel_fixes
);
3357 pa_alsa_mapping
*pa_alsa_mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
3360 if (!pa_startswith(name
, "Mapping "))
3365 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
3368 m
= pa_xnew0(pa_alsa_mapping
, 1);
3369 m
->profile_set
= ps
;
3370 m
->name
= pa_xstrdup(name
);
3371 pa_sample_spec_init(&m
->sample_spec
);
3372 pa_channel_map_init(&m
->channel_map
);
3373 m
->proplist
= pa_proplist_new();
3375 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
3380 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
3383 if (!pa_startswith(name
, "Profile "))
3388 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
3391 p
= pa_xnew0(pa_alsa_profile
, 1);
3392 p
->profile_set
= ps
;
3393 p
->name
= pa_xstrdup(name
);
3395 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3400 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
3401 pa_alsa_decibel_fix
*db_fix
;
3403 if (!pa_startswith(name
, "DecibelFix "))
3408 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
3411 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
3412 db_fix
->profile_set
= ps
;
3413 db_fix
->name
= pa_xstrdup(name
);
3415 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
3420 static int mapping_parse_device_strings(pa_config_parser_state
*state
) {
3421 pa_alsa_profile_set
*ps
;
3426 ps
= state
->userdata
;
3428 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3429 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3433 pa_xstrfreev(m
->device_strings
);
3434 if (!(m
->device_strings
= pa_split_spaces_strv(state
->rvalue
))) {
3435 pa_log("[%s:%u] Device string list empty of '%s'", state
->filename
, state
->lineno
, state
->section
);
3442 static int mapping_parse_channel_map(pa_config_parser_state
*state
) {
3443 pa_alsa_profile_set
*ps
;
3448 ps
= state
->userdata
;
3450 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3451 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3455 if (!(pa_channel_map_parse(&m
->channel_map
, state
->rvalue
))) {
3456 pa_log("[%s:%u] Channel map invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
3463 static int mapping_parse_paths(pa_config_parser_state
*state
) {
3464 pa_alsa_profile_set
*ps
;
3469 ps
= state
->userdata
;
3471 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3472 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3476 if (pa_streq(state
->lvalue
, "paths-input")) {
3477 pa_xstrfreev(m
->input_path_names
);
3478 m
->input_path_names
= pa_split_spaces_strv(state
->rvalue
);
3480 pa_xstrfreev(m
->output_path_names
);
3481 m
->output_path_names
= pa_split_spaces_strv(state
->rvalue
);
3487 static int mapping_parse_element(pa_config_parser_state
*state
) {
3488 pa_alsa_profile_set
*ps
;
3493 ps
= state
->userdata
;
3495 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3496 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3500 if (pa_streq(state
->lvalue
, "element-input")) {
3501 pa_xstrfreev(m
->input_element
);
3502 m
->input_element
= pa_split_spaces_strv(state
->rvalue
);
3504 pa_xstrfreev(m
->output_element
);
3505 m
->output_element
= pa_split_spaces_strv(state
->rvalue
);
3511 static int mapping_parse_direction(pa_config_parser_state
*state
) {
3512 pa_alsa_profile_set
*ps
;
3517 ps
= state
->userdata
;
3519 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3520 pa_log("[%s:%u] Section name %s invalid.", state
->filename
, state
->lineno
, state
->section
);
3524 if (pa_streq(state
->rvalue
, "input"))
3525 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3526 else if (pa_streq(state
->rvalue
, "output"))
3527 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3528 else if (pa_streq(state
->rvalue
, "any"))
3529 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3531 pa_log("[%s:%u] Direction %s invalid.", state
->filename
, state
->lineno
, state
->rvalue
);
3538 static int mapping_parse_description(pa_config_parser_state
*state
) {
3539 pa_alsa_profile_set
*ps
;
3545 ps
= state
->userdata
;
3547 if ((m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3548 pa_xfree(m
->description
);
3549 m
->description
= pa_xstrdup(state
->rvalue
);
3550 } else if ((p
= profile_get(ps
, state
->section
))) {
3551 pa_xfree(p
->description
);
3552 p
->description
= pa_xstrdup(state
->rvalue
);
3554 pa_log("[%s:%u] Section name %s invalid.", state
->filename
, state
->lineno
, state
->section
);
3561 static int mapping_parse_priority(pa_config_parser_state
*state
) {
3562 pa_alsa_profile_set
*ps
;
3569 ps
= state
->userdata
;
3571 if (pa_atou(state
->rvalue
, &prio
) < 0) {
3572 pa_log("[%s:%u] Priority invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
3576 if ((m
= pa_alsa_mapping_get(ps
, state
->section
)))
3578 else if ((p
= profile_get(ps
, state
->section
)))
3581 pa_log("[%s:%u] Section name %s invalid.", state
->filename
, state
->lineno
, state
->section
);
3588 static int profile_parse_mappings(pa_config_parser_state
*state
) {
3589 pa_alsa_profile_set
*ps
;
3594 ps
= state
->userdata
;
3596 if (!(p
= profile_get(ps
, state
->section
))) {
3597 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3601 if (pa_streq(state
->lvalue
, "input-mappings")) {
3602 pa_xstrfreev(p
->input_mapping_names
);
3603 p
->input_mapping_names
= pa_split_spaces_strv(state
->rvalue
);
3605 pa_xstrfreev(p
->output_mapping_names
);
3606 p
->output_mapping_names
= pa_split_spaces_strv(state
->rvalue
);
3612 static int profile_parse_skip_probe(pa_config_parser_state
*state
) {
3613 pa_alsa_profile_set
*ps
;
3619 ps
= state
->userdata
;
3621 if (!(p
= profile_get(ps
, state
->section
))) {
3622 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3626 if ((b
= pa_parse_boolean(state
->rvalue
)) < 0) {
3627 pa_log("[%s:%u] Skip probe invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
3636 static int decibel_fix_parse_db_values(pa_config_parser_state
*state
) {
3637 pa_alsa_profile_set
*ps
;
3638 pa_alsa_decibel_fix
*db_fix
;
3642 unsigned n
= 8; /* Current size of the db_values table. */
3643 unsigned min_step
= 0;
3644 unsigned max_step
= 0;
3645 unsigned i
= 0; /* Index to the items table. */
3646 unsigned prev_step
= 0;
3651 ps
= state
->userdata
;
3653 if (!(db_fix
= decibel_fix_get(ps
, state
->section
))) {
3654 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3658 if (!(items
= pa_split_spaces_strv(state
->rvalue
))) {
3659 pa_log("[%s:%u] Value missing", state
->filename
, state
->lineno
);
3663 db_values
= pa_xnew(long, n
);
3665 while ((item
= items
[i
++])) {
3666 char *s
= item
; /* Step value string. */
3667 char *d
= item
; /* dB value string. */
3671 /* Move d forward until it points to a colon or to the end of the item. */
3672 for (; *d
&& *d
!= ':'; ++d
);
3675 /* item started with colon. */
3676 pa_log("[%s:%u] No step value found in %s", state
->filename
, state
->lineno
, item
);
3680 if (!*d
|| !*(d
+ 1)) {
3681 /* No colon found, or it was the last character in item. */
3682 pa_log("[%s:%u] No dB value found in %s", state
->filename
, state
->lineno
, item
);
3686 /* pa_atou() needs a null-terminating string. Let's replace the colon
3687 * with a zero byte. */
3690 if (pa_atou(s
, &step
) < 0) {
3691 pa_log("[%s:%u] Invalid step value: %s", state
->filename
, state
->lineno
, s
);
3695 if (pa_atod(d
, &db
) < 0) {
3696 pa_log("[%s:%u] Invalid dB value: %s", state
->filename
, state
->lineno
, d
);
3700 if (step
<= prev_step
&& i
!= 1) {
3701 pa_log("[%s:%u] Step value %u not greater than the previous value %u", state
->filename
, state
->lineno
, step
, prev_step
);
3705 if (db
< prev_db
&& i
!= 1) {
3706 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", state
->filename
, state
->lineno
, db
, prev_db
);
3712 db_values
[0] = (long) (db
* 100.0);
3716 /* Interpolate linearly. */
3717 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3719 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3721 /* Reallocate the db_values table if it's about to overflow. */
3722 if (prev_step
+ 1 - min_step
== n
) {
3724 db_values
= pa_xrenew(long, db_values
, n
);
3727 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3734 db_fix
->min_step
= min_step
;
3735 db_fix
->max_step
= max_step
;
3736 pa_xfree(db_fix
->db_values
);
3737 db_fix
->db_values
= db_values
;
3739 pa_xstrfreev(items
);
3744 pa_xstrfreev(items
);
3745 pa_xfree(db_values
);
3750 static void mapping_paths_probe(pa_alsa_mapping
*m
, pa_alsa_profile
*profile
,
3751 pa_alsa_direction_t direction
, pa_hashmap
*used_paths
) {
3755 snd_pcm_t
*pcm_handle
;
3756 pa_alsa_path_set
*ps
;
3757 snd_mixer_t
*mixer_handle
;
3758 snd_hctl_t
*hctl_handle
;
3760 if (direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3761 if (m
->output_path_set
)
3762 return; /* Already probed */
3763 m
->output_path_set
= ps
= pa_alsa_path_set_new(m
, direction
, NULL
); /* FIXME: Handle paths_dir */
3764 pcm_handle
= m
->output_pcm
;
3766 if (m
->input_path_set
)
3767 return; /* Already probed */
3768 m
->input_path_set
= ps
= pa_alsa_path_set_new(m
, direction
, NULL
); /* FIXME: Handle paths_dir */
3769 pcm_handle
= m
->input_pcm
;
3773 return; /* No paths */
3775 pa_assert(pcm_handle
);
3777 mixer_handle
= pa_alsa_open_mixer_for_pcm(pcm_handle
, NULL
, &hctl_handle
);
3778 if (!mixer_handle
|| !hctl_handle
) {
3779 /* Cannot open mixer, remove all entries */
3780 pa_hashmap_remove_all(ps
->paths
);
3784 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3785 if (pa_alsa_path_probe(p
, mixer_handle
, hctl_handle
, m
->profile_set
->ignore_dB
) < 0) {
3786 pa_hashmap_remove(ps
->paths
, p
);
3790 path_set_condense(ps
, mixer_handle
);
3791 path_set_make_path_descriptions_unique(ps
);
3794 snd_mixer_close(mixer_handle
);
3796 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
3797 pa_hashmap_put(used_paths
, p
, p
);
3799 pa_log_debug("Available mixer paths (after tidying):");
3800 pa_alsa_path_set_dump(ps
);
3803 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3805 static const struct description_map well_known_descriptions
[] = {
3806 { "analog-mono", N_("Analog Mono") },
3807 { "analog-stereo", N_("Analog Stereo") },
3808 { "analog-surround-21", N_("Analog Surround 2.1") },
3809 { "analog-surround-30", N_("Analog Surround 3.0") },
3810 { "analog-surround-31", N_("Analog Surround 3.1") },
3811 { "analog-surround-40", N_("Analog Surround 4.0") },
3812 { "analog-surround-41", N_("Analog Surround 4.1") },
3813 { "analog-surround-50", N_("Analog Surround 5.0") },
3814 { "analog-surround-51", N_("Analog Surround 5.1") },
3815 { "analog-surround-61", N_("Analog Surround 6.0") },
3816 { "analog-surround-61", N_("Analog Surround 6.1") },
3817 { "analog-surround-70", N_("Analog Surround 7.0") },
3818 { "analog-surround-71", N_("Analog Surround 7.1") },
3819 { "analog-4-channel-input", N_("Analog 4-channel Input") },
3820 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3821 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3822 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3823 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3824 { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
3825 { "hdmi-stereo", N_("Digital Stereo (HDMI)") },
3826 { "hdmi-surround-51", N_("Digital Surround 5.1 (HDMI)") }
3831 if (!pa_channel_map_valid(&m
->channel_map
)) {
3832 pa_log("Mapping %s is missing channel map.", m
->name
);
3836 if (!m
->device_strings
) {
3837 pa_log("Mapping %s is missing device strings.", m
->name
);
3841 if ((m
->input_path_names
&& m
->input_element
) ||
3842 (m
->output_path_names
&& m
->output_element
)) {
3843 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3847 if (!m
->description
)
3848 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3849 well_known_descriptions
,
3850 PA_ELEMENTSOF(well_known_descriptions
)));
3852 if (!m
->description
)
3853 m
->description
= pa_xstrdup(m
->name
);
3856 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3858 else if (m
->channel_map
.channels
== bonus
->channels
)
3865 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3866 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3870 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3872 pa_strnull(m
->description
),
3874 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3875 pa_yes_no(m
->supported
),
3879 static void profile_set_add_auto_pair(
3880 pa_alsa_profile_set
*ps
,
3881 pa_alsa_mapping
*m
, /* output */
3882 pa_alsa_mapping
*n
/* input */) {
3890 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3893 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3897 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3899 name
= pa_sprintf_malloc("output:%s", m
->name
);
3901 name
= pa_sprintf_malloc("input:%s", n
->name
);
3903 if (pa_hashmap_get(ps
->profiles
, name
)) {
3908 p
= pa_xnew0(pa_alsa_profile
, 1);
3909 p
->profile_set
= ps
;
3913 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3914 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3915 p
->priority
+= m
->priority
* 100;
3919 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3920 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3921 p
->priority
+= n
->priority
;
3924 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3927 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3928 pa_alsa_mapping
*m
, *n
;
3929 void *m_state
, *n_state
;
3933 /* The order is important here:
3934 1) try single inputs and outputs before trying their
3935 combination, because if the half-duplex test failed, we don't have
3937 2) try the output right before the input combinations with
3938 that output, because then the output_pcm is not closed between tests.
3940 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3941 profile_set_add_auto_pair(ps
, NULL
, n
);
3943 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3944 profile_set_add_auto_pair(ps
, m
, NULL
);
3946 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3947 profile_set_add_auto_pair(ps
, m
, n
);
3952 static int profile_verify(pa_alsa_profile
*p
) {
3954 static const struct description_map well_known_descriptions
[] = {
3955 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3956 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3957 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3958 { "off", N_("Off") }
3963 /* Replace the output mapping names by the actual mappings */
3964 if (p
->output_mapping_names
) {
3967 pa_assert(!p
->output_mappings
);
3968 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3970 for (name
= p
->output_mapping_names
; *name
; name
++) {
3973 bool duplicate
= false;
3975 for (in
= name
+ 1; *in
; in
++)
3976 if (pa_streq(*name
, *in
)) {
3984 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3985 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p
->name
, *name
);
3989 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3995 pa_xstrfreev(p
->output_mapping_names
);
3996 p
->output_mapping_names
= NULL
;
3999 /* Replace the input mapping names by the actual mappings */
4000 if (p
->input_mapping_names
) {
4003 pa_assert(!p
->input_mappings
);
4004 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
4006 for (name
= p
->input_mapping_names
; *name
; name
++) {
4009 bool duplicate
= false;
4011 for (in
= name
+ 1; *in
; in
++)
4012 if (pa_streq(*name
, *in
)) {
4020 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
4021 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p
->name
, *name
);
4025 pa_idxset_put(p
->input_mappings
, m
, NULL
);
4031 pa_xstrfreev(p
->input_mapping_names
);
4032 p
->input_mapping_names
= NULL
;
4035 if (!p
->input_mappings
&& !p
->output_mappings
) {
4036 pa_log("Profile '%s' lacks mappings.", p
->name
);
4040 if (!p
->description
)
4041 p
->description
= pa_xstrdup(lookup_description(p
->name
,
4042 well_known_descriptions
,
4043 PA_ELEMENTSOF(well_known_descriptions
)));
4045 if (!p
->description
) {
4050 sb
= pa_strbuf_new();
4052 if (p
->output_mappings
)
4053 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4054 if (!pa_strbuf_isempty(sb
))
4055 pa_strbuf_puts(sb
, " + ");
4057 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
4060 if (p
->input_mappings
)
4061 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4062 if (!pa_strbuf_isempty(sb
))
4063 pa_strbuf_puts(sb
, " + ");
4065 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
4068 p
->description
= pa_strbuf_tostring_free(sb
);
4074 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
4079 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4081 pa_strnull(p
->description
),
4083 pa_yes_no(p
->supported
),
4084 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
4085 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
4087 if (p
->input_mappings
)
4088 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
4089 pa_log_debug("Input %s", m
->name
);
4091 if (p
->output_mappings
)
4092 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
4093 pa_log_debug("Output %s", m
->name
);
4096 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
4099 /* Check that the dB mapping has been configured. Since "db-values" is
4100 * currently the only option in the DecibelFix section, and decibel fix
4101 * objects don't get created if a DecibelFix section is empty, this is
4102 * actually a redundant check. Having this may prevent future bugs,
4104 if (!db_fix
->db_values
) {
4105 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
4112 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
4113 char *db_values
= NULL
;
4117 if (db_fix
->db_values
) {
4119 unsigned long i
, nsteps
;
4121 pa_assert(db_fix
->min_step
<= db_fix
->max_step
);
4122 nsteps
= db_fix
->max_step
- db_fix
->min_step
+ 1;
4124 buf
= pa_strbuf_new();
4125 for (i
= 0; i
< nsteps
; ++i
)
4126 pa_strbuf_printf(buf
, "[%li]:%0.2f ", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
4128 db_values
= pa_strbuf_tostring_free(buf
);
4131 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4132 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
4134 pa_xfree(db_values
);
4137 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
4138 pa_alsa_profile_set
*ps
;
4141 pa_alsa_decibel_fix
*db_fix
;
4146 static pa_config_item items
[] = {
4148 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
4151 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
4152 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
4153 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
4154 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
4155 { "element-input", mapping_parse_element
, NULL
, NULL
},
4156 { "element-output", mapping_parse_element
, NULL
, NULL
},
4157 { "direction", mapping_parse_direction
, NULL
, NULL
},
4159 /* Shared by [Mapping ...] and [Profile ...] */
4160 { "description", mapping_parse_description
, NULL
, NULL
},
4161 { "priority", mapping_parse_priority
, NULL
, NULL
},
4164 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
4165 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
4166 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
4168 /* [DecibelFix ...] */
4169 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
4170 { NULL
, NULL
, NULL
, NULL
}
4173 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
4174 ps
->mappings
= pa_hashmap_new_full(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
, NULL
, (pa_free_cb_t
) mapping_free
);
4175 ps
->profiles
= pa_hashmap_new_full(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
, NULL
, (pa_free_cb_t
) profile_free
);
4176 ps
->decibel_fixes
= pa_hashmap_new_full(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
, NULL
, (pa_free_cb_t
) decibel_fix_free
);
4177 ps
->input_paths
= pa_hashmap_new_full(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
, NULL
, (pa_free_cb_t
) pa_alsa_path_free
);
4178 ps
->output_paths
= pa_hashmap_new_full(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
, NULL
, (pa_free_cb_t
) pa_alsa_path_free
);
4180 items
[0].data
= &ps
->auto_profiles
;
4183 fname
= "default.conf";
4185 fn
= pa_maybe_prefix_path(fname
,
4186 pa_run_from_build_tree() ? PA_SRCDIR
"/modules/alsa/mixer/profile-sets/" :
4187 PA_ALSA_PROFILE_SETS_DIR
);
4189 r
= pa_config_parse(fn
, NULL
, items
, NULL
, ps
);
4195 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4196 if (mapping_verify(m
, bonus
) < 0)
4199 if (ps
->auto_profiles
)
4200 profile_set_add_auto(ps
);
4202 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4203 if (profile_verify(p
) < 0)
4206 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4207 if (decibel_fix_verify(db_fix
) < 0)
4213 pa_alsa_profile_set_free(ps
);
4217 static void profile_finalize_probing(pa_alsa_profile
*to_be_finalized
, pa_alsa_profile
*next
) {
4221 if (!to_be_finalized
)
4224 if (to_be_finalized
->output_mappings
)
4225 PA_IDXSET_FOREACH(m
, to_be_finalized
->output_mappings
, idx
) {
4230 if (to_be_finalized
->supported
)
4233 /* If this mapping is also in the next profile, we won't close the
4234 * pcm handle here, because it would get immediately reopened
4236 if (next
&& next
->output_mappings
&& pa_idxset_get_by_data(next
->output_mappings
, m
, NULL
))
4239 snd_pcm_close(m
->output_pcm
);
4240 m
->output_pcm
= NULL
;
4243 if (to_be_finalized
->input_mappings
)
4244 PA_IDXSET_FOREACH(m
, to_be_finalized
->input_mappings
, idx
) {
4249 if (to_be_finalized
->supported
)
4252 /* If this mapping is also in the next profile, we won't close the
4253 * pcm handle here, because it would get immediately reopened
4255 if (next
&& next
->input_mappings
&& pa_idxset_get_by_data(next
->input_mappings
, m
, NULL
))
4258 snd_pcm_close(m
->input_pcm
);
4259 m
->input_pcm
= NULL
;
4263 static snd_pcm_t
* mapping_open_pcm(pa_alsa_mapping
*m
,
4264 const pa_sample_spec
*ss
,
4267 unsigned default_n_fragments
,
4268 unsigned default_fragment_size_msec
) {
4270 pa_sample_spec try_ss
= *ss
;
4271 pa_channel_map try_map
= m
->channel_map
;
4272 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
4274 try_ss
.channels
= try_map
.channels
;
4277 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
4278 pa_frame_size(&try_ss
);
4279 try_buffer_size
= default_n_fragments
* try_period_size
;
4281 return pa_alsa_open_by_template(
4282 m
->device_strings
, dev_id
, NULL
, &try_ss
,
4283 &try_map
, mode
, &try_period_size
,
4284 &try_buffer_size
, 0, NULL
, NULL
, true);
4287 static void paths_drop_unused(pa_hashmap
* h
, pa_hashmap
*keep
) {
4296 p
= pa_hashmap_iterate(h
, &state
, &key
);
4298 if (pa_hashmap_get(keep
, p
) == NULL
) {
4299 pa_hashmap_remove(h
, key
);
4300 pa_alsa_path_free(p
);
4302 p
= pa_hashmap_iterate(h
, &state
, &key
);
4306 void pa_alsa_profile_set_probe(
4307 pa_alsa_profile_set
*ps
,
4309 const pa_sample_spec
*ss
,
4310 unsigned default_n_fragments
,
4311 unsigned default_fragment_size_msec
) {
4314 pa_alsa_profile
*p
, *last
= NULL
;
4316 pa_hashmap
*broken_inputs
, *broken_outputs
, *used_paths
;
4325 broken_inputs
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
4326 broken_outputs
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
4327 used_paths
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
4329 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
4332 /* Skip if this is already marked that it is supported (i.e. from the config file) */
4333 if (!p
->supported
) {
4335 profile_finalize_probing(last
, p
);
4336 p
->supported
= true;
4338 if (p
->output_mappings
) {
4339 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4340 if (pa_hashmap_get(broken_outputs
, m
) == m
) {
4341 pa_log_debug("Skipping profile %s - will not be able to open output:%s", p
->name
, m
->name
);
4342 p
->supported
= false;
4348 if (p
->input_mappings
&& p
->supported
) {
4349 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4350 if (pa_hashmap_get(broken_inputs
, m
) == m
) {
4351 pa_log_debug("Skipping profile %s - will not be able to open input:%s", p
->name
, m
->name
);
4352 p
->supported
= false;
4359 pa_log_debug("Looking at profile %s", p
->name
);
4361 /* Check if we can open all new ones */
4362 if (p
->output_mappings
&& p
->supported
)
4363 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4368 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
4369 if (!(m
->output_pcm
= mapping_open_pcm(m
, ss
, dev_id
,
4370 SND_PCM_STREAM_PLAYBACK
,
4371 default_n_fragments
,
4372 default_fragment_size_msec
))) {
4373 p
->supported
= false;
4374 if (pa_idxset_size(p
->output_mappings
) == 1 &&
4375 ((!p
->input_mappings
) || pa_idxset_size(p
->input_mappings
) == 0)) {
4376 pa_log_debug("Caching failure to open output:%s", m
->name
);
4377 pa_hashmap_put(broken_outputs
, m
, m
);
4383 if (p
->input_mappings
&& p
->supported
)
4384 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4389 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
4390 if (!(m
->input_pcm
= mapping_open_pcm(m
, ss
, dev_id
,
4391 SND_PCM_STREAM_CAPTURE
,
4392 default_n_fragments
,
4393 default_fragment_size_msec
))) {
4394 p
->supported
= false;
4395 if (pa_idxset_size(p
->input_mappings
) == 1 &&
4396 ((!p
->output_mappings
) || pa_idxset_size(p
->output_mappings
) == 0)) {
4397 pa_log_debug("Caching failure to open input:%s", m
->name
);
4398 pa_hashmap_put(broken_inputs
, m
, m
);
4410 pa_log_debug("Profile %s supported.", p
->name
);
4412 if (p
->output_mappings
)
4413 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
4415 mapping_paths_probe(m
, p
, PA_ALSA_DIRECTION_OUTPUT
, used_paths
);
4417 if (p
->input_mappings
)
4418 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
4420 mapping_paths_probe(m
, p
, PA_ALSA_DIRECTION_INPUT
, used_paths
);
4424 profile_finalize_probing(last
, NULL
);
4426 pa_alsa_profile_set_drop_unsupported(ps
);
4428 paths_drop_unused(ps
->input_paths
, used_paths
);
4429 paths_drop_unused(ps
->output_paths
, used_paths
);
4430 pa_hashmap_free(broken_inputs
);
4431 pa_hashmap_free(broken_outputs
);
4432 pa_hashmap_free(used_paths
);
4437 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
4440 pa_alsa_decibel_fix
*db_fix
;
4445 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4448 pa_yes_no(ps
->auto_profiles
),
4449 pa_yes_no(ps
->probed
),
4450 pa_hashmap_size(ps
->mappings
),
4451 pa_hashmap_size(ps
->profiles
),
4452 pa_hashmap_size(ps
->decibel_fixes
));
4454 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4455 pa_alsa_mapping_dump(m
);
4457 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4458 pa_alsa_profile_dump(p
);
4460 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4461 pa_alsa_decibel_fix_dump(db_fix
);
4464 void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set
*ps
) {
4469 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
4470 if (!p
->supported
) {
4471 pa_hashmap_remove(ps
->profiles
, p
->name
);
4476 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
) {
4477 if (m
->supported
<= 0) {
4478 pa_hashmap_remove(ps
->mappings
, m
->name
);
4484 static pa_device_port
* device_port_alsa_init(pa_hashmap
*ports
, /* card ports */
4486 const char* description
,
4488 pa_alsa_setting
*setting
,
4489 pa_card_profile
*cp
,
4490 pa_hashmap
*extra
, /* sink/source ports */
4497 p
= pa_hashmap_get(ports
, name
);
4500 pa_alsa_port_data
*data
;
4501 pa_device_port_new_data port_data
;
4503 pa_device_port_new_data_init(&port_data
);
4504 pa_device_port_new_data_set_name(&port_data
, name
);
4505 pa_device_port_new_data_set_description(&port_data
, description
);
4506 pa_device_port_new_data_set_direction(&port_data
, path
->direction
== PA_ALSA_DIRECTION_OUTPUT
? PA_DIRECTION_OUTPUT
: PA_DIRECTION_INPUT
);
4508 p
= pa_device_port_new(core
, &port_data
, sizeof(pa_alsa_port_data
));
4509 pa_device_port_new_data_done(&port_data
);
4511 pa_hashmap_put(ports
, p
->name
, p
);
4512 pa_proplist_update(p
->proplist
, PA_UPDATE_REPLACE
, path
->proplist
);
4514 data
= PA_DEVICE_PORT_DATA(p
);
4516 data
->setting
= setting
;
4521 pa_hashmap_put(p
->profiles
, cp
->name
, cp
);
4524 pa_hashmap_put(extra
, p
->name
, p
);
4525 pa_device_port_ref(p
);
4531 void pa_alsa_path_set_add_ports(
4532 pa_alsa_path_set
*ps
,
4533 pa_card_profile
*cp
,
4534 pa_hashmap
*ports
, /* card ports */
4535 pa_hashmap
*extra
, /* sink/source ports */
4546 PA_HASHMAP_FOREACH(path
, ps
->paths
, state
) {
4547 if (!path
->settings
|| !path
->settings
->next
) {
4548 /* If there is no or just one setting we only need a
4550 pa_device_port
*port
= device_port_alsa_init(ports
, path
->name
,
4551 path
->description
, path
, path
->settings
, cp
, extra
, core
);
4552 port
->priority
= path
->priority
* 100;
4556 PA_LLIST_FOREACH(s
, path
->settings
) {
4557 pa_device_port
*port
;
4560 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4562 if (s
->description
[0])
4563 d
= pa_sprintf_malloc("%s / %s", path
->description
, s
->description
);
4565 d
= pa_xstrdup(path
->description
);
4567 port
= device_port_alsa_init(ports
, n
, d
, path
, s
, cp
, extra
, core
);
4568 port
->priority
= path
->priority
* 100 + s
->priority
;
4577 void pa_alsa_add_ports(void *sink_or_source_new_data
, pa_alsa_path_set
*ps
, pa_card
*card
) {
4580 pa_assert(sink_or_source_new_data
);
4583 if (ps
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
4584 ports
= ((pa_sink_new_data
*) sink_or_source_new_data
)->ports
;
4586 ports
= ((pa_source_new_data
*) sink_or_source_new_data
)->ports
;
4588 if (ps
->paths
&& pa_hashmap_size(ps
->paths
) > 0) {
4590 pa_alsa_path_set_add_ports(ps
, NULL
, card
->ports
, ports
, card
->core
);
4593 pa_log_debug("Added %u ports", pa_hashmap_size(ports
));