2 This file is part of PulseAudio.
4 Copyright 2004-2009 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 #include <sys/types.h>
28 #include <asoundlib.h>
31 #ifdef HAVE_VALGRIND_MEMCHECK_H
32 #include <valgrind/memcheck.h>
35 #include <pulse/mainloop-api.h>
36 #include <pulse/sample.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/volume.h>
40 #include <pulse/xmalloc.h>
41 #include <pulse/utf8.h>
43 #include <pulsecore/i18n.h>
44 #include <pulsecore/log.h>
45 #include <pulsecore/macro.h>
46 #include <pulsecore/core-util.h>
47 #include <pulsecore/conf-parser.h>
48 #include <pulsecore/strbuf.h>
50 #include "alsa-mixer.h"
51 #include "alsa-util.h"
53 static int setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
);
55 struct description_map
{
57 const char *description
;
60 static const char *lookup_description(const char *key
, const struct description_map dm
[], unsigned n
) {
66 for (i
= 0; i
< n
; i
++)
67 if (pa_streq(dm
[i
].key
, key
))
68 return _(dm
[i
].description
);
73 struct pa_alsa_fdlist
{
76 /* This is a temporary buffer used to avoid lots of mallocs */
77 struct pollfd
*work_fds
;
83 pa_defer_event
*defer
;
88 void (*cb
)(void *userdata
);
92 static void io_cb(pa_mainloop_api
*a
, pa_io_event
*e
, int fd
, pa_io_event_flags_t events
, void *userdata
) {
94 struct pa_alsa_fdlist
*fdl
= userdata
;
97 unsigned short revents
;
101 pa_assert(fdl
->mixer
|| fdl
->hctl
);
103 pa_assert(fdl
->work_fds
);
110 memcpy(fdl
->work_fds
, fdl
->fds
, sizeof(struct pollfd
) * fdl
->num_fds
);
112 for (i
= 0; i
< fdl
->num_fds
; i
++) {
113 if (e
== fdl
->ios
[i
]) {
114 if (events
& PA_IO_EVENT_INPUT
)
115 fdl
->work_fds
[i
].revents
|= POLLIN
;
116 if (events
& PA_IO_EVENT_OUTPUT
)
117 fdl
->work_fds
[i
].revents
|= POLLOUT
;
118 if (events
& PA_IO_EVENT_ERROR
)
119 fdl
->work_fds
[i
].revents
|= POLLERR
;
120 if (events
& PA_IO_EVENT_HANGUP
)
121 fdl
->work_fds
[i
].revents
|= POLLHUP
;
126 pa_assert(i
!= fdl
->num_fds
);
129 err
= snd_hctl_poll_descriptors_revents(fdl
->hctl
, fdl
->work_fds
, fdl
->num_fds
, &revents
);
131 err
= snd_mixer_poll_descriptors_revents(fdl
->mixer
, fdl
->work_fds
, fdl
->num_fds
, &revents
);
134 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err
));
138 a
->defer_enable(fdl
->defer
, 1);
142 snd_hctl_handle_events(fdl
->hctl
);
144 snd_mixer_handle_events(fdl
->mixer
);
148 static void defer_cb(pa_mainloop_api
*a
, pa_defer_event
*e
, void *userdata
) {
149 struct pa_alsa_fdlist
*fdl
= userdata
;
156 pa_assert(fdl
->mixer
|| fdl
->hctl
);
158 a
->defer_enable(fdl
->defer
, 0);
161 n
= snd_hctl_poll_descriptors_count(fdl
->hctl
);
163 n
= snd_mixer_poll_descriptors_count(fdl
->mixer
);
166 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n
));
169 num_fds
= (unsigned) n
;
171 if (num_fds
!= fdl
->num_fds
) {
175 pa_xfree(fdl
->work_fds
);
176 fdl
->fds
= pa_xnew0(struct pollfd
, num_fds
);
177 fdl
->work_fds
= pa_xnew(struct pollfd
, num_fds
);
180 memset(fdl
->work_fds
, 0, sizeof(struct pollfd
) * num_fds
);
183 err
= snd_hctl_poll_descriptors(fdl
->hctl
, fdl
->work_fds
, num_fds
);
185 err
= snd_mixer_poll_descriptors(fdl
->mixer
, fdl
->work_fds
, num_fds
);
188 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err
));
194 if (memcmp(fdl
->fds
, fdl
->work_fds
, sizeof(struct pollfd
) * num_fds
) == 0)
198 for (i
= 0; i
< fdl
->num_fds
; i
++)
199 a
->io_free(fdl
->ios
[i
]);
201 if (num_fds
!= fdl
->num_fds
) {
208 fdl
->ios
= pa_xnew(pa_io_event
*, num_fds
);
211 temp
= fdl
->work_fds
;
212 fdl
->work_fds
= fdl
->fds
;
215 fdl
->num_fds
= num_fds
;
217 for (i
= 0;i
< num_fds
;i
++)
218 fdl
->ios
[i
] = a
->io_new(a
, fdl
->fds
[i
].fd
,
219 ((fdl
->fds
[i
].events
& POLLIN
) ? PA_IO_EVENT_INPUT
: 0) |
220 ((fdl
->fds
[i
].events
& POLLOUT
) ? PA_IO_EVENT_OUTPUT
: 0),
224 struct pa_alsa_fdlist
*pa_alsa_fdlist_new(void) {
225 struct pa_alsa_fdlist
*fdl
;
227 fdl
= pa_xnew0(struct pa_alsa_fdlist
, 1);
232 void pa_alsa_fdlist_free(struct pa_alsa_fdlist
*fdl
) {
237 fdl
->m
->defer_free(fdl
->defer
);
243 for (i
= 0; i
< fdl
->num_fds
; i
++)
244 fdl
->m
->io_free(fdl
->ios
[i
]);
251 pa_xfree(fdl
->work_fds
);
256 /* We can listen to either a snd_hctl_t or a snd_mixer_t, but not both */
257 int pa_alsa_fdlist_set_handle(struct pa_alsa_fdlist
*fdl
, snd_mixer_t
*mixer_handle
, snd_hctl_t
*hctl_handle
, pa_mainloop_api
*m
) {
259 pa_assert(hctl_handle
|| mixer_handle
);
260 pa_assert(!(hctl_handle
&& mixer_handle
));
264 fdl
->hctl
= hctl_handle
;
265 fdl
->mixer
= mixer_handle
;
267 fdl
->defer
= m
->defer_new(m
, defer_cb
, fdl
);
272 struct pa_alsa_mixer_pdata
{
274 pa_rtpoll_item
*poll_item
;
278 struct pa_alsa_mixer_pdata
*pa_alsa_mixer_pdata_new(void) {
279 struct pa_alsa_mixer_pdata
*pd
;
281 pd
= pa_xnew0(struct pa_alsa_mixer_pdata
, 1);
286 void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata
*pd
) {
290 pa_rtpoll_item_free(pd
->poll_item
);
296 static int rtpoll_work_cb(pa_rtpoll_item
*i
) {
297 struct pa_alsa_mixer_pdata
*pd
;
300 unsigned short revents
= 0;
303 pd
= pa_rtpoll_item_get_userdata(i
);
305 pa_assert_fp(i
== pd
->poll_item
);
307 p
= pa_rtpoll_item_get_pollfd(i
, &n_fds
);
309 if ((err
= snd_mixer_poll_descriptors_revents(pd
->mixer
, p
, n_fds
, &revents
)) < 0) {
310 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err
));
316 if (revents
& (POLLNVAL
| POLLERR
)) {
317 pa_log_debug("Device disconnected, stopping poll on mixer");
319 } else if (revents
& POLLERR
) {
320 /* This shouldn't happen. */
321 pa_log_error("Got a POLLERR (revents = %04x), stopping poll on mixer", revents
);
325 err
= snd_mixer_handle_events(pd
->mixer
);
327 if (PA_LIKELY(err
>= 0)) {
328 pa_rtpoll_item_free(i
);
329 pa_alsa_set_mixer_rtpoll(pd
, pd
->mixer
, pd
->rtpoll
);
331 pa_log_error("Error handling mixer event: %s", pa_alsa_strerror(err
));
340 pa_rtpoll_item_free(i
);
342 pd
->poll_item
= NULL
;
349 int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata
*pd
, snd_mixer_t
*mixer
, pa_rtpoll
*rtp
) {
358 if ((n
= snd_mixer_poll_descriptors_count(mixer
)) < 0) {
359 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n
));
363 i
= pa_rtpoll_item_new(rtp
, PA_RTPOLL_LATE
, (unsigned) n
);
365 p
= pa_rtpoll_item_get_pollfd(i
, NULL
);
367 memset(p
, 0, sizeof(struct pollfd
) * n
);
369 if ((err
= snd_mixer_poll_descriptors(mixer
, p
, (unsigned) n
)) < 0) {
370 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err
));
371 pa_rtpoll_item_free(i
);
379 pa_rtpoll_item_set_userdata(i
, pd
);
380 pa_rtpoll_item_set_work_callback(i
, rtpoll_work_cb
);
385 static const snd_mixer_selem_channel_id_t alsa_channel_ids
[PA_CHANNEL_POSITION_MAX
] = {
386 [PA_CHANNEL_POSITION_MONO
] = SND_MIXER_SCHN_MONO
, /* The ALSA name is just an alias! */
388 [PA_CHANNEL_POSITION_FRONT_CENTER
] = SND_MIXER_SCHN_FRONT_CENTER
,
389 [PA_CHANNEL_POSITION_FRONT_LEFT
] = SND_MIXER_SCHN_FRONT_LEFT
,
390 [PA_CHANNEL_POSITION_FRONT_RIGHT
] = SND_MIXER_SCHN_FRONT_RIGHT
,
392 [PA_CHANNEL_POSITION_REAR_CENTER
] = SND_MIXER_SCHN_REAR_CENTER
,
393 [PA_CHANNEL_POSITION_REAR_LEFT
] = SND_MIXER_SCHN_REAR_LEFT
,
394 [PA_CHANNEL_POSITION_REAR_RIGHT
] = SND_MIXER_SCHN_REAR_RIGHT
,
396 [PA_CHANNEL_POSITION_LFE
] = SND_MIXER_SCHN_WOOFER
,
398 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
399 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
401 [PA_CHANNEL_POSITION_SIDE_LEFT
] = SND_MIXER_SCHN_SIDE_LEFT
,
402 [PA_CHANNEL_POSITION_SIDE_RIGHT
] = SND_MIXER_SCHN_SIDE_RIGHT
,
404 [PA_CHANNEL_POSITION_AUX0
] = SND_MIXER_SCHN_UNKNOWN
,
405 [PA_CHANNEL_POSITION_AUX1
] = SND_MIXER_SCHN_UNKNOWN
,
406 [PA_CHANNEL_POSITION_AUX2
] = SND_MIXER_SCHN_UNKNOWN
,
407 [PA_CHANNEL_POSITION_AUX3
] = SND_MIXER_SCHN_UNKNOWN
,
408 [PA_CHANNEL_POSITION_AUX4
] = SND_MIXER_SCHN_UNKNOWN
,
409 [PA_CHANNEL_POSITION_AUX5
] = SND_MIXER_SCHN_UNKNOWN
,
410 [PA_CHANNEL_POSITION_AUX6
] = SND_MIXER_SCHN_UNKNOWN
,
411 [PA_CHANNEL_POSITION_AUX7
] = SND_MIXER_SCHN_UNKNOWN
,
412 [PA_CHANNEL_POSITION_AUX8
] = SND_MIXER_SCHN_UNKNOWN
,
413 [PA_CHANNEL_POSITION_AUX9
] = SND_MIXER_SCHN_UNKNOWN
,
414 [PA_CHANNEL_POSITION_AUX10
] = SND_MIXER_SCHN_UNKNOWN
,
415 [PA_CHANNEL_POSITION_AUX11
] = SND_MIXER_SCHN_UNKNOWN
,
416 [PA_CHANNEL_POSITION_AUX12
] = SND_MIXER_SCHN_UNKNOWN
,
417 [PA_CHANNEL_POSITION_AUX13
] = SND_MIXER_SCHN_UNKNOWN
,
418 [PA_CHANNEL_POSITION_AUX14
] = SND_MIXER_SCHN_UNKNOWN
,
419 [PA_CHANNEL_POSITION_AUX15
] = SND_MIXER_SCHN_UNKNOWN
,
420 [PA_CHANNEL_POSITION_AUX16
] = SND_MIXER_SCHN_UNKNOWN
,
421 [PA_CHANNEL_POSITION_AUX17
] = SND_MIXER_SCHN_UNKNOWN
,
422 [PA_CHANNEL_POSITION_AUX18
] = SND_MIXER_SCHN_UNKNOWN
,
423 [PA_CHANNEL_POSITION_AUX19
] = SND_MIXER_SCHN_UNKNOWN
,
424 [PA_CHANNEL_POSITION_AUX20
] = SND_MIXER_SCHN_UNKNOWN
,
425 [PA_CHANNEL_POSITION_AUX21
] = SND_MIXER_SCHN_UNKNOWN
,
426 [PA_CHANNEL_POSITION_AUX22
] = SND_MIXER_SCHN_UNKNOWN
,
427 [PA_CHANNEL_POSITION_AUX23
] = SND_MIXER_SCHN_UNKNOWN
,
428 [PA_CHANNEL_POSITION_AUX24
] = SND_MIXER_SCHN_UNKNOWN
,
429 [PA_CHANNEL_POSITION_AUX25
] = SND_MIXER_SCHN_UNKNOWN
,
430 [PA_CHANNEL_POSITION_AUX26
] = SND_MIXER_SCHN_UNKNOWN
,
431 [PA_CHANNEL_POSITION_AUX27
] = SND_MIXER_SCHN_UNKNOWN
,
432 [PA_CHANNEL_POSITION_AUX28
] = SND_MIXER_SCHN_UNKNOWN
,
433 [PA_CHANNEL_POSITION_AUX29
] = SND_MIXER_SCHN_UNKNOWN
,
434 [PA_CHANNEL_POSITION_AUX30
] = SND_MIXER_SCHN_UNKNOWN
,
435 [PA_CHANNEL_POSITION_AUX31
] = SND_MIXER_SCHN_UNKNOWN
,
437 [PA_CHANNEL_POSITION_TOP_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
439 [PA_CHANNEL_POSITION_TOP_FRONT_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
440 [PA_CHANNEL_POSITION_TOP_FRONT_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
441 [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
,
443 [PA_CHANNEL_POSITION_TOP_REAR_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
444 [PA_CHANNEL_POSITION_TOP_REAR_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
445 [PA_CHANNEL_POSITION_TOP_REAR_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
448 static void setting_free(pa_alsa_setting
*s
) {
452 pa_idxset_free(s
->options
, NULL
);
455 pa_xfree(s
->description
);
459 static void option_free(pa_alsa_option
*o
) {
462 pa_xfree(o
->alsa_name
);
464 pa_xfree(o
->description
);
468 static void decibel_fix_free(pa_alsa_decibel_fix
*db_fix
) {
471 pa_xfree(db_fix
->name
);
472 pa_xfree(db_fix
->db_values
);
477 static void jack_free(pa_alsa_jack
*j
) {
480 pa_xfree(j
->alsa_name
);
485 static void element_free(pa_alsa_element
*e
) {
489 while ((o
= e
->options
)) {
490 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
495 decibel_fix_free(e
->db_fix
);
497 pa_xfree(e
->alsa_name
);
501 void pa_alsa_path_free(pa_alsa_path
*p
) {
508 while ((j
= p
->jacks
)) {
509 PA_LLIST_REMOVE(pa_alsa_jack
, p
->jacks
, j
);
513 while ((e
= p
->elements
)) {
514 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
518 while ((s
= p
->settings
)) {
519 PA_LLIST_REMOVE(pa_alsa_setting
, p
->settings
, s
);
523 pa_proplist_free(p
->proplist
);
525 pa_xfree(p
->description
);
526 pa_xfree(p
->description_key
);
530 void pa_alsa_path_set_free(pa_alsa_path_set
*ps
) {
534 pa_hashmap_free(ps
->paths
);
539 static long to_alsa_dB(pa_volume_t v
) {
540 return (long) (pa_sw_volume_to_dB(v
) * 100.0);
543 static pa_volume_t
from_alsa_dB(long v
) {
544 return pa_sw_volume_from_dB((double) v
/ 100.0);
547 static long to_alsa_volume(pa_volume_t v
, long min
, long max
) {
550 w
= (long) round(((double) v
* (double) (max
- min
)) / PA_VOLUME_NORM
) + min
;
551 return PA_CLAMP_UNLIKELY(w
, min
, max
);
554 static pa_volume_t
from_alsa_volume(long v
, long min
, long max
) {
555 return (pa_volume_t
) round(((double) (v
- min
) * PA_VOLUME_NORM
) / (double) (max
- min
));
558 #define SELEM_INIT(sid, name) \
560 snd_mixer_selem_id_alloca(&(sid)); \
561 snd_mixer_selem_id_set_name((sid), (name)); \
562 snd_mixer_selem_id_set_index((sid), 0); \
565 static int element_get_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
566 snd_mixer_selem_id_t
*sid
;
567 snd_mixer_elem_t
*me
;
568 snd_mixer_selem_channel_id_t c
;
569 pa_channel_position_mask_t mask
= 0;
577 SELEM_INIT(sid
, e
->alsa_name
);
578 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
579 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
583 pa_cvolume_mute(v
, cm
->channels
);
585 /* We take the highest volume of all channels that match */
587 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
594 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
595 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
597 if ((r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
)) >= 0) {
598 /* If the channel volume is outside the limits set
599 * by the dB fix, we clamp the hw volume to be
600 * within the limits. */
601 if (value
< e
->db_fix
->min_step
) {
602 value
= e
->db_fix
->min_step
;
603 snd_mixer_selem_set_playback_volume(me
, c
, value
);
604 pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
605 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
606 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
607 } else if (value
> e
->db_fix
->max_step
) {
608 value
= e
->db_fix
->max_step
;
609 snd_mixer_selem_set_playback_volume(me
, c
, value
);
610 pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
611 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
612 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
615 /* Volume step -> dB value conversion. */
616 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
619 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
623 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
625 if ((r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
)) >= 0) {
626 /* If the channel volume is outside the limits set
627 * by the dB fix, we clamp the hw volume to be
628 * within the limits. */
629 if (value
< e
->db_fix
->min_step
) {
630 value
= e
->db_fix
->min_step
;
631 snd_mixer_selem_set_capture_volume(me
, c
, value
);
632 pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
633 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
634 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
635 } else if (value
> e
->db_fix
->max_step
) {
636 value
= e
->db_fix
->max_step
;
637 snd_mixer_selem_set_capture_volume(me
, c
, value
);
638 pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
639 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
640 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
643 /* Volume step -> dB value conversion. */
644 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
647 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
655 #ifdef HAVE_VALGRIND_MEMCHECK_H
656 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
659 f
= from_alsa_dB(value
);
664 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
665 if (snd_mixer_selem_has_playback_channel(me
, c
))
666 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
670 if (snd_mixer_selem_has_capture_channel(me
, c
))
671 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
679 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
682 for (k
= 0; k
< cm
->channels
; k
++)
683 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
684 if (v
->values
[k
] < f
)
687 mask
|= e
->masks
[c
][e
->n_channels
-1];
690 for (k
= 0; k
< cm
->channels
; k
++)
691 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
692 v
->values
[k
] = PA_VOLUME_NORM
;
697 int pa_alsa_path_get_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
708 pa_cvolume_reset(v
, cm
->channels
);
710 PA_LLIST_FOREACH(e
, p
->elements
) {
713 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
716 pa_assert(!p
->has_dB
|| e
->has_dB
);
718 if (element_get_volume(e
, m
, cm
, &ev
) < 0)
721 /* If we have no dB information all we can do is take the first element and leave */
727 pa_sw_cvolume_multiply(v
, v
, &ev
);
733 static int element_get_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, bool *b
) {
734 snd_mixer_selem_id_t
*sid
;
735 snd_mixer_elem_t
*me
;
736 snd_mixer_selem_channel_id_t c
;
742 SELEM_INIT(sid
, e
->alsa_name
);
743 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
744 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
748 /* We return muted if at least one channel is muted */
750 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
754 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
755 if (snd_mixer_selem_has_playback_channel(me
, c
))
756 r
= snd_mixer_selem_get_playback_switch(me
, c
, &value
);
760 if (snd_mixer_selem_has_capture_channel(me
, c
))
761 r
= snd_mixer_selem_get_capture_switch(me
, c
, &value
);
779 int pa_alsa_path_get_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, bool *muted
) {
789 PA_LLIST_FOREACH(e
, p
->elements
) {
792 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
795 if (element_get_switch(e
, m
, &b
) < 0)
808 /* Finds the closest item in db_fix->db_values and returns the corresponding
809 * step. *db_value is replaced with the value from the db_values table.
810 * Rounding is done based on the rounding parameter: -1 means rounding down and
811 * +1 means rounding up. */
812 static long decibel_fix_get_step(pa_alsa_decibel_fix
*db_fix
, long *db_value
, int rounding
) {
818 pa_assert(rounding
!= 0);
820 max_i
= db_fix
->max_step
- db_fix
->min_step
;
823 for (i
= 0; i
< max_i
; i
++) {
824 if (db_fix
->db_values
[i
] >= *db_value
)
828 for (i
= 0; i
< max_i
; i
++) {
829 if (db_fix
->db_values
[i
+ 1] > *db_value
)
834 *db_value
= db_fix
->db_values
[i
];
836 return i
+ db_fix
->min_step
;
839 /* Alsa lib documentation says for snd_mixer_selem_set_playback_dB() direction argument,
840 * that "-1 = accurate or first below, 0 = accurate, 1 = accurate or first above".
841 * But even with accurate nearest dB volume step is not selected, so that is why we need
842 * this function. Returns 0 and nearest selectable volume in *value_dB on success or
843 * negative error code if fails. */
844 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
) {
854 if (d
== PA_ALSA_DIRECTION_OUTPUT
) {
855 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, *value_dB
, +1, &alsa_val
)) >= 0)
856 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value_high
);
861 if (value_high
== *value_dB
)
864 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, *value_dB
, -1, &alsa_val
)) >= 0)
865 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value_low
);
867 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, *value_dB
, +1, &alsa_val
)) >= 0)
868 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value_high
);
873 if (value_high
== *value_dB
)
876 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, *value_dB
, -1, &alsa_val
)) >= 0)
877 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value_low
);
883 if (labs(value_high
- *value_dB
) < labs(value_low
- *value_dB
))
884 *value_dB
= value_high
;
886 *value_dB
= value_low
;
891 static int element_set_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
, bool deferred_volume
, bool write_to_hw
) {
893 snd_mixer_selem_id_t
*sid
;
895 snd_mixer_elem_t
*me
;
896 snd_mixer_selem_channel_id_t c
;
897 pa_channel_position_mask_t mask
= 0;
904 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
906 SELEM_INIT(sid
, e
->alsa_name
);
907 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
908 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
912 pa_cvolume_mute(&rv
, cm
->channels
);
914 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
916 pa_volume_t f
= PA_VOLUME_MUTED
;
919 for (k
= 0; k
< cm
->channels
; k
++)
920 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
])) {
922 if (v
->values
[k
] > f
)
927 /* Hmm, so this channel does not exist in the volume
928 * struct, so let's bind it to the overall max of the
930 f
= pa_cvolume_max(v
);
934 long value
= to_alsa_dB(f
);
937 if (e
->volume_limit
>= 0 && value
> (e
->max_dB
* 100))
938 value
= e
->max_dB
* 100;
940 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
941 /* If we call set_playback_volume() without checking first
942 * if the channel is available, ALSA behaves very
943 * strangely and doesn't fail the call */
944 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
948 r
= snd_mixer_selem_set_playback_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
950 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
956 if (deferred_volume
) {
957 if ((r
= element_get_nearest_alsa_dB(me
, c
, PA_ALSA_DIRECTION_OUTPUT
, &value
)) >= 0)
958 r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, 0);
960 if ((r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, rounding
)) >= 0)
961 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
965 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
966 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value
);
972 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
976 r
= snd_mixer_selem_set_capture_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
978 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
984 if (deferred_volume
) {
985 if ((r
= element_get_nearest_alsa_dB(me
, c
, PA_ALSA_DIRECTION_INPUT
, &value
)) >= 0)
986 r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, 0);
988 if ((r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, rounding
)) >= 0)
989 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
993 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
994 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value
);
1004 #ifdef HAVE_VALGRIND_MEMCHECK_H
1005 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
1008 f
= from_alsa_dB(value
);
1013 value
= to_alsa_volume(f
, e
->min_volume
, e
->max_volume
);
1015 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1016 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
1017 if ((r
= snd_mixer_selem_set_playback_volume(me
, c
, value
)) >= 0)
1018 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
1022 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
1023 if ((r
= snd_mixer_selem_set_capture_volume(me
, c
, value
)) >= 0)
1024 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
1032 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
1035 for (k
= 0; k
< cm
->channels
; k
++)
1036 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
1037 if (rv
.values
[k
] < f
)
1040 mask
|= e
->masks
[c
][e
->n_channels
-1];
1043 for (k
= 0; k
< cm
->channels
; k
++)
1044 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
1045 rv
.values
[k
] = PA_VOLUME_NORM
;
1051 int pa_alsa_path_set_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
, bool deferred_volume
, bool write_to_hw
) {
1060 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
1065 rv
= *v
; /* Remaining adjustment */
1066 pa_cvolume_reset(v
, cm
->channels
); /* Adjustment done */
1068 PA_LLIST_FOREACH(e
, p
->elements
) {
1071 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
1074 pa_assert(!p
->has_dB
|| e
->has_dB
);
1077 if (element_set_volume(e
, m
, cm
, &ev
, deferred_volume
, write_to_hw
) < 0)
1085 pa_sw_cvolume_multiply(v
, v
, &ev
);
1086 pa_sw_cvolume_divide(&rv
, &rv
, &ev
);
1092 static int element_set_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, bool b
) {
1093 snd_mixer_elem_t
*me
;
1094 snd_mixer_selem_id_t
*sid
;
1100 SELEM_INIT(sid
, e
->alsa_name
);
1101 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1102 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1106 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1107 r
= snd_mixer_selem_set_playback_switch_all(me
, b
);
1109 r
= snd_mixer_selem_set_capture_switch_all(me
, b
);
1112 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1117 int pa_alsa_path_set_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, bool muted
) {
1126 PA_LLIST_FOREACH(e
, p
->elements
) {
1128 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
1131 if (element_set_switch(e
, m
, !muted
) < 0)
1138 /* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
1139 * function sets all channels of the volume element to e->min_volume, 0 dB or
1140 * e->constant_volume. */
1141 static int element_set_constant_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1142 snd_mixer_elem_t
*me
= NULL
;
1143 snd_mixer_selem_id_t
*sid
= NULL
;
1146 bool volume_set
= false;
1151 SELEM_INIT(sid
, e
->alsa_name
);
1152 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1153 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1157 switch (e
->volume_use
) {
1158 case PA_ALSA_VOLUME_OFF
:
1159 volume
= e
->min_volume
;
1163 case PA_ALSA_VOLUME_ZERO
:
1167 volume
= decibel_fix_get_step(e
->db_fix
, &dB
, (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
? +1 : -1));
1172 case PA_ALSA_VOLUME_CONSTANT
:
1173 volume
= e
->constant_volume
;
1178 pa_assert_not_reached();
1182 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1183 r
= snd_mixer_selem_set_playback_volume_all(me
, volume
);
1185 r
= snd_mixer_selem_set_capture_volume_all(me
, volume
);
1187 pa_assert(e
->volume_use
== PA_ALSA_VOLUME_ZERO
);
1188 pa_assert(!e
->db_fix
);
1190 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1191 r
= snd_mixer_selem_set_playback_dB_all(me
, 0, +1);
1193 r
= snd_mixer_selem_set_capture_dB_all(me
, 0, -1);
1197 pa_log_warn("Failed to set volume of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1202 int pa_alsa_path_select(pa_alsa_path
*p
, pa_alsa_setting
*s
, snd_mixer_t
*m
, bool device_is_muted
) {
1209 pa_log_debug("Activating path %s", p
->name
);
1210 pa_alsa_path_dump(p
);
1212 /* First turn on hw mute if available, to avoid noise
1213 * when setting the mixer controls. */
1214 if (p
->mute_during_activation
) {
1215 PA_LLIST_FOREACH(e
, p
->elements
) {
1216 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
1217 /* If the muting fails here, that's not a critical problem for
1218 * selecting a path, so we ignore the return value.
1219 * element_set_switch() will print a warning anyway, so this
1220 * won't be a silent failure either. */
1221 (void) element_set_switch(e
, m
, false);
1225 PA_LLIST_FOREACH(e
, p
->elements
) {
1227 switch (e
->switch_use
) {
1228 case PA_ALSA_SWITCH_OFF
:
1229 r
= element_set_switch(e
, m
, false);
1232 case PA_ALSA_SWITCH_ON
:
1233 r
= element_set_switch(e
, m
, true);
1236 case PA_ALSA_SWITCH_MUTE
:
1237 case PA_ALSA_SWITCH_IGNORE
:
1238 case PA_ALSA_SWITCH_SELECT
:
1246 switch (e
->volume_use
) {
1247 case PA_ALSA_VOLUME_OFF
:
1248 case PA_ALSA_VOLUME_ZERO
:
1249 case PA_ALSA_VOLUME_CONSTANT
:
1250 r
= element_set_constant_volume(e
, m
);
1253 case PA_ALSA_VOLUME_MERGE
:
1254 case PA_ALSA_VOLUME_IGNORE
:
1264 setting_select(s
, m
);
1266 /* Finally restore hw mute to the device mute status. */
1267 if (p
->mute_during_activation
) {
1268 PA_LLIST_FOREACH(e
, p
->elements
) {
1269 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
) {
1270 if (element_set_switch(e
, m
, !device_is_muted
) < 0)
1279 static int check_required(pa_alsa_element
*e
, snd_mixer_elem_t
*me
) {
1281 bool has_enumeration
;
1287 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1289 snd_mixer_selem_has_playback_switch(me
) ||
1290 (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
));
1293 snd_mixer_selem_has_capture_switch(me
) ||
1294 (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
));
1297 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1299 snd_mixer_selem_has_playback_volume(me
) ||
1300 (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
));
1303 snd_mixer_selem_has_capture_volume(me
) ||
1304 (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
));
1307 has_enumeration
= snd_mixer_selem_is_enumerated(me
);
1309 if ((e
->required
== PA_ALSA_REQUIRED_SWITCH
&& !has_switch
) ||
1310 (e
->required
== PA_ALSA_REQUIRED_VOLUME
&& !has_volume
) ||
1311 (e
->required
== PA_ALSA_REQUIRED_ENUMERATION
&& !has_enumeration
))
1314 if (e
->required
== PA_ALSA_REQUIRED_ANY
&& !(has_switch
|| has_volume
|| has_enumeration
))
1317 if ((e
->required_absent
== PA_ALSA_REQUIRED_SWITCH
&& has_switch
) ||
1318 (e
->required_absent
== PA_ALSA_REQUIRED_VOLUME
&& has_volume
) ||
1319 (e
->required_absent
== PA_ALSA_REQUIRED_ENUMERATION
&& has_enumeration
))
1322 if (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& (has_switch
|| has_volume
|| has_enumeration
))
1325 if (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) {
1326 switch (e
->required_any
) {
1327 case PA_ALSA_REQUIRED_VOLUME
:
1328 e
->path
->req_any_present
|= (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
);
1330 case PA_ALSA_REQUIRED_SWITCH
:
1331 e
->path
->req_any_present
|= (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
);
1333 case PA_ALSA_REQUIRED_ENUMERATION
:
1334 e
->path
->req_any_present
|= (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1336 case PA_ALSA_REQUIRED_ANY
:
1337 e
->path
->req_any_present
|=
1338 (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) ||
1339 (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) ||
1340 (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1343 pa_assert_not_reached();
1347 if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1349 PA_LLIST_FOREACH(o
, e
->options
) {
1350 e
->path
->req_any_present
|= (o
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) &&
1352 if (o
->required
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
< 0)
1354 if (o
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
>= 0)
1362 static int element_probe(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1363 snd_mixer_selem_id_t
*sid
;
1364 snd_mixer_elem_t
*me
;
1370 SELEM_INIT(sid
, e
->alsa_name
);
1372 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1374 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1377 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1378 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1379 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1384 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1385 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1387 if (!snd_mixer_selem_has_playback_switch(me
)) {
1388 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1389 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1391 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1396 if (!snd_mixer_selem_has_capture_switch(me
)) {
1397 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1398 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1400 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1404 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1405 e
->direction_try_other
= false;
1408 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1410 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1412 if (!snd_mixer_selem_has_playback_volume(me
)) {
1413 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1414 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1416 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1421 if (!snd_mixer_selem_has_capture_volume(me
)) {
1422 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1423 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1425 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1429 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1430 long min_dB
= 0, max_dB
= 0;
1433 e
->direction_try_other
= false;
1435 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1436 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1438 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1441 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1445 if (e
->min_volume
>= e
->max_volume
) {
1446 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
);
1447 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1449 } else if (e
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&&
1450 (e
->min_volume
> e
->constant_volume
|| e
->max_volume
< e
->constant_volume
)) {
1451 pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
1452 e
->constant_volume
, e
->alsa_name
, e
->min_volume
, e
->max_volume
);
1453 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1457 pa_channel_position_t p
;
1460 ((e
->min_volume
> e
->db_fix
->min_step
) ||
1461 (e
->max_volume
< e
->db_fix
->max_step
))) {
1462 pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1463 "real hardware range (%li-%li). Disabling the decibel fix.", e
->alsa_name
,
1464 e
->db_fix
->min_step
, e
->db_fix
->max_step
,
1465 e
->min_volume
, e
->max_volume
);
1467 decibel_fix_free(e
->db_fix
);
1473 e
->min_volume
= e
->db_fix
->min_step
;
1474 e
->max_volume
= e
->db_fix
->max_step
;
1475 min_dB
= e
->db_fix
->db_values
[0];
1476 max_dB
= e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
];
1477 } else if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1478 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1480 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1482 /* Check that the kernel driver returns consistent limits with
1483 * both _get_*_dB_range() and _ask_*_vol_dB(). */
1484 if (e
->has_dB
&& !e
->db_fix
) {
1485 long min_dB_checked
= 0;
1486 long max_dB_checked
= 0;
1488 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1489 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1491 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1494 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->min_volume
);
1498 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1499 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1501 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1504 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->max_volume
);
1508 if (min_dB
!= min_dB_checked
|| max_dB
!= max_dB_checked
) {
1509 pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
1510 "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
1511 "%0.2f dB at level %li.",
1513 min_dB
/ 100.0, max_dB
/ 100.0,
1514 min_dB_checked
/ 100.0, e
->min_volume
, max_dB_checked
/ 100.0, e
->max_volume
);
1520 #ifdef HAVE_VALGRIND_MEMCHECK_H
1521 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1522 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1525 e
->min_dB
= ((double) min_dB
) / 100.0;
1526 e
->max_dB
= ((double) max_dB
) / 100.0;
1528 if (min_dB
>= max_dB
) {
1529 pa_assert(!e
->db_fix
);
1530 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
);
1535 if (e
->volume_limit
>= 0) {
1536 if (e
->volume_limit
<= e
->min_volume
|| e
->volume_limit
> e
->max_volume
)
1537 pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1538 "%li-%li. The volume limit is ignored.",
1539 e
->alsa_name
, e
->path
->name
, e
->volume_limit
, e
->min_volume
+ 1, e
->max_volume
);
1542 e
->max_volume
= e
->volume_limit
;
1546 e
->db_fix
->max_step
= e
->max_volume
;
1547 e
->max_dB
= ((double) e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
]) / 100.0;
1550 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1551 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB
);
1553 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB
);
1556 pa_log_warn("Failed to get dB value of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1559 e
->max_dB
= ((double) max_dB
) / 100.0;
1565 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1566 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1568 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1573 if (!e
->override_map
) {
1574 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1575 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1578 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1581 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1584 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1587 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1589 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1592 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1593 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1595 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1598 if (e
->n_channels
<= 0) {
1599 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1603 if (e
->n_channels
> 2) {
1604 /* FIXME: In some places code like this is used:
1606 * e->masks[alsa_channel_ids[p]][e->n_channels-1]
1608 * The definition of e->masks is
1610 * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][2];
1612 * Since the array size is fixed at 2, we obviously
1613 * don't support elements with more than two
1615 pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e
->alsa_name
, e
->n_channels
);
1619 if (!e
->override_map
) {
1620 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1623 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1626 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1627 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1629 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1631 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1636 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1637 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1640 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1648 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1651 PA_LLIST_FOREACH(o
, e
->options
)
1652 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1653 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1657 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1658 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1662 PA_LLIST_FOREACH(o
, e
->options
) {
1665 for (i
= 0; i
< n
; i
++) {
1668 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1671 if (!pa_streq(buf
, o
->alsa_name
))
1679 if (check_required(e
, me
) < 0)
1685 static int jack_probe(pa_alsa_jack
*j
, snd_hctl_t
*h
) {
1690 j
->has_control
= pa_alsa_find_jack(h
, j
->alsa_name
) != NULL
;
1692 if (j
->has_control
) {
1693 if (j
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
)
1695 if (j
->required_any
!= PA_ALSA_REQUIRED_IGNORE
)
1696 j
->path
->req_any_present
= true;
1698 if (j
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1705 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, bool prefixed
) {
1712 if (!pa_startswith(section
, "Element "))
1718 /* This is not an element section, but an enum section? */
1719 if (strchr(section
, ':'))
1722 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1723 return p
->last_element
;
1725 PA_LLIST_FOREACH(e
, p
->elements
)
1726 if (pa_streq(e
->alsa_name
, section
))
1729 e
= pa_xnew0(pa_alsa_element
, 1);
1731 e
->alsa_name
= pa_xstrdup(section
);
1732 e
->direction
= p
->direction
;
1733 e
->volume_limit
= -1;
1735 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1738 p
->last_element
= e
;
1742 static pa_alsa_jack
* jack_get(pa_alsa_path
*p
, const char *section
) {
1745 if (!pa_startswith(section
, "Jack "))
1749 if (p
->last_jack
&& pa_streq(p
->last_jack
->name
, section
))
1750 return p
->last_jack
;
1752 PA_LLIST_FOREACH(j
, p
->jacks
)
1753 if (pa_streq(j
->name
, section
))
1756 j
= pa_xnew0(pa_alsa_jack
, 1);
1757 j
->state_unplugged
= PA_AVAILABLE_NO
;
1758 j
->state_plugged
= PA_AVAILABLE_YES
;
1760 j
->name
= pa_xstrdup(section
);
1761 j
->alsa_name
= pa_sprintf_malloc("%s Jack", section
);
1762 PA_LLIST_INSERT_AFTER(pa_alsa_jack
, p
->jacks
, p
->last_jack
, j
);
1769 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1775 if (!pa_startswith(section
, "Option "))
1780 /* This is not an enum section, but an element section? */
1781 if (!(on
= strchr(section
, ':')))
1784 en
= pa_xstrndup(section
, on
- section
);
1787 if (p
->last_option
&&
1788 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1789 pa_streq(p
->last_option
->alsa_name
, on
)) {
1791 return p
->last_option
;
1794 pa_assert_se(e
= element_get(p
, en
, false));
1797 PA_LLIST_FOREACH(o
, e
->options
)
1798 if (pa_streq(o
->alsa_name
, on
))
1801 o
= pa_xnew0(pa_alsa_option
, 1);
1803 o
->alsa_name
= pa_xstrdup(on
);
1806 if (p
->last_option
&& p
->last_option
->element
== e
)
1807 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1809 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1816 static int element_parse_switch(pa_config_parser_state
*state
) {
1822 p
= state
->userdata
;
1824 if (!(e
= element_get(p
, state
->section
, true))) {
1825 pa_log("[%s:%u] Switch makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1829 if (pa_streq(state
->rvalue
, "ignore"))
1830 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1831 else if (pa_streq(state
->rvalue
, "mute"))
1832 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1833 else if (pa_streq(state
->rvalue
, "off"))
1834 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1835 else if (pa_streq(state
->rvalue
, "on"))
1836 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1837 else if (pa_streq(state
->rvalue
, "select"))
1838 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1840 pa_log("[%s:%u] Switch invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1847 static int element_parse_volume(pa_config_parser_state
*state
) {
1853 p
= state
->userdata
;
1855 if (!(e
= element_get(p
, state
->section
, true))) {
1856 pa_log("[%s:%u] Volume makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1860 if (pa_streq(state
->rvalue
, "ignore"))
1861 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1862 else if (pa_streq(state
->rvalue
, "merge"))
1863 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1864 else if (pa_streq(state
->rvalue
, "off"))
1865 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1866 else if (pa_streq(state
->rvalue
, "zero"))
1867 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1871 if (pa_atou(state
->rvalue
, &constant
) >= 0) {
1872 e
->volume_use
= PA_ALSA_VOLUME_CONSTANT
;
1873 e
->constant_volume
= constant
;
1875 pa_log("[%s:%u] Volume invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1883 static int element_parse_enumeration(pa_config_parser_state
*state
) {
1889 p
= state
->userdata
;
1891 if (!(e
= element_get(p
, state
->section
, true))) {
1892 pa_log("[%s:%u] Enumeration makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1896 if (pa_streq(state
->rvalue
, "ignore"))
1897 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1898 else if (pa_streq(state
->rvalue
, "select"))
1899 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1901 pa_log("[%s:%u] Enumeration invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1908 static int option_parse_priority(pa_config_parser_state
*state
) {
1915 p
= state
->userdata
;
1917 if (!(o
= option_get(p
, state
->section
))) {
1918 pa_log("[%s:%u] Priority makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1922 if (pa_atou(state
->rvalue
, &prio
) < 0) {
1923 pa_log("[%s:%u] Priority invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1931 static int option_parse_name(pa_config_parser_state
*state
) {
1937 p
= state
->userdata
;
1939 if (!(o
= option_get(p
, state
->section
))) {
1940 pa_log("[%s:%u] Name makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1945 o
->name
= pa_xstrdup(state
->rvalue
);
1950 static int element_parse_required(pa_config_parser_state
*state
) {
1955 pa_alsa_required_t req
;
1959 p
= state
->userdata
;
1961 e
= element_get(p
, state
->section
, true);
1962 o
= option_get(p
, state
->section
);
1963 j
= jack_get(p
, state
->section
);
1964 if (!e
&& !o
&& !j
) {
1965 pa_log("[%s:%u] Required makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
1969 if (pa_streq(state
->rvalue
, "ignore"))
1970 req
= PA_ALSA_REQUIRED_IGNORE
;
1971 else if (pa_streq(state
->rvalue
, "switch") && e
)
1972 req
= PA_ALSA_REQUIRED_SWITCH
;
1973 else if (pa_streq(state
->rvalue
, "volume") && e
)
1974 req
= PA_ALSA_REQUIRED_VOLUME
;
1975 else if (pa_streq(state
->rvalue
, "enumeration"))
1976 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1977 else if (pa_streq(state
->rvalue
, "any"))
1978 req
= PA_ALSA_REQUIRED_ANY
;
1980 pa_log("[%s:%u] Required invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
1984 if (pa_streq(state
->lvalue
, "required-absent")) {
1986 e
->required_absent
= req
;
1988 o
->required_absent
= req
;
1990 j
->required_absent
= req
;
1992 else if (pa_streq(state
->lvalue
, "required-any")) {
1994 e
->required_any
= req
;
1995 e
->path
->has_req_any
|= (req
!= PA_ALSA_REQUIRED_IGNORE
);
1998 o
->required_any
= req
;
1999 o
->element
->path
->has_req_any
|= (req
!= PA_ALSA_REQUIRED_IGNORE
);
2002 j
->required_any
= req
;
2003 j
->path
->has_req_any
|= (req
!= PA_ALSA_REQUIRED_IGNORE
);
2019 static int element_parse_direction(pa_config_parser_state
*state
) {
2025 p
= state
->userdata
;
2027 if (!(e
= element_get(p
, state
->section
, true))) {
2028 pa_log("[%s:%u] Direction makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2032 if (pa_streq(state
->rvalue
, "playback"))
2033 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
2034 else if (pa_streq(state
->rvalue
, "capture"))
2035 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
2037 pa_log("[%s:%u] Direction invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
2044 static int element_parse_direction_try_other(pa_config_parser_state
*state
) {
2051 p
= state
->userdata
;
2053 if (!(e
= element_get(p
, state
->section
, true))) {
2054 pa_log("[%s:%u] Direction makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2058 if ((yes
= pa_parse_boolean(state
->rvalue
)) < 0) {
2059 pa_log("[%s:%u] Direction invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
2063 e
->direction_try_other
= !!yes
;
2067 static int element_parse_volume_limit(pa_config_parser_state
*state
) {
2074 p
= state
->userdata
;
2076 if (!(e
= element_get(p
, state
->section
, true))) {
2077 pa_log("[%s:%u] volume-limit makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2081 if (pa_atol(state
->rvalue
, &volume_limit
) < 0 || volume_limit
< 0) {
2082 pa_log("[%s:%u] Invalid value for volume-limit", state
->filename
, state
->lineno
);
2086 e
->volume_limit
= volume_limit
;
2090 static pa_channel_position_mask_t
parse_mask(const char *m
) {
2091 pa_channel_position_mask_t v
;
2093 if (pa_streq(m
, "all-left"))
2094 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
2095 else if (pa_streq(m
, "all-right"))
2096 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
2097 else if (pa_streq(m
, "all-center"))
2098 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
2099 else if (pa_streq(m
, "all-front"))
2100 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
2101 else if (pa_streq(m
, "all-rear"))
2102 v
= PA_CHANNEL_POSITION_MASK_REAR
;
2103 else if (pa_streq(m
, "all-side"))
2104 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
2105 else if (pa_streq(m
, "all-top"))
2106 v
= PA_CHANNEL_POSITION_MASK_TOP
;
2107 else if (pa_streq(m
, "all-no-lfe"))
2108 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
2109 else if (pa_streq(m
, "all"))
2110 v
= PA_CHANNEL_POSITION_MASK_ALL
;
2112 pa_channel_position_t p
;
2114 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
2117 v
= PA_CHANNEL_POSITION_MASK(p
);
2123 static int element_parse_override_map(pa_config_parser_state
*state
) {
2126 const char *split_state
= NULL
;
2132 p
= state
->userdata
;
2134 if (!(e
= element_get(p
, state
->section
, true))) {
2135 pa_log("[%s:%u] Override map makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2139 while ((n
= pa_split(state
->rvalue
, ",", &split_state
))) {
2140 pa_channel_position_mask_t m
;
2145 if ((m
= parse_mask(n
)) == 0) {
2146 pa_log("[%s:%u] Override map '%s' invalid in '%s'", state
->filename
, state
->lineno
, n
, state
->section
);
2152 if (pa_streq(state
->lvalue
, "override-map.1"))
2153 e
->masks
[i
++][0] = m
;
2155 e
->masks
[i
++][1] = m
;
2157 /* Later on we might add override-map.3 and so on here ... */
2162 e
->override_map
= true;
2167 static int jack_parse_state(pa_config_parser_state
*state
) {
2174 p
= state
->userdata
;
2176 if (!(j
= jack_get(p
, state
->section
))) {
2177 pa_log("[%s:%u] state makes no sense in '%s'", state
->filename
, state
->lineno
, state
->section
);
2181 if (pa_streq(state
->rvalue
, "yes"))
2182 pa
= PA_AVAILABLE_YES
;
2183 else if (pa_streq(state
->rvalue
, "no"))
2184 pa
= PA_AVAILABLE_NO
;
2185 else if (pa_streq(state
->rvalue
, "unknown"))
2186 pa
= PA_AVAILABLE_UNKNOWN
;
2188 pa_log("[%s:%u] state must be 'yes', 'no' or 'unknown' in '%s'", state
->filename
, state
->lineno
, state
->section
);
2192 if (pa_streq(state
->lvalue
, "state.unplugged"))
2193 j
->state_unplugged
= pa
;
2195 j
->state_plugged
= pa
;
2196 pa_assert(pa_streq(state
->lvalue
, "state.plugged"));
2202 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
2203 snd_mixer_selem_id_t
*sid
;
2204 snd_mixer_elem_t
*me
;
2210 SELEM_INIT(sid
, e
->alsa_name
);
2211 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2212 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2216 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
2218 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
2219 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
2221 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
2224 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2227 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
2229 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
2230 pa_log_warn("Failed to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2236 static int setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
2243 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
2244 element_set_option(o
->element
, m
, o
->alsa_idx
);
2249 static int option_verify(pa_alsa_option
*o
) {
2250 static const struct description_map well_known_descriptions
[] = {
2251 { "input", N_("Input") },
2252 { "input-docking", N_("Docking Station Input") },
2253 { "input-docking-microphone", N_("Docking Station Microphone") },
2254 { "input-docking-linein", N_("Docking Station Line In") },
2255 { "input-linein", N_("Line In") },
2256 { "input-microphone", N_("Microphone") },
2257 { "input-microphone-front", N_("Front Microphone") },
2258 { "input-microphone-rear", N_("Rear Microphone") },
2259 { "input-microphone-external", N_("External Microphone") },
2260 { "input-microphone-internal", N_("Internal Microphone") },
2261 { "input-radio", N_("Radio") },
2262 { "input-video", N_("Video") },
2263 { "input-agc-on", N_("Automatic Gain Control") },
2264 { "input-agc-off", N_("No Automatic Gain Control") },
2265 { "input-boost-on", N_("Boost") },
2266 { "input-boost-off", N_("No Boost") },
2267 { "output-amplifier-on", N_("Amplifier") },
2268 { "output-amplifier-off", N_("No Amplifier") },
2269 { "output-bass-boost-on", N_("Bass Boost") },
2270 { "output-bass-boost-off", N_("No Bass Boost") },
2271 { "output-speaker", N_("Speaker") },
2272 { "output-headphones", N_("Headphones") }
2278 pa_log("No name set for option %s", o
->alsa_name
);
2282 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
2283 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
2284 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
2288 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
2289 !pa_streq(o
->alsa_name
, "on") &&
2290 !pa_streq(o
->alsa_name
, "off")) {
2291 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
2295 if (!o
->description
)
2296 o
->description
= pa_xstrdup(lookup_description(o
->name
,
2297 well_known_descriptions
,
2298 PA_ELEMENTSOF(well_known_descriptions
)));
2299 if (!o
->description
)
2300 o
->description
= pa_xstrdup(o
->name
);
2305 static int element_verify(pa_alsa_element
*e
) {
2310 // 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);
2311 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
2312 (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required_any
== e
->required_absent
) ||
2313 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) ||
2314 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
2315 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
2319 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
2320 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
2324 PA_LLIST_FOREACH(o
, e
->options
)
2325 if (option_verify(o
) < 0)
2331 static int path_verify(pa_alsa_path
*p
) {
2332 static const struct description_map well_known_descriptions
[] = {
2333 { "analog-input", N_("Analog Input") },
2334 { "analog-input-microphone", N_("Microphone") },
2335 { "analog-input-microphone-front", N_("Front Microphone") },
2336 { "analog-input-microphone-rear", N_("Rear Microphone") },
2337 { "analog-input-microphone-dock", N_("Dock Microphone") },
2338 { "analog-input-microphone-internal", N_("Internal Microphone") },
2339 { "analog-input-microphone-headset", N_("Headset Microphone") },
2340 { "analog-input-linein", N_("Line In") },
2341 { "analog-input-radio", N_("Radio") },
2342 { "analog-input-video", N_("Video") },
2343 { "analog-output", N_("Analog Output") },
2344 { "analog-output-headphones", N_("Headphones") },
2345 { "analog-output-lfe-on-mono", N_("LFE on Separate Mono Output") },
2346 { "analog-output-lineout", N_("Line Out") },
2347 { "analog-output-mono", N_("Analog Mono Output") },
2348 { "analog-output-speaker", N_("Speakers") },
2349 { "hdmi-output", N_("HDMI / DisplayPort") },
2350 { "iec958-stereo-output", N_("Digital Output (S/PDIF)") },
2351 { "iec958-stereo-input", N_("Digital Input (S/PDIF)") },
2352 { "iec958-passthrough-output", N_("Digital Passthrough (S/PDIF)") }
2359 PA_LLIST_FOREACH(e
, p
->elements
)
2360 if (element_verify(e
) < 0)
2363 if (!p
->description
)
2364 p
->description
= pa_xstrdup(lookup_description(p
->description_key
? p
->description_key
: p
->name
,
2365 well_known_descriptions
,
2366 PA_ELEMENTSOF(well_known_descriptions
)));
2368 if (!p
->description
) {
2369 if (p
->description_key
)
2370 pa_log_warn("Path %s: Unrecognized description key: %s", p
->name
, p
->description_key
);
2372 p
->description
= pa_xstrdup(p
->name
);
2378 static const char *get_default_paths_dir(void) {
2379 if (pa_run_from_build_tree())
2380 return PA_SRCDIR
"/modules/alsa/mixer/paths/";
2382 return PA_ALSA_PATHS_DIR
;
2385 pa_alsa_path
* pa_alsa_path_new(const char *paths_dir
, const char *fname
, pa_alsa_direction_t direction
) {
2390 bool mute_during_activation
= false;
2392 pa_config_item items
[] = {
2394 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
2395 { "description-key", pa_config_parse_string
, NULL
, "General" },
2396 { "description", pa_config_parse_string
, NULL
, "General" },
2397 { "mute-during-activation", pa_config_parse_bool
, NULL
, "General" },
2398 { "eld-device", pa_config_parse_int
, NULL
, "General" },
2401 { "priority", option_parse_priority
, NULL
, NULL
},
2402 { "name", option_parse_name
, NULL
, NULL
},
2405 { "state.plugged", jack_parse_state
, NULL
, NULL
},
2406 { "state.unplugged", jack_parse_state
, NULL
, NULL
},
2409 { "switch", element_parse_switch
, NULL
, NULL
},
2410 { "volume", element_parse_volume
, NULL
, NULL
},
2411 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
2412 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
2413 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
2414 /* ... later on we might add override-map.3 and so on here ... */
2415 { "required", element_parse_required
, NULL
, NULL
},
2416 { "required-any", element_parse_required
, NULL
, NULL
},
2417 { "required-absent", element_parse_required
, NULL
, NULL
},
2418 { "direction", element_parse_direction
, NULL
, NULL
},
2419 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2420 { "volume-limit", element_parse_volume_limit
, NULL
, NULL
},
2421 { NULL
, NULL
, NULL
, NULL
}
2426 p
= pa_xnew0(pa_alsa_path
, 1);
2427 n
= pa_path_get_filename(fname
);
2428 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2429 p
->proplist
= pa_proplist_new();
2430 p
->direction
= direction
;
2433 items
[0].data
= &p
->priority
;
2434 items
[1].data
= &p
->description_key
;
2435 items
[2].data
= &p
->description
;
2436 items
[3].data
= &mute_during_activation
;
2437 items
[4].data
= &p
->eld_device
;
2440 paths_dir
= get_default_paths_dir();
2442 fn
= pa_maybe_prefix_path(fname
, paths_dir
);
2444 r
= pa_config_parse(fn
, NULL
, items
, p
->proplist
, p
);
2450 p
->mute_during_activation
= mute_during_activation
;
2452 if (path_verify(p
) < 0)
2458 pa_alsa_path_free(p
);
2462 pa_alsa_path
*pa_alsa_path_synthesize(const char *element
, pa_alsa_direction_t direction
) {
2468 p
= pa_xnew0(pa_alsa_path
, 1);
2469 p
->name
= pa_xstrdup(element
);
2470 p
->direction
= direction
;
2472 e
= pa_xnew0(pa_alsa_element
, 1);
2474 e
->alsa_name
= pa_xstrdup(element
);
2475 e
->direction
= direction
;
2476 e
->volume_limit
= -1;
2478 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2479 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2481 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2482 p
->last_element
= e
;
2486 static bool element_drop_unsupported(pa_alsa_element
*e
) {
2487 pa_alsa_option
*o
, *n
;
2491 for (o
= e
->options
; o
; o
= n
) {
2494 if (o
->alsa_idx
< 0) {
2495 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2501 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2502 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2503 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2506 static void path_drop_unsupported(pa_alsa_path
*p
) {
2507 pa_alsa_element
*e
, *n
;
2511 for (e
= p
->elements
; e
; e
= n
) {
2514 if (!element_drop_unsupported(e
)) {
2515 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2521 static void path_make_options_unique(pa_alsa_path
*p
) {
2523 pa_alsa_option
*o
, *u
;
2525 PA_LLIST_FOREACH(e
, p
->elements
) {
2526 PA_LLIST_FOREACH(o
, e
->options
) {
2530 for (u
= o
->next
; u
; u
= u
->next
)
2531 if (pa_streq(u
->name
, o
->name
))
2537 m
= pa_xstrdup(o
->name
);
2539 /* OK, this name is not unique, hence let's rename */
2540 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2543 if (!pa_streq(u
->name
, m
))
2546 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2550 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2551 pa_xfree(u
->description
);
2552 u
->description
= nd
;
2562 static bool element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2565 for (; e
; e
= e
->next
)
2566 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2567 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2573 for (o
= e
->options
; o
; o
= o
->next
) {
2577 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2578 s
->options
= pa_idxset_copy(template->options
, NULL
);
2579 s
->name
= pa_sprintf_malloc("%s+%s", template->name
, o
->name
);
2581 (template->description
[0] && o
->description
[0])
2582 ? pa_sprintf_malloc("%s / %s", template->description
, o
->description
)
2583 : (template->description
[0]
2584 ? pa_xstrdup(template->description
)
2585 : pa_xstrdup(o
->description
));
2587 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2589 s
= pa_xnew0(pa_alsa_setting
, 1);
2590 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2591 s
->name
= pa_xstrdup(o
->name
);
2592 s
->description
= pa_xstrdup(o
->description
);
2593 s
->priority
= o
->priority
;
2596 pa_idxset_put(s
->options
, o
, NULL
);
2598 if (element_create_settings(e
->next
, s
))
2599 /* This is not a leaf, so let's get rid of it */
2602 /* This is a leaf, so let's add it */
2603 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2605 e
->path
->last_setting
= s
;
2612 static void path_create_settings(pa_alsa_path
*p
) {
2615 element_create_settings(p
->elements
, NULL
);
2618 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_hctl_t
*hctl
, bool ignore_dB
) {
2621 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2622 pa_channel_position_t t
;
2623 pa_channel_position_mask_t path_volume_channels
= 0;
2629 return p
->supported
? 0 : -1;
2635 pa_log_debug("Probing path '%s'", p
->name
);
2637 PA_LLIST_FOREACH(j
, p
->jacks
) {
2638 if (jack_probe(j
, hctl
) < 0) {
2639 p
->supported
= false;
2640 pa_log_debug("Probe of jack '%s' failed.", j
->alsa_name
);
2643 pa_log_debug("Probe of jack '%s' succeeded (%s)", j
->alsa_name
, j
->has_control
? "found!" : "not found");
2646 PA_LLIST_FOREACH(e
, p
->elements
) {
2647 if (element_probe(e
, m
) < 0) {
2648 p
->supported
= false;
2649 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2652 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
);
2657 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2659 if (!p
->has_volume
) {
2660 p
->min_volume
= e
->min_volume
;
2661 p
->max_volume
= e
->max_volume
;
2665 if (!p
->has_volume
) {
2666 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2667 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2668 min_dB
[t
] = e
->min_dB
;
2669 max_dB
[t
] = e
->max_dB
;
2670 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2677 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2678 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2679 min_dB
[t
] += e
->min_dB
;
2680 max_dB
[t
] += e
->max_dB
;
2681 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2684 /* Hmm, there's another element before us
2685 * which cannot do dB volumes, so we we need
2686 * to 'neutralize' this slider */
2687 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2688 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2691 } else if (p
->has_volume
) {
2692 /* We can't use this volume, so let's ignore it */
2693 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2694 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2696 p
->has_volume
= true;
2699 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2703 if (p
->has_req_any
&& !p
->req_any_present
) {
2704 p
->supported
= false;
2705 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2709 path_drop_unsupported(p
);
2710 path_make_options_unique(p
);
2711 path_create_settings(p
);
2713 p
->supported
= true;
2715 p
->min_dB
= INFINITY
;
2716 p
->max_dB
= -INFINITY
;
2718 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2719 if (path_volume_channels
& PA_CHANNEL_POSITION_MASK(t
)) {
2720 if (p
->min_dB
> min_dB
[t
])
2721 p
->min_dB
= min_dB
[t
];
2723 if (p
->max_dB
< max_dB
[t
])
2724 p
->max_dB
= max_dB
[t
];
2731 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2734 pa_log_debug("Setting %s (%s) priority=%u",
2736 pa_strnull(s
->description
),
2740 void pa_alsa_jack_dump(pa_alsa_jack
*j
) {
2743 pa_log_debug("Jack %s, alsa_name='%s', detection %s", j
->name
, j
->alsa_name
, j
->has_control
? "possible" : "unavailable");
2746 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2749 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2751 pa_strnull(o
->name
),
2752 pa_strnull(o
->description
),
2757 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2761 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",
2771 (long long unsigned) e
->merged_mask
,
2773 pa_yes_no(e
->override_map
));
2775 PA_LLIST_FOREACH(o
, e
->options
)
2776 pa_alsa_option_dump(o
);
2779 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2785 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2786 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2788 pa_strnull(p
->description
),
2791 pa_yes_no(p
->probed
),
2792 pa_yes_no(p
->supported
),
2793 pa_yes_no(p
->has_mute
),
2794 pa_yes_no(p
->has_volume
),
2795 pa_yes_no(p
->has_dB
),
2796 p
->min_volume
, p
->max_volume
,
2797 p
->min_dB
, p
->max_dB
);
2799 PA_LLIST_FOREACH(e
, p
->elements
)
2800 pa_alsa_element_dump(e
);
2802 PA_LLIST_FOREACH(j
, p
->jacks
)
2803 pa_alsa_jack_dump(j
);
2805 PA_LLIST_FOREACH(s
, p
->settings
)
2806 pa_alsa_setting_dump(s
);
2809 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2810 snd_mixer_selem_id_t
*sid
;
2811 snd_mixer_elem_t
*me
;
2817 SELEM_INIT(sid
, e
->alsa_name
);
2818 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2819 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2823 snd_mixer_elem_set_callback(me
, cb
);
2824 snd_mixer_elem_set_callback_private(me
, userdata
);
2827 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2834 PA_LLIST_FOREACH(e
, p
->elements
)
2835 element_set_callback(e
, m
, cb
, userdata
);
2838 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2846 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
2847 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2850 static pa_alsa_path
*profile_set_get_path(pa_alsa_profile_set
*ps
, const char *path_name
) {
2854 pa_assert(path_name
);
2856 if ((path
= pa_hashmap_get(ps
->output_paths
, path_name
)))
2859 return pa_hashmap_get(ps
->input_paths
, path_name
);
2862 static void profile_set_add_path(pa_alsa_profile_set
*ps
, pa_alsa_path
*path
) {
2866 switch (path
->direction
) {
2867 case PA_ALSA_DIRECTION_OUTPUT
:
2868 pa_assert_se(pa_hashmap_put(ps
->output_paths
, path
->name
, path
) >= 0);
2871 case PA_ALSA_DIRECTION_INPUT
:
2872 pa_assert_se(pa_hashmap_put(ps
->input_paths
, path
->name
, path
) >= 0);
2876 pa_assert_not_reached();
2880 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
, const char *paths_dir
) {
2881 pa_alsa_path_set
*ps
;
2882 char **pn
= NULL
, **en
= NULL
, **ie
;
2883 pa_alsa_decibel_fix
*db_fix
;
2884 void *state
, *state2
;
2887 pa_assert(m
->profile_set
);
2888 pa_assert(m
->profile_set
->decibel_fixes
);
2889 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2891 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2894 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2895 ps
->direction
= direction
;
2896 ps
->paths
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2898 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2899 pn
= m
->output_path_names
;
2901 pn
= m
->input_path_names
;
2906 for (in
= pn
; *in
; in
++) {
2907 pa_alsa_path
*p
= NULL
;
2908 bool duplicate
= false;
2911 for (kn
= pn
; kn
< in
; kn
++)
2912 if (pa_streq(*kn
, *in
)) {
2920 p
= profile_set_get_path(m
->profile_set
, *in
);
2922 if (p
&& p
->direction
!= direction
) {
2923 pa_log("Configuration error: Path %s is used both as an input and as an output path.", p
->name
);
2928 char *fn
= pa_sprintf_malloc("%s.conf", *in
);
2929 p
= pa_alsa_path_new(paths_dir
, fn
, direction
);
2932 profile_set_add_path(m
->profile_set
, p
);
2936 pa_hashmap_put(ps
->paths
, p
, p
);
2943 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2944 en
= m
->output_element
;
2946 en
= m
->input_element
;
2951 for (ie
= en
; *ie
; ie
++) {
2955 p
= pa_alsa_path_synthesize(*ie
, direction
);
2957 /* Mark all other passed elements for require-absent */
2958 for (je
= en
; *je
; je
++) {
2964 e
= pa_xnew0(pa_alsa_element
, 1);
2966 e
->alsa_name
= pa_xstrdup(*je
);
2967 e
->direction
= direction
;
2968 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2969 e
->volume_limit
= -1;
2971 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2972 p
->last_element
= e
;
2975 pa_hashmap_put(ps
->paths
, *ie
, p
);
2979 /* Assign decibel fixes to elements. */
2980 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2983 PA_HASHMAP_FOREACH(p
, ps
->paths
, state2
) {
2986 PA_LLIST_FOREACH(e
, p
->elements
) {
2987 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2988 /* The profile set that contains the dB fix may be freed
2989 * before the element, so we have to copy the dB fix
2991 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2992 e
->db_fix
->profile_set
= NULL
;
2993 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2994 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
3004 pa_alsa_path_set_free(ps
);
3009 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
3014 pa_log_debug("Path Set %p, direction=%i",
3018 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
3019 pa_alsa_path_dump(p
);
3022 static bool options_have_option(pa_alsa_option
*options
, const char *alsa_name
) {
3026 pa_assert(alsa_name
);
3028 PA_LLIST_FOREACH(o
, options
) {
3029 if (pa_streq(o
->alsa_name
, alsa_name
))
3035 static bool enumeration_is_subset(pa_alsa_option
*a_options
, pa_alsa_option
*b_options
) {
3036 pa_alsa_option
*oa
, *ob
;
3038 if (!a_options
) return true;
3039 if (!b_options
) return false;
3041 /* If there is an option A offers that B does not, then A is not a subset of B. */
3042 PA_LLIST_FOREACH(oa
, a_options
) {
3044 PA_LLIST_FOREACH(ob
, b_options
) {
3045 if (pa_streq(oa
->alsa_name
, ob
->alsa_name
)) {
3057 * Compares two elements to see if a is a subset of b
3059 static bool element_is_subset(pa_alsa_element
*a
, pa_alsa_element
*b
, snd_mixer_t
*m
) {
3065 * Every state is a subset of itself (with caveats for volume_limits and options)
3066 * IGNORE is a subset of every other state */
3068 /* Check the volume_use */
3069 if (a
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
3071 /* "Constant" is subset of "Constant" only when their constant values are equal */
3072 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& b
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& a
->constant_volume
!= b
->constant_volume
)
3075 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
3076 if (a
->volume_use
!= b
->volume_use
&& b
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
3079 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
3080 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
3081 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
3082 if (b
->volume_use
== PA_ALSA_VOLUME_MERGE
&& b
->volume_limit
>= 0) {
3085 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
)
3086 a_limit
= a
->constant_volume
;
3087 else if (a
->volume_use
== PA_ALSA_VOLUME_ZERO
) {
3091 int rounding
= (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
? +1 : -1);
3092 a_limit
= decibel_fix_get_step(a
->db_fix
, &dB
, rounding
);
3094 snd_mixer_selem_id_t
*sid
;
3095 snd_mixer_elem_t
*me
;
3097 SELEM_INIT(sid
, a
->alsa_name
);
3098 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
3099 pa_log_warn("Element %s seems to have disappeared.", a
->alsa_name
);
3103 if (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3104 if (snd_mixer_selem_ask_playback_dB_vol(me
, dB
, +1, &a_limit
) < 0)
3107 if (snd_mixer_selem_ask_capture_dB_vol(me
, dB
, -1, &a_limit
) < 0)
3111 } else if (a
->volume_use
== PA_ALSA_VOLUME_OFF
)
3112 a_limit
= a
->min_volume
;
3113 else if (a
->volume_use
== PA_ALSA_VOLUME_MERGE
)
3114 a_limit
= a
->volume_limit
;
3116 /* This should never be reached */
3119 if (a_limit
> b
->volume_limit
)
3123 if (a
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
3125 /* If override-maps are different, they're not subsets */
3126 if (a
->n_channels
!= b
->n_channels
)
3128 for (s
= 0; s
<= SND_MIXER_SCHN_LAST
; s
++)
3129 if (a
->masks
[s
][a
->n_channels
-1] != b
->masks
[s
][b
->n_channels
-1]) {
3130 pa_log_debug("Element %s is not a subset - mask a: 0x%" PRIx64
", mask b: 0x%" PRIx64
", at channel %d",
3131 a
->alsa_name
, a
->masks
[s
][a
->n_channels
-1], b
->masks
[s
][b
->n_channels
-1], s
);
3137 if (a
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
3138 /* "On" is a subset of "Mute".
3139 * "Off" is a subset of "Mute".
3140 * "On" is a subset of "Select", if there is an "Option:On" in B.
3141 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3142 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3144 if (a
->switch_use
!= b
->switch_use
) {
3146 if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
|| a
->switch_use
== PA_ALSA_SWITCH_MUTE
3147 || b
->switch_use
== PA_ALSA_SWITCH_OFF
|| b
->switch_use
== PA_ALSA_SWITCH_ON
)
3150 if (b
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3151 if (a
->switch_use
== PA_ALSA_SWITCH_ON
) {
3152 if (!options_have_option(b
->options
, "on"))
3154 } else if (a
->switch_use
== PA_ALSA_SWITCH_OFF
) {
3155 if (!options_have_option(b
->options
, "off"))
3159 } else if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3160 if (!enumeration_is_subset(a
->options
, b
->options
))
3165 if (a
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
) {
3166 if (b
->enumeration_use
== PA_ALSA_ENUMERATION_IGNORE
)
3168 if (!enumeration_is_subset(a
->options
, b
->options
))
3175 static void path_set_condense(pa_alsa_path_set
*ps
, snd_mixer_t
*m
) {
3182 /* If we only have one path, then don't bother */
3183 if (pa_hashmap_size(ps
->paths
) < 2)
3186 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3190 PA_HASHMAP_FOREACH(p2
, ps
->paths
, state2
) {
3191 pa_alsa_element
*ea
, *eb
;
3192 pa_alsa_jack
*ja
, *jb
;
3193 bool is_subset
= true;
3198 /* If a has a jack that b does not have, a is not a subset */
3199 PA_LLIST_FOREACH(ja
, p
->jacks
) {
3200 bool exists
= false;
3202 if (!ja
->has_control
)
3205 PA_LLIST_FOREACH(jb
, p2
->jacks
) {
3206 if (jb
->has_control
&& pa_streq(jb
->alsa_name
, ja
->alsa_name
) &&
3207 (ja
->state_plugged
== jb
->state_plugged
) &&
3208 (ja
->state_unplugged
== jb
->state_unplugged
)) {
3220 /* Compare the elements of each set... */
3227 else if ((ea
&& !eb
) || (!ea
&& eb
))
3229 else if (pa_streq(ea
->alsa_name
, eb
->alsa_name
)) {
3230 if (element_is_subset(ea
, eb
, m
)) {
3240 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p
->name
, p2
->name
);
3241 pa_hashmap_remove(ps
->paths
, p
);
3248 static pa_alsa_path
* path_set_find_path_by_description(pa_alsa_path_set
*ps
, const char* description
, pa_alsa_path
*ignore
) {
3252 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
3253 if (p
!= ignore
&& pa_streq(p
->description
, description
))
3259 static void path_set_make_path_descriptions_unique(pa_alsa_path_set
*ps
) {
3260 pa_alsa_path
*p
, *q
;
3261 void *state
, *state2
;
3263 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3265 char *old_description
;
3267 q
= path_set_find_path_by_description(ps
, p
->description
, p
);
3272 old_description
= pa_xstrdup(p
->description
);
3274 /* OK, this description is not unique, hence let's rename */
3276 PA_HASHMAP_FOREACH(q
, ps
->paths
, state2
) {
3277 char *new_description
;
3279 if (!pa_streq(q
->description
, old_description
))
3282 new_description
= pa_sprintf_malloc("%s %u", q
->description
, i
);
3283 pa_xfree(q
->description
);
3284 q
->description
= new_description
;
3289 pa_xfree(old_description
);
3293 static void mapping_free(pa_alsa_mapping
*m
) {
3297 pa_xfree(m
->description
);
3299 pa_proplist_free(m
->proplist
);
3301 pa_xstrfreev(m
->device_strings
);
3302 pa_xstrfreev(m
->input_path_names
);
3303 pa_xstrfreev(m
->output_path_names
);
3304 pa_xstrfreev(m
->input_element
);
3305 pa_xstrfreev(m
->output_element
);
3306 if (m
->input_path_set
)
3307 pa_alsa_path_set_free(m
->input_path_set
);
3308 if (m
->output_path_set
)
3309 pa_alsa_path_set_free(m
->output_path_set
);
3311 pa_assert(!m
->input_pcm
);
3312 pa_assert(!m
->output_pcm
);
3314 pa_alsa_ucm_mapping_context_free(&m
->ucm_context
);
3319 static void profile_free(pa_alsa_profile
*p
) {
3323 pa_xfree(p
->description
);
3325 pa_xstrfreev(p
->input_mapping_names
);
3326 pa_xstrfreev(p
->output_mapping_names
);
3328 if (p
->input_mappings
)
3329 pa_idxset_free(p
->input_mappings
, NULL
);
3331 if (p
->output_mappings
)
3332 pa_idxset_free(p
->output_mappings
, NULL
);
3337 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
3340 if (ps
->input_paths
)
3341 pa_hashmap_free(ps
->input_paths
);
3343 if (ps
->output_paths
)
3344 pa_hashmap_free(ps
->output_paths
);
3347 pa_hashmap_free(ps
->profiles
);
3350 pa_hashmap_free(ps
->mappings
);
3352 if (ps
->decibel_fixes
)
3353 pa_hashmap_free(ps
->decibel_fixes
);
3358 pa_alsa_mapping
*pa_alsa_mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
3361 if (!pa_startswith(name
, "Mapping "))
3366 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
3369 m
= pa_xnew0(pa_alsa_mapping
, 1);
3370 m
->profile_set
= ps
;
3371 m
->name
= pa_xstrdup(name
);
3372 pa_sample_spec_init(&m
->sample_spec
);
3373 pa_channel_map_init(&m
->channel_map
);
3374 m
->proplist
= pa_proplist_new();
3376 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
3381 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
3384 if (!pa_startswith(name
, "Profile "))
3389 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
3392 p
= pa_xnew0(pa_alsa_profile
, 1);
3393 p
->profile_set
= ps
;
3394 p
->name
= pa_xstrdup(name
);
3396 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3401 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
3402 pa_alsa_decibel_fix
*db_fix
;
3404 if (!pa_startswith(name
, "DecibelFix "))
3409 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
3412 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
3413 db_fix
->profile_set
= ps
;
3414 db_fix
->name
= pa_xstrdup(name
);
3416 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
3421 static int mapping_parse_device_strings(pa_config_parser_state
*state
) {
3422 pa_alsa_profile_set
*ps
;
3427 ps
= state
->userdata
;
3429 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3430 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3434 pa_xstrfreev(m
->device_strings
);
3435 if (!(m
->device_strings
= pa_split_spaces_strv(state
->rvalue
))) {
3436 pa_log("[%s:%u] Device string list empty of '%s'", state
->filename
, state
->lineno
, state
->section
);
3443 static int mapping_parse_channel_map(pa_config_parser_state
*state
) {
3444 pa_alsa_profile_set
*ps
;
3449 ps
= state
->userdata
;
3451 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3452 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3456 if (!(pa_channel_map_parse(&m
->channel_map
, state
->rvalue
))) {
3457 pa_log("[%s:%u] Channel map invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
3464 static int mapping_parse_paths(pa_config_parser_state
*state
) {
3465 pa_alsa_profile_set
*ps
;
3470 ps
= state
->userdata
;
3472 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3473 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3477 if (pa_streq(state
->lvalue
, "paths-input")) {
3478 pa_xstrfreev(m
->input_path_names
);
3479 m
->input_path_names
= pa_split_spaces_strv(state
->rvalue
);
3481 pa_xstrfreev(m
->output_path_names
);
3482 m
->output_path_names
= pa_split_spaces_strv(state
->rvalue
);
3488 static int mapping_parse_element(pa_config_parser_state
*state
) {
3489 pa_alsa_profile_set
*ps
;
3494 ps
= state
->userdata
;
3496 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3497 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3501 if (pa_streq(state
->lvalue
, "element-input")) {
3502 pa_xstrfreev(m
->input_element
);
3503 m
->input_element
= pa_split_spaces_strv(state
->rvalue
);
3505 pa_xstrfreev(m
->output_element
);
3506 m
->output_element
= pa_split_spaces_strv(state
->rvalue
);
3512 static int mapping_parse_direction(pa_config_parser_state
*state
) {
3513 pa_alsa_profile_set
*ps
;
3518 ps
= state
->userdata
;
3520 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3521 pa_log("[%s:%u] Section name %s invalid.", state
->filename
, state
->lineno
, state
->section
);
3525 if (pa_streq(state
->rvalue
, "input"))
3526 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3527 else if (pa_streq(state
->rvalue
, "output"))
3528 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3529 else if (pa_streq(state
->rvalue
, "any"))
3530 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3532 pa_log("[%s:%u] Direction %s invalid.", state
->filename
, state
->lineno
, state
->rvalue
);
3539 static int mapping_parse_description(pa_config_parser_state
*state
) {
3540 pa_alsa_profile_set
*ps
;
3546 ps
= state
->userdata
;
3548 if ((m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3549 pa_xfree(m
->description
);
3550 m
->description
= pa_xstrdup(state
->rvalue
);
3551 } else if ((p
= profile_get(ps
, state
->section
))) {
3552 pa_xfree(p
->description
);
3553 p
->description
= pa_xstrdup(state
->rvalue
);
3555 pa_log("[%s:%u] Section name %s invalid.", state
->filename
, state
->lineno
, state
->section
);
3562 static int mapping_parse_priority(pa_config_parser_state
*state
) {
3563 pa_alsa_profile_set
*ps
;
3570 ps
= state
->userdata
;
3572 if (pa_atou(state
->rvalue
, &prio
) < 0) {
3573 pa_log("[%s:%u] Priority invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
3577 if ((m
= pa_alsa_mapping_get(ps
, state
->section
)))
3579 else if ((p
= profile_get(ps
, state
->section
)))
3582 pa_log("[%s:%u] Section name %s invalid.", state
->filename
, state
->lineno
, state
->section
);
3589 static int profile_parse_mappings(pa_config_parser_state
*state
) {
3590 pa_alsa_profile_set
*ps
;
3595 ps
= state
->userdata
;
3597 if (!(p
= profile_get(ps
, state
->section
))) {
3598 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3602 if (pa_streq(state
->lvalue
, "input-mappings")) {
3603 pa_xstrfreev(p
->input_mapping_names
);
3604 p
->input_mapping_names
= pa_split_spaces_strv(state
->rvalue
);
3606 pa_xstrfreev(p
->output_mapping_names
);
3607 p
->output_mapping_names
= pa_split_spaces_strv(state
->rvalue
);
3613 static int profile_parse_skip_probe(pa_config_parser_state
*state
) {
3614 pa_alsa_profile_set
*ps
;
3620 ps
= state
->userdata
;
3622 if (!(p
= profile_get(ps
, state
->section
))) {
3623 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3627 if ((b
= pa_parse_boolean(state
->rvalue
)) < 0) {
3628 pa_log("[%s:%u] Skip probe invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
3637 static int decibel_fix_parse_db_values(pa_config_parser_state
*state
) {
3638 pa_alsa_profile_set
*ps
;
3639 pa_alsa_decibel_fix
*db_fix
;
3643 unsigned n
= 8; /* Current size of the db_values table. */
3644 unsigned min_step
= 0;
3645 unsigned max_step
= 0;
3646 unsigned i
= 0; /* Index to the items table. */
3647 unsigned prev_step
= 0;
3652 ps
= state
->userdata
;
3654 if (!(db_fix
= decibel_fix_get(ps
, state
->section
))) {
3655 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3659 if (!(items
= pa_split_spaces_strv(state
->rvalue
))) {
3660 pa_log("[%s:%u] Value missing", state
->filename
, state
->lineno
);
3664 db_values
= pa_xnew(long, n
);
3666 while ((item
= items
[i
++])) {
3667 char *s
= item
; /* Step value string. */
3668 char *d
= item
; /* dB value string. */
3672 /* Move d forward until it points to a colon or to the end of the item. */
3673 for (; *d
&& *d
!= ':'; ++d
);
3676 /* item started with colon. */
3677 pa_log("[%s:%u] No step value found in %s", state
->filename
, state
->lineno
, item
);
3681 if (!*d
|| !*(d
+ 1)) {
3682 /* No colon found, or it was the last character in item. */
3683 pa_log("[%s:%u] No dB value found in %s", state
->filename
, state
->lineno
, item
);
3687 /* pa_atou() needs a null-terminating string. Let's replace the colon
3688 * with a zero byte. */
3691 if (pa_atou(s
, &step
) < 0) {
3692 pa_log("[%s:%u] Invalid step value: %s", state
->filename
, state
->lineno
, s
);
3696 if (pa_atod(d
, &db
) < 0) {
3697 pa_log("[%s:%u] Invalid dB value: %s", state
->filename
, state
->lineno
, d
);
3701 if (step
<= prev_step
&& i
!= 1) {
3702 pa_log("[%s:%u] Step value %u not greater than the previous value %u", state
->filename
, state
->lineno
, step
, prev_step
);
3706 if (db
< prev_db
&& i
!= 1) {
3707 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", state
->filename
, state
->lineno
, db
, prev_db
);
3713 db_values
[0] = (long) (db
* 100.0);
3717 /* Interpolate linearly. */
3718 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3720 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3722 /* Reallocate the db_values table if it's about to overflow. */
3723 if (prev_step
+ 1 - min_step
== n
) {
3725 db_values
= pa_xrenew(long, db_values
, n
);
3728 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3735 db_fix
->min_step
= min_step
;
3736 db_fix
->max_step
= max_step
;
3737 pa_xfree(db_fix
->db_values
);
3738 db_fix
->db_values
= db_values
;
3740 pa_xstrfreev(items
);
3745 pa_xstrfreev(items
);
3746 pa_xfree(db_values
);
3751 static void mapping_paths_probe(pa_alsa_mapping
*m
, pa_alsa_profile
*profile
,
3752 pa_alsa_direction_t direction
, pa_hashmap
*used_paths
) {
3756 snd_pcm_t
*pcm_handle
;
3757 pa_alsa_path_set
*ps
;
3758 snd_mixer_t
*mixer_handle
;
3759 snd_hctl_t
*hctl_handle
;
3761 if (direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3762 if (m
->output_path_set
)
3763 return; /* Already probed */
3764 m
->output_path_set
= ps
= pa_alsa_path_set_new(m
, direction
, NULL
); /* FIXME: Handle paths_dir */
3765 pcm_handle
= m
->output_pcm
;
3767 if (m
->input_path_set
)
3768 return; /* Already probed */
3769 m
->input_path_set
= ps
= pa_alsa_path_set_new(m
, direction
, NULL
); /* FIXME: Handle paths_dir */
3770 pcm_handle
= m
->input_pcm
;
3774 return; /* No paths */
3776 pa_assert(pcm_handle
);
3778 mixer_handle
= pa_alsa_open_mixer_for_pcm(pcm_handle
, NULL
, &hctl_handle
);
3779 if (!mixer_handle
) {
3780 /* Cannot open mixer, remove all entries */
3781 pa_hashmap_remove_all(ps
->paths
);
3785 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3786 if (pa_alsa_path_probe(p
, mixer_handle
, hctl_handle
, m
->profile_set
->ignore_dB
) < 0) {
3787 pa_hashmap_remove(ps
->paths
, p
);
3791 path_set_condense(ps
, mixer_handle
);
3792 path_set_make_path_descriptions_unique(ps
);
3795 snd_mixer_close(mixer_handle
);
3797 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
3798 pa_hashmap_put(used_paths
, p
, p
);
3800 pa_log_debug("Available mixer paths (after tidying):");
3801 pa_alsa_path_set_dump(ps
);
3804 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3806 static const struct description_map well_known_descriptions
[] = {
3807 { "analog-mono", N_("Analog Mono") },
3808 { "analog-stereo", N_("Analog Stereo") },
3809 { "analog-surround-21", N_("Analog Surround 2.1") },
3810 { "analog-surround-30", N_("Analog Surround 3.0") },
3811 { "analog-surround-31", N_("Analog Surround 3.1") },
3812 { "analog-surround-40", N_("Analog Surround 4.0") },
3813 { "analog-surround-41", N_("Analog Surround 4.1") },
3814 { "analog-surround-50", N_("Analog Surround 5.0") },
3815 { "analog-surround-51", N_("Analog Surround 5.1") },
3816 { "analog-surround-61", N_("Analog Surround 6.0") },
3817 { "analog-surround-61", N_("Analog Surround 6.1") },
3818 { "analog-surround-70", N_("Analog Surround 7.0") },
3819 { "analog-surround-71", N_("Analog Surround 7.1") },
3820 { "analog-4-channel-input", N_("Analog 4-channel Input") },
3821 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3822 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3823 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3824 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3825 { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
3826 { "hdmi-stereo", N_("Digital Stereo (HDMI)") },
3827 { "hdmi-surround-51", N_("Digital Surround 5.1 (HDMI)") }
3832 if (!pa_channel_map_valid(&m
->channel_map
)) {
3833 pa_log("Mapping %s is missing channel map.", m
->name
);
3837 if (!m
->device_strings
) {
3838 pa_log("Mapping %s is missing device strings.", m
->name
);
3842 if ((m
->input_path_names
&& m
->input_element
) ||
3843 (m
->output_path_names
&& m
->output_element
)) {
3844 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3848 if (!m
->description
)
3849 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3850 well_known_descriptions
,
3851 PA_ELEMENTSOF(well_known_descriptions
)));
3853 if (!m
->description
)
3854 m
->description
= pa_xstrdup(m
->name
);
3857 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3859 else if (m
->channel_map
.channels
== bonus
->channels
)
3866 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3867 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3871 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3873 pa_strnull(m
->description
),
3875 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3876 pa_yes_no(m
->supported
),
3880 static void profile_set_add_auto_pair(
3881 pa_alsa_profile_set
*ps
,
3882 pa_alsa_mapping
*m
, /* output */
3883 pa_alsa_mapping
*n
/* input */) {
3891 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3894 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3898 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3900 name
= pa_sprintf_malloc("output:%s", m
->name
);
3902 name
= pa_sprintf_malloc("input:%s", n
->name
);
3904 if (pa_hashmap_get(ps
->profiles
, name
)) {
3909 p
= pa_xnew0(pa_alsa_profile
, 1);
3910 p
->profile_set
= ps
;
3914 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3915 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3916 p
->priority
+= m
->priority
* 100;
3920 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3921 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3922 p
->priority
+= n
->priority
;
3925 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3928 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3929 pa_alsa_mapping
*m
, *n
;
3930 void *m_state
, *n_state
;
3934 /* The order is important here:
3935 1) try single inputs and outputs before trying their
3936 combination, because if the half-duplex test failed, we don't have
3938 2) try the output right before the input combinations with
3939 that output, because then the output_pcm is not closed between tests.
3941 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3942 profile_set_add_auto_pair(ps
, NULL
, n
);
3944 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3945 profile_set_add_auto_pair(ps
, m
, NULL
);
3947 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3948 profile_set_add_auto_pair(ps
, m
, n
);
3953 static int profile_verify(pa_alsa_profile
*p
) {
3955 static const struct description_map well_known_descriptions
[] = {
3956 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3957 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3958 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3959 { "off", N_("Off") }
3964 /* Replace the output mapping names by the actual mappings */
3965 if (p
->output_mapping_names
) {
3968 pa_assert(!p
->output_mappings
);
3969 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3971 for (name
= p
->output_mapping_names
; *name
; name
++) {
3974 bool duplicate
= false;
3976 for (in
= name
+ 1; *in
; in
++)
3977 if (pa_streq(*name
, *in
)) {
3985 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3986 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p
->name
, *name
);
3990 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3996 pa_xstrfreev(p
->output_mapping_names
);
3997 p
->output_mapping_names
= NULL
;
4000 /* Replace the input mapping names by the actual mappings */
4001 if (p
->input_mapping_names
) {
4004 pa_assert(!p
->input_mappings
);
4005 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
4007 for (name
= p
->input_mapping_names
; *name
; name
++) {
4010 bool duplicate
= false;
4012 for (in
= name
+ 1; *in
; in
++)
4013 if (pa_streq(*name
, *in
)) {
4021 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
4022 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p
->name
, *name
);
4026 pa_idxset_put(p
->input_mappings
, m
, NULL
);
4032 pa_xstrfreev(p
->input_mapping_names
);
4033 p
->input_mapping_names
= NULL
;
4036 if (!p
->input_mappings
&& !p
->output_mappings
) {
4037 pa_log("Profile '%s' lacks mappings.", p
->name
);
4041 if (!p
->description
)
4042 p
->description
= pa_xstrdup(lookup_description(p
->name
,
4043 well_known_descriptions
,
4044 PA_ELEMENTSOF(well_known_descriptions
)));
4046 if (!p
->description
) {
4051 sb
= pa_strbuf_new();
4053 if (p
->output_mappings
)
4054 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4055 if (!pa_strbuf_isempty(sb
))
4056 pa_strbuf_puts(sb
, " + ");
4058 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
4061 if (p
->input_mappings
)
4062 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4063 if (!pa_strbuf_isempty(sb
))
4064 pa_strbuf_puts(sb
, " + ");
4066 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
4069 p
->description
= pa_strbuf_tostring_free(sb
);
4075 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
4080 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4082 pa_strnull(p
->description
),
4084 pa_yes_no(p
->supported
),
4085 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
4086 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
4088 if (p
->input_mappings
)
4089 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
4090 pa_log_debug("Input %s", m
->name
);
4092 if (p
->output_mappings
)
4093 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
4094 pa_log_debug("Output %s", m
->name
);
4097 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
4100 /* Check that the dB mapping has been configured. Since "db-values" is
4101 * currently the only option in the DecibelFix section, and decibel fix
4102 * objects don't get created if a DecibelFix section is empty, this is
4103 * actually a redundant check. Having this may prevent future bugs,
4105 if (!db_fix
->db_values
) {
4106 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
4113 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
4114 char *db_values
= NULL
;
4118 if (db_fix
->db_values
) {
4120 unsigned long i
, nsteps
;
4122 pa_assert(db_fix
->min_step
<= db_fix
->max_step
);
4123 nsteps
= db_fix
->max_step
- db_fix
->min_step
+ 1;
4125 buf
= pa_strbuf_new();
4126 for (i
= 0; i
< nsteps
; ++i
)
4127 pa_strbuf_printf(buf
, "[%li]:%0.2f ", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
4129 db_values
= pa_strbuf_tostring_free(buf
);
4132 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4133 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
4135 pa_xfree(db_values
);
4138 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
4139 pa_alsa_profile_set
*ps
;
4142 pa_alsa_decibel_fix
*db_fix
;
4147 static pa_config_item items
[] = {
4149 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
4152 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
4153 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
4154 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
4155 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
4156 { "element-input", mapping_parse_element
, NULL
, NULL
},
4157 { "element-output", mapping_parse_element
, NULL
, NULL
},
4158 { "direction", mapping_parse_direction
, NULL
, NULL
},
4160 /* Shared by [Mapping ...] and [Profile ...] */
4161 { "description", mapping_parse_description
, NULL
, NULL
},
4162 { "priority", mapping_parse_priority
, NULL
, NULL
},
4165 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
4166 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
4167 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
4169 /* [DecibelFix ...] */
4170 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
4171 { NULL
, NULL
, NULL
, NULL
}
4174 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
4175 ps
->mappings
= pa_hashmap_new_full(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
, NULL
, (pa_free_cb_t
) mapping_free
);
4176 ps
->profiles
= pa_hashmap_new_full(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
, NULL
, (pa_free_cb_t
) profile_free
);
4177 ps
->decibel_fixes
= pa_hashmap_new_full(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
, NULL
, (pa_free_cb_t
) decibel_fix_free
);
4178 ps
->input_paths
= pa_hashmap_new_full(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
, NULL
, (pa_free_cb_t
) pa_alsa_path_free
);
4179 ps
->output_paths
= pa_hashmap_new_full(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
, NULL
, (pa_free_cb_t
) pa_alsa_path_free
);
4181 items
[0].data
= &ps
->auto_profiles
;
4184 fname
= "default.conf";
4186 fn
= pa_maybe_prefix_path(fname
,
4187 pa_run_from_build_tree() ? PA_SRCDIR
"/modules/alsa/mixer/profile-sets/" :
4188 PA_ALSA_PROFILE_SETS_DIR
);
4190 r
= pa_config_parse(fn
, NULL
, items
, NULL
, ps
);
4196 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4197 if (mapping_verify(m
, bonus
) < 0)
4200 if (ps
->auto_profiles
)
4201 profile_set_add_auto(ps
);
4203 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4204 if (profile_verify(p
) < 0)
4207 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4208 if (decibel_fix_verify(db_fix
) < 0)
4214 pa_alsa_profile_set_free(ps
);
4218 static void profile_finalize_probing(pa_alsa_profile
*to_be_finalized
, pa_alsa_profile
*next
) {
4222 if (!to_be_finalized
)
4225 if (to_be_finalized
->output_mappings
)
4226 PA_IDXSET_FOREACH(m
, to_be_finalized
->output_mappings
, idx
) {
4231 if (to_be_finalized
->supported
)
4234 /* If this mapping is also in the next profile, we won't close the
4235 * pcm handle here, because it would get immediately reopened
4237 if (next
&& next
->output_mappings
&& pa_idxset_get_by_data(next
->output_mappings
, m
, NULL
))
4240 snd_pcm_close(m
->output_pcm
);
4241 m
->output_pcm
= NULL
;
4244 if (to_be_finalized
->input_mappings
)
4245 PA_IDXSET_FOREACH(m
, to_be_finalized
->input_mappings
, idx
) {
4250 if (to_be_finalized
->supported
)
4253 /* If this mapping is also in the next profile, we won't close the
4254 * pcm handle here, because it would get immediately reopened
4256 if (next
&& next
->input_mappings
&& pa_idxset_get_by_data(next
->input_mappings
, m
, NULL
))
4259 snd_pcm_close(m
->input_pcm
);
4260 m
->input_pcm
= NULL
;
4264 static snd_pcm_t
* mapping_open_pcm(pa_alsa_mapping
*m
,
4265 const pa_sample_spec
*ss
,
4268 unsigned default_n_fragments
,
4269 unsigned default_fragment_size_msec
) {
4271 pa_sample_spec try_ss
= *ss
;
4272 pa_channel_map try_map
= m
->channel_map
;
4273 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
4275 try_ss
.channels
= try_map
.channels
;
4278 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
4279 pa_frame_size(&try_ss
);
4280 try_buffer_size
= default_n_fragments
* try_period_size
;
4282 return pa_alsa_open_by_template(
4283 m
->device_strings
, dev_id
, NULL
, &try_ss
,
4284 &try_map
, mode
, &try_period_size
,
4285 &try_buffer_size
, 0, NULL
, NULL
, true);
4288 static void paths_drop_unused(pa_hashmap
* h
, pa_hashmap
*keep
) {
4297 p
= pa_hashmap_iterate(h
, &state
, &key
);
4299 if (pa_hashmap_get(keep
, p
) == NULL
) {
4300 pa_hashmap_remove(h
, key
);
4301 pa_alsa_path_free(p
);
4303 p
= pa_hashmap_iterate(h
, &state
, &key
);
4307 void pa_alsa_profile_set_probe(
4308 pa_alsa_profile_set
*ps
,
4310 const pa_sample_spec
*ss
,
4311 unsigned default_n_fragments
,
4312 unsigned default_fragment_size_msec
) {
4315 pa_alsa_profile
*p
, *last
= NULL
;
4317 pa_hashmap
*broken_inputs
, *broken_outputs
, *used_paths
;
4326 broken_inputs
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
4327 broken_outputs
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
4328 used_paths
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
4330 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
4333 /* Skip if this is already marked that it is supported (i.e. from the config file) */
4334 if (!p
->supported
) {
4336 profile_finalize_probing(last
, p
);
4337 p
->supported
= true;
4339 if (p
->output_mappings
) {
4340 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4341 if (pa_hashmap_get(broken_outputs
, m
) == m
) {
4342 pa_log_debug("Skipping profile %s - will not be able to open output:%s", p
->name
, m
->name
);
4343 p
->supported
= false;
4349 if (p
->input_mappings
&& p
->supported
) {
4350 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4351 if (pa_hashmap_get(broken_inputs
, m
) == m
) {
4352 pa_log_debug("Skipping profile %s - will not be able to open input:%s", p
->name
, m
->name
);
4353 p
->supported
= false;
4360 pa_log_debug("Looking at profile %s", p
->name
);
4362 /* Check if we can open all new ones */
4363 if (p
->output_mappings
&& p
->supported
)
4364 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4369 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
4370 if (!(m
->output_pcm
= mapping_open_pcm(m
, ss
, dev_id
,
4371 SND_PCM_STREAM_PLAYBACK
,
4372 default_n_fragments
,
4373 default_fragment_size_msec
))) {
4374 p
->supported
= false;
4375 if (pa_idxset_size(p
->output_mappings
) == 1 &&
4376 ((!p
->input_mappings
) || pa_idxset_size(p
->input_mappings
) == 0)) {
4377 pa_log_debug("Caching failure to open output:%s", m
->name
);
4378 pa_hashmap_put(broken_outputs
, m
, m
);
4384 if (p
->input_mappings
&& p
->supported
)
4385 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4390 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
4391 if (!(m
->input_pcm
= mapping_open_pcm(m
, ss
, dev_id
,
4392 SND_PCM_STREAM_CAPTURE
,
4393 default_n_fragments
,
4394 default_fragment_size_msec
))) {
4395 p
->supported
= false;
4396 if (pa_idxset_size(p
->input_mappings
) == 1 &&
4397 ((!p
->output_mappings
) || pa_idxset_size(p
->output_mappings
) == 0)) {
4398 pa_log_debug("Caching failure to open input:%s", m
->name
);
4399 pa_hashmap_put(broken_inputs
, m
, m
);
4411 pa_log_debug("Profile %s supported.", p
->name
);
4413 if (p
->output_mappings
)
4414 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
4416 mapping_paths_probe(m
, p
, PA_ALSA_DIRECTION_OUTPUT
, used_paths
);
4418 if (p
->input_mappings
)
4419 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
4421 mapping_paths_probe(m
, p
, PA_ALSA_DIRECTION_INPUT
, used_paths
);
4425 profile_finalize_probing(last
, NULL
);
4427 pa_alsa_profile_set_drop_unsupported(ps
);
4429 paths_drop_unused(ps
->input_paths
, used_paths
);
4430 paths_drop_unused(ps
->output_paths
, used_paths
);
4431 pa_hashmap_free(broken_inputs
);
4432 pa_hashmap_free(broken_outputs
);
4433 pa_hashmap_free(used_paths
);
4438 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
4441 pa_alsa_decibel_fix
*db_fix
;
4446 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4449 pa_yes_no(ps
->auto_profiles
),
4450 pa_yes_no(ps
->probed
),
4451 pa_hashmap_size(ps
->mappings
),
4452 pa_hashmap_size(ps
->profiles
),
4453 pa_hashmap_size(ps
->decibel_fixes
));
4455 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4456 pa_alsa_mapping_dump(m
);
4458 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4459 pa_alsa_profile_dump(p
);
4461 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4462 pa_alsa_decibel_fix_dump(db_fix
);
4465 void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set
*ps
) {
4470 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
4471 if (!p
->supported
) {
4472 pa_hashmap_remove(ps
->profiles
, p
->name
);
4477 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
) {
4478 if (m
->supported
<= 0) {
4479 pa_hashmap_remove(ps
->mappings
, m
->name
);
4485 static pa_device_port
* device_port_alsa_init(pa_hashmap
*ports
, /* card ports */
4487 const char* description
,
4489 pa_alsa_setting
*setting
,
4490 pa_card_profile
*cp
,
4491 pa_hashmap
*extra
, /* sink/source ports */
4498 p
= pa_hashmap_get(ports
, name
);
4501 pa_alsa_port_data
*data
;
4502 pa_device_port_new_data port_data
;
4504 pa_device_port_new_data_init(&port_data
);
4505 pa_device_port_new_data_set_name(&port_data
, name
);
4506 pa_device_port_new_data_set_description(&port_data
, description
);
4507 pa_device_port_new_data_set_direction(&port_data
, path
->direction
== PA_ALSA_DIRECTION_OUTPUT
? PA_DIRECTION_OUTPUT
: PA_DIRECTION_INPUT
);
4509 p
= pa_device_port_new(core
, &port_data
, sizeof(pa_alsa_port_data
));
4510 pa_device_port_new_data_done(&port_data
);
4512 pa_hashmap_put(ports
, p
->name
, p
);
4513 pa_proplist_update(p
->proplist
, PA_UPDATE_REPLACE
, path
->proplist
);
4515 data
= PA_DEVICE_PORT_DATA(p
);
4517 data
->setting
= setting
;
4522 pa_hashmap_put(p
->profiles
, cp
->name
, cp
);
4525 pa_hashmap_put(extra
, p
->name
, p
);
4526 pa_device_port_ref(p
);
4532 void pa_alsa_path_set_add_ports(
4533 pa_alsa_path_set
*ps
,
4534 pa_card_profile
*cp
,
4535 pa_hashmap
*ports
, /* card ports */
4536 pa_hashmap
*extra
, /* sink/source ports */
4547 PA_HASHMAP_FOREACH(path
, ps
->paths
, state
) {
4548 if (!path
->settings
|| !path
->settings
->next
) {
4549 /* If there is no or just one setting we only need a
4551 pa_device_port
*port
= device_port_alsa_init(ports
, path
->name
,
4552 path
->description
, path
, path
->settings
, cp
, extra
, core
);
4553 port
->priority
= path
->priority
* 100;
4557 PA_LLIST_FOREACH(s
, path
->settings
) {
4558 pa_device_port
*port
;
4561 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4563 if (s
->description
[0])
4564 d
= pa_sprintf_malloc("%s / %s", path
->description
, s
->description
);
4566 d
= pa_xstrdup(path
->description
);
4568 port
= device_port_alsa_init(ports
, n
, d
, path
, s
, cp
, extra
, core
);
4569 port
->priority
= path
->priority
* 100 + s
->priority
;
4578 void pa_alsa_add_ports(void *sink_or_source_new_data
, pa_alsa_path_set
*ps
, pa_card
*card
) {
4581 pa_assert(sink_or_source_new_data
);
4584 if (ps
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
4585 ports
= ((pa_sink_new_data
*) sink_or_source_new_data
)->ports
;
4587 ports
= ((pa_source_new_data
*) sink_or_source_new_data
)->ports
;
4589 if (ps
->paths
&& pa_hashmap_size(ps
->paths
) > 0) {
4591 pa_alsa_path_set_add_ports(ps
, NULL
, card
->ports
, ports
, card
->core
);
4594 pa_log_debug("Added %u ports", pa_hashmap_size(ports
));