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
;
279 struct pa_alsa_mixer_pdata
*pa_alsa_mixer_pdata_new(void) {
280 struct pa_alsa_mixer_pdata
*pd
;
282 pd
= pa_xnew0(struct pa_alsa_mixer_pdata
, 1);
287 void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata
*pd
) {
291 pa_rtpoll_item_free(pd
->poll_item
);
297 static int rtpoll_work_cb(pa_rtpoll_item
*i
) {
298 struct pa_alsa_mixer_pdata
*pd
;
301 unsigned short revents
= 0;
304 pd
= pa_rtpoll_item_get_userdata(i
);
306 pa_assert_fp(i
== pd
->poll_item
);
308 p
= pa_rtpoll_item_get_pollfd(i
, &n_fds
);
310 if ((err
= snd_mixer_poll_descriptors_revents(pd
->mixer
, p
, n_fds
, &revents
)) < 0) {
311 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err
));
317 if (revents
& (POLLNVAL
| POLLERR
)) {
318 pa_log_debug("Device disconnected, stopping poll on mixer");
320 } else if (revents
& POLLERR
) {
321 /* This shouldn't happen. */
322 pa_log_error("Got a POLLERR (revents = %04x), stopping poll on mixer", revents
);
326 err
= snd_mixer_handle_events(pd
->mixer
);
328 if (PA_LIKELY(err
>= 0)) {
329 pa_rtpoll_item_free(i
);
330 pa_alsa_set_mixer_rtpoll(pd
, pd
->mixer
, pd
->rtpoll
);
332 pa_log_error("Error handling mixer event: %s", pa_alsa_strerror(err
));
341 pa_rtpoll_item_free(i
);
343 pd
->poll_item
= NULL
;
350 int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata
*pd
, snd_mixer_t
*mixer
, pa_rtpoll
*rtp
) {
359 if ((n
= snd_mixer_poll_descriptors_count(mixer
)) < 0) {
360 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n
));
364 i
= pa_rtpoll_item_new(rtp
, PA_RTPOLL_LATE
, (unsigned) n
);
366 p
= pa_rtpoll_item_get_pollfd(i
, NULL
);
368 memset(p
, 0, sizeof(struct pollfd
) * n
);
370 if ((err
= snd_mixer_poll_descriptors(mixer
, p
, (unsigned) n
)) < 0) {
371 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err
));
372 pa_rtpoll_item_free(i
);
380 pa_rtpoll_item_set_userdata(i
, pd
);
381 pa_rtpoll_item_set_work_callback(i
, rtpoll_work_cb
);
388 static const snd_mixer_selem_channel_id_t alsa_channel_ids
[PA_CHANNEL_POSITION_MAX
] = {
389 [PA_CHANNEL_POSITION_MONO
] = SND_MIXER_SCHN_MONO
, /* The ALSA name is just an alias! */
391 [PA_CHANNEL_POSITION_FRONT_CENTER
] = SND_MIXER_SCHN_FRONT_CENTER
,
392 [PA_CHANNEL_POSITION_FRONT_LEFT
] = SND_MIXER_SCHN_FRONT_LEFT
,
393 [PA_CHANNEL_POSITION_FRONT_RIGHT
] = SND_MIXER_SCHN_FRONT_RIGHT
,
395 [PA_CHANNEL_POSITION_REAR_CENTER
] = SND_MIXER_SCHN_REAR_CENTER
,
396 [PA_CHANNEL_POSITION_REAR_LEFT
] = SND_MIXER_SCHN_REAR_LEFT
,
397 [PA_CHANNEL_POSITION_REAR_RIGHT
] = SND_MIXER_SCHN_REAR_RIGHT
,
399 [PA_CHANNEL_POSITION_LFE
] = SND_MIXER_SCHN_WOOFER
,
401 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
402 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
404 [PA_CHANNEL_POSITION_SIDE_LEFT
] = SND_MIXER_SCHN_SIDE_LEFT
,
405 [PA_CHANNEL_POSITION_SIDE_RIGHT
] = SND_MIXER_SCHN_SIDE_RIGHT
,
407 [PA_CHANNEL_POSITION_AUX0
] = SND_MIXER_SCHN_UNKNOWN
,
408 [PA_CHANNEL_POSITION_AUX1
] = SND_MIXER_SCHN_UNKNOWN
,
409 [PA_CHANNEL_POSITION_AUX2
] = SND_MIXER_SCHN_UNKNOWN
,
410 [PA_CHANNEL_POSITION_AUX3
] = SND_MIXER_SCHN_UNKNOWN
,
411 [PA_CHANNEL_POSITION_AUX4
] = SND_MIXER_SCHN_UNKNOWN
,
412 [PA_CHANNEL_POSITION_AUX5
] = SND_MIXER_SCHN_UNKNOWN
,
413 [PA_CHANNEL_POSITION_AUX6
] = SND_MIXER_SCHN_UNKNOWN
,
414 [PA_CHANNEL_POSITION_AUX7
] = SND_MIXER_SCHN_UNKNOWN
,
415 [PA_CHANNEL_POSITION_AUX8
] = SND_MIXER_SCHN_UNKNOWN
,
416 [PA_CHANNEL_POSITION_AUX9
] = SND_MIXER_SCHN_UNKNOWN
,
417 [PA_CHANNEL_POSITION_AUX10
] = SND_MIXER_SCHN_UNKNOWN
,
418 [PA_CHANNEL_POSITION_AUX11
] = SND_MIXER_SCHN_UNKNOWN
,
419 [PA_CHANNEL_POSITION_AUX12
] = SND_MIXER_SCHN_UNKNOWN
,
420 [PA_CHANNEL_POSITION_AUX13
] = SND_MIXER_SCHN_UNKNOWN
,
421 [PA_CHANNEL_POSITION_AUX14
] = SND_MIXER_SCHN_UNKNOWN
,
422 [PA_CHANNEL_POSITION_AUX15
] = SND_MIXER_SCHN_UNKNOWN
,
423 [PA_CHANNEL_POSITION_AUX16
] = SND_MIXER_SCHN_UNKNOWN
,
424 [PA_CHANNEL_POSITION_AUX17
] = SND_MIXER_SCHN_UNKNOWN
,
425 [PA_CHANNEL_POSITION_AUX18
] = SND_MIXER_SCHN_UNKNOWN
,
426 [PA_CHANNEL_POSITION_AUX19
] = SND_MIXER_SCHN_UNKNOWN
,
427 [PA_CHANNEL_POSITION_AUX20
] = SND_MIXER_SCHN_UNKNOWN
,
428 [PA_CHANNEL_POSITION_AUX21
] = SND_MIXER_SCHN_UNKNOWN
,
429 [PA_CHANNEL_POSITION_AUX22
] = SND_MIXER_SCHN_UNKNOWN
,
430 [PA_CHANNEL_POSITION_AUX23
] = SND_MIXER_SCHN_UNKNOWN
,
431 [PA_CHANNEL_POSITION_AUX24
] = SND_MIXER_SCHN_UNKNOWN
,
432 [PA_CHANNEL_POSITION_AUX25
] = SND_MIXER_SCHN_UNKNOWN
,
433 [PA_CHANNEL_POSITION_AUX26
] = SND_MIXER_SCHN_UNKNOWN
,
434 [PA_CHANNEL_POSITION_AUX27
] = SND_MIXER_SCHN_UNKNOWN
,
435 [PA_CHANNEL_POSITION_AUX28
] = SND_MIXER_SCHN_UNKNOWN
,
436 [PA_CHANNEL_POSITION_AUX29
] = SND_MIXER_SCHN_UNKNOWN
,
437 [PA_CHANNEL_POSITION_AUX30
] = SND_MIXER_SCHN_UNKNOWN
,
438 [PA_CHANNEL_POSITION_AUX31
] = SND_MIXER_SCHN_UNKNOWN
,
440 [PA_CHANNEL_POSITION_TOP_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
442 [PA_CHANNEL_POSITION_TOP_FRONT_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
443 [PA_CHANNEL_POSITION_TOP_FRONT_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
444 [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
,
446 [PA_CHANNEL_POSITION_TOP_REAR_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
447 [PA_CHANNEL_POSITION_TOP_REAR_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
448 [PA_CHANNEL_POSITION_TOP_REAR_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
451 static void setting_free(pa_alsa_setting
*s
) {
455 pa_idxset_free(s
->options
, NULL
);
458 pa_xfree(s
->description
);
462 static void option_free(pa_alsa_option
*o
) {
465 pa_xfree(o
->alsa_name
);
467 pa_xfree(o
->description
);
471 static void decibel_fix_free(pa_alsa_decibel_fix
*db_fix
) {
474 pa_xfree(db_fix
->name
);
475 pa_xfree(db_fix
->db_values
);
480 static void jack_free(pa_alsa_jack
*j
) {
483 pa_xfree(j
->alsa_name
);
488 static void element_free(pa_alsa_element
*e
) {
492 while ((o
= e
->options
)) {
493 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
498 decibel_fix_free(e
->db_fix
);
500 pa_xfree(e
->alsa_name
);
504 void pa_alsa_path_free(pa_alsa_path
*p
) {
511 while ((j
= p
->jacks
)) {
512 PA_LLIST_REMOVE(pa_alsa_jack
, p
->jacks
, j
);
516 while ((e
= p
->elements
)) {
517 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
521 while ((s
= p
->settings
)) {
522 PA_LLIST_REMOVE(pa_alsa_setting
, p
->settings
, s
);
526 pa_proplist_free(p
->proplist
);
528 pa_xfree(p
->description
);
532 void pa_alsa_path_set_free(pa_alsa_path_set
*ps
) {
536 pa_hashmap_free(ps
->paths
, NULL
);
541 static long to_alsa_dB(pa_volume_t v
) {
542 return (long) (pa_sw_volume_to_dB(v
) * 100.0);
545 static pa_volume_t
from_alsa_dB(long v
) {
546 return pa_sw_volume_from_dB((double) v
/ 100.0);
549 static long to_alsa_volume(pa_volume_t v
, long min
, long max
) {
552 w
= (long) round(((double) v
* (double) (max
- min
)) / PA_VOLUME_NORM
) + min
;
553 return PA_CLAMP_UNLIKELY(w
, min
, max
);
556 static pa_volume_t
from_alsa_volume(long v
, long min
, long max
) {
557 return (pa_volume_t
) round(((double) (v
- min
) * PA_VOLUME_NORM
) / (double) (max
- min
));
560 #define SELEM_INIT(sid, name) \
562 snd_mixer_selem_id_alloca(&(sid)); \
563 snd_mixer_selem_id_set_name((sid), (name)); \
564 snd_mixer_selem_id_set_index((sid), 0); \
567 static int element_get_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
568 snd_mixer_selem_id_t
*sid
;
569 snd_mixer_elem_t
*me
;
570 snd_mixer_selem_channel_id_t c
;
571 pa_channel_position_mask_t mask
= 0;
579 SELEM_INIT(sid
, e
->alsa_name
);
580 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
581 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
585 pa_cvolume_mute(v
, cm
->channels
);
587 /* We take the highest volume of all channels that match */
589 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
596 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
597 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
599 if ((r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
)) >= 0) {
600 /* If the channel volume is outside the limits set
601 * by the dB fix, we clamp the hw volume to be
602 * within the limits. */
603 if (value
< e
->db_fix
->min_step
) {
604 value
= e
->db_fix
->min_step
;
605 snd_mixer_selem_set_playback_volume(me
, c
, value
);
606 pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
607 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
608 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
609 } else if (value
> e
->db_fix
->max_step
) {
610 value
= e
->db_fix
->max_step
;
611 snd_mixer_selem_set_playback_volume(me
, c
, value
);
612 pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
613 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
614 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
617 /* Volume step -> dB value conversion. */
618 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
621 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
625 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
627 if ((r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
)) >= 0) {
628 /* If the channel volume is outside the limits set
629 * by the dB fix, we clamp the hw volume to be
630 * within the limits. */
631 if (value
< e
->db_fix
->min_step
) {
632 value
= e
->db_fix
->min_step
;
633 snd_mixer_selem_set_capture_volume(me
, c
, value
);
634 pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
635 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
636 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
637 } else if (value
> e
->db_fix
->max_step
) {
638 value
= e
->db_fix
->max_step
;
639 snd_mixer_selem_set_capture_volume(me
, c
, value
);
640 pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
641 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
642 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
645 /* Volume step -> dB value conversion. */
646 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
649 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
657 #ifdef HAVE_VALGRIND_MEMCHECK_H
658 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
661 f
= from_alsa_dB(value
);
666 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
667 if (snd_mixer_selem_has_playback_channel(me
, c
))
668 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
672 if (snd_mixer_selem_has_capture_channel(me
, c
))
673 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
681 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
684 for (k
= 0; k
< cm
->channels
; k
++)
685 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
686 if (v
->values
[k
] < f
)
689 mask
|= e
->masks
[c
][e
->n_channels
-1];
692 for (k
= 0; k
< cm
->channels
; k
++)
693 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
694 v
->values
[k
] = PA_VOLUME_NORM
;
699 int pa_alsa_path_get_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
710 pa_cvolume_reset(v
, cm
->channels
);
712 PA_LLIST_FOREACH(e
, p
->elements
) {
715 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
718 pa_assert(!p
->has_dB
|| e
->has_dB
);
720 if (element_get_volume(e
, m
, cm
, &ev
) < 0)
723 /* If we have no dB information all we can do is take the first element and leave */
729 pa_sw_cvolume_multiply(v
, v
, &ev
);
735 static int element_get_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t
*b
) {
736 snd_mixer_selem_id_t
*sid
;
737 snd_mixer_elem_t
*me
;
738 snd_mixer_selem_channel_id_t c
;
744 SELEM_INIT(sid
, e
->alsa_name
);
745 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
746 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
750 /* We return muted if at least one channel is muted */
752 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
756 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
757 if (snd_mixer_selem_has_playback_channel(me
, c
))
758 r
= snd_mixer_selem_get_playback_switch(me
, c
, &value
);
762 if (snd_mixer_selem_has_capture_channel(me
, c
))
763 r
= snd_mixer_selem_get_capture_switch(me
, c
, &value
);
781 int pa_alsa_path_get_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t
*muted
) {
791 PA_LLIST_FOREACH(e
, p
->elements
) {
794 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
797 if (element_get_switch(e
, m
, &b
) < 0)
810 /* Finds the closest item in db_fix->db_values and returns the corresponding
811 * step. *db_value is replaced with the value from the db_values table.
812 * Rounding is done based on the rounding parameter: -1 means rounding down and
813 * +1 means rounding up. */
814 static long decibel_fix_get_step(pa_alsa_decibel_fix
*db_fix
, long *db_value
, int rounding
) {
820 pa_assert(rounding
!= 0);
822 max_i
= db_fix
->max_step
- db_fix
->min_step
;
825 for (i
= 0; i
< max_i
; i
++) {
826 if (db_fix
->db_values
[i
] >= *db_value
)
830 for (i
= 0; i
< max_i
; i
++) {
831 if (db_fix
->db_values
[i
+ 1] > *db_value
)
836 *db_value
= db_fix
->db_values
[i
];
838 return i
+ db_fix
->min_step
;
841 /* Alsa lib documentation says for snd_mixer_selem_set_playback_dB() direction argument,
842 * that "-1 = accurate or first below, 0 = accurate, 1 = accurate or first above".
843 * But even with accurate nearest dB volume step is not selected, so that is why we need
844 * this function. Returns 0 and nearest selectable volume in *value_dB on success or
845 * negative error code if fails. */
846 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
) {
856 if (d
== PA_ALSA_DIRECTION_OUTPUT
) {
857 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, *value_dB
, +1, &alsa_val
)) >= 0)
858 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value_high
);
863 if (value_high
== *value_dB
)
866 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, *value_dB
, -1, &alsa_val
)) >= 0)
867 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value_low
);
869 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, *value_dB
, +1, &alsa_val
)) >= 0)
870 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value_high
);
875 if (value_high
== *value_dB
)
878 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, *value_dB
, -1, &alsa_val
)) >= 0)
879 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value_low
);
885 if (labs(value_high
- *value_dB
) < labs(value_low
- *value_dB
))
886 *value_dB
= value_high
;
888 *value_dB
= value_low
;
893 static int element_set_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
, pa_bool_t deferred_volume
, pa_bool_t write_to_hw
) {
895 snd_mixer_selem_id_t
*sid
;
897 snd_mixer_elem_t
*me
;
898 snd_mixer_selem_channel_id_t c
;
899 pa_channel_position_mask_t mask
= 0;
906 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
908 SELEM_INIT(sid
, e
->alsa_name
);
909 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
910 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
914 pa_cvolume_mute(&rv
, cm
->channels
);
916 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
918 pa_volume_t f
= PA_VOLUME_MUTED
;
919 pa_bool_t found
= FALSE
;
921 for (k
= 0; k
< cm
->channels
; k
++)
922 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
])) {
924 if (v
->values
[k
] > f
)
929 /* Hmm, so this channel does not exist in the volume
930 * struct, so let's bind it to the overall max of the
932 f
= pa_cvolume_max(v
);
936 long value
= to_alsa_dB(f
);
939 if (e
->volume_limit
>= 0 && value
> (e
->max_dB
* 100))
940 value
= e
->max_dB
* 100;
942 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
943 /* If we call set_playback_volume() without checking first
944 * if the channel is available, ALSA behaves very
945 * strangely and doesn't fail the call */
946 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
950 r
= snd_mixer_selem_set_playback_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
952 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
958 if (deferred_volume
) {
959 if ((r
= element_get_nearest_alsa_dB(me
, c
, PA_ALSA_DIRECTION_OUTPUT
, &value
)) >= 0)
960 r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, 0);
962 if ((r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, rounding
)) >= 0)
963 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
967 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
968 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value
);
974 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
978 r
= snd_mixer_selem_set_capture_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
980 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
986 if (deferred_volume
) {
987 if ((r
= element_get_nearest_alsa_dB(me
, c
, PA_ALSA_DIRECTION_INPUT
, &value
)) >= 0)
988 r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, 0);
990 if ((r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, rounding
)) >= 0)
991 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
995 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
996 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value
);
1006 #ifdef HAVE_VALGRIND_MEMCHECK_H
1007 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
1010 f
= from_alsa_dB(value
);
1015 value
= to_alsa_volume(f
, e
->min_volume
, e
->max_volume
);
1017 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1018 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
1019 if ((r
= snd_mixer_selem_set_playback_volume(me
, c
, value
)) >= 0)
1020 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
1024 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
1025 if ((r
= snd_mixer_selem_set_capture_volume(me
, c
, value
)) >= 0)
1026 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
1034 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
1037 for (k
= 0; k
< cm
->channels
; k
++)
1038 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
1039 if (rv
.values
[k
] < f
)
1042 mask
|= e
->masks
[c
][e
->n_channels
-1];
1045 for (k
= 0; k
< cm
->channels
; k
++)
1046 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
1047 rv
.values
[k
] = PA_VOLUME_NORM
;
1053 int pa_alsa_path_set_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
, pa_bool_t deferred_volume
, pa_bool_t write_to_hw
) {
1062 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
1067 rv
= *v
; /* Remaining adjustment */
1068 pa_cvolume_reset(v
, cm
->channels
); /* Adjustment done */
1070 PA_LLIST_FOREACH(e
, p
->elements
) {
1073 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
1076 pa_assert(!p
->has_dB
|| e
->has_dB
);
1079 if (element_set_volume(e
, m
, cm
, &ev
, deferred_volume
, write_to_hw
) < 0)
1087 pa_sw_cvolume_multiply(v
, v
, &ev
);
1088 pa_sw_cvolume_divide(&rv
, &rv
, &ev
);
1094 static int element_set_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t b
) {
1095 snd_mixer_elem_t
*me
;
1096 snd_mixer_selem_id_t
*sid
;
1102 SELEM_INIT(sid
, e
->alsa_name
);
1103 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1104 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1108 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1109 r
= snd_mixer_selem_set_playback_switch_all(me
, b
);
1111 r
= snd_mixer_selem_set_capture_switch_all(me
, b
);
1114 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1119 int pa_alsa_path_set_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t muted
) {
1128 PA_LLIST_FOREACH(e
, p
->elements
) {
1130 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
1133 if (element_set_switch(e
, m
, !muted
) < 0)
1140 /* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
1141 * function sets all channels of the volume element to e->min_volume, 0 dB or
1142 * e->constant_volume. */
1143 static int element_set_constant_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1144 snd_mixer_elem_t
*me
= NULL
;
1145 snd_mixer_selem_id_t
*sid
= NULL
;
1148 pa_bool_t volume_set
= FALSE
;
1153 SELEM_INIT(sid
, e
->alsa_name
);
1154 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1155 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1159 switch (e
->volume_use
) {
1160 case PA_ALSA_VOLUME_OFF
:
1161 volume
= e
->min_volume
;
1165 case PA_ALSA_VOLUME_ZERO
:
1169 volume
= decibel_fix_get_step(e
->db_fix
, &dB
, (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
? +1 : -1));
1174 case PA_ALSA_VOLUME_CONSTANT
:
1175 volume
= e
->constant_volume
;
1180 pa_assert_not_reached();
1184 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1185 r
= snd_mixer_selem_set_playback_volume_all(me
, volume
);
1187 r
= snd_mixer_selem_set_capture_volume_all(me
, volume
);
1189 pa_assert(e
->volume_use
== PA_ALSA_VOLUME_ZERO
);
1190 pa_assert(!e
->db_fix
);
1192 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1193 r
= snd_mixer_selem_set_playback_dB_all(me
, 0, +1);
1195 r
= snd_mixer_selem_set_capture_dB_all(me
, 0, -1);
1199 pa_log_warn("Failed to set volume of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1204 int pa_alsa_path_select(pa_alsa_path
*p
, pa_alsa_setting
*s
, snd_mixer_t
*m
, bool device_is_muted
) {
1211 pa_log_debug("Activating path %s", p
->name
);
1212 pa_alsa_path_dump(p
);
1214 /* First turn on hw mute if available, to avoid noise
1215 * when setting the mixer controls. */
1216 if (p
->mute_during_activation
) {
1217 PA_LLIST_FOREACH(e
, p
->elements
) {
1218 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
1219 /* If the muting fails here, that's not a critical problem for
1220 * selecting a path, so we ignore the return value.
1221 * element_set_switch() will print a warning anyway, so this
1222 * won't be a silent failure either. */
1223 (void) element_set_switch(e
, m
, FALSE
);
1227 PA_LLIST_FOREACH(e
, p
->elements
) {
1229 switch (e
->switch_use
) {
1230 case PA_ALSA_SWITCH_OFF
:
1231 r
= element_set_switch(e
, m
, FALSE
);
1234 case PA_ALSA_SWITCH_ON
:
1235 r
= element_set_switch(e
, m
, TRUE
);
1238 case PA_ALSA_SWITCH_MUTE
:
1239 case PA_ALSA_SWITCH_IGNORE
:
1240 case PA_ALSA_SWITCH_SELECT
:
1248 switch (e
->volume_use
) {
1249 case PA_ALSA_VOLUME_OFF
:
1250 case PA_ALSA_VOLUME_ZERO
:
1251 case PA_ALSA_VOLUME_CONSTANT
:
1252 r
= element_set_constant_volume(e
, m
);
1255 case PA_ALSA_VOLUME_MERGE
:
1256 case PA_ALSA_VOLUME_IGNORE
:
1266 setting_select(s
, m
);
1268 /* Finally restore hw mute to the device mute status. */
1269 if (p
->mute_during_activation
) {
1270 PA_LLIST_FOREACH(e
, p
->elements
) {
1271 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
) {
1272 if (element_set_switch(e
, m
, !device_is_muted
) < 0)
1281 static int check_required(pa_alsa_element
*e
, snd_mixer_elem_t
*me
) {
1282 pa_bool_t has_switch
;
1283 pa_bool_t has_enumeration
;
1284 pa_bool_t has_volume
;
1289 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1291 snd_mixer_selem_has_playback_switch(me
) ||
1292 (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
));
1295 snd_mixer_selem_has_capture_switch(me
) ||
1296 (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
));
1299 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1301 snd_mixer_selem_has_playback_volume(me
) ||
1302 (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
));
1305 snd_mixer_selem_has_capture_volume(me
) ||
1306 (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
));
1309 has_enumeration
= snd_mixer_selem_is_enumerated(me
);
1311 if ((e
->required
== PA_ALSA_REQUIRED_SWITCH
&& !has_switch
) ||
1312 (e
->required
== PA_ALSA_REQUIRED_VOLUME
&& !has_volume
) ||
1313 (e
->required
== PA_ALSA_REQUIRED_ENUMERATION
&& !has_enumeration
))
1316 if (e
->required
== PA_ALSA_REQUIRED_ANY
&& !(has_switch
|| has_volume
|| has_enumeration
))
1319 if ((e
->required_absent
== PA_ALSA_REQUIRED_SWITCH
&& has_switch
) ||
1320 (e
->required_absent
== PA_ALSA_REQUIRED_VOLUME
&& has_volume
) ||
1321 (e
->required_absent
== PA_ALSA_REQUIRED_ENUMERATION
&& has_enumeration
))
1324 if (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& (has_switch
|| has_volume
|| has_enumeration
))
1327 if (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) {
1328 switch (e
->required_any
) {
1329 case PA_ALSA_REQUIRED_VOLUME
:
1330 e
->path
->req_any_present
|= (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
);
1332 case PA_ALSA_REQUIRED_SWITCH
:
1333 e
->path
->req_any_present
|= (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
);
1335 case PA_ALSA_REQUIRED_ENUMERATION
:
1336 e
->path
->req_any_present
|= (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1338 case PA_ALSA_REQUIRED_ANY
:
1339 e
->path
->req_any_present
|=
1340 (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) ||
1341 (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) ||
1342 (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1345 pa_assert_not_reached();
1349 if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1351 PA_LLIST_FOREACH(o
, e
->options
) {
1352 e
->path
->req_any_present
|= (o
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) &&
1354 if (o
->required
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
< 0)
1356 if (o
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
>= 0)
1364 static int element_probe(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1365 snd_mixer_selem_id_t
*sid
;
1366 snd_mixer_elem_t
*me
;
1372 SELEM_INIT(sid
, e
->alsa_name
);
1374 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1376 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1379 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1380 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1381 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1386 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1387 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1389 if (!snd_mixer_selem_has_playback_switch(me
)) {
1390 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1391 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1393 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1398 if (!snd_mixer_selem_has_capture_switch(me
)) {
1399 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1400 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1402 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1406 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1407 e
->direction_try_other
= FALSE
;
1410 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1412 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1414 if (!snd_mixer_selem_has_playback_volume(me
)) {
1415 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1416 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1418 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1423 if (!snd_mixer_selem_has_capture_volume(me
)) {
1424 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1425 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1427 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1431 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1432 long min_dB
= 0, max_dB
= 0;
1435 e
->direction_try_other
= FALSE
;
1437 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1438 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1440 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1443 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1447 if (e
->min_volume
>= e
->max_volume
) {
1448 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
);
1449 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1451 } else if (e
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&&
1452 (e
->min_volume
> e
->constant_volume
|| e
->max_volume
< e
->constant_volume
)) {
1453 pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
1454 e
->constant_volume
, e
->alsa_name
, e
->min_volume
, e
->max_volume
);
1455 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1459 pa_channel_position_t p
;
1462 ((e
->min_volume
> e
->db_fix
->min_step
) ||
1463 (e
->max_volume
< e
->db_fix
->max_step
))) {
1464 pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1465 "real hardware range (%li-%li). Disabling the decibel fix.", e
->alsa_name
,
1466 e
->db_fix
->min_step
, e
->db_fix
->max_step
,
1467 e
->min_volume
, e
->max_volume
);
1469 decibel_fix_free(e
->db_fix
);
1475 e
->min_volume
= e
->db_fix
->min_step
;
1476 e
->max_volume
= e
->db_fix
->max_step
;
1477 min_dB
= e
->db_fix
->db_values
[0];
1478 max_dB
= e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
];
1479 } else if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1480 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1482 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1484 /* Check that the kernel driver returns consistent limits with
1485 * both _get_*_dB_range() and _ask_*_vol_dB(). */
1486 if (e
->has_dB
&& !e
->db_fix
) {
1487 long min_dB_checked
= 0;
1488 long max_dB_checked
= 0;
1490 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1491 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1493 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1496 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->min_volume
);
1500 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1501 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1503 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1506 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->max_volume
);
1510 if (min_dB
!= min_dB_checked
|| max_dB
!= max_dB_checked
) {
1511 pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
1512 "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
1513 "%0.2f dB at level %li.",
1515 min_dB
/ 100.0, max_dB
/ 100.0,
1516 min_dB_checked
/ 100.0, e
->min_volume
, max_dB_checked
/ 100.0, e
->max_volume
);
1522 #ifdef HAVE_VALGRIND_MEMCHECK_H
1523 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1524 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1527 e
->min_dB
= ((double) min_dB
) / 100.0;
1528 e
->max_dB
= ((double) max_dB
) / 100.0;
1530 if (min_dB
>= max_dB
) {
1531 pa_assert(!e
->db_fix
);
1532 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
);
1537 if (e
->volume_limit
>= 0) {
1538 if (e
->volume_limit
<= e
->min_volume
|| e
->volume_limit
> e
->max_volume
)
1539 pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1540 "%li-%li. The volume limit is ignored.",
1541 e
->alsa_name
, e
->path
->name
, e
->volume_limit
, e
->min_volume
+ 1, e
->max_volume
);
1544 e
->max_volume
= e
->volume_limit
;
1548 e
->db_fix
->max_step
= e
->max_volume
;
1549 e
->max_dB
= ((double) e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
]) / 100.0;
1552 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1553 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB
);
1555 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB
);
1558 pa_log_warn("Failed to get dB value of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1561 e
->max_dB
= ((double) max_dB
) / 100.0;
1567 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1568 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1570 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1575 if (!e
->override_map
) {
1576 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1577 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1580 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1583 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1586 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1589 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1591 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1594 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1595 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1597 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1600 if (e
->n_channels
<= 0) {
1601 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1605 if (e
->n_channels
> 2) {
1606 /* FIXME: In some places code like this is used:
1608 * e->masks[alsa_channel_ids[p]][e->n_channels-1]
1610 * The definition of e->masks is
1612 * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][2];
1614 * Since the array size is fixed at 2, we obviously
1615 * don't support elements with more than two
1617 pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e
->alsa_name
, e
->n_channels
);
1621 if (!e
->override_map
) {
1622 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1623 pa_bool_t has_channel
;
1625 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1628 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1629 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1631 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1633 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1638 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1639 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1642 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1650 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1653 PA_LLIST_FOREACH(o
, e
->options
)
1654 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1655 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1659 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1660 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1664 PA_LLIST_FOREACH(o
, e
->options
) {
1667 for (i
= 0; i
< n
; i
++) {
1670 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1673 if (!pa_streq(buf
, o
->alsa_name
))
1681 if (check_required(e
, me
) < 0)
1687 static int jack_probe(pa_alsa_jack
*j
, snd_hctl_t
*h
) {
1692 j
->has_control
= pa_alsa_find_jack(h
, j
->alsa_name
) != NULL
;
1694 if (j
->has_control
) {
1695 if (j
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
)
1697 if (j
->required_any
!= PA_ALSA_REQUIRED_IGNORE
)
1698 j
->path
->req_any_present
= TRUE
;
1700 if (j
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1707 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, pa_bool_t prefixed
) {
1714 if (!pa_startswith(section
, "Element "))
1720 /* This is not an element section, but an enum section? */
1721 if (strchr(section
, ':'))
1724 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1725 return p
->last_element
;
1727 PA_LLIST_FOREACH(e
, p
->elements
)
1728 if (pa_streq(e
->alsa_name
, section
))
1731 e
= pa_xnew0(pa_alsa_element
, 1);
1733 e
->alsa_name
= pa_xstrdup(section
);
1734 e
->direction
= p
->direction
;
1735 e
->volume_limit
= -1;
1737 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1740 p
->last_element
= e
;
1744 static pa_alsa_jack
* jack_get(pa_alsa_path
*p
, const char *section
) {
1747 if (!pa_startswith(section
, "Jack "))
1751 if (p
->last_jack
&& pa_streq(p
->last_jack
->name
, section
))
1752 return p
->last_jack
;
1754 PA_LLIST_FOREACH(j
, p
->jacks
)
1755 if (pa_streq(j
->name
, section
))
1758 j
= pa_xnew0(pa_alsa_jack
, 1);
1759 j
->state_unplugged
= PA_AVAILABLE_NO
;
1760 j
->state_plugged
= PA_AVAILABLE_YES
;
1762 j
->name
= pa_xstrdup(section
);
1763 j
->alsa_name
= pa_sprintf_malloc("%s Jack", section
);
1764 PA_LLIST_INSERT_AFTER(pa_alsa_jack
, p
->jacks
, p
->last_jack
, j
);
1772 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1778 if (!pa_startswith(section
, "Option "))
1783 /* This is not an enum section, but an element section? */
1784 if (!(on
= strchr(section
, ':')))
1787 en
= pa_xstrndup(section
, on
- section
);
1790 if (p
->last_option
&&
1791 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1792 pa_streq(p
->last_option
->alsa_name
, on
)) {
1794 return p
->last_option
;
1797 pa_assert_se(e
= element_get(p
, en
, FALSE
));
1800 PA_LLIST_FOREACH(o
, e
->options
)
1801 if (pa_streq(o
->alsa_name
, on
))
1804 o
= pa_xnew0(pa_alsa_option
, 1);
1806 o
->alsa_name
= pa_xstrdup(on
);
1809 if (p
->last_option
&& p
->last_option
->element
== e
)
1810 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1812 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1819 static int element_parse_switch(pa_config_parser_state
*state
) {
1825 p
= state
->userdata
;
1827 if (!(e
= element_get(p
, state
->section
, TRUE
))) {
1828 pa_log("[%s:%u] Switch makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1832 if (pa_streq(state
->rvalue
, "ignore"))
1833 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1834 else if (pa_streq(state
->rvalue
, "mute"))
1835 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1836 else if (pa_streq(state
->rvalue
, "off"))
1837 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1838 else if (pa_streq(state
->rvalue
, "on"))
1839 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1840 else if (pa_streq(state
->rvalue
, "select"))
1841 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1843 pa_log("[%s:%u] Switch invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1850 static int element_parse_volume(pa_config_parser_state
*state
) {
1856 p
= state
->userdata
;
1858 if (!(e
= element_get(p
, state
->section
, TRUE
))) {
1859 pa_log("[%s:%u] Volume makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1863 if (pa_streq(state
->rvalue
, "ignore"))
1864 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1865 else if (pa_streq(state
->rvalue
, "merge"))
1866 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1867 else if (pa_streq(state
->rvalue
, "off"))
1868 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1869 else if (pa_streq(state
->rvalue
, "zero"))
1870 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1874 if (pa_atou(state
->rvalue
, &constant
) >= 0) {
1875 e
->volume_use
= PA_ALSA_VOLUME_CONSTANT
;
1876 e
->constant_volume
= constant
;
1878 pa_log("[%s:%u] Volume invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1886 static int element_parse_enumeration(pa_config_parser_state
*state
) {
1892 p
= state
->userdata
;
1894 if (!(e
= element_get(p
, state
->section
, TRUE
))) {
1895 pa_log("[%s:%u] Enumeration makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1899 if (pa_streq(state
->rvalue
, "ignore"))
1900 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1901 else if (pa_streq(state
->rvalue
, "select"))
1902 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1904 pa_log("[%s:%u] Enumeration invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1911 static int option_parse_priority(pa_config_parser_state
*state
) {
1918 p
= state
->userdata
;
1920 if (!(o
= option_get(p
, state
->section
))) {
1921 pa_log("[%s:%u] Priority makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1925 if (pa_atou(state
->rvalue
, &prio
) < 0) {
1926 pa_log("[%s:%u] Priority invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1934 static int option_parse_name(pa_config_parser_state
*state
) {
1940 p
= state
->userdata
;
1942 if (!(o
= option_get(p
, state
->section
))) {
1943 pa_log("[%s:%u] Name makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1948 o
->name
= pa_xstrdup(state
->rvalue
);
1953 static int element_parse_required(pa_config_parser_state
*state
) {
1958 pa_alsa_required_t req
;
1962 p
= state
->userdata
;
1964 e
= element_get(p
, state
->section
, TRUE
);
1965 o
= option_get(p
, state
->section
);
1966 j
= jack_get(p
, state
->section
);
1967 if (!e
&& !o
&& !j
) {
1968 pa_log("[%s:%u] Required makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1972 if (pa_streq(state
->rvalue
, "ignore"))
1973 req
= PA_ALSA_REQUIRED_IGNORE
;
1974 else if (pa_streq(state
->rvalue
, "switch") && e
)
1975 req
= PA_ALSA_REQUIRED_SWITCH
;
1976 else if (pa_streq(state
->rvalue
, "volume") && e
)
1977 req
= PA_ALSA_REQUIRED_VOLUME
;
1978 else if (pa_streq(state
->rvalue
, "enumeration"))
1979 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1980 else if (pa_streq(state
->rvalue
, "any"))
1981 req
= PA_ALSA_REQUIRED_ANY
;
1983 pa_log("[%s:%u] Required invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1987 if (pa_streq(state
->lvalue
, "required-absent")) {
1989 e
->required_absent
= req
;
1991 o
->required_absent
= req
;
1993 j
->required_absent
= req
;
1995 else if (pa_streq(state
->lvalue
, "required-any")) {
1997 e
->required_any
= req
;
1998 e
->path
->has_req_any
|= (req
!= PA_ALSA_REQUIRED_IGNORE
);
2001 o
->required_any
= req
;
2002 o
->element
->path
->has_req_any
|= (req
!= PA_ALSA_REQUIRED_IGNORE
);
2005 j
->required_any
= req
;
2006 j
->path
->has_req_any
|= (req
!= PA_ALSA_REQUIRED_IGNORE
);
2022 static int element_parse_direction(pa_config_parser_state
*state
) {
2028 p
= state
->userdata
;
2030 if (!(e
= element_get(p
, state
->section
, TRUE
))) {
2031 pa_log("[%s:%u] Direction makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2035 if (pa_streq(state
->rvalue
, "playback"))
2036 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
2037 else if (pa_streq(state
->rvalue
, "capture"))
2038 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
2040 pa_log("[%s:%u] Direction invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
2047 static int element_parse_direction_try_other(pa_config_parser_state
*state
) {
2054 p
= state
->userdata
;
2056 if (!(e
= element_get(p
, state
->section
, TRUE
))) {
2057 pa_log("[%s:%u] Direction makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2061 if ((yes
= pa_parse_boolean(state
->rvalue
)) < 0) {
2062 pa_log("[%s:%u] Direction invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
2066 e
->direction_try_other
= !!yes
;
2070 static int element_parse_volume_limit(pa_config_parser_state
*state
) {
2077 p
= state
->userdata
;
2079 if (!(e
= element_get(p
, state
->section
, TRUE
))) {
2080 pa_log("[%s:%u] volume-limit makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2084 if (pa_atol(state
->rvalue
, &volume_limit
) < 0 || volume_limit
< 0) {
2085 pa_log("[%s:%u] Invalid value for volume-limit", state
->filename
, state
->lineno
);
2089 e
->volume_limit
= volume_limit
;
2093 static pa_channel_position_mask_t
parse_mask(const char *m
) {
2094 pa_channel_position_mask_t v
;
2096 if (pa_streq(m
, "all-left"))
2097 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
2098 else if (pa_streq(m
, "all-right"))
2099 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
2100 else if (pa_streq(m
, "all-center"))
2101 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
2102 else if (pa_streq(m
, "all-front"))
2103 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
2104 else if (pa_streq(m
, "all-rear"))
2105 v
= PA_CHANNEL_POSITION_MASK_REAR
;
2106 else if (pa_streq(m
, "all-side"))
2107 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
2108 else if (pa_streq(m
, "all-top"))
2109 v
= PA_CHANNEL_POSITION_MASK_TOP
;
2110 else if (pa_streq(m
, "all-no-lfe"))
2111 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
2112 else if (pa_streq(m
, "all"))
2113 v
= PA_CHANNEL_POSITION_MASK_ALL
;
2115 pa_channel_position_t p
;
2117 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
2120 v
= PA_CHANNEL_POSITION_MASK(p
);
2126 static int element_parse_override_map(pa_config_parser_state
*state
) {
2129 const char *split_state
= NULL
;
2135 p
= state
->userdata
;
2137 if (!(e
= element_get(p
, state
->section
, TRUE
))) {
2138 pa_log("[%s:%u] Override map makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2142 while ((n
= pa_split(state
->rvalue
, ",", &split_state
))) {
2143 pa_channel_position_mask_t m
;
2148 if ((m
= parse_mask(n
)) == 0) {
2149 pa_log("[%s:%u] Override map '%s' invalid in '%s'", state
->filename
, state
->lineno
, n
, state
->section
);
2155 if (pa_streq(state
->lvalue
, "override-map.1"))
2156 e
->masks
[i
++][0] = m
;
2158 e
->masks
[i
++][1] = m
;
2160 /* Later on we might add override-map.3 and so on here ... */
2165 e
->override_map
= TRUE
;
2170 static int jack_parse_state(pa_config_parser_state
*state
) {
2177 p
= state
->userdata
;
2179 if (!(j
= jack_get(p
, state
->section
))) {
2180 pa_log("[%s:%u] state makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2184 if (pa_streq(state
->rvalue
, "yes"))
2185 pa
= PA_AVAILABLE_YES
;
2186 else if (pa_streq(state
->rvalue
, "no"))
2187 pa
= PA_AVAILABLE_NO
;
2188 else if (pa_streq(state
->rvalue
, "unknown"))
2189 pa
= PA_AVAILABLE_UNKNOWN
;
2191 pa_log("[%s:%u] state must be 'yes', 'no' or 'unknown' in '%s'", state
->filename
, state
->lineno
, state
->section
);
2195 if (pa_streq(state
->lvalue
, "state.unplugged"))
2196 j
->state_unplugged
= pa
;
2198 j
->state_plugged
= pa
;
2199 pa_assert(pa_streq(state
->lvalue
, "state.plugged"));
2205 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
2206 snd_mixer_selem_id_t
*sid
;
2207 snd_mixer_elem_t
*me
;
2213 SELEM_INIT(sid
, e
->alsa_name
);
2214 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2215 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2219 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
2221 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
2222 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
2224 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
2227 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2230 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
2232 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
2233 pa_log_warn("Failed to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2239 static int setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
2246 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
2247 element_set_option(o
->element
, m
, o
->alsa_idx
);
2252 static int option_verify(pa_alsa_option
*o
) {
2253 static const struct description_map well_known_descriptions
[] = {
2254 { "input", N_("Input") },
2255 { "input-docking", N_("Docking Station Input") },
2256 { "input-docking-microphone", N_("Docking Station Microphone") },
2257 { "input-docking-linein", N_("Docking Station Line In") },
2258 { "input-linein", N_("Line In") },
2259 { "input-microphone", N_("Microphone") },
2260 { "input-microphone-front", N_("Front Microphone") },
2261 { "input-microphone-rear", N_("Rear Microphone") },
2262 { "input-microphone-external", N_("External Microphone") },
2263 { "input-microphone-internal", N_("Internal Microphone") },
2264 { "input-radio", N_("Radio") },
2265 { "input-video", N_("Video") },
2266 { "input-agc-on", N_("Automatic Gain Control") },
2267 { "input-agc-off", N_("No Automatic Gain Control") },
2268 { "input-boost-on", N_("Boost") },
2269 { "input-boost-off", N_("No Boost") },
2270 { "output-amplifier-on", N_("Amplifier") },
2271 { "output-amplifier-off", N_("No Amplifier") },
2272 { "output-bass-boost-on", N_("Bass Boost") },
2273 { "output-bass-boost-off", N_("No Bass Boost") },
2274 { "output-speaker", N_("Speaker") },
2275 { "output-headphones", N_("Headphones") }
2281 pa_log("No name set for option %s", o
->alsa_name
);
2285 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
2286 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
2287 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
2291 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
2292 !pa_streq(o
->alsa_name
, "on") &&
2293 !pa_streq(o
->alsa_name
, "off")) {
2294 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
2298 if (!o
->description
)
2299 o
->description
= pa_xstrdup(lookup_description(o
->name
,
2300 well_known_descriptions
,
2301 PA_ELEMENTSOF(well_known_descriptions
)));
2302 if (!o
->description
)
2303 o
->description
= pa_xstrdup(o
->name
);
2308 static int element_verify(pa_alsa_element
*e
) {
2313 // 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);
2314 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
2315 (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required_any
== e
->required_absent
) ||
2316 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) ||
2317 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
2318 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
2322 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
2323 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
2327 PA_LLIST_FOREACH(o
, e
->options
)
2328 if (option_verify(o
) < 0)
2334 static int path_verify(pa_alsa_path
*p
) {
2335 static const struct description_map well_known_descriptions
[] = {
2336 { "analog-input", N_("Analog Input") },
2337 { "analog-input-microphone", N_("Microphone") },
2338 { "analog-input-microphone-front", N_("Front Microphone") },
2339 { "analog-input-microphone-rear", N_("Rear Microphone") },
2340 { "analog-input-microphone-dock", N_("Dock Microphone") },
2341 { "analog-input-microphone-internal", N_("Internal Microphone") },
2342 { "analog-input-microphone-headset", N_("Headset Microphone") },
2343 { "analog-input-linein", N_("Line In") },
2344 { "analog-input-radio", N_("Radio") },
2345 { "analog-input-video", N_("Video") },
2346 { "analog-output", N_("Analog Output") },
2347 { "analog-output-headphones", N_("Headphones") },
2348 { "analog-output-lfe-on-mono", N_("LFE on Separate Mono Output") },
2349 { "analog-output-lineout", N_("Line Out") },
2350 { "analog-output-mono", N_("Analog Mono Output") },
2351 { "analog-output-speaker", N_("Speakers") },
2352 { "hdmi-output", N_("HDMI / DisplayPort") },
2353 { "iec958-stereo-output", N_("Digital Output (S/PDIF)") },
2354 { "iec958-stereo-input", N_("Digital Input (S/PDIF)") },
2355 { "iec958-passthrough-output", N_("Digital Passthrough (S/PDIF)") }
2362 PA_LLIST_FOREACH(e
, p
->elements
)
2363 if (element_verify(e
) < 0)
2366 if (!p
->description
)
2367 p
->description
= pa_xstrdup(lookup_description(p
->description_key
? p
->description_key
: p
->name
,
2368 well_known_descriptions
,
2369 PA_ELEMENTSOF(well_known_descriptions
)));
2371 if (!p
->description
) {
2372 if (p
->description_key
)
2373 pa_log_warn("Path %s: Unrecognized description key: %s", p
->name
, p
->description_key
);
2375 p
->description
= pa_xstrdup(p
->name
);
2381 static const char *get_default_paths_dir(void) {
2382 if (pa_run_from_build_tree())
2383 return PA_SRCDIR
"/modules/alsa/mixer/paths/";
2385 return PA_ALSA_PATHS_DIR
;
2388 pa_alsa_path
* pa_alsa_path_new(const char *paths_dir
, const char *fname
, pa_alsa_direction_t direction
) {
2393 bool mute_during_activation
= false;
2395 pa_config_item items
[] = {
2397 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
2398 { "description-key", pa_config_parse_string
, NULL
, "General" },
2399 { "description", pa_config_parse_string
, NULL
, "General" },
2400 { "mute-during-activation", pa_config_parse_bool
, NULL
, "General" },
2401 { "eld-device", pa_config_parse_int
, NULL
, "General" },
2404 { "priority", option_parse_priority
, NULL
, NULL
},
2405 { "name", option_parse_name
, NULL
, NULL
},
2408 { "state.plugged", jack_parse_state
, NULL
, NULL
},
2409 { "state.unplugged", jack_parse_state
, NULL
, NULL
},
2412 { "switch", element_parse_switch
, NULL
, NULL
},
2413 { "volume", element_parse_volume
, NULL
, NULL
},
2414 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
2415 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
2416 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
2417 /* ... later on we might add override-map.3 and so on here ... */
2418 { "required", element_parse_required
, NULL
, NULL
},
2419 { "required-any", element_parse_required
, NULL
, NULL
},
2420 { "required-absent", element_parse_required
, NULL
, NULL
},
2421 { "direction", element_parse_direction
, NULL
, NULL
},
2422 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2423 { "volume-limit", element_parse_volume_limit
, NULL
, NULL
},
2424 { NULL
, NULL
, NULL
, NULL
}
2429 p
= pa_xnew0(pa_alsa_path
, 1);
2430 n
= pa_path_get_filename(fname
);
2431 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2432 p
->proplist
= pa_proplist_new();
2433 p
->direction
= direction
;
2436 items
[0].data
= &p
->priority
;
2437 items
[1].data
= &p
->description_key
;
2438 items
[2].data
= &p
->description
;
2439 items
[3].data
= &mute_during_activation
;
2440 items
[4].data
= &p
->eld_device
;
2443 paths_dir
= get_default_paths_dir();
2445 fn
= pa_maybe_prefix_path(fname
, paths_dir
);
2447 r
= pa_config_parse(fn
, NULL
, items
, p
->proplist
, p
);
2453 p
->mute_during_activation
= mute_during_activation
;
2455 if (path_verify(p
) < 0)
2461 pa_alsa_path_free(p
);
2465 pa_alsa_path
*pa_alsa_path_synthesize(const char *element
, pa_alsa_direction_t direction
) {
2471 p
= pa_xnew0(pa_alsa_path
, 1);
2472 p
->name
= pa_xstrdup(element
);
2473 p
->direction
= direction
;
2475 e
= pa_xnew0(pa_alsa_element
, 1);
2477 e
->alsa_name
= pa_xstrdup(element
);
2478 e
->direction
= direction
;
2479 e
->volume_limit
= -1;
2481 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2482 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2484 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2485 p
->last_element
= e
;
2489 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2490 pa_alsa_option
*o
, *n
;
2494 for (o
= e
->options
; o
; o
= n
) {
2497 if (o
->alsa_idx
< 0) {
2498 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2504 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2505 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2506 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2509 static void path_drop_unsupported(pa_alsa_path
*p
) {
2510 pa_alsa_element
*e
, *n
;
2514 for (e
= p
->elements
; e
; e
= n
) {
2517 if (!element_drop_unsupported(e
)) {
2518 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2524 static void path_make_options_unique(pa_alsa_path
*p
) {
2526 pa_alsa_option
*o
, *u
;
2528 PA_LLIST_FOREACH(e
, p
->elements
) {
2529 PA_LLIST_FOREACH(o
, e
->options
) {
2533 for (u
= o
->next
; u
; u
= u
->next
)
2534 if (pa_streq(u
->name
, o
->name
))
2540 m
= pa_xstrdup(o
->name
);
2542 /* OK, this name is not unique, hence let's rename */
2543 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2546 if (!pa_streq(u
->name
, m
))
2549 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2553 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2554 pa_xfree(u
->description
);
2555 u
->description
= nd
;
2565 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2568 for (; e
; e
= e
->next
)
2569 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2570 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2576 for (o
= e
->options
; o
; o
= o
->next
) {
2580 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2581 s
->options
= pa_idxset_copy(template->options
);
2582 s
->name
= pa_sprintf_malloc("%s+%s", template->name
, o
->name
);
2584 (template->description
[0] && o
->description
[0])
2585 ? pa_sprintf_malloc("%s / %s", template->description
, o
->description
)
2586 : (template->description
[0]
2587 ? pa_xstrdup(template->description
)
2588 : pa_xstrdup(o
->description
));
2590 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2592 s
= pa_xnew0(pa_alsa_setting
, 1);
2593 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2594 s
->name
= pa_xstrdup(o
->name
);
2595 s
->description
= pa_xstrdup(o
->description
);
2596 s
->priority
= o
->priority
;
2599 pa_idxset_put(s
->options
, o
, NULL
);
2601 if (element_create_settings(e
->next
, s
))
2602 /* This is not a leaf, so let's get rid of it */
2605 /* This is a leaf, so let's add it */
2606 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2608 e
->path
->last_setting
= s
;
2615 static void path_create_settings(pa_alsa_path
*p
) {
2618 element_create_settings(p
->elements
, NULL
);
2621 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_hctl_t
*hctl
, pa_bool_t ignore_dB
) {
2624 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2625 pa_channel_position_t t
;
2626 pa_channel_position_mask_t path_volume_channels
= 0;
2632 return p
->supported
? 0 : -1;
2638 pa_log_debug("Probing path '%s'", p
->name
);
2640 PA_LLIST_FOREACH(j
, p
->jacks
) {
2641 if (jack_probe(j
, hctl
) < 0) {
2642 p
->supported
= FALSE
;
2643 pa_log_debug("Probe of jack '%s' failed.", j
->alsa_name
);
2646 pa_log_debug("Probe of jack '%s' succeeded (%s)", j
->alsa_name
, j
->has_control
? "found!" : "not found");
2649 PA_LLIST_FOREACH(e
, p
->elements
) {
2650 if (element_probe(e
, m
) < 0) {
2651 p
->supported
= FALSE
;
2652 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2655 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
);
2660 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2662 if (!p
->has_volume
) {
2663 p
->min_volume
= e
->min_volume
;
2664 p
->max_volume
= e
->max_volume
;
2668 if (!p
->has_volume
) {
2669 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2670 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2671 min_dB
[t
] = e
->min_dB
;
2672 max_dB
[t
] = e
->max_dB
;
2673 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2680 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2681 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2682 min_dB
[t
] += e
->min_dB
;
2683 max_dB
[t
] += e
->max_dB
;
2684 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2687 /* Hmm, there's another element before us
2688 * which cannot do dB volumes, so we we need
2689 * to 'neutralize' this slider */
2690 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2691 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2694 } else if (p
->has_volume
) {
2695 /* We can't use this volume, so let's ignore it */
2696 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2697 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2699 p
->has_volume
= TRUE
;
2702 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2706 if (p
->has_req_any
&& !p
->req_any_present
) {
2707 p
->supported
= FALSE
;
2708 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2712 path_drop_unsupported(p
);
2713 path_make_options_unique(p
);
2714 path_create_settings(p
);
2716 p
->supported
= TRUE
;
2718 p
->min_dB
= INFINITY
;
2719 p
->max_dB
= -INFINITY
;
2721 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2722 if (path_volume_channels
& PA_CHANNEL_POSITION_MASK(t
)) {
2723 if (p
->min_dB
> min_dB
[t
])
2724 p
->min_dB
= min_dB
[t
];
2726 if (p
->max_dB
< max_dB
[t
])
2727 p
->max_dB
= max_dB
[t
];
2734 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2737 pa_log_debug("Setting %s (%s) priority=%u",
2739 pa_strnull(s
->description
),
2743 void pa_alsa_jack_dump(pa_alsa_jack
*j
) {
2746 pa_log_debug("Jack %s, alsa_name='%s', detection %s", j
->name
, j
->alsa_name
, j
->has_control
? "possible" : "unavailable");
2749 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2752 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2754 pa_strnull(o
->name
),
2755 pa_strnull(o
->description
),
2760 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2764 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",
2774 (long long unsigned) e
->merged_mask
,
2776 pa_yes_no(e
->override_map
));
2778 PA_LLIST_FOREACH(o
, e
->options
)
2779 pa_alsa_option_dump(o
);
2782 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2788 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2789 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2791 pa_strnull(p
->description
),
2794 pa_yes_no(p
->probed
),
2795 pa_yes_no(p
->supported
),
2796 pa_yes_no(p
->has_mute
),
2797 pa_yes_no(p
->has_volume
),
2798 pa_yes_no(p
->has_dB
),
2799 p
->min_volume
, p
->max_volume
,
2800 p
->min_dB
, p
->max_dB
);
2802 PA_LLIST_FOREACH(e
, p
->elements
)
2803 pa_alsa_element_dump(e
);
2805 PA_LLIST_FOREACH(j
, p
->jacks
)
2806 pa_alsa_jack_dump(j
);
2808 PA_LLIST_FOREACH(s
, p
->settings
)
2809 pa_alsa_setting_dump(s
);
2812 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2813 snd_mixer_selem_id_t
*sid
;
2814 snd_mixer_elem_t
*me
;
2820 SELEM_INIT(sid
, e
->alsa_name
);
2821 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2822 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2826 snd_mixer_elem_set_callback(me
, cb
);
2827 snd_mixer_elem_set_callback_private(me
, userdata
);
2830 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2837 PA_LLIST_FOREACH(e
, p
->elements
)
2838 element_set_callback(e
, m
, cb
, userdata
);
2841 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2849 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
2850 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2853 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
, const char *paths_dir
) {
2854 pa_alsa_path_set
*ps
;
2855 char **pn
= NULL
, **en
= NULL
, **ie
;
2856 pa_alsa_decibel_fix
*db_fix
;
2857 void *state
, *state2
;
2861 pa_assert(m
->profile_set
);
2862 pa_assert(m
->profile_set
->decibel_fixes
);
2863 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2865 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2868 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2869 ps
->direction
= direction
;
2870 ps
->paths
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2872 if (direction
== PA_ALSA_DIRECTION_OUTPUT
) {
2873 pn
= m
->output_path_names
;
2874 cache
= m
->profile_set
->output_paths
;
2876 else if (direction
== PA_ALSA_DIRECTION_INPUT
) {
2877 pn
= m
->input_path_names
;
2878 cache
= m
->profile_set
->input_paths
;
2884 for (in
= pn
; *in
; in
++) {
2885 pa_alsa_path
*p
= NULL
;
2886 pa_bool_t duplicate
= FALSE
;
2889 for (kn
= pn
; kn
< in
; kn
++)
2890 if (pa_streq(*kn
, *in
)) {
2898 p
= pa_hashmap_get(cache
, *in
);
2900 char *fn
= pa_sprintf_malloc("%s.conf", *in
);
2901 p
= pa_alsa_path_new(paths_dir
, fn
, direction
);
2904 pa_hashmap_put(cache
, *in
, p
);
2906 pa_assert(pa_hashmap_get(cache
, *in
) == p
);
2908 pa_hashmap_put(ps
->paths
, p
, p
);
2915 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2916 en
= m
->output_element
;
2917 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2918 en
= m
->input_element
;
2921 pa_alsa_path_set_free(ps
);
2925 for (ie
= en
; *ie
; ie
++) {
2929 p
= pa_alsa_path_synthesize(*ie
, direction
);
2931 /* Mark all other passed elements for require-absent */
2932 for (je
= en
; *je
; je
++) {
2938 e
= pa_xnew0(pa_alsa_element
, 1);
2940 e
->alsa_name
= pa_xstrdup(*je
);
2941 e
->direction
= direction
;
2942 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2943 e
->volume_limit
= -1;
2945 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2946 p
->last_element
= e
;
2949 pa_hashmap_put(ps
->paths
, *ie
, p
);
2953 /* Assign decibel fixes to elements. */
2954 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2957 PA_HASHMAP_FOREACH(p
, ps
->paths
, state2
) {
2960 PA_LLIST_FOREACH(e
, p
->elements
) {
2961 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2962 /* The profile set that contains the dB fix may be freed
2963 * before the element, so we have to copy the dB fix
2965 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2966 e
->db_fix
->profile_set
= NULL
;
2967 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2968 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
2977 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2982 pa_log_debug("Path Set %p, direction=%i",
2986 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
2987 pa_alsa_path_dump(p
);
2991 static pa_bool_t
options_have_option(pa_alsa_option
*options
, const char *alsa_name
) {
2995 pa_assert(alsa_name
);
2997 PA_LLIST_FOREACH(o
, options
) {
2998 if (pa_streq(o
->alsa_name
, alsa_name
))
3004 static pa_bool_t
enumeration_is_subset(pa_alsa_option
*a_options
, pa_alsa_option
*b_options
) {
3005 pa_alsa_option
*oa
, *ob
;
3007 if (!a_options
) return TRUE
;
3008 if (!b_options
) return FALSE
;
3010 /* If there is an option A offers that B does not, then A is not a subset of B. */
3011 PA_LLIST_FOREACH(oa
, a_options
) {
3012 pa_bool_t found
= FALSE
;
3013 PA_LLIST_FOREACH(ob
, b_options
) {
3014 if (pa_streq(oa
->alsa_name
, ob
->alsa_name
)) {
3026 * Compares two elements to see if a is a subset of b
3028 static pa_bool_t
element_is_subset(pa_alsa_element
*a
, pa_alsa_element
*b
, snd_mixer_t
*m
) {
3034 * Every state is a subset of itself (with caveats for volume_limits and options)
3035 * IGNORE is a subset of every other state */
3037 /* Check the volume_use */
3038 if (a
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
3040 /* "Constant" is subset of "Constant" only when their constant values are equal */
3041 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& b
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& a
->constant_volume
!= b
->constant_volume
)
3044 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
3045 if (a
->volume_use
!= b
->volume_use
&& b
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
3048 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
3049 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
3050 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
3051 if (b
->volume_use
== PA_ALSA_VOLUME_MERGE
&& b
->volume_limit
>= 0) {
3054 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
)
3055 a_limit
= a
->constant_volume
;
3056 else if (a
->volume_use
== PA_ALSA_VOLUME_ZERO
) {
3060 int rounding
= (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
? +1 : -1);
3061 a_limit
= decibel_fix_get_step(a
->db_fix
, &dB
, rounding
);
3063 snd_mixer_selem_id_t
*sid
;
3064 snd_mixer_elem_t
*me
;
3066 SELEM_INIT(sid
, a
->alsa_name
);
3067 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
3068 pa_log_warn("Element %s seems to have disappeared.", a
->alsa_name
);
3072 if (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3073 if (snd_mixer_selem_ask_playback_dB_vol(me
, dB
, +1, &a_limit
) < 0)
3076 if (snd_mixer_selem_ask_capture_dB_vol(me
, dB
, -1, &a_limit
) < 0)
3080 } else if (a
->volume_use
== PA_ALSA_VOLUME_OFF
)
3081 a_limit
= a
->min_volume
;
3082 else if (a
->volume_use
== PA_ALSA_VOLUME_MERGE
)
3083 a_limit
= a
->volume_limit
;
3085 /* This should never be reached */
3088 if (a_limit
> b
->volume_limit
)
3092 if (a
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
3094 /* If override-maps are different, they're not subsets */
3095 if (a
->n_channels
!= b
->n_channels
)
3097 for (s
= 0; s
<= SND_MIXER_SCHN_LAST
; s
++)
3098 if (a
->masks
[s
][a
->n_channels
-1] != b
->masks
[s
][b
->n_channels
-1]) {
3099 pa_log_debug("Element %s is not a subset - mask a: 0x%" PRIx64
", mask b: 0x%" PRIx64
", at channel %d",
3100 a
->alsa_name
, a
->masks
[s
][a
->n_channels
-1], b
->masks
[s
][b
->n_channels
-1], s
);
3106 if (a
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
3107 /* "On" is a subset of "Mute".
3108 * "Off" is a subset of "Mute".
3109 * "On" is a subset of "Select", if there is an "Option:On" in B.
3110 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3111 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3113 if (a
->switch_use
!= b
->switch_use
) {
3115 if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
|| a
->switch_use
== PA_ALSA_SWITCH_MUTE
3116 || b
->switch_use
== PA_ALSA_SWITCH_OFF
|| b
->switch_use
== PA_ALSA_SWITCH_ON
)
3119 if (b
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3120 if (a
->switch_use
== PA_ALSA_SWITCH_ON
) {
3121 if (!options_have_option(b
->options
, "on"))
3123 } else if (a
->switch_use
== PA_ALSA_SWITCH_OFF
) {
3124 if (!options_have_option(b
->options
, "off"))
3128 } else if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3129 if (!enumeration_is_subset(a
->options
, b
->options
))
3134 if (a
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
) {
3135 if (b
->enumeration_use
== PA_ALSA_ENUMERATION_IGNORE
)
3137 if (!enumeration_is_subset(a
->options
, b
->options
))
3144 static void path_set_condense(pa_alsa_path_set
*ps
, snd_mixer_t
*m
) {
3151 /* If we only have one path, then don't bother */
3152 if (pa_hashmap_size(ps
->paths
) < 2)
3155 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3159 PA_HASHMAP_FOREACH(p2
, ps
->paths
, state2
) {
3160 pa_alsa_element
*ea
, *eb
;
3161 pa_alsa_jack
*ja
, *jb
;
3162 bool is_subset
= true;
3167 /* If a has a jack that b does not have, a is not a subset */
3168 PA_LLIST_FOREACH(ja
, p
->jacks
) {
3169 bool exists
= false;
3171 if (!ja
->has_control
)
3174 PA_LLIST_FOREACH(jb
, p2
->jacks
) {
3175 if (jb
->has_control
&& pa_streq(jb
->alsa_name
, ja
->alsa_name
) &&
3176 (ja
->state_plugged
== jb
->state_plugged
) &&
3177 (ja
->state_unplugged
== jb
->state_unplugged
)) {
3189 /* Compare the elements of each set... */
3196 else if ((ea
&& !eb
) || (!ea
&& eb
))
3198 else if (pa_streq(ea
->alsa_name
, eb
->alsa_name
)) {
3199 if (element_is_subset(ea
, eb
, m
)) {
3209 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p
->name
, p2
->name
);
3210 pa_hashmap_remove(ps
->paths
, p
);
3217 static pa_alsa_path
* path_set_find_path_by_description(pa_alsa_path_set
*ps
, const char* description
, pa_alsa_path
*ignore
) {
3221 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
3222 if (p
!= ignore
&& pa_streq(p
->description
, description
))
3228 static void path_set_make_path_descriptions_unique(pa_alsa_path_set
*ps
) {
3229 pa_alsa_path
*p
, *q
;
3230 void *state
, *state2
;
3232 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3234 char *old_description
;
3236 q
= path_set_find_path_by_description(ps
, p
->description
, p
);
3241 old_description
= pa_xstrdup(p
->description
);
3243 /* OK, this description is not unique, hence let's rename */
3245 PA_HASHMAP_FOREACH(q
, ps
->paths
, state2
) {
3246 char *new_description
;
3248 if (!pa_streq(q
->description
, old_description
))
3251 new_description
= pa_sprintf_malloc("%s %u", q
->description
, i
);
3252 pa_xfree(q
->description
);
3253 q
->description
= new_description
;
3258 pa_xfree(old_description
);
3262 static void mapping_free(pa_alsa_mapping
*m
) {
3266 pa_xfree(m
->description
);
3268 pa_proplist_free(m
->proplist
);
3270 pa_xstrfreev(m
->device_strings
);
3271 pa_xstrfreev(m
->input_path_names
);
3272 pa_xstrfreev(m
->output_path_names
);
3273 pa_xstrfreev(m
->input_element
);
3274 pa_xstrfreev(m
->output_element
);
3275 if (m
->input_path_set
)
3276 pa_alsa_path_set_free(m
->input_path_set
);
3277 if (m
->output_path_set
)
3278 pa_alsa_path_set_free(m
->output_path_set
);
3280 pa_assert(!m
->input_pcm
);
3281 pa_assert(!m
->output_pcm
);
3283 pa_alsa_ucm_mapping_context_free(&m
->ucm_context
);
3288 static void profile_free(pa_alsa_profile
*p
) {
3292 pa_xfree(p
->description
);
3294 pa_xstrfreev(p
->input_mapping_names
);
3295 pa_xstrfreev(p
->output_mapping_names
);
3297 if (p
->input_mappings
)
3298 pa_idxset_free(p
->input_mappings
, NULL
);
3300 if (p
->output_mappings
)
3301 pa_idxset_free(p
->output_mappings
, NULL
);
3306 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
3309 if (ps
->input_paths
)
3310 pa_hashmap_free(ps
->input_paths
, (pa_free_cb_t
) pa_alsa_path_free
);
3312 if (ps
->output_paths
)
3313 pa_hashmap_free(ps
->output_paths
, (pa_free_cb_t
) pa_alsa_path_free
);
3316 pa_hashmap_free(ps
->profiles
, (pa_free_cb_t
) profile_free
);
3319 pa_hashmap_free(ps
->mappings
, (pa_free_cb_t
) mapping_free
);
3321 if (ps
->decibel_fixes
)
3322 pa_hashmap_free(ps
->decibel_fixes
, (pa_free_cb_t
) decibel_fix_free
);
3327 pa_alsa_mapping
*pa_alsa_mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
3330 if (!pa_startswith(name
, "Mapping "))
3335 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
3338 m
= pa_xnew0(pa_alsa_mapping
, 1);
3339 m
->profile_set
= ps
;
3340 m
->name
= pa_xstrdup(name
);
3341 pa_channel_map_init(&m
->channel_map
);
3342 m
->proplist
= pa_proplist_new();
3344 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
3349 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
3352 if (!pa_startswith(name
, "Profile "))
3357 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
3360 p
= pa_xnew0(pa_alsa_profile
, 1);
3361 p
->profile_set
= ps
;
3362 p
->name
= pa_xstrdup(name
);
3364 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3369 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
3370 pa_alsa_decibel_fix
*db_fix
;
3372 if (!pa_startswith(name
, "DecibelFix "))
3377 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
3380 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
3381 db_fix
->profile_set
= ps
;
3382 db_fix
->name
= pa_xstrdup(name
);
3384 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
3389 static int mapping_parse_device_strings(pa_config_parser_state
*state
) {
3390 pa_alsa_profile_set
*ps
;
3395 ps
= state
->userdata
;
3397 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3398 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3402 pa_xstrfreev(m
->device_strings
);
3403 if (!(m
->device_strings
= pa_split_spaces_strv(state
->rvalue
))) {
3404 pa_log("[%s:%u] Device string list empty of '%s'", state
->filename
, state
->lineno
, state
->section
);
3411 static int mapping_parse_channel_map(pa_config_parser_state
*state
) {
3412 pa_alsa_profile_set
*ps
;
3417 ps
= state
->userdata
;
3419 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3420 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3424 if (!(pa_channel_map_parse(&m
->channel_map
, state
->rvalue
))) {
3425 pa_log("[%s:%u] Channel map invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
3432 static int mapping_parse_paths(pa_config_parser_state
*state
) {
3433 pa_alsa_profile_set
*ps
;
3438 ps
= state
->userdata
;
3440 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3441 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3445 if (pa_streq(state
->lvalue
, "paths-input")) {
3446 pa_xstrfreev(m
->input_path_names
);
3447 m
->input_path_names
= pa_split_spaces_strv(state
->rvalue
);
3449 pa_xstrfreev(m
->output_path_names
);
3450 m
->output_path_names
= pa_split_spaces_strv(state
->rvalue
);
3456 static int mapping_parse_element(pa_config_parser_state
*state
) {
3457 pa_alsa_profile_set
*ps
;
3462 ps
= state
->userdata
;
3464 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3465 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3469 if (pa_streq(state
->lvalue
, "element-input")) {
3470 pa_xstrfreev(m
->input_element
);
3471 m
->input_element
= pa_split_spaces_strv(state
->rvalue
);
3473 pa_xstrfreev(m
->output_element
);
3474 m
->output_element
= pa_split_spaces_strv(state
->rvalue
);
3480 static int mapping_parse_direction(pa_config_parser_state
*state
) {
3481 pa_alsa_profile_set
*ps
;
3486 ps
= state
->userdata
;
3488 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3489 pa_log("[%s:%u] Section name %s invalid.", state
->filename
, state
->lineno
, state
->section
);
3493 if (pa_streq(state
->rvalue
, "input"))
3494 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3495 else if (pa_streq(state
->rvalue
, "output"))
3496 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3497 else if (pa_streq(state
->rvalue
, "any"))
3498 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3500 pa_log("[%s:%u] Direction %s invalid.", state
->filename
, state
->lineno
, state
->rvalue
);
3507 static int mapping_parse_description(pa_config_parser_state
*state
) {
3508 pa_alsa_profile_set
*ps
;
3514 ps
= state
->userdata
;
3516 if ((m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3517 pa_xfree(m
->description
);
3518 m
->description
= pa_xstrdup(state
->rvalue
);
3519 } else if ((p
= profile_get(ps
, state
->section
))) {
3520 pa_xfree(p
->description
);
3521 p
->description
= pa_xstrdup(state
->rvalue
);
3523 pa_log("[%s:%u] Section name %s invalid.", state
->filename
, state
->lineno
, state
->section
);
3530 static int mapping_parse_priority(pa_config_parser_state
*state
) {
3531 pa_alsa_profile_set
*ps
;
3538 ps
= state
->userdata
;
3540 if (pa_atou(state
->rvalue
, &prio
) < 0) {
3541 pa_log("[%s:%u] Priority invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
3545 if ((m
= pa_alsa_mapping_get(ps
, state
->section
)))
3547 else if ((p
= profile_get(ps
, state
->section
)))
3550 pa_log("[%s:%u] Section name %s invalid.", state
->filename
, state
->lineno
, state
->section
);
3557 static int profile_parse_mappings(pa_config_parser_state
*state
) {
3558 pa_alsa_profile_set
*ps
;
3563 ps
= state
->userdata
;
3565 if (!(p
= profile_get(ps
, state
->section
))) {
3566 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3570 if (pa_streq(state
->lvalue
, "input-mappings")) {
3571 pa_xstrfreev(p
->input_mapping_names
);
3572 p
->input_mapping_names
= pa_split_spaces_strv(state
->rvalue
);
3574 pa_xstrfreev(p
->output_mapping_names
);
3575 p
->output_mapping_names
= pa_split_spaces_strv(state
->rvalue
);
3581 static int profile_parse_skip_probe(pa_config_parser_state
*state
) {
3582 pa_alsa_profile_set
*ps
;
3588 ps
= state
->userdata
;
3590 if (!(p
= profile_get(ps
, state
->section
))) {
3591 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3595 if ((b
= pa_parse_boolean(state
->rvalue
)) < 0) {
3596 pa_log("[%s:%u] Skip probe invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
3605 static int decibel_fix_parse_db_values(pa_config_parser_state
*state
) {
3606 pa_alsa_profile_set
*ps
;
3607 pa_alsa_decibel_fix
*db_fix
;
3611 unsigned n
= 8; /* Current size of the db_values table. */
3612 unsigned min_step
= 0;
3613 unsigned max_step
= 0;
3614 unsigned i
= 0; /* Index to the items table. */
3615 unsigned prev_step
= 0;
3620 ps
= state
->userdata
;
3622 if (!(db_fix
= decibel_fix_get(ps
, state
->section
))) {
3623 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3627 if (!(items
= pa_split_spaces_strv(state
->rvalue
))) {
3628 pa_log("[%s:%u] Value missing", state
->filename
, state
->lineno
);
3632 db_values
= pa_xnew(long, n
);
3634 while ((item
= items
[i
++])) {
3635 char *s
= item
; /* Step value string. */
3636 char *d
= item
; /* dB value string. */
3640 /* Move d forward until it points to a colon or to the end of the item. */
3641 for (; *d
&& *d
!= ':'; ++d
);
3644 /* item started with colon. */
3645 pa_log("[%s:%u] No step value found in %s", state
->filename
, state
->lineno
, item
);
3649 if (!*d
|| !*(d
+ 1)) {
3650 /* No colon found, or it was the last character in item. */
3651 pa_log("[%s:%u] No dB value found in %s", state
->filename
, state
->lineno
, item
);
3655 /* pa_atou() needs a null-terminating string. Let's replace the colon
3656 * with a zero byte. */
3659 if (pa_atou(s
, &step
) < 0) {
3660 pa_log("[%s:%u] Invalid step value: %s", state
->filename
, state
->lineno
, s
);
3664 if (pa_atod(d
, &db
) < 0) {
3665 pa_log("[%s:%u] Invalid dB value: %s", state
->filename
, state
->lineno
, d
);
3669 if (step
<= prev_step
&& i
!= 1) {
3670 pa_log("[%s:%u] Step value %u not greater than the previous value %u", state
->filename
, state
->lineno
, step
, prev_step
);
3674 if (db
< prev_db
&& i
!= 1) {
3675 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", state
->filename
, state
->lineno
, db
, prev_db
);
3681 db_values
[0] = (long) (db
* 100.0);
3685 /* Interpolate linearly. */
3686 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3688 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3690 /* Reallocate the db_values table if it's about to overflow. */
3691 if (prev_step
+ 1 - min_step
== n
) {
3693 db_values
= pa_xrenew(long, db_values
, n
);
3696 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3703 db_fix
->min_step
= min_step
;
3704 db_fix
->max_step
= max_step
;
3705 pa_xfree(db_fix
->db_values
);
3706 db_fix
->db_values
= db_values
;
3708 pa_xstrfreev(items
);
3713 pa_xstrfreev(items
);
3714 pa_xfree(db_values
);
3719 static void mapping_paths_probe(pa_alsa_mapping
*m
, pa_alsa_profile
*profile
,
3720 pa_alsa_direction_t direction
) {
3724 snd_pcm_t
*pcm_handle
;
3725 pa_alsa_path_set
*ps
;
3726 snd_mixer_t
*mixer_handle
;
3727 snd_hctl_t
*hctl_handle
;
3729 if (direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3730 if (m
->output_path_set
)
3731 return; /* Already probed */
3732 m
->output_path_set
= ps
= pa_alsa_path_set_new(m
, direction
, NULL
); /* FIXME: Handle paths_dir */
3733 pcm_handle
= m
->output_pcm
;
3735 if (m
->input_path_set
)
3736 return; /* Already probed */
3737 m
->input_path_set
= ps
= pa_alsa_path_set_new(m
, direction
, NULL
); /* FIXME: Handle paths_dir */
3738 pcm_handle
= m
->input_pcm
;
3742 return; /* No paths */
3744 pa_assert(pcm_handle
);
3746 mixer_handle
= pa_alsa_open_mixer_for_pcm(pcm_handle
, NULL
, &hctl_handle
);
3747 if (!mixer_handle
|| !hctl_handle
) {
3748 /* Cannot open mixer, remove all entries */
3749 pa_hashmap_remove_all(ps
->paths
, NULL
);
3754 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3755 if (pa_alsa_path_probe(p
, mixer_handle
, hctl_handle
, m
->profile_set
->ignore_dB
) < 0) {
3756 pa_hashmap_remove(ps
->paths
, p
);
3760 path_set_condense(ps
, mixer_handle
);
3761 path_set_make_path_descriptions_unique(ps
);
3764 snd_mixer_close(mixer_handle
);
3766 pa_log_debug("Available mixer paths (after tidying):");
3767 pa_alsa_path_set_dump(ps
);
3770 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3772 static const struct description_map well_known_descriptions
[] = {
3773 { "analog-mono", N_("Analog Mono") },
3774 { "analog-stereo", N_("Analog Stereo") },
3775 { "analog-surround-21", N_("Analog Surround 2.1") },
3776 { "analog-surround-30", N_("Analog Surround 3.0") },
3777 { "analog-surround-31", N_("Analog Surround 3.1") },
3778 { "analog-surround-40", N_("Analog Surround 4.0") },
3779 { "analog-surround-41", N_("Analog Surround 4.1") },
3780 { "analog-surround-50", N_("Analog Surround 5.0") },
3781 { "analog-surround-51", N_("Analog Surround 5.1") },
3782 { "analog-surround-61", N_("Analog Surround 6.0") },
3783 { "analog-surround-61", N_("Analog Surround 6.1") },
3784 { "analog-surround-70", N_("Analog Surround 7.0") },
3785 { "analog-surround-71", N_("Analog Surround 7.1") },
3786 { "analog-4-channel-input", N_("Analog 4-channel Input") },
3787 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3788 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3789 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3790 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3791 { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
3792 { "hdmi-stereo", N_("Digital Stereo (HDMI)") },
3793 { "hdmi-surround-51", N_("Digital Surround 5.1 (HDMI)") }
3798 if (!pa_channel_map_valid(&m
->channel_map
)) {
3799 pa_log("Mapping %s is missing channel map.", m
->name
);
3803 if (!m
->device_strings
) {
3804 pa_log("Mapping %s is missing device strings.", m
->name
);
3808 if ((m
->input_path_names
&& m
->input_element
) ||
3809 (m
->output_path_names
&& m
->output_element
)) {
3810 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3814 if (!m
->description
)
3815 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3816 well_known_descriptions
,
3817 PA_ELEMENTSOF(well_known_descriptions
)));
3819 if (!m
->description
)
3820 m
->description
= pa_xstrdup(m
->name
);
3823 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3825 else if (m
->channel_map
.channels
== bonus
->channels
)
3832 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3833 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3837 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3839 pa_strnull(m
->description
),
3841 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3842 pa_yes_no(m
->supported
),
3846 static void profile_set_add_auto_pair(
3847 pa_alsa_profile_set
*ps
,
3848 pa_alsa_mapping
*m
, /* output */
3849 pa_alsa_mapping
*n
/* input */) {
3857 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3860 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3864 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3866 name
= pa_sprintf_malloc("output:%s", m
->name
);
3868 name
= pa_sprintf_malloc("input:%s", n
->name
);
3870 if (pa_hashmap_get(ps
->profiles
, name
)) {
3875 p
= pa_xnew0(pa_alsa_profile
, 1);
3876 p
->profile_set
= ps
;
3880 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3881 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3882 p
->priority
+= m
->priority
* 100;
3886 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3887 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3888 p
->priority
+= n
->priority
;
3891 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3894 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3895 pa_alsa_mapping
*m
, *n
;
3896 void *m_state
, *n_state
;
3900 /* The order is important here:
3901 1) try single inputs and outputs before trying their
3902 combination, because if the half-duplex test failed, we don't have
3904 2) try the output right before the input combinations with
3905 that output, because then the output_pcm is not closed between tests.
3907 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3908 profile_set_add_auto_pair(ps
, NULL
, n
);
3910 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3911 profile_set_add_auto_pair(ps
, m
, NULL
);
3913 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3914 profile_set_add_auto_pair(ps
, m
, n
);
3919 static int profile_verify(pa_alsa_profile
*p
) {
3921 static const struct description_map well_known_descriptions
[] = {
3922 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3923 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3924 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3925 { "off", N_("Off") }
3930 /* Replace the output mapping names by the actual mappings */
3931 if (p
->output_mapping_names
) {
3934 pa_assert(!p
->output_mappings
);
3935 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3937 for (name
= p
->output_mapping_names
; *name
; name
++) {
3940 pa_bool_t duplicate
= FALSE
;
3942 for (in
= name
+ 1; *in
; in
++)
3943 if (pa_streq(*name
, *in
)) {
3951 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3952 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p
->name
, *name
);
3956 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3962 pa_xstrfreev(p
->output_mapping_names
);
3963 p
->output_mapping_names
= NULL
;
3966 /* Replace the input mapping names by the actual mappings */
3967 if (p
->input_mapping_names
) {
3970 pa_assert(!p
->input_mappings
);
3971 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3973 for (name
= p
->input_mapping_names
; *name
; name
++) {
3976 pa_bool_t duplicate
= FALSE
;
3978 for (in
= name
+ 1; *in
; in
++)
3979 if (pa_streq(*name
, *in
)) {
3987 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3988 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p
->name
, *name
);
3992 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3998 pa_xstrfreev(p
->input_mapping_names
);
3999 p
->input_mapping_names
= NULL
;
4002 if (!p
->input_mappings
&& !p
->output_mappings
) {
4003 pa_log("Profile '%s' lacks mappings.", p
->name
);
4007 if (!p
->description
)
4008 p
->description
= pa_xstrdup(lookup_description(p
->name
,
4009 well_known_descriptions
,
4010 PA_ELEMENTSOF(well_known_descriptions
)));
4012 if (!p
->description
) {
4017 sb
= pa_strbuf_new();
4019 if (p
->output_mappings
)
4020 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4021 if (!pa_strbuf_isempty(sb
))
4022 pa_strbuf_puts(sb
, " + ");
4024 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
4027 if (p
->input_mappings
)
4028 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4029 if (!pa_strbuf_isempty(sb
))
4030 pa_strbuf_puts(sb
, " + ");
4032 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
4035 p
->description
= pa_strbuf_tostring_free(sb
);
4041 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
4046 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4048 pa_strnull(p
->description
),
4050 pa_yes_no(p
->supported
),
4051 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
4052 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
4054 if (p
->input_mappings
)
4055 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
4056 pa_log_debug("Input %s", m
->name
);
4058 if (p
->output_mappings
)
4059 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
4060 pa_log_debug("Output %s", m
->name
);
4063 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
4066 /* Check that the dB mapping has been configured. Since "db-values" is
4067 * currently the only option in the DecibelFix section, and decibel fix
4068 * objects don't get created if a DecibelFix section is empty, this is
4069 * actually a redundant check. Having this may prevent future bugs,
4071 if (!db_fix
->db_values
) {
4072 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
4079 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
4080 char *db_values
= NULL
;
4084 if (db_fix
->db_values
) {
4086 unsigned long i
, nsteps
;
4088 pa_assert(db_fix
->min_step
<= db_fix
->max_step
);
4089 nsteps
= db_fix
->max_step
- db_fix
->min_step
+ 1;
4091 buf
= pa_strbuf_new();
4092 for (i
= 0; i
< nsteps
; ++i
)
4093 pa_strbuf_printf(buf
, "[%li]:%0.2f ", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
4095 db_values
= pa_strbuf_tostring_free(buf
);
4098 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4099 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
4101 pa_xfree(db_values
);
4104 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
4105 pa_alsa_profile_set
*ps
;
4108 pa_alsa_decibel_fix
*db_fix
;
4113 static pa_config_item items
[] = {
4115 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
4118 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
4119 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
4120 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
4121 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
4122 { "element-input", mapping_parse_element
, NULL
, NULL
},
4123 { "element-output", mapping_parse_element
, NULL
, NULL
},
4124 { "direction", mapping_parse_direction
, NULL
, NULL
},
4126 /* Shared by [Mapping ...] and [Profile ...] */
4127 { "description", mapping_parse_description
, NULL
, NULL
},
4128 { "priority", mapping_parse_priority
, NULL
, NULL
},
4131 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
4132 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
4133 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
4135 /* [DecibelFix ...] */
4136 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
4137 { NULL
, NULL
, NULL
, NULL
}
4140 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
4141 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4142 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4143 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4144 ps
->input_paths
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4145 ps
->output_paths
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4147 items
[0].data
= &ps
->auto_profiles
;
4150 fname
= "default.conf";
4152 fn
= pa_maybe_prefix_path(fname
,
4153 pa_run_from_build_tree() ? PA_SRCDIR
"/modules/alsa/mixer/profile-sets/" :
4154 PA_ALSA_PROFILE_SETS_DIR
);
4156 r
= pa_config_parse(fn
, NULL
, items
, NULL
, ps
);
4162 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4163 if (mapping_verify(m
, bonus
) < 0)
4166 if (ps
->auto_profiles
)
4167 profile_set_add_auto(ps
);
4169 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4170 if (profile_verify(p
) < 0)
4173 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4174 if (decibel_fix_verify(db_fix
) < 0)
4180 pa_alsa_profile_set_free(ps
);
4184 static void profile_finalize_probing(pa_alsa_profile
*to_be_finalized
, pa_alsa_profile
*next
) {
4188 if (!to_be_finalized
)
4191 if (to_be_finalized
->output_mappings
)
4192 PA_IDXSET_FOREACH(m
, to_be_finalized
->output_mappings
, idx
) {
4197 if (to_be_finalized
->supported
)
4200 /* If this mapping is also in the next profile, we won't close the
4201 * pcm handle here, because it would get immediately reopened
4203 if (next
&& next
->output_mappings
&& pa_idxset_get_by_data(next
->output_mappings
, m
, NULL
))
4206 snd_pcm_close(m
->output_pcm
);
4207 m
->output_pcm
= NULL
;
4210 if (to_be_finalized
->input_mappings
)
4211 PA_IDXSET_FOREACH(m
, to_be_finalized
->input_mappings
, idx
) {
4216 if (to_be_finalized
->supported
)
4219 /* If this mapping is also in the next profile, we won't close the
4220 * pcm handle here, because it would get immediately reopened
4222 if (next
&& next
->input_mappings
&& pa_idxset_get_by_data(next
->input_mappings
, m
, NULL
))
4225 snd_pcm_close(m
->input_pcm
);
4226 m
->input_pcm
= NULL
;
4230 static snd_pcm_t
* mapping_open_pcm(pa_alsa_mapping
*m
,
4231 const pa_sample_spec
*ss
,
4234 unsigned default_n_fragments
,
4235 unsigned default_fragment_size_msec
) {
4237 pa_sample_spec try_ss
= *ss
;
4238 pa_channel_map try_map
= m
->channel_map
;
4239 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
4241 try_ss
.channels
= try_map
.channels
;
4244 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
4245 pa_frame_size(&try_ss
);
4246 try_buffer_size
= default_n_fragments
* try_period_size
;
4248 return pa_alsa_open_by_template(
4249 m
->device_strings
, dev_id
, NULL
, &try_ss
,
4250 &try_map
, mode
, &try_period_size
,
4251 &try_buffer_size
, 0, NULL
, NULL
, TRUE
);
4254 static void paths_drop_unsupported(pa_hashmap
* h
) {
4261 p
= pa_hashmap_iterate(h
, &state
, &key
);
4263 if (p
->supported
<= 0) {
4264 pa_hashmap_remove(h
, key
);
4265 pa_alsa_path_free(p
);
4267 p
= pa_hashmap_iterate(h
, &state
, &key
);
4271 void pa_alsa_profile_set_probe(
4272 pa_alsa_profile_set
*ps
,
4274 const pa_sample_spec
*ss
,
4275 unsigned default_n_fragments
,
4276 unsigned default_fragment_size_msec
) {
4279 pa_alsa_profile
*p
, *last
= NULL
;
4281 pa_hashmap
*broken_inputs
, *broken_outputs
;
4290 broken_inputs
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
4291 broken_outputs
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
4293 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
4296 /* Skip if this is already marked that it is supported (i.e. from the config file) */
4297 if (!p
->supported
) {
4299 profile_finalize_probing(last
, p
);
4300 p
->supported
= TRUE
;
4302 if (p
->output_mappings
) {
4303 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4304 if (pa_hashmap_get(broken_outputs
, m
) == m
) {
4305 pa_log_debug("Skipping profile %s - will not be able to open output:%s", p
->name
, m
->name
);
4306 p
->supported
= FALSE
;
4312 if (p
->input_mappings
&& p
->supported
) {
4313 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4314 if (pa_hashmap_get(broken_inputs
, m
) == m
) {
4315 pa_log_debug("Skipping profile %s - will not be able to open input:%s", p
->name
, m
->name
);
4316 p
->supported
= FALSE
;
4323 pa_log_debug("Looking at profile %s", p
->name
);
4325 /* Check if we can open all new ones */
4326 if (p
->output_mappings
&& p
->supported
)
4327 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4332 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
4333 if (!(m
->output_pcm
= mapping_open_pcm(m
, ss
, dev_id
,
4334 SND_PCM_STREAM_PLAYBACK
,
4335 default_n_fragments
,
4336 default_fragment_size_msec
))) {
4337 p
->supported
= FALSE
;
4338 if (pa_idxset_size(p
->output_mappings
) == 1 &&
4339 ((!p
->input_mappings
) || pa_idxset_size(p
->input_mappings
) == 0)) {
4340 pa_log_debug("Caching failure to open output:%s", m
->name
);
4341 pa_hashmap_put(broken_outputs
, m
, m
);
4347 if (p
->input_mappings
&& p
->supported
)
4348 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4353 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
4354 if (!(m
->input_pcm
= mapping_open_pcm(m
, ss
, dev_id
,
4355 SND_PCM_STREAM_CAPTURE
,
4356 default_n_fragments
,
4357 default_fragment_size_msec
))) {
4358 p
->supported
= FALSE
;
4359 if (pa_idxset_size(p
->input_mappings
) == 1 &&
4360 ((!p
->output_mappings
) || pa_idxset_size(p
->output_mappings
) == 0)) {
4361 pa_log_debug("Caching failure to open input:%s", m
->name
);
4362 pa_hashmap_put(broken_inputs
, m
, m
);
4374 pa_log_debug("Profile %s supported.", p
->name
);
4376 if (p
->output_mappings
)
4377 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
4379 mapping_paths_probe(m
, p
, PA_ALSA_DIRECTION_OUTPUT
);
4381 if (p
->input_mappings
)
4382 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
4384 mapping_paths_probe(m
, p
, PA_ALSA_DIRECTION_INPUT
);
4388 profile_finalize_probing(last
, NULL
);
4390 pa_alsa_profile_set_drop_unsupported(ps
);
4392 paths_drop_unsupported(ps
->input_paths
);
4393 paths_drop_unsupported(ps
->output_paths
);
4394 pa_hashmap_free(broken_inputs
, NULL
);
4395 pa_hashmap_free(broken_outputs
, NULL
);
4400 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
4403 pa_alsa_decibel_fix
*db_fix
;
4408 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4411 pa_yes_no(ps
->auto_profiles
),
4412 pa_yes_no(ps
->probed
),
4413 pa_hashmap_size(ps
->mappings
),
4414 pa_hashmap_size(ps
->profiles
),
4415 pa_hashmap_size(ps
->decibel_fixes
));
4417 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4418 pa_alsa_mapping_dump(m
);
4420 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4421 pa_alsa_profile_dump(p
);
4423 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4424 pa_alsa_decibel_fix_dump(db_fix
);
4427 void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set
*ps
) {
4432 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
4433 if (!p
->supported
) {
4434 pa_hashmap_remove(ps
->profiles
, p
->name
);
4439 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
) {
4440 if (m
->supported
<= 0) {
4441 pa_hashmap_remove(ps
->mappings
, m
->name
);
4447 static pa_device_port
* device_port_alsa_init(pa_hashmap
*ports
,
4449 const char* description
,
4451 pa_alsa_setting
*setting
,
4452 pa_card_profile
*cp
,
4460 p
= pa_hashmap_get(ports
, name
);
4463 pa_alsa_port_data
*data
;
4465 p
= pa_device_port_new(core
, name
, description
, sizeof(pa_alsa_port_data
));
4467 pa_hashmap_put(ports
, p
->name
, p
);
4468 pa_proplist_update(p
->proplist
, PA_UPDATE_REPLACE
, path
->proplist
);
4470 data
= PA_DEVICE_PORT_DATA(p
);
4472 data
->setting
= setting
;
4476 p
->is_input
|= path
->direction
== PA_ALSA_DIRECTION_ANY
|| path
->direction
== PA_ALSA_DIRECTION_INPUT
;
4477 p
->is_output
|= path
->direction
== PA_ALSA_DIRECTION_ANY
|| path
->direction
== PA_ALSA_DIRECTION_OUTPUT
;
4480 pa_hashmap_put(p
->profiles
, cp
->name
, cp
);
4483 pa_hashmap_put(extra
, p
->name
, p
);
4484 pa_device_port_ref(p
);
4490 void pa_alsa_path_set_add_ports(
4491 pa_alsa_path_set
*ps
,
4492 pa_card_profile
*cp
,
4505 PA_HASHMAP_FOREACH(path
, ps
->paths
, state
) {
4506 if (!path
->settings
|| !path
->settings
->next
) {
4507 /* If there is no or just one setting we only need a
4509 pa_device_port
*port
= device_port_alsa_init(ports
, path
->name
,
4510 path
->description
, path
, path
->settings
, cp
, extra
, core
);
4511 port
->priority
= path
->priority
* 100;
4515 PA_LLIST_FOREACH(s
, path
->settings
) {
4516 pa_device_port
*port
;
4519 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4521 if (s
->description
[0])
4522 d
= pa_sprintf_malloc("%s / %s", path
->description
, s
->description
);
4524 d
= pa_xstrdup(path
->description
);
4526 port
= device_port_alsa_init(ports
, n
, d
, path
, s
, cp
, extra
, core
);
4527 port
->priority
= path
->priority
* 100 + s
->priority
;
4536 void pa_alsa_add_ports(void *sink_or_source_new_data
, pa_alsa_path_set
*ps
, pa_card
*card
) {
4539 pa_assert(sink_or_source_new_data
);
4542 if (ps
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
4543 ports
= ((pa_sink_new_data
*) sink_or_source_new_data
)->ports
;
4545 ports
= ((pa_source_new_data
*) sink_or_source_new_data
)->ports
;
4547 if (ps
->paths
&& pa_hashmap_size(ps
->paths
) > 0) {
4549 pa_alsa_path_set_add_ports(ps
, NULL
, card
->ports
, ports
, card
->core
);
4552 pa_log_debug("Added %u ports", pa_hashmap_size(ports
));