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 { "name", pa_config_parse_string
, NULL
, "General" },
2401 { "mute-during-activation", pa_config_parse_bool
, NULL
, "General" },
2402 { "eld-device", pa_config_parse_int
, NULL
, "General" },
2405 { "priority", option_parse_priority
, NULL
, NULL
},
2406 { "name", option_parse_name
, NULL
, NULL
},
2409 { "state.plugged", jack_parse_state
, NULL
, NULL
},
2410 { "state.unplugged", jack_parse_state
, NULL
, NULL
},
2413 { "switch", element_parse_switch
, NULL
, NULL
},
2414 { "volume", element_parse_volume
, NULL
, NULL
},
2415 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
2416 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
2417 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
2418 /* ... later on we might add override-map.3 and so on here ... */
2419 { "required", element_parse_required
, NULL
, NULL
},
2420 { "required-any", element_parse_required
, NULL
, NULL
},
2421 { "required-absent", element_parse_required
, NULL
, NULL
},
2422 { "direction", element_parse_direction
, NULL
, NULL
},
2423 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2424 { "volume-limit", element_parse_volume_limit
, NULL
, NULL
},
2425 { NULL
, NULL
, NULL
, NULL
}
2430 p
= pa_xnew0(pa_alsa_path
, 1);
2431 n
= pa_path_get_filename(fname
);
2432 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2433 p
->proplist
= pa_proplist_new();
2434 p
->direction
= direction
;
2437 items
[0].data
= &p
->priority
;
2438 items
[1].data
= &p
->description_key
;
2439 items
[2].data
= &p
->description
;
2440 items
[3].data
= &p
->name
;
2441 items
[4].data
= &mute_during_activation
;
2442 items
[5].data
= &p
->eld_device
;
2445 paths_dir
= get_default_paths_dir();
2447 fn
= pa_maybe_prefix_path(fname
, paths_dir
);
2449 r
= pa_config_parse(fn
, NULL
, items
, p
->proplist
, p
);
2455 p
->mute_during_activation
= mute_during_activation
;
2457 if (path_verify(p
) < 0)
2463 pa_alsa_path_free(p
);
2467 pa_alsa_path
*pa_alsa_path_synthesize(const char *element
, pa_alsa_direction_t direction
) {
2473 p
= pa_xnew0(pa_alsa_path
, 1);
2474 p
->name
= pa_xstrdup(element
);
2475 p
->direction
= direction
;
2477 e
= pa_xnew0(pa_alsa_element
, 1);
2479 e
->alsa_name
= pa_xstrdup(element
);
2480 e
->direction
= direction
;
2481 e
->volume_limit
= -1;
2483 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2484 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2486 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2487 p
->last_element
= e
;
2491 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2492 pa_alsa_option
*o
, *n
;
2496 for (o
= e
->options
; o
; o
= n
) {
2499 if (o
->alsa_idx
< 0) {
2500 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2506 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2507 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2508 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2511 static void path_drop_unsupported(pa_alsa_path
*p
) {
2512 pa_alsa_element
*e
, *n
;
2516 for (e
= p
->elements
; e
; e
= n
) {
2519 if (!element_drop_unsupported(e
)) {
2520 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2526 static void path_make_options_unique(pa_alsa_path
*p
) {
2528 pa_alsa_option
*o
, *u
;
2530 PA_LLIST_FOREACH(e
, p
->elements
) {
2531 PA_LLIST_FOREACH(o
, e
->options
) {
2535 for (u
= o
->next
; u
; u
= u
->next
)
2536 if (pa_streq(u
->name
, o
->name
))
2542 m
= pa_xstrdup(o
->name
);
2544 /* OK, this name is not unique, hence let's rename */
2545 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2548 if (!pa_streq(u
->name
, m
))
2551 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2555 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2556 pa_xfree(u
->description
);
2557 u
->description
= nd
;
2567 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2570 for (; e
; e
= e
->next
)
2571 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2572 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2578 for (o
= e
->options
; o
; o
= o
->next
) {
2582 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2583 s
->options
= pa_idxset_copy(template->options
);
2584 s
->name
= pa_sprintf_malloc("%s+%s", template->name
, o
->name
);
2586 (template->description
[0] && o
->description
[0])
2587 ? pa_sprintf_malloc("%s / %s", template->description
, o
->description
)
2588 : (template->description
[0]
2589 ? pa_xstrdup(template->description
)
2590 : pa_xstrdup(o
->description
));
2592 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2594 s
= pa_xnew0(pa_alsa_setting
, 1);
2595 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2596 s
->name
= pa_xstrdup(o
->name
);
2597 s
->description
= pa_xstrdup(o
->description
);
2598 s
->priority
= o
->priority
;
2601 pa_idxset_put(s
->options
, o
, NULL
);
2603 if (element_create_settings(e
->next
, s
))
2604 /* This is not a leaf, so let's get rid of it */
2607 /* This is a leaf, so let's add it */
2608 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2610 e
->path
->last_setting
= s
;
2617 static void path_create_settings(pa_alsa_path
*p
) {
2620 element_create_settings(p
->elements
, NULL
);
2623 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_hctl_t
*hctl
, pa_bool_t ignore_dB
) {
2626 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2627 pa_channel_position_t t
;
2628 pa_channel_position_mask_t path_volume_channels
= 0;
2634 return p
->supported
? 0 : -1;
2640 pa_log_debug("Probing path '%s'", p
->name
);
2642 PA_LLIST_FOREACH(j
, p
->jacks
) {
2643 if (jack_probe(j
, hctl
) < 0) {
2644 p
->supported
= FALSE
;
2645 pa_log_debug("Probe of jack '%s' failed.", j
->alsa_name
);
2648 pa_log_debug("Probe of jack '%s' succeeded (%s)", j
->alsa_name
, j
->has_control
? "found!" : "not found");
2651 PA_LLIST_FOREACH(e
, p
->elements
) {
2652 if (element_probe(e
, m
) < 0) {
2653 p
->supported
= FALSE
;
2654 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2657 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
);
2662 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2664 if (!p
->has_volume
) {
2665 p
->min_volume
= e
->min_volume
;
2666 p
->max_volume
= e
->max_volume
;
2670 if (!p
->has_volume
) {
2671 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2672 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2673 min_dB
[t
] = e
->min_dB
;
2674 max_dB
[t
] = e
->max_dB
;
2675 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2682 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2683 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2684 min_dB
[t
] += e
->min_dB
;
2685 max_dB
[t
] += e
->max_dB
;
2686 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2689 /* Hmm, there's another element before us
2690 * which cannot do dB volumes, so we we need
2691 * to 'neutralize' this slider */
2692 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2693 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2696 } else if (p
->has_volume
) {
2697 /* We can't use this volume, so let's ignore it */
2698 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2699 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2701 p
->has_volume
= TRUE
;
2704 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2708 if (p
->has_req_any
&& !p
->req_any_present
) {
2709 p
->supported
= FALSE
;
2710 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2714 path_drop_unsupported(p
);
2715 path_make_options_unique(p
);
2716 path_create_settings(p
);
2718 p
->supported
= TRUE
;
2720 p
->min_dB
= INFINITY
;
2721 p
->max_dB
= -INFINITY
;
2723 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2724 if (path_volume_channels
& PA_CHANNEL_POSITION_MASK(t
)) {
2725 if (p
->min_dB
> min_dB
[t
])
2726 p
->min_dB
= min_dB
[t
];
2728 if (p
->max_dB
< max_dB
[t
])
2729 p
->max_dB
= max_dB
[t
];
2736 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2739 pa_log_debug("Setting %s (%s) priority=%u",
2741 pa_strnull(s
->description
),
2745 void pa_alsa_jack_dump(pa_alsa_jack
*j
) {
2748 pa_log_debug("Jack %s, alsa_name='%s', detection %s", j
->name
, j
->alsa_name
, j
->has_control
? "possible" : "unavailable");
2751 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2754 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2756 pa_strnull(o
->name
),
2757 pa_strnull(o
->description
),
2762 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2766 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",
2776 (long long unsigned) e
->merged_mask
,
2778 pa_yes_no(e
->override_map
));
2780 PA_LLIST_FOREACH(o
, e
->options
)
2781 pa_alsa_option_dump(o
);
2784 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2790 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2791 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2793 pa_strnull(p
->description
),
2796 pa_yes_no(p
->probed
),
2797 pa_yes_no(p
->supported
),
2798 pa_yes_no(p
->has_mute
),
2799 pa_yes_no(p
->has_volume
),
2800 pa_yes_no(p
->has_dB
),
2801 p
->min_volume
, p
->max_volume
,
2802 p
->min_dB
, p
->max_dB
);
2804 PA_LLIST_FOREACH(e
, p
->elements
)
2805 pa_alsa_element_dump(e
);
2807 PA_LLIST_FOREACH(j
, p
->jacks
)
2808 pa_alsa_jack_dump(j
);
2810 PA_LLIST_FOREACH(s
, p
->settings
)
2811 pa_alsa_setting_dump(s
);
2814 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2815 snd_mixer_selem_id_t
*sid
;
2816 snd_mixer_elem_t
*me
;
2822 SELEM_INIT(sid
, e
->alsa_name
);
2823 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2824 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2828 snd_mixer_elem_set_callback(me
, cb
);
2829 snd_mixer_elem_set_callback_private(me
, userdata
);
2832 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2839 PA_LLIST_FOREACH(e
, p
->elements
)
2840 element_set_callback(e
, m
, cb
, userdata
);
2843 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2851 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
2852 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2855 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
, const char *paths_dir
) {
2856 pa_alsa_path_set
*ps
;
2857 char **pn
= NULL
, **en
= NULL
, **ie
;
2858 pa_alsa_decibel_fix
*db_fix
;
2859 void *state
, *state2
;
2863 pa_assert(m
->profile_set
);
2864 pa_assert(m
->profile_set
->decibel_fixes
);
2865 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2867 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2870 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2871 ps
->direction
= direction
;
2872 ps
->paths
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2874 if (direction
== PA_ALSA_DIRECTION_OUTPUT
) {
2875 pn
= m
->output_path_names
;
2876 cache
= m
->profile_set
->output_paths
;
2878 else if (direction
== PA_ALSA_DIRECTION_INPUT
) {
2879 pn
= m
->input_path_names
;
2880 cache
= m
->profile_set
->input_paths
;
2886 for (in
= pn
; *in
; in
++) {
2887 pa_alsa_path
*p
= NULL
;
2888 pa_bool_t duplicate
= FALSE
;
2891 for (kn
= pn
; kn
< in
; kn
++)
2892 if (pa_streq(*kn
, *in
)) {
2900 p
= pa_hashmap_get(cache
, *in
);
2902 char *fn
= pa_sprintf_malloc("%s.conf", *in
);
2903 p
= pa_alsa_path_new(paths_dir
, fn
, direction
);
2906 pa_hashmap_put(cache
, *in
, p
);
2908 pa_assert(pa_hashmap_get(cache
, *in
) == p
);
2910 pa_hashmap_put(ps
->paths
, p
, p
);
2917 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2918 en
= m
->output_element
;
2919 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2920 en
= m
->input_element
;
2923 pa_alsa_path_set_free(ps
);
2927 for (ie
= en
; *ie
; ie
++) {
2931 p
= pa_alsa_path_synthesize(*ie
, direction
);
2933 /* Mark all other passed elements for require-absent */
2934 for (je
= en
; *je
; je
++) {
2940 e
= pa_xnew0(pa_alsa_element
, 1);
2942 e
->alsa_name
= pa_xstrdup(*je
);
2943 e
->direction
= direction
;
2944 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2945 e
->volume_limit
= -1;
2947 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2948 p
->last_element
= e
;
2951 pa_hashmap_put(ps
->paths
, *ie
, p
);
2955 /* Assign decibel fixes to elements. */
2956 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2959 PA_HASHMAP_FOREACH(p
, ps
->paths
, state2
) {
2962 PA_LLIST_FOREACH(e
, p
->elements
) {
2963 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2964 /* The profile set that contains the dB fix may be freed
2965 * before the element, so we have to copy the dB fix
2967 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2968 e
->db_fix
->profile_set
= NULL
;
2969 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2970 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
2979 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2984 pa_log_debug("Path Set %p, direction=%i",
2988 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
2989 pa_alsa_path_dump(p
);
2993 static pa_bool_t
options_have_option(pa_alsa_option
*options
, const char *alsa_name
) {
2997 pa_assert(alsa_name
);
2999 PA_LLIST_FOREACH(o
, options
) {
3000 if (pa_streq(o
->alsa_name
, alsa_name
))
3006 static pa_bool_t
enumeration_is_subset(pa_alsa_option
*a_options
, pa_alsa_option
*b_options
) {
3007 pa_alsa_option
*oa
, *ob
;
3009 if (!a_options
) return TRUE
;
3010 if (!b_options
) return FALSE
;
3012 /* If there is an option A offers that B does not, then A is not a subset of B. */
3013 PA_LLIST_FOREACH(oa
, a_options
) {
3014 pa_bool_t found
= FALSE
;
3015 PA_LLIST_FOREACH(ob
, b_options
) {
3016 if (pa_streq(oa
->alsa_name
, ob
->alsa_name
)) {
3028 * Compares two elements to see if a is a subset of b
3030 static pa_bool_t
element_is_subset(pa_alsa_element
*a
, pa_alsa_element
*b
, snd_mixer_t
*m
) {
3036 * Every state is a subset of itself (with caveats for volume_limits and options)
3037 * IGNORE is a subset of every other state */
3039 /* Check the volume_use */
3040 if (a
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
3042 /* "Constant" is subset of "Constant" only when their constant values are equal */
3043 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& b
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& a
->constant_volume
!= b
->constant_volume
)
3046 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
3047 if (a
->volume_use
!= b
->volume_use
&& b
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
3050 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
3051 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
3052 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
3053 if (b
->volume_use
== PA_ALSA_VOLUME_MERGE
&& b
->volume_limit
>= 0) {
3056 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
)
3057 a_limit
= a
->constant_volume
;
3058 else if (a
->volume_use
== PA_ALSA_VOLUME_ZERO
) {
3062 int rounding
= (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
? +1 : -1);
3063 a_limit
= decibel_fix_get_step(a
->db_fix
, &dB
, rounding
);
3065 snd_mixer_selem_id_t
*sid
;
3066 snd_mixer_elem_t
*me
;
3068 SELEM_INIT(sid
, a
->alsa_name
);
3069 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
3070 pa_log_warn("Element %s seems to have disappeared.", a
->alsa_name
);
3074 if (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3075 if (snd_mixer_selem_ask_playback_dB_vol(me
, dB
, +1, &a_limit
) < 0)
3078 if (snd_mixer_selem_ask_capture_dB_vol(me
, dB
, -1, &a_limit
) < 0)
3082 } else if (a
->volume_use
== PA_ALSA_VOLUME_OFF
)
3083 a_limit
= a
->min_volume
;
3084 else if (a
->volume_use
== PA_ALSA_VOLUME_MERGE
)
3085 a_limit
= a
->volume_limit
;
3087 /* This should never be reached */
3090 if (a_limit
> b
->volume_limit
)
3094 if (a
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
3096 /* If override-maps are different, they're not subsets */
3097 if (a
->n_channels
!= b
->n_channels
)
3099 for (s
= 0; s
<= SND_MIXER_SCHN_LAST
; s
++)
3100 if (a
->masks
[s
][a
->n_channels
-1] != b
->masks
[s
][b
->n_channels
-1]) {
3101 pa_log_debug("Element %s is not a subset - mask a: 0x%" PRIx64
", mask b: 0x%" PRIx64
", at channel %d",
3102 a
->alsa_name
, a
->masks
[s
][a
->n_channels
-1], b
->masks
[s
][b
->n_channels
-1], s
);
3108 if (a
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
3109 /* "On" is a subset of "Mute".
3110 * "Off" is a subset of "Mute".
3111 * "On" is a subset of "Select", if there is an "Option:On" in B.
3112 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3113 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3115 if (a
->switch_use
!= b
->switch_use
) {
3117 if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
|| a
->switch_use
== PA_ALSA_SWITCH_MUTE
3118 || b
->switch_use
== PA_ALSA_SWITCH_OFF
|| b
->switch_use
== PA_ALSA_SWITCH_ON
)
3121 if (b
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3122 if (a
->switch_use
== PA_ALSA_SWITCH_ON
) {
3123 if (!options_have_option(b
->options
, "on"))
3125 } else if (a
->switch_use
== PA_ALSA_SWITCH_OFF
) {
3126 if (!options_have_option(b
->options
, "off"))
3130 } else if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3131 if (!enumeration_is_subset(a
->options
, b
->options
))
3136 if (a
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
) {
3137 if (b
->enumeration_use
== PA_ALSA_ENUMERATION_IGNORE
)
3139 if (!enumeration_is_subset(a
->options
, b
->options
))
3146 static void path_set_condense(pa_alsa_path_set
*ps
, snd_mixer_t
*m
) {
3153 /* If we only have one path, then don't bother */
3154 if (pa_hashmap_size(ps
->paths
) < 2)
3157 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3161 PA_HASHMAP_FOREACH(p2
, ps
->paths
, state2
) {
3162 pa_alsa_element
*ea
, *eb
;
3163 pa_alsa_jack
*ja
, *jb
;
3164 bool is_subset
= true;
3169 /* If a has a jack that b does not have, a is not a subset */
3170 PA_LLIST_FOREACH(ja
, p
->jacks
) {
3171 bool exists
= false;
3173 if (!ja
->has_control
)
3176 PA_LLIST_FOREACH(jb
, p2
->jacks
) {
3177 if (jb
->has_control
&& pa_streq(jb
->alsa_name
, ja
->alsa_name
) &&
3178 (ja
->state_plugged
== jb
->state_plugged
) &&
3179 (ja
->state_unplugged
== jb
->state_unplugged
)) {
3191 /* Compare the elements of each set... */
3198 else if ((ea
&& !eb
) || (!ea
&& eb
))
3200 else if (pa_streq(ea
->alsa_name
, eb
->alsa_name
)) {
3201 if (element_is_subset(ea
, eb
, m
)) {
3211 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p
->name
, p2
->name
);
3212 pa_hashmap_remove(ps
->paths
, p
);
3219 static pa_alsa_path
* path_set_find_path_by_name(pa_alsa_path_set
*ps
, const char* name
, pa_alsa_path
*ignore
)
3224 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
3225 if (p
!= ignore
&& pa_streq(p
->name
, name
))
3230 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
3231 pa_alsa_path
*p
, *q
;
3232 void *state
, *state2
;
3234 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3238 q
= path_set_find_path_by_name(ps
, p
->name
, p
);
3243 m
= pa_xstrdup(p
->name
);
3245 /* OK, this name is not unique, hence let's rename */
3247 PA_HASHMAP_FOREACH(q
, ps
->paths
, state2
) {
3250 if (!pa_streq(q
->name
, m
))
3253 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
3257 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
3258 pa_xfree(q
->description
);
3259 q
->description
= nd
;
3268 static void mapping_free(pa_alsa_mapping
*m
) {
3272 pa_xfree(m
->description
);
3274 pa_proplist_free(m
->proplist
);
3276 pa_xstrfreev(m
->device_strings
);
3277 pa_xstrfreev(m
->input_path_names
);
3278 pa_xstrfreev(m
->output_path_names
);
3279 pa_xstrfreev(m
->input_element
);
3280 pa_xstrfreev(m
->output_element
);
3281 if (m
->input_path_set
)
3282 pa_alsa_path_set_free(m
->input_path_set
);
3283 if (m
->output_path_set
)
3284 pa_alsa_path_set_free(m
->output_path_set
);
3286 pa_assert(!m
->input_pcm
);
3287 pa_assert(!m
->output_pcm
);
3289 pa_alsa_ucm_mapping_context_free(&m
->ucm_context
);
3294 static void profile_free(pa_alsa_profile
*p
) {
3298 pa_xfree(p
->description
);
3300 pa_xstrfreev(p
->input_mapping_names
);
3301 pa_xstrfreev(p
->output_mapping_names
);
3303 if (p
->input_mappings
)
3304 pa_idxset_free(p
->input_mappings
, NULL
);
3306 if (p
->output_mappings
)
3307 pa_idxset_free(p
->output_mappings
, NULL
);
3312 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
3315 if (ps
->input_paths
)
3316 pa_hashmap_free(ps
->input_paths
, (pa_free_cb_t
) pa_alsa_path_free
);
3318 if (ps
->output_paths
)
3319 pa_hashmap_free(ps
->output_paths
, (pa_free_cb_t
) pa_alsa_path_free
);
3322 pa_hashmap_free(ps
->profiles
, (pa_free_cb_t
) profile_free
);
3325 pa_hashmap_free(ps
->mappings
, (pa_free_cb_t
) mapping_free
);
3327 if (ps
->decibel_fixes
)
3328 pa_hashmap_free(ps
->decibel_fixes
, (pa_free_cb_t
) decibel_fix_free
);
3333 pa_alsa_mapping
*pa_alsa_mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
3336 if (!pa_startswith(name
, "Mapping "))
3341 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
3344 m
= pa_xnew0(pa_alsa_mapping
, 1);
3345 m
->profile_set
= ps
;
3346 m
->name
= pa_xstrdup(name
);
3347 pa_channel_map_init(&m
->channel_map
);
3348 m
->proplist
= pa_proplist_new();
3350 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
3355 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
3358 if (!pa_startswith(name
, "Profile "))
3363 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
3366 p
= pa_xnew0(pa_alsa_profile
, 1);
3367 p
->profile_set
= ps
;
3368 p
->name
= pa_xstrdup(name
);
3370 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3375 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
3376 pa_alsa_decibel_fix
*db_fix
;
3378 if (!pa_startswith(name
, "DecibelFix "))
3383 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
3386 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
3387 db_fix
->profile_set
= ps
;
3388 db_fix
->name
= pa_xstrdup(name
);
3390 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
3395 static int mapping_parse_device_strings(pa_config_parser_state
*state
) {
3396 pa_alsa_profile_set
*ps
;
3401 ps
= state
->userdata
;
3403 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3404 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3408 pa_xstrfreev(m
->device_strings
);
3409 if (!(m
->device_strings
= pa_split_spaces_strv(state
->rvalue
))) {
3410 pa_log("[%s:%u] Device string list empty of '%s'", state
->filename
, state
->lineno
, state
->section
);
3417 static int mapping_parse_channel_map(pa_config_parser_state
*state
) {
3418 pa_alsa_profile_set
*ps
;
3423 ps
= state
->userdata
;
3425 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3426 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3430 if (!(pa_channel_map_parse(&m
->channel_map
, state
->rvalue
))) {
3431 pa_log("[%s:%u] Channel map invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
3438 static int mapping_parse_paths(pa_config_parser_state
*state
) {
3439 pa_alsa_profile_set
*ps
;
3444 ps
= state
->userdata
;
3446 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3447 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3451 if (pa_streq(state
->lvalue
, "paths-input")) {
3452 pa_xstrfreev(m
->input_path_names
);
3453 m
->input_path_names
= pa_split_spaces_strv(state
->rvalue
);
3455 pa_xstrfreev(m
->output_path_names
);
3456 m
->output_path_names
= pa_split_spaces_strv(state
->rvalue
);
3462 static int mapping_parse_element(pa_config_parser_state
*state
) {
3463 pa_alsa_profile_set
*ps
;
3468 ps
= state
->userdata
;
3470 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3471 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3475 if (pa_streq(state
->lvalue
, "element-input")) {
3476 pa_xstrfreev(m
->input_element
);
3477 m
->input_element
= pa_split_spaces_strv(state
->rvalue
);
3479 pa_xstrfreev(m
->output_element
);
3480 m
->output_element
= pa_split_spaces_strv(state
->rvalue
);
3486 static int mapping_parse_direction(pa_config_parser_state
*state
) {
3487 pa_alsa_profile_set
*ps
;
3492 ps
= state
->userdata
;
3494 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3495 pa_log("[%s:%u] Section name %s invalid.", state
->filename
, state
->lineno
, state
->section
);
3499 if (pa_streq(state
->rvalue
, "input"))
3500 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3501 else if (pa_streq(state
->rvalue
, "output"))
3502 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3503 else if (pa_streq(state
->rvalue
, "any"))
3504 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3506 pa_log("[%s:%u] Direction %s invalid.", state
->filename
, state
->lineno
, state
->rvalue
);
3513 static int mapping_parse_description(pa_config_parser_state
*state
) {
3514 pa_alsa_profile_set
*ps
;
3520 ps
= state
->userdata
;
3522 if ((m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3523 pa_xfree(m
->description
);
3524 m
->description
= pa_xstrdup(state
->rvalue
);
3525 } else if ((p
= profile_get(ps
, state
->section
))) {
3526 pa_xfree(p
->description
);
3527 p
->description
= pa_xstrdup(state
->rvalue
);
3529 pa_log("[%s:%u] Section name %s invalid.", state
->filename
, state
->lineno
, state
->section
);
3536 static int mapping_parse_priority(pa_config_parser_state
*state
) {
3537 pa_alsa_profile_set
*ps
;
3544 ps
= state
->userdata
;
3546 if (pa_atou(state
->rvalue
, &prio
) < 0) {
3547 pa_log("[%s:%u] Priority invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
3551 if ((m
= pa_alsa_mapping_get(ps
, state
->section
)))
3553 else if ((p
= profile_get(ps
, state
->section
)))
3556 pa_log("[%s:%u] Section name %s invalid.", state
->filename
, state
->lineno
, state
->section
);
3563 static int profile_parse_mappings(pa_config_parser_state
*state
) {
3564 pa_alsa_profile_set
*ps
;
3569 ps
= state
->userdata
;
3571 if (!(p
= profile_get(ps
, state
->section
))) {
3572 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3576 if (pa_streq(state
->lvalue
, "input-mappings")) {
3577 pa_xstrfreev(p
->input_mapping_names
);
3578 p
->input_mapping_names
= pa_split_spaces_strv(state
->rvalue
);
3580 pa_xstrfreev(p
->output_mapping_names
);
3581 p
->output_mapping_names
= pa_split_spaces_strv(state
->rvalue
);
3587 static int profile_parse_skip_probe(pa_config_parser_state
*state
) {
3588 pa_alsa_profile_set
*ps
;
3594 ps
= state
->userdata
;
3596 if (!(p
= profile_get(ps
, state
->section
))) {
3597 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3601 if ((b
= pa_parse_boolean(state
->rvalue
)) < 0) {
3602 pa_log("[%s:%u] Skip probe invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
3611 static int decibel_fix_parse_db_values(pa_config_parser_state
*state
) {
3612 pa_alsa_profile_set
*ps
;
3613 pa_alsa_decibel_fix
*db_fix
;
3617 unsigned n
= 8; /* Current size of the db_values table. */
3618 unsigned min_step
= 0;
3619 unsigned max_step
= 0;
3620 unsigned i
= 0; /* Index to the items table. */
3621 unsigned prev_step
= 0;
3626 ps
= state
->userdata
;
3628 if (!(db_fix
= decibel_fix_get(ps
, state
->section
))) {
3629 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3633 if (!(items
= pa_split_spaces_strv(state
->rvalue
))) {
3634 pa_log("[%s:%u] Value missing", state
->filename
, state
->lineno
);
3638 db_values
= pa_xnew(long, n
);
3640 while ((item
= items
[i
++])) {
3641 char *s
= item
; /* Step value string. */
3642 char *d
= item
; /* dB value string. */
3646 /* Move d forward until it points to a colon or to the end of the item. */
3647 for (; *d
&& *d
!= ':'; ++d
);
3650 /* item started with colon. */
3651 pa_log("[%s:%u] No step value found in %s", state
->filename
, state
->lineno
, item
);
3655 if (!*d
|| !*(d
+ 1)) {
3656 /* No colon found, or it was the last character in item. */
3657 pa_log("[%s:%u] No dB value found in %s", state
->filename
, state
->lineno
, item
);
3661 /* pa_atou() needs a null-terminating string. Let's replace the colon
3662 * with a zero byte. */
3665 if (pa_atou(s
, &step
) < 0) {
3666 pa_log("[%s:%u] Invalid step value: %s", state
->filename
, state
->lineno
, s
);
3670 if (pa_atod(d
, &db
) < 0) {
3671 pa_log("[%s:%u] Invalid dB value: %s", state
->filename
, state
->lineno
, d
);
3675 if (step
<= prev_step
&& i
!= 1) {
3676 pa_log("[%s:%u] Step value %u not greater than the previous value %u", state
->filename
, state
->lineno
, step
, prev_step
);
3680 if (db
< prev_db
&& i
!= 1) {
3681 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", state
->filename
, state
->lineno
, db
, prev_db
);
3687 db_values
[0] = (long) (db
* 100.0);
3691 /* Interpolate linearly. */
3692 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3694 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3696 /* Reallocate the db_values table if it's about to overflow. */
3697 if (prev_step
+ 1 - min_step
== n
) {
3699 db_values
= pa_xrenew(long, db_values
, n
);
3702 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3709 db_fix
->min_step
= min_step
;
3710 db_fix
->max_step
= max_step
;
3711 pa_xfree(db_fix
->db_values
);
3712 db_fix
->db_values
= db_values
;
3714 pa_xstrfreev(items
);
3719 pa_xstrfreev(items
);
3720 pa_xfree(db_values
);
3725 static void mapping_paths_probe(pa_alsa_mapping
*m
, pa_alsa_profile
*profile
,
3726 pa_alsa_direction_t direction
) {
3730 snd_pcm_t
*pcm_handle
;
3731 pa_alsa_path_set
*ps
;
3732 snd_mixer_t
*mixer_handle
;
3733 snd_hctl_t
*hctl_handle
;
3735 if (direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3736 if (m
->output_path_set
)
3737 return; /* Already probed */
3738 m
->output_path_set
= ps
= pa_alsa_path_set_new(m
, direction
, NULL
); /* FIXME: Handle paths_dir */
3739 pcm_handle
= m
->output_pcm
;
3741 if (m
->input_path_set
)
3742 return; /* Already probed */
3743 m
->input_path_set
= ps
= pa_alsa_path_set_new(m
, direction
, NULL
); /* FIXME: Handle paths_dir */
3744 pcm_handle
= m
->input_pcm
;
3748 return; /* No paths */
3750 pa_assert(pcm_handle
);
3752 mixer_handle
= pa_alsa_open_mixer_for_pcm(pcm_handle
, NULL
, &hctl_handle
);
3753 if (!mixer_handle
|| !hctl_handle
) {
3754 /* Cannot open mixer, remove all entries */
3755 pa_hashmap_remove_all(ps
->paths
, NULL
);
3760 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3761 if (pa_alsa_path_probe(p
, mixer_handle
, hctl_handle
, m
->profile_set
->ignore_dB
) < 0) {
3762 pa_hashmap_remove(ps
->paths
, p
);
3766 path_set_condense(ps
, mixer_handle
);
3767 path_set_make_paths_unique(ps
);
3770 snd_mixer_close(mixer_handle
);
3772 pa_log_debug("Available mixer paths (after tidying):");
3773 pa_alsa_path_set_dump(ps
);
3776 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3778 static const struct description_map well_known_descriptions
[] = {
3779 { "analog-mono", N_("Analog Mono") },
3780 { "analog-stereo", N_("Analog Stereo") },
3781 { "analog-surround-21", N_("Analog Surround 2.1") },
3782 { "analog-surround-30", N_("Analog Surround 3.0") },
3783 { "analog-surround-31", N_("Analog Surround 3.1") },
3784 { "analog-surround-40", N_("Analog Surround 4.0") },
3785 { "analog-surround-41", N_("Analog Surround 4.1") },
3786 { "analog-surround-50", N_("Analog Surround 5.0") },
3787 { "analog-surround-51", N_("Analog Surround 5.1") },
3788 { "analog-surround-61", N_("Analog Surround 6.0") },
3789 { "analog-surround-61", N_("Analog Surround 6.1") },
3790 { "analog-surround-70", N_("Analog Surround 7.0") },
3791 { "analog-surround-71", N_("Analog Surround 7.1") },
3792 { "analog-4-channel-input", N_("Analog 4-channel Input") },
3793 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3794 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3795 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3796 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3797 { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
3798 { "hdmi-stereo", N_("Digital Stereo (HDMI)") },
3799 { "hdmi-surround-51", N_("Digital Surround 5.1 (HDMI)") }
3804 if (!pa_channel_map_valid(&m
->channel_map
)) {
3805 pa_log("Mapping %s is missing channel map.", m
->name
);
3809 if (!m
->device_strings
) {
3810 pa_log("Mapping %s is missing device strings.", m
->name
);
3814 if ((m
->input_path_names
&& m
->input_element
) ||
3815 (m
->output_path_names
&& m
->output_element
)) {
3816 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3820 if (!m
->description
)
3821 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3822 well_known_descriptions
,
3823 PA_ELEMENTSOF(well_known_descriptions
)));
3825 if (!m
->description
)
3826 m
->description
= pa_xstrdup(m
->name
);
3829 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3831 else if (m
->channel_map
.channels
== bonus
->channels
)
3838 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3839 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3843 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3845 pa_strnull(m
->description
),
3847 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3848 pa_yes_no(m
->supported
),
3852 static void profile_set_add_auto_pair(
3853 pa_alsa_profile_set
*ps
,
3854 pa_alsa_mapping
*m
, /* output */
3855 pa_alsa_mapping
*n
/* input */) {
3863 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3866 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3870 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3872 name
= pa_sprintf_malloc("output:%s", m
->name
);
3874 name
= pa_sprintf_malloc("input:%s", n
->name
);
3876 if (pa_hashmap_get(ps
->profiles
, name
)) {
3881 p
= pa_xnew0(pa_alsa_profile
, 1);
3882 p
->profile_set
= ps
;
3886 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3887 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3888 p
->priority
+= m
->priority
* 100;
3892 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3893 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3894 p
->priority
+= n
->priority
;
3897 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3900 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3901 pa_alsa_mapping
*m
, *n
;
3902 void *m_state
, *n_state
;
3906 /* The order is important here:
3907 1) try single inputs and outputs before trying their
3908 combination, because if the half-duplex test failed, we don't have
3910 2) try the output right before the input combinations with
3911 that output, because then the output_pcm is not closed between tests.
3913 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3914 profile_set_add_auto_pair(ps
, NULL
, n
);
3916 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3917 profile_set_add_auto_pair(ps
, m
, NULL
);
3919 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3920 profile_set_add_auto_pair(ps
, m
, n
);
3925 static int profile_verify(pa_alsa_profile
*p
) {
3927 static const struct description_map well_known_descriptions
[] = {
3928 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3929 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3930 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3931 { "off", N_("Off") }
3936 /* Replace the output mapping names by the actual mappings */
3937 if (p
->output_mapping_names
) {
3940 pa_assert(!p
->output_mappings
);
3941 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3943 for (name
= p
->output_mapping_names
; *name
; name
++) {
3946 pa_bool_t duplicate
= FALSE
;
3948 for (in
= name
+ 1; *in
; in
++)
3949 if (pa_streq(*name
, *in
)) {
3957 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3958 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p
->name
, *name
);
3962 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3968 pa_xstrfreev(p
->output_mapping_names
);
3969 p
->output_mapping_names
= NULL
;
3972 /* Replace the input mapping names by the actual mappings */
3973 if (p
->input_mapping_names
) {
3976 pa_assert(!p
->input_mappings
);
3977 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3979 for (name
= p
->input_mapping_names
; *name
; name
++) {
3982 pa_bool_t duplicate
= FALSE
;
3984 for (in
= name
+ 1; *in
; in
++)
3985 if (pa_streq(*name
, *in
)) {
3993 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3994 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p
->name
, *name
);
3998 pa_idxset_put(p
->input_mappings
, m
, NULL
);
4004 pa_xstrfreev(p
->input_mapping_names
);
4005 p
->input_mapping_names
= NULL
;
4008 if (!p
->input_mappings
&& !p
->output_mappings
) {
4009 pa_log("Profile '%s' lacks mappings.", p
->name
);
4013 if (!p
->description
)
4014 p
->description
= pa_xstrdup(lookup_description(p
->name
,
4015 well_known_descriptions
,
4016 PA_ELEMENTSOF(well_known_descriptions
)));
4018 if (!p
->description
) {
4023 sb
= pa_strbuf_new();
4025 if (p
->output_mappings
)
4026 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4027 if (!pa_strbuf_isempty(sb
))
4028 pa_strbuf_puts(sb
, " + ");
4030 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
4033 if (p
->input_mappings
)
4034 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4035 if (!pa_strbuf_isempty(sb
))
4036 pa_strbuf_puts(sb
, " + ");
4038 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
4041 p
->description
= pa_strbuf_tostring_free(sb
);
4047 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
4052 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4054 pa_strnull(p
->description
),
4056 pa_yes_no(p
->supported
),
4057 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
4058 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
4060 if (p
->input_mappings
)
4061 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
4062 pa_log_debug("Input %s", m
->name
);
4064 if (p
->output_mappings
)
4065 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
4066 pa_log_debug("Output %s", m
->name
);
4069 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
4072 /* Check that the dB mapping has been configured. Since "db-values" is
4073 * currently the only option in the DecibelFix section, and decibel fix
4074 * objects don't get created if a DecibelFix section is empty, this is
4075 * actually a redundant check. Having this may prevent future bugs,
4077 if (!db_fix
->db_values
) {
4078 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
4085 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
4086 char *db_values
= NULL
;
4090 if (db_fix
->db_values
) {
4092 unsigned long i
, nsteps
;
4094 pa_assert(db_fix
->min_step
<= db_fix
->max_step
);
4095 nsteps
= db_fix
->max_step
- db_fix
->min_step
+ 1;
4097 buf
= pa_strbuf_new();
4098 for (i
= 0; i
< nsteps
; ++i
)
4099 pa_strbuf_printf(buf
, "[%li]:%0.2f ", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
4101 db_values
= pa_strbuf_tostring_free(buf
);
4104 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4105 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
4107 pa_xfree(db_values
);
4110 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
4111 pa_alsa_profile_set
*ps
;
4114 pa_alsa_decibel_fix
*db_fix
;
4119 static pa_config_item items
[] = {
4121 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
4124 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
4125 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
4126 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
4127 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
4128 { "element-input", mapping_parse_element
, NULL
, NULL
},
4129 { "element-output", mapping_parse_element
, NULL
, NULL
},
4130 { "direction", mapping_parse_direction
, NULL
, NULL
},
4132 /* Shared by [Mapping ...] and [Profile ...] */
4133 { "description", mapping_parse_description
, NULL
, NULL
},
4134 { "priority", mapping_parse_priority
, NULL
, NULL
},
4137 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
4138 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
4139 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
4141 /* [DecibelFix ...] */
4142 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
4143 { NULL
, NULL
, NULL
, NULL
}
4146 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
4147 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4148 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4149 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4150 ps
->input_paths
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4151 ps
->output_paths
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4153 items
[0].data
= &ps
->auto_profiles
;
4156 fname
= "default.conf";
4158 fn
= pa_maybe_prefix_path(fname
,
4159 pa_run_from_build_tree() ? PA_SRCDIR
"/modules/alsa/mixer/profile-sets/" :
4160 PA_ALSA_PROFILE_SETS_DIR
);
4162 r
= pa_config_parse(fn
, NULL
, items
, NULL
, ps
);
4168 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4169 if (mapping_verify(m
, bonus
) < 0)
4172 if (ps
->auto_profiles
)
4173 profile_set_add_auto(ps
);
4175 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4176 if (profile_verify(p
) < 0)
4179 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4180 if (decibel_fix_verify(db_fix
) < 0)
4186 pa_alsa_profile_set_free(ps
);
4190 static void profile_finalize_probing(pa_alsa_profile
*to_be_finalized
, pa_alsa_profile
*next
) {
4194 if (!to_be_finalized
)
4197 if (to_be_finalized
->output_mappings
)
4198 PA_IDXSET_FOREACH(m
, to_be_finalized
->output_mappings
, idx
) {
4203 if (to_be_finalized
->supported
)
4206 /* If this mapping is also in the next profile, we won't close the
4207 * pcm handle here, because it would get immediately reopened
4209 if (next
&& next
->output_mappings
&& pa_idxset_get_by_data(next
->output_mappings
, m
, NULL
))
4212 snd_pcm_close(m
->output_pcm
);
4213 m
->output_pcm
= NULL
;
4216 if (to_be_finalized
->input_mappings
)
4217 PA_IDXSET_FOREACH(m
, to_be_finalized
->input_mappings
, idx
) {
4222 if (to_be_finalized
->supported
)
4225 /* If this mapping is also in the next profile, we won't close the
4226 * pcm handle here, because it would get immediately reopened
4228 if (next
&& next
->input_mappings
&& pa_idxset_get_by_data(next
->input_mappings
, m
, NULL
))
4231 snd_pcm_close(m
->input_pcm
);
4232 m
->input_pcm
= NULL
;
4236 static snd_pcm_t
* mapping_open_pcm(pa_alsa_mapping
*m
,
4237 const pa_sample_spec
*ss
,
4240 unsigned default_n_fragments
,
4241 unsigned default_fragment_size_msec
) {
4243 pa_sample_spec try_ss
= *ss
;
4244 pa_channel_map try_map
= m
->channel_map
;
4245 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
4247 try_ss
.channels
= try_map
.channels
;
4250 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
4251 pa_frame_size(&try_ss
);
4252 try_buffer_size
= default_n_fragments
* try_period_size
;
4254 return pa_alsa_open_by_template(
4255 m
->device_strings
, dev_id
, NULL
, &try_ss
,
4256 &try_map
, mode
, &try_period_size
,
4257 &try_buffer_size
, 0, NULL
, NULL
, TRUE
);
4260 static void paths_drop_unsupported(pa_hashmap
* h
) {
4267 p
= pa_hashmap_iterate(h
, &state
, &key
);
4269 if (p
->supported
<= 0) {
4270 pa_hashmap_remove(h
, key
);
4271 pa_alsa_path_free(p
);
4273 p
= pa_hashmap_iterate(h
, &state
, &key
);
4277 void pa_alsa_profile_set_probe(
4278 pa_alsa_profile_set
*ps
,
4280 const pa_sample_spec
*ss
,
4281 unsigned default_n_fragments
,
4282 unsigned default_fragment_size_msec
) {
4285 pa_alsa_profile
*p
, *last
= NULL
;
4287 pa_hashmap
*broken_inputs
, *broken_outputs
;
4296 broken_inputs
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
4297 broken_outputs
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
4299 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
4302 /* Skip if this is already marked that it is supported (i.e. from the config file) */
4303 if (!p
->supported
) {
4305 profile_finalize_probing(last
, p
);
4306 p
->supported
= TRUE
;
4308 if (p
->output_mappings
) {
4309 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4310 if (pa_hashmap_get(broken_outputs
, m
) == m
) {
4311 pa_log_debug("Skipping profile %s - will not be able to open output:%s", p
->name
, m
->name
);
4312 p
->supported
= FALSE
;
4318 if (p
->input_mappings
&& p
->supported
) {
4319 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4320 if (pa_hashmap_get(broken_inputs
, m
) == m
) {
4321 pa_log_debug("Skipping profile %s - will not be able to open input:%s", p
->name
, m
->name
);
4322 p
->supported
= FALSE
;
4329 pa_log_debug("Looking at profile %s", p
->name
);
4331 /* Check if we can open all new ones */
4332 if (p
->output_mappings
&& p
->supported
)
4333 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4338 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
4339 if (!(m
->output_pcm
= mapping_open_pcm(m
, ss
, dev_id
,
4340 SND_PCM_STREAM_PLAYBACK
,
4341 default_n_fragments
,
4342 default_fragment_size_msec
))) {
4343 p
->supported
= FALSE
;
4344 if (pa_idxset_size(p
->output_mappings
) == 1 &&
4345 ((!p
->input_mappings
) || pa_idxset_size(p
->input_mappings
) == 0)) {
4346 pa_log_debug("Caching failure to open output:%s", m
->name
);
4347 pa_hashmap_put(broken_outputs
, m
, m
);
4353 if (p
->input_mappings
&& p
->supported
)
4354 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4359 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
4360 if (!(m
->input_pcm
= mapping_open_pcm(m
, ss
, dev_id
,
4361 SND_PCM_STREAM_CAPTURE
,
4362 default_n_fragments
,
4363 default_fragment_size_msec
))) {
4364 p
->supported
= FALSE
;
4365 if (pa_idxset_size(p
->input_mappings
) == 1 &&
4366 ((!p
->output_mappings
) || pa_idxset_size(p
->output_mappings
) == 0)) {
4367 pa_log_debug("Caching failure to open input:%s", m
->name
);
4368 pa_hashmap_put(broken_inputs
, m
, m
);
4380 pa_log_debug("Profile %s supported.", p
->name
);
4382 if (p
->output_mappings
)
4383 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
4385 mapping_paths_probe(m
, p
, PA_ALSA_DIRECTION_OUTPUT
);
4387 if (p
->input_mappings
)
4388 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
4390 mapping_paths_probe(m
, p
, PA_ALSA_DIRECTION_INPUT
);
4394 profile_finalize_probing(last
, NULL
);
4396 pa_alsa_profile_set_drop_unsupported(ps
);
4398 paths_drop_unsupported(ps
->input_paths
);
4399 paths_drop_unsupported(ps
->output_paths
);
4400 pa_hashmap_free(broken_inputs
, NULL
);
4401 pa_hashmap_free(broken_outputs
, NULL
);
4406 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
4409 pa_alsa_decibel_fix
*db_fix
;
4414 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4417 pa_yes_no(ps
->auto_profiles
),
4418 pa_yes_no(ps
->probed
),
4419 pa_hashmap_size(ps
->mappings
),
4420 pa_hashmap_size(ps
->profiles
),
4421 pa_hashmap_size(ps
->decibel_fixes
));
4423 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4424 pa_alsa_mapping_dump(m
);
4426 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4427 pa_alsa_profile_dump(p
);
4429 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4430 pa_alsa_decibel_fix_dump(db_fix
);
4433 void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set
*ps
) {
4438 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
4439 if (!p
->supported
) {
4440 pa_hashmap_remove(ps
->profiles
, p
->name
);
4445 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
) {
4446 if (m
->supported
<= 0) {
4447 pa_hashmap_remove(ps
->mappings
, m
->name
);
4453 static pa_device_port
* device_port_alsa_init(pa_hashmap
*ports
,
4455 const char* description
,
4457 pa_alsa_setting
*setting
,
4458 pa_card_profile
*cp
,
4466 p
= pa_hashmap_get(ports
, name
);
4469 pa_alsa_port_data
*data
;
4471 p
= pa_device_port_new(core
, name
, description
, sizeof(pa_alsa_port_data
));
4473 pa_hashmap_put(ports
, p
->name
, p
);
4474 pa_proplist_update(p
->proplist
, PA_UPDATE_REPLACE
, path
->proplist
);
4476 data
= PA_DEVICE_PORT_DATA(p
);
4478 data
->setting
= setting
;
4482 p
->is_input
|= path
->direction
== PA_ALSA_DIRECTION_ANY
|| path
->direction
== PA_ALSA_DIRECTION_INPUT
;
4483 p
->is_output
|= path
->direction
== PA_ALSA_DIRECTION_ANY
|| path
->direction
== PA_ALSA_DIRECTION_OUTPUT
;
4486 pa_hashmap_put(p
->profiles
, cp
->name
, cp
);
4489 pa_hashmap_put(extra
, p
->name
, p
);
4490 pa_device_port_ref(p
);
4496 void pa_alsa_path_set_add_ports(
4497 pa_alsa_path_set
*ps
,
4498 pa_card_profile
*cp
,
4511 PA_HASHMAP_FOREACH(path
, ps
->paths
, state
) {
4512 if (!path
->settings
|| !path
->settings
->next
) {
4513 /* If there is no or just one setting we only need a
4515 pa_device_port
*port
= device_port_alsa_init(ports
, path
->name
,
4516 path
->description
, path
, path
->settings
, cp
, extra
, core
);
4517 port
->priority
= path
->priority
* 100;
4521 PA_LLIST_FOREACH(s
, path
->settings
) {
4522 pa_device_port
*port
;
4525 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4527 if (s
->description
[0])
4528 d
= pa_sprintf_malloc("%s / %s", path
->description
, s
->description
);
4530 d
= pa_xstrdup(path
->description
);
4532 port
= device_port_alsa_init(ports
, n
, d
, path
, s
, cp
, extra
, core
);
4533 port
->priority
= path
->priority
* 100 + s
->priority
;
4542 void pa_alsa_add_ports(void *sink_or_source_new_data
, pa_alsa_path_set
*ps
, pa_card
*card
) {
4545 pa_assert(sink_or_source_new_data
);
4548 if (ps
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
4549 ports
= ((pa_sink_new_data
*) sink_or_source_new_data
)->ports
;
4551 ports
= ((pa_source_new_data
*) sink_or_source_new_data
)->ports
;
4553 if (ps
->paths
&& pa_hashmap_size(ps
->paths
) > 0) {
4555 pa_alsa_path_set_add_ports(ps
, NULL
, card
->ports
, ports
, card
->core
);
4558 pa_log_debug("Added %u ports", pa_hashmap_size(ports
));