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 *name
, const struct description_map dm
[], unsigned n
) {
63 for (i
= 0; i
< n
; i
++)
64 if (pa_streq(dm
[i
].name
, name
))
65 return _(dm
[i
].description
);
70 struct pa_alsa_fdlist
{
73 /* This is a temporary buffer used to avoid lots of mallocs */
74 struct pollfd
*work_fds
;
80 pa_defer_event
*defer
;
85 void (*cb
)(void *userdata
);
89 static void io_cb(pa_mainloop_api
*a
, pa_io_event
*e
, int fd
, pa_io_event_flags_t events
, void *userdata
) {
91 struct pa_alsa_fdlist
*fdl
= userdata
;
94 unsigned short revents
;
98 pa_assert(fdl
->mixer
|| fdl
->hctl
);
100 pa_assert(fdl
->work_fds
);
107 memcpy(fdl
->work_fds
, fdl
->fds
, sizeof(struct pollfd
) * fdl
->num_fds
);
109 for (i
= 0; i
< fdl
->num_fds
; i
++) {
110 if (e
== fdl
->ios
[i
]) {
111 if (events
& PA_IO_EVENT_INPUT
)
112 fdl
->work_fds
[i
].revents
|= POLLIN
;
113 if (events
& PA_IO_EVENT_OUTPUT
)
114 fdl
->work_fds
[i
].revents
|= POLLOUT
;
115 if (events
& PA_IO_EVENT_ERROR
)
116 fdl
->work_fds
[i
].revents
|= POLLERR
;
117 if (events
& PA_IO_EVENT_HANGUP
)
118 fdl
->work_fds
[i
].revents
|= POLLHUP
;
123 pa_assert(i
!= fdl
->num_fds
);
126 err
= snd_hctl_poll_descriptors_revents(fdl
->hctl
, fdl
->work_fds
, fdl
->num_fds
, &revents
);
128 err
= snd_mixer_poll_descriptors_revents(fdl
->mixer
, fdl
->work_fds
, fdl
->num_fds
, &revents
);
131 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err
));
135 a
->defer_enable(fdl
->defer
, 1);
139 snd_hctl_handle_events(fdl
->hctl
);
141 snd_mixer_handle_events(fdl
->mixer
);
145 static void defer_cb(pa_mainloop_api
*a
, pa_defer_event
*e
, void *userdata
) {
146 struct pa_alsa_fdlist
*fdl
= userdata
;
153 pa_assert(fdl
->mixer
|| fdl
->hctl
);
155 a
->defer_enable(fdl
->defer
, 0);
158 n
= snd_hctl_poll_descriptors_count(fdl
->hctl
);
160 n
= snd_mixer_poll_descriptors_count(fdl
->mixer
);
163 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n
));
166 num_fds
= (unsigned) n
;
168 if (num_fds
!= fdl
->num_fds
) {
172 pa_xfree(fdl
->work_fds
);
173 fdl
->fds
= pa_xnew0(struct pollfd
, num_fds
);
174 fdl
->work_fds
= pa_xnew(struct pollfd
, num_fds
);
177 memset(fdl
->work_fds
, 0, sizeof(struct pollfd
) * num_fds
);
180 err
= snd_hctl_poll_descriptors(fdl
->hctl
, fdl
->work_fds
, num_fds
);
182 err
= snd_mixer_poll_descriptors(fdl
->mixer
, fdl
->work_fds
, num_fds
);
185 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err
));
191 if (memcmp(fdl
->fds
, fdl
->work_fds
, sizeof(struct pollfd
) * num_fds
) == 0)
195 for (i
= 0; i
< fdl
->num_fds
; i
++)
196 a
->io_free(fdl
->ios
[i
]);
198 if (num_fds
!= fdl
->num_fds
) {
205 fdl
->ios
= pa_xnew(pa_io_event
*, num_fds
);
208 temp
= fdl
->work_fds
;
209 fdl
->work_fds
= fdl
->fds
;
212 fdl
->num_fds
= num_fds
;
214 for (i
= 0;i
< num_fds
;i
++)
215 fdl
->ios
[i
] = a
->io_new(a
, fdl
->fds
[i
].fd
,
216 ((fdl
->fds
[i
].events
& POLLIN
) ? PA_IO_EVENT_INPUT
: 0) |
217 ((fdl
->fds
[i
].events
& POLLOUT
) ? PA_IO_EVENT_OUTPUT
: 0),
221 struct pa_alsa_fdlist
*pa_alsa_fdlist_new(void) {
222 struct pa_alsa_fdlist
*fdl
;
224 fdl
= pa_xnew0(struct pa_alsa_fdlist
, 1);
229 void pa_alsa_fdlist_free(struct pa_alsa_fdlist
*fdl
) {
234 fdl
->m
->defer_free(fdl
->defer
);
240 for (i
= 0; i
< fdl
->num_fds
; i
++)
241 fdl
->m
->io_free(fdl
->ios
[i
]);
248 pa_xfree(fdl
->work_fds
);
253 /* We can listen to either a snd_hctl_t or a snd_mixer_t, but not both */
254 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
) {
256 pa_assert(hctl_handle
|| mixer_handle
);
257 pa_assert(!(hctl_handle
&& mixer_handle
));
261 fdl
->hctl
= hctl_handle
;
262 fdl
->mixer
= mixer_handle
;
264 fdl
->defer
= m
->defer_new(m
, defer_cb
, fdl
);
269 struct pa_alsa_mixer_pdata
{
271 pa_rtpoll_item
*poll_item
;
276 struct pa_alsa_mixer_pdata
*pa_alsa_mixer_pdata_new(void) {
277 struct pa_alsa_mixer_pdata
*pd
;
279 pd
= pa_xnew0(struct pa_alsa_mixer_pdata
, 1);
284 void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata
*pd
) {
288 pa_rtpoll_item_free(pd
->poll_item
);
294 static int rtpoll_work_cb(pa_rtpoll_item
*i
) {
295 struct pa_alsa_mixer_pdata
*pd
;
298 unsigned short revents
= 0;
301 pd
= pa_rtpoll_item_get_userdata(i
);
303 pa_assert_fp(i
== pd
->poll_item
);
305 p
= pa_rtpoll_item_get_pollfd(i
, &n_fds
);
307 if ((err
= snd_mixer_poll_descriptors_revents(pd
->mixer
, p
, n_fds
, &revents
)) < 0) {
308 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err
));
314 if (revents
& (POLLNVAL
| POLLERR
)) {
315 pa_log_debug("Device disconnected, stopping poll on mixer");
317 } else if (revents
& POLLERR
) {
318 /* This shouldn't happen. */
319 pa_log_error("Got a POLLERR (revents = %04x), stopping poll on mixer", revents
);
323 err
= snd_mixer_handle_events(pd
->mixer
);
325 if (PA_LIKELY(err
>= 0)) {
326 pa_rtpoll_item_free(i
);
327 pa_alsa_set_mixer_rtpoll(pd
, pd
->mixer
, pd
->rtpoll
);
329 pa_log_error("Error handling mixer event: %s", pa_alsa_strerror(err
));
338 pa_rtpoll_item_free(i
);
340 pd
->poll_item
= NULL
;
347 int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata
*pd
, snd_mixer_t
*mixer
, pa_rtpoll
*rtp
) {
356 if ((n
= snd_mixer_poll_descriptors_count(mixer
)) < 0) {
357 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n
));
361 i
= pa_rtpoll_item_new(rtp
, PA_RTPOLL_LATE
, (unsigned) n
);
363 p
= pa_rtpoll_item_get_pollfd(i
, NULL
);
365 memset(p
, 0, sizeof(struct pollfd
) * n
);
367 if ((err
= snd_mixer_poll_descriptors(mixer
, p
, (unsigned) n
)) < 0) {
368 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err
));
369 pa_rtpoll_item_free(i
);
377 pa_rtpoll_item_set_userdata(i
, pd
);
378 pa_rtpoll_item_set_work_callback(i
, rtpoll_work_cb
);
385 static const snd_mixer_selem_channel_id_t alsa_channel_ids
[PA_CHANNEL_POSITION_MAX
] = {
386 [PA_CHANNEL_POSITION_MONO
] = SND_MIXER_SCHN_MONO
, /* The ALSA name is just an alias! */
388 [PA_CHANNEL_POSITION_FRONT_CENTER
] = SND_MIXER_SCHN_FRONT_CENTER
,
389 [PA_CHANNEL_POSITION_FRONT_LEFT
] = SND_MIXER_SCHN_FRONT_LEFT
,
390 [PA_CHANNEL_POSITION_FRONT_RIGHT
] = SND_MIXER_SCHN_FRONT_RIGHT
,
392 [PA_CHANNEL_POSITION_REAR_CENTER
] = SND_MIXER_SCHN_REAR_CENTER
,
393 [PA_CHANNEL_POSITION_REAR_LEFT
] = SND_MIXER_SCHN_REAR_LEFT
,
394 [PA_CHANNEL_POSITION_REAR_RIGHT
] = SND_MIXER_SCHN_REAR_RIGHT
,
396 [PA_CHANNEL_POSITION_LFE
] = SND_MIXER_SCHN_WOOFER
,
398 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
399 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
401 [PA_CHANNEL_POSITION_SIDE_LEFT
] = SND_MIXER_SCHN_SIDE_LEFT
,
402 [PA_CHANNEL_POSITION_SIDE_RIGHT
] = SND_MIXER_SCHN_SIDE_RIGHT
,
404 [PA_CHANNEL_POSITION_AUX0
] = SND_MIXER_SCHN_UNKNOWN
,
405 [PA_CHANNEL_POSITION_AUX1
] = SND_MIXER_SCHN_UNKNOWN
,
406 [PA_CHANNEL_POSITION_AUX2
] = SND_MIXER_SCHN_UNKNOWN
,
407 [PA_CHANNEL_POSITION_AUX3
] = SND_MIXER_SCHN_UNKNOWN
,
408 [PA_CHANNEL_POSITION_AUX4
] = SND_MIXER_SCHN_UNKNOWN
,
409 [PA_CHANNEL_POSITION_AUX5
] = SND_MIXER_SCHN_UNKNOWN
,
410 [PA_CHANNEL_POSITION_AUX6
] = SND_MIXER_SCHN_UNKNOWN
,
411 [PA_CHANNEL_POSITION_AUX7
] = SND_MIXER_SCHN_UNKNOWN
,
412 [PA_CHANNEL_POSITION_AUX8
] = SND_MIXER_SCHN_UNKNOWN
,
413 [PA_CHANNEL_POSITION_AUX9
] = SND_MIXER_SCHN_UNKNOWN
,
414 [PA_CHANNEL_POSITION_AUX10
] = SND_MIXER_SCHN_UNKNOWN
,
415 [PA_CHANNEL_POSITION_AUX11
] = SND_MIXER_SCHN_UNKNOWN
,
416 [PA_CHANNEL_POSITION_AUX12
] = SND_MIXER_SCHN_UNKNOWN
,
417 [PA_CHANNEL_POSITION_AUX13
] = SND_MIXER_SCHN_UNKNOWN
,
418 [PA_CHANNEL_POSITION_AUX14
] = SND_MIXER_SCHN_UNKNOWN
,
419 [PA_CHANNEL_POSITION_AUX15
] = SND_MIXER_SCHN_UNKNOWN
,
420 [PA_CHANNEL_POSITION_AUX16
] = SND_MIXER_SCHN_UNKNOWN
,
421 [PA_CHANNEL_POSITION_AUX17
] = SND_MIXER_SCHN_UNKNOWN
,
422 [PA_CHANNEL_POSITION_AUX18
] = SND_MIXER_SCHN_UNKNOWN
,
423 [PA_CHANNEL_POSITION_AUX19
] = SND_MIXER_SCHN_UNKNOWN
,
424 [PA_CHANNEL_POSITION_AUX20
] = SND_MIXER_SCHN_UNKNOWN
,
425 [PA_CHANNEL_POSITION_AUX21
] = SND_MIXER_SCHN_UNKNOWN
,
426 [PA_CHANNEL_POSITION_AUX22
] = SND_MIXER_SCHN_UNKNOWN
,
427 [PA_CHANNEL_POSITION_AUX23
] = SND_MIXER_SCHN_UNKNOWN
,
428 [PA_CHANNEL_POSITION_AUX24
] = SND_MIXER_SCHN_UNKNOWN
,
429 [PA_CHANNEL_POSITION_AUX25
] = SND_MIXER_SCHN_UNKNOWN
,
430 [PA_CHANNEL_POSITION_AUX26
] = SND_MIXER_SCHN_UNKNOWN
,
431 [PA_CHANNEL_POSITION_AUX27
] = SND_MIXER_SCHN_UNKNOWN
,
432 [PA_CHANNEL_POSITION_AUX28
] = SND_MIXER_SCHN_UNKNOWN
,
433 [PA_CHANNEL_POSITION_AUX29
] = SND_MIXER_SCHN_UNKNOWN
,
434 [PA_CHANNEL_POSITION_AUX30
] = SND_MIXER_SCHN_UNKNOWN
,
435 [PA_CHANNEL_POSITION_AUX31
] = SND_MIXER_SCHN_UNKNOWN
,
437 [PA_CHANNEL_POSITION_TOP_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
439 [PA_CHANNEL_POSITION_TOP_FRONT_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
440 [PA_CHANNEL_POSITION_TOP_FRONT_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
441 [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
,
443 [PA_CHANNEL_POSITION_TOP_REAR_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
444 [PA_CHANNEL_POSITION_TOP_REAR_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
445 [PA_CHANNEL_POSITION_TOP_REAR_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
448 static void setting_free(pa_alsa_setting
*s
) {
452 pa_idxset_free(s
->options
, NULL
);
455 pa_xfree(s
->description
);
459 static void option_free(pa_alsa_option
*o
) {
462 pa_xfree(o
->alsa_name
);
464 pa_xfree(o
->description
);
468 static void decibel_fix_free(pa_alsa_decibel_fix
*db_fix
) {
471 pa_xfree(db_fix
->name
);
472 pa_xfree(db_fix
->db_values
);
477 static void jack_free(pa_alsa_jack
*j
) {
480 pa_xfree(j
->alsa_name
);
485 static void element_free(pa_alsa_element
*e
) {
489 while ((o
= e
->options
)) {
490 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
495 decibel_fix_free(e
->db_fix
);
497 pa_xfree(e
->alsa_name
);
501 void pa_alsa_path_free(pa_alsa_path
*p
) {
508 while ((j
= p
->jacks
)) {
509 PA_LLIST_REMOVE(pa_alsa_jack
, p
->jacks
, j
);
513 while ((e
= p
->elements
)) {
514 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
518 while ((s
= p
->settings
)) {
519 PA_LLIST_REMOVE(pa_alsa_setting
, p
->settings
, s
);
523 pa_proplist_free(p
->proplist
);
525 pa_xfree(p
->description
);
529 void pa_alsa_path_set_free(pa_alsa_path_set
*ps
) {
533 pa_hashmap_free(ps
->paths
, NULL
);
538 static long to_alsa_dB(pa_volume_t v
) {
539 return (long) (pa_sw_volume_to_dB(v
) * 100.0);
542 static pa_volume_t
from_alsa_dB(long v
) {
543 return pa_sw_volume_from_dB((double) v
/ 100.0);
546 static long to_alsa_volume(pa_volume_t v
, long min
, long max
) {
549 w
= (long) round(((double) v
* (double) (max
- min
)) / PA_VOLUME_NORM
) + min
;
550 return PA_CLAMP_UNLIKELY(w
, min
, max
);
553 static pa_volume_t
from_alsa_volume(long v
, long min
, long max
) {
554 return (pa_volume_t
) round(((double) (v
- min
) * PA_VOLUME_NORM
) / (double) (max
- min
));
557 #define SELEM_INIT(sid, name) \
559 snd_mixer_selem_id_alloca(&(sid)); \
560 snd_mixer_selem_id_set_name((sid), (name)); \
561 snd_mixer_selem_id_set_index((sid), 0); \
564 static int element_get_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
565 snd_mixer_selem_id_t
*sid
;
566 snd_mixer_elem_t
*me
;
567 snd_mixer_selem_channel_id_t c
;
568 pa_channel_position_mask_t mask
= 0;
576 SELEM_INIT(sid
, e
->alsa_name
);
577 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
578 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
582 pa_cvolume_mute(v
, cm
->channels
);
584 /* We take the highest volume of all channels that match */
586 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
593 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
594 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
596 if ((r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
)) >= 0) {
597 /* If the channel volume is outside the limits set
598 * by the dB fix, we clamp the hw volume to be
599 * within the limits. */
600 if (value
< e
->db_fix
->min_step
) {
601 value
= e
->db_fix
->min_step
;
602 snd_mixer_selem_set_playback_volume(me
, c
, value
);
603 pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
604 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
605 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
606 } else if (value
> e
->db_fix
->max_step
) {
607 value
= e
->db_fix
->max_step
;
608 snd_mixer_selem_set_playback_volume(me
, c
, value
);
609 pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
610 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
611 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
614 /* Volume step -> dB value conversion. */
615 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
618 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
622 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
624 if ((r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
)) >= 0) {
625 /* If the channel volume is outside the limits set
626 * by the dB fix, we clamp the hw volume to be
627 * within the limits. */
628 if (value
< e
->db_fix
->min_step
) {
629 value
= e
->db_fix
->min_step
;
630 snd_mixer_selem_set_capture_volume(me
, c
, value
);
631 pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
632 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
633 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
634 } else if (value
> e
->db_fix
->max_step
) {
635 value
= e
->db_fix
->max_step
;
636 snd_mixer_selem_set_capture_volume(me
, c
, value
);
637 pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
638 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
639 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
642 /* Volume step -> dB value conversion. */
643 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
646 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
654 #ifdef HAVE_VALGRIND_MEMCHECK_H
655 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
658 f
= from_alsa_dB(value
);
663 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
664 if (snd_mixer_selem_has_playback_channel(me
, c
))
665 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
669 if (snd_mixer_selem_has_capture_channel(me
, c
))
670 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
678 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
681 for (k
= 0; k
< cm
->channels
; k
++)
682 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
683 if (v
->values
[k
] < f
)
686 mask
|= e
->masks
[c
][e
->n_channels
-1];
689 for (k
= 0; k
< cm
->channels
; k
++)
690 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
691 v
->values
[k
] = PA_VOLUME_NORM
;
696 int pa_alsa_path_get_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
707 pa_cvolume_reset(v
, cm
->channels
);
709 PA_LLIST_FOREACH(e
, p
->elements
) {
712 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
715 pa_assert(!p
->has_dB
|| e
->has_dB
);
717 if (element_get_volume(e
, m
, cm
, &ev
) < 0)
720 /* If we have no dB information all we can do is take the first element and leave */
726 pa_sw_cvolume_multiply(v
, v
, &ev
);
732 static int element_get_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t
*b
) {
733 snd_mixer_selem_id_t
*sid
;
734 snd_mixer_elem_t
*me
;
735 snd_mixer_selem_channel_id_t c
;
741 SELEM_INIT(sid
, e
->alsa_name
);
742 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
743 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
747 /* We return muted if at least one channel is muted */
749 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
753 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
754 if (snd_mixer_selem_has_playback_channel(me
, c
))
755 r
= snd_mixer_selem_get_playback_switch(me
, c
, &value
);
759 if (snd_mixer_selem_has_capture_channel(me
, c
))
760 r
= snd_mixer_selem_get_capture_switch(me
, c
, &value
);
778 int pa_alsa_path_get_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t
*muted
) {
788 PA_LLIST_FOREACH(e
, p
->elements
) {
791 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
794 if (element_get_switch(e
, m
, &b
) < 0)
807 /* Finds the closest item in db_fix->db_values and returns the corresponding
808 * step. *db_value is replaced with the value from the db_values table.
809 * Rounding is done based on the rounding parameter: -1 means rounding down and
810 * +1 means rounding up. */
811 static long decibel_fix_get_step(pa_alsa_decibel_fix
*db_fix
, long *db_value
, int rounding
) {
817 pa_assert(rounding
!= 0);
819 max_i
= db_fix
->max_step
- db_fix
->min_step
;
822 for (i
= 0; i
< max_i
; i
++) {
823 if (db_fix
->db_values
[i
] >= *db_value
)
827 for (i
= 0; i
< max_i
; i
++) {
828 if (db_fix
->db_values
[i
+ 1] > *db_value
)
833 *db_value
= db_fix
->db_values
[i
];
835 return i
+ db_fix
->min_step
;
838 /* Alsa lib documentation says for snd_mixer_selem_set_playback_dB() direction argument,
839 * that "-1 = accurate or first below, 0 = accurate, 1 = accurate or first above".
840 * But even with accurate nearest dB volume step is not selected, so that is why we need
841 * this function. Returns 0 and nearest selectable volume in *value_dB on success or
842 * negative error code if fails. */
843 static int element_get_nearest_alsa_dB(snd_mixer_elem_t
*me
, snd_mixer_selem_channel_id_t c
, pa_alsa_direction_t d
, long *value_dB
) {
853 if (d
== PA_ALSA_DIRECTION_OUTPUT
) {
854 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, *value_dB
, +1, &alsa_val
)) >= 0)
855 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value_high
);
860 if (value_high
== *value_dB
)
863 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, *value_dB
, -1, &alsa_val
)) >= 0)
864 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value_low
);
866 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, *value_dB
, +1, &alsa_val
)) >= 0)
867 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value_high
);
872 if (value_high
== *value_dB
)
875 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, *value_dB
, -1, &alsa_val
)) >= 0)
876 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value_low
);
882 if (labs(value_high
- *value_dB
) < labs(value_low
- *value_dB
))
883 *value_dB
= value_high
;
885 *value_dB
= value_low
;
890 static int element_set_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
, pa_bool_t deferred_volume
, pa_bool_t write_to_hw
) {
892 snd_mixer_selem_id_t
*sid
;
894 snd_mixer_elem_t
*me
;
895 snd_mixer_selem_channel_id_t c
;
896 pa_channel_position_mask_t mask
= 0;
903 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
905 SELEM_INIT(sid
, e
->alsa_name
);
906 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
907 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
911 pa_cvolume_mute(&rv
, cm
->channels
);
913 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
915 pa_volume_t f
= PA_VOLUME_MUTED
;
916 pa_bool_t found
= FALSE
;
918 for (k
= 0; k
< cm
->channels
; k
++)
919 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
])) {
921 if (v
->values
[k
] > f
)
926 /* Hmm, so this channel does not exist in the volume
927 * struct, so let's bind it to the overall max of the
929 f
= pa_cvolume_max(v
);
933 long value
= to_alsa_dB(f
);
936 if (e
->volume_limit
>= 0 && value
> (e
->max_dB
* 100))
937 value
= e
->max_dB
* 100;
939 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
940 /* If we call set_playback_volume() without checking first
941 * if the channel is available, ALSA behaves very
942 * strangely and doesn't fail the call */
943 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
947 r
= snd_mixer_selem_set_playback_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
949 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
955 if (deferred_volume
) {
956 if ((r
= element_get_nearest_alsa_dB(me
, c
, PA_ALSA_DIRECTION_OUTPUT
, &value
)) >= 0)
957 r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, 0);
959 if ((r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, rounding
)) >= 0)
960 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
964 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
965 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value
);
971 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
975 r
= snd_mixer_selem_set_capture_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
977 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
983 if (deferred_volume
) {
984 if ((r
= element_get_nearest_alsa_dB(me
, c
, PA_ALSA_DIRECTION_INPUT
, &value
)) >= 0)
985 r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, 0);
987 if ((r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, rounding
)) >= 0)
988 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
992 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
993 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value
);
1003 #ifdef HAVE_VALGRIND_MEMCHECK_H
1004 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
1007 f
= from_alsa_dB(value
);
1012 value
= to_alsa_volume(f
, e
->min_volume
, e
->max_volume
);
1014 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1015 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
1016 if ((r
= snd_mixer_selem_set_playback_volume(me
, c
, value
)) >= 0)
1017 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
1021 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
1022 if ((r
= snd_mixer_selem_set_capture_volume(me
, c
, value
)) >= 0)
1023 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
1031 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
1034 for (k
= 0; k
< cm
->channels
; k
++)
1035 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
1036 if (rv
.values
[k
] < f
)
1039 mask
|= e
->masks
[c
][e
->n_channels
-1];
1042 for (k
= 0; k
< cm
->channels
; k
++)
1043 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
1044 rv
.values
[k
] = PA_VOLUME_NORM
;
1050 int pa_alsa_path_set_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
, pa_bool_t deferred_volume
, pa_bool_t write_to_hw
) {
1059 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
1064 rv
= *v
; /* Remaining adjustment */
1065 pa_cvolume_reset(v
, cm
->channels
); /* Adjustment done */
1067 PA_LLIST_FOREACH(e
, p
->elements
) {
1070 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
1073 pa_assert(!p
->has_dB
|| e
->has_dB
);
1076 if (element_set_volume(e
, m
, cm
, &ev
, deferred_volume
, write_to_hw
) < 0)
1084 pa_sw_cvolume_multiply(v
, v
, &ev
);
1085 pa_sw_cvolume_divide(&rv
, &rv
, &ev
);
1091 static int element_set_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t b
) {
1092 snd_mixer_elem_t
*me
;
1093 snd_mixer_selem_id_t
*sid
;
1099 SELEM_INIT(sid
, e
->alsa_name
);
1100 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1101 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1105 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1106 r
= snd_mixer_selem_set_playback_switch_all(me
, b
);
1108 r
= snd_mixer_selem_set_capture_switch_all(me
, b
);
1111 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1116 int pa_alsa_path_set_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t muted
) {
1125 PA_LLIST_FOREACH(e
, p
->elements
) {
1127 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
1130 if (element_set_switch(e
, m
, !muted
) < 0)
1137 /* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
1138 * function sets all channels of the volume element to e->min_volume, 0 dB or
1139 * e->constant_volume. */
1140 static int element_set_constant_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1141 snd_mixer_elem_t
*me
= NULL
;
1142 snd_mixer_selem_id_t
*sid
= NULL
;
1145 pa_bool_t volume_set
= FALSE
;
1150 SELEM_INIT(sid
, e
->alsa_name
);
1151 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1152 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1156 switch (e
->volume_use
) {
1157 case PA_ALSA_VOLUME_OFF
:
1158 volume
= e
->min_volume
;
1162 case PA_ALSA_VOLUME_ZERO
:
1166 volume
= decibel_fix_get_step(e
->db_fix
, &dB
, (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
? +1 : -1));
1171 case PA_ALSA_VOLUME_CONSTANT
:
1172 volume
= e
->constant_volume
;
1177 pa_assert_not_reached();
1181 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1182 r
= snd_mixer_selem_set_playback_volume_all(me
, volume
);
1184 r
= snd_mixer_selem_set_capture_volume_all(me
, volume
);
1186 pa_assert(e
->volume_use
== PA_ALSA_VOLUME_ZERO
);
1187 pa_assert(!e
->db_fix
);
1189 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1190 r
= snd_mixer_selem_set_playback_dB_all(me
, 0, +1);
1192 r
= snd_mixer_selem_set_capture_dB_all(me
, 0, -1);
1196 pa_log_warn("Failed to set volume of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1201 int pa_alsa_path_select(pa_alsa_path
*p
, pa_alsa_setting
*s
, snd_mixer_t
*m
, bool device_is_muted
) {
1208 pa_log_debug("Activating path %s", p
->name
);
1209 pa_alsa_path_dump(p
);
1211 /* First turn on hw mute if available, to avoid noise
1212 * when setting the mixer controls. */
1213 if (p
->mute_during_activation
) {
1214 PA_LLIST_FOREACH(e
, p
->elements
) {
1215 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
1216 /* If the muting fails here, that's not a critical problem for
1217 * selecting a path, so we ignore the return value.
1218 * element_set_switch() will print a warning anyway, so this
1219 * won't be a silent failure either. */
1220 (void) element_set_switch(e
, m
, FALSE
);
1224 PA_LLIST_FOREACH(e
, p
->elements
) {
1226 switch (e
->switch_use
) {
1227 case PA_ALSA_SWITCH_OFF
:
1228 r
= element_set_switch(e
, m
, FALSE
);
1231 case PA_ALSA_SWITCH_ON
:
1232 r
= element_set_switch(e
, m
, TRUE
);
1235 case PA_ALSA_SWITCH_MUTE
:
1236 case PA_ALSA_SWITCH_IGNORE
:
1237 case PA_ALSA_SWITCH_SELECT
:
1245 switch (e
->volume_use
) {
1246 case PA_ALSA_VOLUME_OFF
:
1247 case PA_ALSA_VOLUME_ZERO
:
1248 case PA_ALSA_VOLUME_CONSTANT
:
1249 r
= element_set_constant_volume(e
, m
);
1252 case PA_ALSA_VOLUME_MERGE
:
1253 case PA_ALSA_VOLUME_IGNORE
:
1263 setting_select(s
, m
);
1265 /* Finally restore hw mute to the device mute status. */
1266 if (p
->mute_during_activation
) {
1267 PA_LLIST_FOREACH(e
, p
->elements
) {
1268 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
) {
1269 if (element_set_switch(e
, m
, !device_is_muted
) < 0)
1278 static int check_required(pa_alsa_element
*e
, snd_mixer_elem_t
*me
) {
1279 pa_bool_t has_switch
;
1280 pa_bool_t has_enumeration
;
1281 pa_bool_t has_volume
;
1286 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1288 snd_mixer_selem_has_playback_switch(me
) ||
1289 (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
));
1292 snd_mixer_selem_has_capture_switch(me
) ||
1293 (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
));
1296 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1298 snd_mixer_selem_has_playback_volume(me
) ||
1299 (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
));
1302 snd_mixer_selem_has_capture_volume(me
) ||
1303 (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
));
1306 has_enumeration
= snd_mixer_selem_is_enumerated(me
);
1308 if ((e
->required
== PA_ALSA_REQUIRED_SWITCH
&& !has_switch
) ||
1309 (e
->required
== PA_ALSA_REQUIRED_VOLUME
&& !has_volume
) ||
1310 (e
->required
== PA_ALSA_REQUIRED_ENUMERATION
&& !has_enumeration
))
1313 if (e
->required
== PA_ALSA_REQUIRED_ANY
&& !(has_switch
|| has_volume
|| has_enumeration
))
1316 if ((e
->required_absent
== PA_ALSA_REQUIRED_SWITCH
&& has_switch
) ||
1317 (e
->required_absent
== PA_ALSA_REQUIRED_VOLUME
&& has_volume
) ||
1318 (e
->required_absent
== PA_ALSA_REQUIRED_ENUMERATION
&& has_enumeration
))
1321 if (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& (has_switch
|| has_volume
|| has_enumeration
))
1324 if (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) {
1325 switch (e
->required_any
) {
1326 case PA_ALSA_REQUIRED_VOLUME
:
1327 e
->path
->req_any_present
|= (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
);
1329 case PA_ALSA_REQUIRED_SWITCH
:
1330 e
->path
->req_any_present
|= (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
);
1332 case PA_ALSA_REQUIRED_ENUMERATION
:
1333 e
->path
->req_any_present
|= (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1335 case PA_ALSA_REQUIRED_ANY
:
1336 e
->path
->req_any_present
|=
1337 (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) ||
1338 (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) ||
1339 (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1342 pa_assert_not_reached();
1346 if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1348 PA_LLIST_FOREACH(o
, e
->options
) {
1349 e
->path
->req_any_present
|= (o
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) &&
1351 if (o
->required
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
< 0)
1353 if (o
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
>= 0)
1361 static int element_probe(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1362 snd_mixer_selem_id_t
*sid
;
1363 snd_mixer_elem_t
*me
;
1369 SELEM_INIT(sid
, e
->alsa_name
);
1371 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1373 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1376 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1377 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1378 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1383 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1384 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1386 if (!snd_mixer_selem_has_playback_switch(me
)) {
1387 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1388 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1390 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1395 if (!snd_mixer_selem_has_capture_switch(me
)) {
1396 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1397 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1399 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1403 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1404 e
->direction_try_other
= FALSE
;
1407 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1409 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1411 if (!snd_mixer_selem_has_playback_volume(me
)) {
1412 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1413 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1415 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1420 if (!snd_mixer_selem_has_capture_volume(me
)) {
1421 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1422 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1424 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1428 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1429 long min_dB
= 0, max_dB
= 0;
1432 e
->direction_try_other
= FALSE
;
1434 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1435 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1437 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1440 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1444 if (e
->min_volume
>= e
->max_volume
) {
1445 pa_log_warn("Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense.", e
->min_volume
, e
->max_volume
);
1446 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1448 } else if (e
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&&
1449 (e
->min_volume
> e
->constant_volume
|| e
->max_volume
< e
->constant_volume
)) {
1450 pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
1451 e
->constant_volume
, e
->alsa_name
, e
->min_volume
, e
->max_volume
);
1452 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1456 pa_channel_position_t p
;
1459 ((e
->min_volume
> e
->db_fix
->min_step
) ||
1460 (e
->max_volume
< e
->db_fix
->max_step
))) {
1461 pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1462 "real hardware range (%li-%li). Disabling the decibel fix.", e
->alsa_name
,
1463 e
->db_fix
->min_step
, e
->db_fix
->max_step
,
1464 e
->min_volume
, e
->max_volume
);
1466 decibel_fix_free(e
->db_fix
);
1472 e
->min_volume
= e
->db_fix
->min_step
;
1473 e
->max_volume
= e
->db_fix
->max_step
;
1474 min_dB
= e
->db_fix
->db_values
[0];
1475 max_dB
= e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
];
1476 } else if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1477 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1479 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1481 /* Check that the kernel driver returns consistent limits with
1482 * both _get_*_dB_range() and _ask_*_vol_dB(). */
1483 if (e
->has_dB
&& !e
->db_fix
) {
1484 long min_dB_checked
= 0;
1485 long max_dB_checked
= 0;
1487 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1488 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1490 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1493 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->min_volume
);
1497 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1498 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1500 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1503 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->max_volume
);
1507 if (min_dB
!= min_dB_checked
|| max_dB
!= max_dB_checked
) {
1508 pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
1509 "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
1510 "%0.2f dB at level %li.",
1512 min_dB
/ 100.0, max_dB
/ 100.0,
1513 min_dB_checked
/ 100.0, e
->min_volume
, max_dB_checked
/ 100.0, e
->max_volume
);
1519 #ifdef HAVE_VALGRIND_MEMCHECK_H
1520 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1521 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1524 e
->min_dB
= ((double) min_dB
) / 100.0;
1525 e
->max_dB
= ((double) max_dB
) / 100.0;
1527 if (min_dB
>= max_dB
) {
1528 pa_assert(!e
->db_fix
);
1529 pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.", e
->min_dB
, e
->max_dB
);
1534 if (e
->volume_limit
>= 0) {
1535 if (e
->volume_limit
<= e
->min_volume
|| e
->volume_limit
> e
->max_volume
)
1536 pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1537 "%li-%li. The volume limit is ignored.",
1538 e
->alsa_name
, e
->path
->name
, e
->volume_limit
, e
->min_volume
+ 1, e
->max_volume
);
1541 e
->max_volume
= e
->volume_limit
;
1545 e
->db_fix
->max_step
= e
->max_volume
;
1546 e
->max_dB
= ((double) e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
]) / 100.0;
1549 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1550 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB
);
1552 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB
);
1555 pa_log_warn("Failed to get dB value of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1558 e
->max_dB
= ((double) max_dB
) / 100.0;
1564 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1565 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1567 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1572 if (!e
->override_map
) {
1573 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1574 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1577 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1580 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1583 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1586 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1588 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1591 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1592 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1594 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1597 if (e
->n_channels
<= 0) {
1598 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1602 if (e
->n_channels
> 2) {
1603 /* FIXME: In some places code like this is used:
1605 * e->masks[alsa_channel_ids[p]][e->n_channels-1]
1607 * The definition of e->masks is
1609 * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][2];
1611 * Since the array size is fixed at 2, we obviously
1612 * don't support elements with more than two
1614 pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e
->alsa_name
, e
->n_channels
);
1618 if (!e
->override_map
) {
1619 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1620 pa_bool_t has_channel
;
1622 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1625 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1626 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1628 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1630 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1635 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1636 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1639 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1647 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1650 PA_LLIST_FOREACH(o
, e
->options
)
1651 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1652 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1656 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1657 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1661 PA_LLIST_FOREACH(o
, e
->options
) {
1664 for (i
= 0; i
< n
; i
++) {
1667 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1670 if (!pa_streq(buf
, o
->alsa_name
))
1678 if (check_required(e
, me
) < 0)
1684 static int jack_probe(pa_alsa_jack
*j
, snd_hctl_t
*h
) {
1689 j
->has_control
= pa_alsa_find_jack(h
, j
->alsa_name
) != NULL
;
1691 if (j
->has_control
) {
1692 if (j
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
)
1694 if (j
->required_any
!= PA_ALSA_REQUIRED_IGNORE
)
1695 j
->path
->req_any_present
= TRUE
;
1697 if (j
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1704 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, pa_bool_t prefixed
) {
1711 if (!pa_startswith(section
, "Element "))
1717 /* This is not an element section, but an enum section? */
1718 if (strchr(section
, ':'))
1721 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1722 return p
->last_element
;
1724 PA_LLIST_FOREACH(e
, p
->elements
)
1725 if (pa_streq(e
->alsa_name
, section
))
1728 e
= pa_xnew0(pa_alsa_element
, 1);
1730 e
->alsa_name
= pa_xstrdup(section
);
1731 e
->direction
= p
->direction
;
1732 e
->volume_limit
= -1;
1734 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1737 p
->last_element
= e
;
1741 static pa_alsa_jack
* jack_get(pa_alsa_path
*p
, const char *section
) {
1744 if (!pa_startswith(section
, "Jack "))
1748 if (p
->last_jack
&& pa_streq(p
->last_jack
->name
, section
))
1749 return p
->last_jack
;
1751 PA_LLIST_FOREACH(j
, p
->jacks
)
1752 if (pa_streq(j
->name
, section
))
1755 j
= pa_xnew0(pa_alsa_jack
, 1);
1756 j
->state_unplugged
= PA_AVAILABLE_NO
;
1757 j
->state_plugged
= PA_AVAILABLE_YES
;
1759 j
->name
= pa_xstrdup(section
);
1760 j
->alsa_name
= pa_sprintf_malloc("%s Jack", section
);
1761 PA_LLIST_INSERT_AFTER(pa_alsa_jack
, p
->jacks
, p
->last_jack
, j
);
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
->name
,
2365 well_known_descriptions
,
2366 PA_ELEMENTSOF(well_known_descriptions
)));
2368 if (!p
->description
)
2369 p
->description
= pa_xstrdup(p
->name
);
2374 static const char *get_default_paths_dir(void) {
2375 if (pa_run_from_build_tree())
2376 return PA_BUILDDIR
"/modules/alsa/mixer/paths/";
2378 return PA_ALSA_PATHS_DIR
;
2381 pa_alsa_path
* pa_alsa_path_new(const char *paths_dir
, const char *fname
, pa_alsa_direction_t direction
) {
2386 bool mute_during_activation
= false;
2388 pa_config_item items
[] = {
2390 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
2391 { "description", pa_config_parse_string
, NULL
, "General" },
2392 { "name", pa_config_parse_string
, NULL
, "General" },
2393 { "mute-during-activation", pa_config_parse_bool
, NULL
, "General" },
2394 { "eld-device", pa_config_parse_int
, NULL
, "General" },
2397 { "priority", option_parse_priority
, NULL
, NULL
},
2398 { "name", option_parse_name
, NULL
, NULL
},
2401 { "state.plugged", jack_parse_state
, NULL
, NULL
},
2402 { "state.unplugged", jack_parse_state
, NULL
, NULL
},
2405 { "switch", element_parse_switch
, NULL
, NULL
},
2406 { "volume", element_parse_volume
, NULL
, NULL
},
2407 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
2408 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
2409 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
2410 /* ... later on we might add override-map.3 and so on here ... */
2411 { "required", element_parse_required
, NULL
, NULL
},
2412 { "required-any", element_parse_required
, NULL
, NULL
},
2413 { "required-absent", element_parse_required
, NULL
, NULL
},
2414 { "direction", element_parse_direction
, NULL
, NULL
},
2415 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2416 { "volume-limit", element_parse_volume_limit
, NULL
, NULL
},
2417 { NULL
, NULL
, NULL
, NULL
}
2422 p
= pa_xnew0(pa_alsa_path
, 1);
2423 n
= pa_path_get_filename(fname
);
2424 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2425 p
->proplist
= pa_proplist_new();
2426 p
->direction
= direction
;
2429 items
[0].data
= &p
->priority
;
2430 items
[1].data
= &p
->description
;
2431 items
[2].data
= &p
->name
;
2432 items
[3].data
= &mute_during_activation
;
2433 items
[4].data
= &p
->eld_device
;
2436 paths_dir
= get_default_paths_dir();
2438 fn
= pa_maybe_prefix_path(fname
, paths_dir
);
2440 r
= pa_config_parse(fn
, NULL
, items
, p
->proplist
, p
);
2446 p
->mute_during_activation
= mute_during_activation
;
2448 if (path_verify(p
) < 0)
2454 pa_alsa_path_free(p
);
2458 pa_alsa_path
*pa_alsa_path_synthesize(const char *element
, pa_alsa_direction_t direction
) {
2464 p
= pa_xnew0(pa_alsa_path
, 1);
2465 p
->name
= pa_xstrdup(element
);
2466 p
->direction
= direction
;
2468 e
= pa_xnew0(pa_alsa_element
, 1);
2470 e
->alsa_name
= pa_xstrdup(element
);
2471 e
->direction
= direction
;
2472 e
->volume_limit
= -1;
2474 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2475 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2477 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2478 p
->last_element
= e
;
2482 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2483 pa_alsa_option
*o
, *n
;
2487 for (o
= e
->options
; o
; o
= n
) {
2490 if (o
->alsa_idx
< 0) {
2491 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2497 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2498 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2499 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2502 static void path_drop_unsupported(pa_alsa_path
*p
) {
2503 pa_alsa_element
*e
, *n
;
2507 for (e
= p
->elements
; e
; e
= n
) {
2510 if (!element_drop_unsupported(e
)) {
2511 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2517 static void path_make_options_unique(pa_alsa_path
*p
) {
2519 pa_alsa_option
*o
, *u
;
2521 PA_LLIST_FOREACH(e
, p
->elements
) {
2522 PA_LLIST_FOREACH(o
, e
->options
) {
2526 for (u
= o
->next
; u
; u
= u
->next
)
2527 if (pa_streq(u
->name
, o
->name
))
2533 m
= pa_xstrdup(o
->name
);
2535 /* OK, this name is not unique, hence let's rename */
2536 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2539 if (!pa_streq(u
->name
, m
))
2542 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2546 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2547 pa_xfree(u
->description
);
2548 u
->description
= nd
;
2558 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2561 for (; e
; e
= e
->next
)
2562 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2563 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2569 for (o
= e
->options
; o
; o
= o
->next
) {
2573 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2574 s
->options
= pa_idxset_copy(template->options
);
2575 s
->name
= pa_sprintf_malloc("%s+%s", template->name
, o
->name
);
2577 (template->description
[0] && o
->description
[0])
2578 ? pa_sprintf_malloc("%s / %s", template->description
, o
->description
)
2579 : (template->description
[0]
2580 ? pa_xstrdup(template->description
)
2581 : pa_xstrdup(o
->description
));
2583 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2585 s
= pa_xnew0(pa_alsa_setting
, 1);
2586 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2587 s
->name
= pa_xstrdup(o
->name
);
2588 s
->description
= pa_xstrdup(o
->description
);
2589 s
->priority
= o
->priority
;
2592 pa_idxset_put(s
->options
, o
, NULL
);
2594 if (element_create_settings(e
->next
, s
))
2595 /* This is not a leaf, so let's get rid of it */
2598 /* This is a leaf, so let's add it */
2599 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2601 e
->path
->last_setting
= s
;
2608 static void path_create_settings(pa_alsa_path
*p
) {
2611 element_create_settings(p
->elements
, NULL
);
2614 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_hctl_t
*hctl
, pa_bool_t ignore_dB
) {
2617 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2618 pa_channel_position_t t
;
2619 pa_channel_position_mask_t path_volume_channels
= 0;
2625 return p
->supported
? 0 : -1;
2631 pa_log_debug("Probing path '%s'", p
->name
);
2633 PA_LLIST_FOREACH(j
, p
->jacks
) {
2634 if (jack_probe(j
, hctl
) < 0) {
2635 p
->supported
= FALSE
;
2636 pa_log_debug("Probe of jack '%s' failed.", j
->alsa_name
);
2639 pa_log_debug("Probe of jack '%s' succeeded (%s)", j
->alsa_name
, j
->has_control
? "found!" : "not found");
2642 PA_LLIST_FOREACH(e
, p
->elements
) {
2643 if (element_probe(e
, m
) < 0) {
2644 p
->supported
= FALSE
;
2645 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2648 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
);
2653 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2655 if (!p
->has_volume
) {
2656 p
->min_volume
= e
->min_volume
;
2657 p
->max_volume
= e
->max_volume
;
2661 if (!p
->has_volume
) {
2662 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2663 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2664 min_dB
[t
] = e
->min_dB
;
2665 max_dB
[t
] = e
->max_dB
;
2666 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2673 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2674 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2675 min_dB
[t
] += e
->min_dB
;
2676 max_dB
[t
] += e
->max_dB
;
2677 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2680 /* Hmm, there's another element before us
2681 * which cannot do dB volumes, so we we need
2682 * to 'neutralize' this slider */
2683 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2684 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2687 } else if (p
->has_volume
) {
2688 /* We can't use this volume, so let's ignore it */
2689 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2690 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2692 p
->has_volume
= TRUE
;
2695 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2699 if (p
->has_req_any
&& !p
->req_any_present
) {
2700 p
->supported
= FALSE
;
2701 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2705 path_drop_unsupported(p
);
2706 path_make_options_unique(p
);
2707 path_create_settings(p
);
2709 p
->supported
= TRUE
;
2711 p
->min_dB
= INFINITY
;
2712 p
->max_dB
= -INFINITY
;
2714 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2715 if (path_volume_channels
& PA_CHANNEL_POSITION_MASK(t
)) {
2716 if (p
->min_dB
> min_dB
[t
])
2717 p
->min_dB
= min_dB
[t
];
2719 if (p
->max_dB
< max_dB
[t
])
2720 p
->max_dB
= max_dB
[t
];
2727 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2730 pa_log_debug("Setting %s (%s) priority=%u",
2732 pa_strnull(s
->description
),
2736 void pa_alsa_jack_dump(pa_alsa_jack
*j
) {
2739 pa_log_debug("Jack %s, alsa_name='%s', detection %s", j
->name
, j
->alsa_name
, j
->has_control
? "possible" : "unavailable");
2742 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2745 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2747 pa_strnull(o
->name
),
2748 pa_strnull(o
->description
),
2753 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2757 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",
2767 (long long unsigned) e
->merged_mask
,
2769 pa_yes_no(e
->override_map
));
2771 PA_LLIST_FOREACH(o
, e
->options
)
2772 pa_alsa_option_dump(o
);
2775 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2781 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2782 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2784 pa_strnull(p
->description
),
2787 pa_yes_no(p
->probed
),
2788 pa_yes_no(p
->supported
),
2789 pa_yes_no(p
->has_mute
),
2790 pa_yes_no(p
->has_volume
),
2791 pa_yes_no(p
->has_dB
),
2792 p
->min_volume
, p
->max_volume
,
2793 p
->min_dB
, p
->max_dB
);
2795 PA_LLIST_FOREACH(e
, p
->elements
)
2796 pa_alsa_element_dump(e
);
2798 PA_LLIST_FOREACH(j
, p
->jacks
)
2799 pa_alsa_jack_dump(j
);
2801 PA_LLIST_FOREACH(s
, p
->settings
)
2802 pa_alsa_setting_dump(s
);
2805 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2806 snd_mixer_selem_id_t
*sid
;
2807 snd_mixer_elem_t
*me
;
2813 SELEM_INIT(sid
, e
->alsa_name
);
2814 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2815 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2819 snd_mixer_elem_set_callback(me
, cb
);
2820 snd_mixer_elem_set_callback_private(me
, userdata
);
2823 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2830 PA_LLIST_FOREACH(e
, p
->elements
)
2831 element_set_callback(e
, m
, cb
, userdata
);
2834 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2842 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
2843 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2846 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
, const char *paths_dir
) {
2847 pa_alsa_path_set
*ps
;
2848 char **pn
= NULL
, **en
= NULL
, **ie
;
2849 pa_alsa_decibel_fix
*db_fix
;
2850 void *state
, *state2
;
2854 pa_assert(m
->profile_set
);
2855 pa_assert(m
->profile_set
->decibel_fixes
);
2856 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2858 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2861 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2862 ps
->direction
= direction
;
2863 ps
->paths
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2865 if (direction
== PA_ALSA_DIRECTION_OUTPUT
) {
2866 pn
= m
->output_path_names
;
2867 cache
= m
->profile_set
->output_paths
;
2869 else if (direction
== PA_ALSA_DIRECTION_INPUT
) {
2870 pn
= m
->input_path_names
;
2871 cache
= m
->profile_set
->input_paths
;
2877 for (in
= pn
; *in
; in
++) {
2878 pa_alsa_path
*p
= NULL
;
2879 pa_bool_t duplicate
= FALSE
;
2882 for (kn
= pn
; kn
< in
; kn
++)
2883 if (pa_streq(*kn
, *in
)) {
2891 p
= pa_hashmap_get(cache
, *in
);
2893 char *fn
= pa_sprintf_malloc("%s.conf", *in
);
2894 p
= pa_alsa_path_new(paths_dir
, fn
, direction
);
2897 pa_hashmap_put(cache
, *in
, p
);
2899 pa_assert(pa_hashmap_get(cache
, *in
) == p
);
2901 pa_hashmap_put(ps
->paths
, p
, p
);
2908 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2909 en
= m
->output_element
;
2910 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2911 en
= m
->input_element
;
2914 pa_alsa_path_set_free(ps
);
2918 for (ie
= en
; *ie
; ie
++) {
2922 p
= pa_alsa_path_synthesize(*ie
, direction
);
2924 /* Mark all other passed elements for require-absent */
2925 for (je
= en
; *je
; je
++) {
2931 e
= pa_xnew0(pa_alsa_element
, 1);
2933 e
->alsa_name
= pa_xstrdup(*je
);
2934 e
->direction
= direction
;
2935 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2936 e
->volume_limit
= -1;
2938 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2939 p
->last_element
= e
;
2942 pa_hashmap_put(ps
->paths
, *ie
, p
);
2946 /* Assign decibel fixes to elements. */
2947 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2950 PA_HASHMAP_FOREACH(p
, ps
->paths
, state2
) {
2953 PA_LLIST_FOREACH(e
, p
->elements
) {
2954 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2955 /* The profile set that contains the dB fix may be freed
2956 * before the element, so we have to copy the dB fix
2958 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2959 e
->db_fix
->profile_set
= NULL
;
2960 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2961 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
2970 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2975 pa_log_debug("Path Set %p, direction=%i",
2979 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
2980 pa_alsa_path_dump(p
);
2984 static pa_bool_t
options_have_option(pa_alsa_option
*options
, const char *alsa_name
) {
2988 pa_assert(alsa_name
);
2990 PA_LLIST_FOREACH(o
, options
) {
2991 if (pa_streq(o
->alsa_name
, alsa_name
))
2997 static pa_bool_t
enumeration_is_subset(pa_alsa_option
*a_options
, pa_alsa_option
*b_options
) {
2998 pa_alsa_option
*oa
, *ob
;
3000 if (!a_options
) return TRUE
;
3001 if (!b_options
) return FALSE
;
3003 /* If there is an option A offers that B does not, then A is not a subset of B. */
3004 PA_LLIST_FOREACH(oa
, a_options
) {
3005 pa_bool_t found
= FALSE
;
3006 PA_LLIST_FOREACH(ob
, b_options
) {
3007 if (pa_streq(oa
->alsa_name
, ob
->alsa_name
)) {
3019 * Compares two elements to see if a is a subset of b
3021 static pa_bool_t
element_is_subset(pa_alsa_element
*a
, pa_alsa_element
*b
, snd_mixer_t
*m
) {
3027 * Every state is a subset of itself (with caveats for volume_limits and options)
3028 * IGNORE is a subset of every other state */
3030 /* Check the volume_use */
3031 if (a
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
3033 /* "Constant" is subset of "Constant" only when their constant values are equal */
3034 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& b
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& a
->constant_volume
!= b
->constant_volume
)
3037 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
3038 if (a
->volume_use
!= b
->volume_use
&& b
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
3041 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
3042 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
3043 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
3044 if (b
->volume_use
== PA_ALSA_VOLUME_MERGE
&& b
->volume_limit
>= 0) {
3047 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
)
3048 a_limit
= a
->constant_volume
;
3049 else if (a
->volume_use
== PA_ALSA_VOLUME_ZERO
) {
3053 int rounding
= (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
? +1 : -1);
3054 a_limit
= decibel_fix_get_step(a
->db_fix
, &dB
, rounding
);
3056 snd_mixer_selem_id_t
*sid
;
3057 snd_mixer_elem_t
*me
;
3059 SELEM_INIT(sid
, a
->alsa_name
);
3060 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
3061 pa_log_warn("Element %s seems to have disappeared.", a
->alsa_name
);
3065 if (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3066 if (snd_mixer_selem_ask_playback_dB_vol(me
, dB
, +1, &a_limit
) < 0)
3069 if (snd_mixer_selem_ask_capture_dB_vol(me
, dB
, -1, &a_limit
) < 0)
3073 } else if (a
->volume_use
== PA_ALSA_VOLUME_OFF
)
3074 a_limit
= a
->min_volume
;
3075 else if (a
->volume_use
== PA_ALSA_VOLUME_MERGE
)
3076 a_limit
= a
->volume_limit
;
3078 /* This should never be reached */
3081 if (a_limit
> b
->volume_limit
)
3085 if (a
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
3087 /* If override-maps are different, they're not subsets */
3088 if (a
->n_channels
!= b
->n_channels
)
3090 for (s
= 0; s
<= SND_MIXER_SCHN_LAST
; s
++)
3091 if (a
->masks
[s
][a
->n_channels
-1] != b
->masks
[s
][b
->n_channels
-1]) {
3092 pa_log_debug("Element %s is not a subset - mask a: 0x%" PRIx64
", mask b: 0x%" PRIx64
", at channel %d",
3093 a
->alsa_name
, a
->masks
[s
][a
->n_channels
-1], b
->masks
[s
][b
->n_channels
-1], s
);
3099 if (a
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
3100 /* "On" is a subset of "Mute".
3101 * "Off" is a subset of "Mute".
3102 * "On" is a subset of "Select", if there is an "Option:On" in B.
3103 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3104 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3106 if (a
->switch_use
!= b
->switch_use
) {
3108 if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
|| a
->switch_use
== PA_ALSA_SWITCH_MUTE
3109 || b
->switch_use
== PA_ALSA_SWITCH_OFF
|| b
->switch_use
== PA_ALSA_SWITCH_ON
)
3112 if (b
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3113 if (a
->switch_use
== PA_ALSA_SWITCH_ON
) {
3114 if (!options_have_option(b
->options
, "on"))
3116 } else if (a
->switch_use
== PA_ALSA_SWITCH_OFF
) {
3117 if (!options_have_option(b
->options
, "off"))
3121 } else if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3122 if (!enumeration_is_subset(a
->options
, b
->options
))
3127 if (a
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
) {
3128 if (b
->enumeration_use
== PA_ALSA_ENUMERATION_IGNORE
)
3130 if (!enumeration_is_subset(a
->options
, b
->options
))
3137 static void path_set_condense(pa_alsa_path_set
*ps
, snd_mixer_t
*m
) {
3144 /* If we only have one path, then don't bother */
3145 if (pa_hashmap_size(ps
->paths
) < 2)
3148 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3152 PA_HASHMAP_FOREACH(p2
, ps
->paths
, state2
) {
3153 pa_alsa_element
*ea
, *eb
;
3154 pa_alsa_jack
*ja
, *jb
;
3155 pa_bool_t is_subset
= TRUE
;
3160 /* If a has a jack that b does not have, a is not a subset */
3161 PA_LLIST_FOREACH(ja
, p
->jacks
) {
3162 pa_bool_t exists
= FALSE
;
3164 if (!ja
->has_control
)
3167 PA_LLIST_FOREACH(jb
, p2
->jacks
) {
3168 if (jb
->has_control
&& pa_streq(jb
->alsa_name
, ja
->alsa_name
) &&
3169 (ja
->state_plugged
== jb
->state_plugged
) &&
3170 (ja
->state_unplugged
== jb
->state_unplugged
)) {
3182 /* Compare the elements of each set... */
3183 pa_assert_se(ea
= p
->elements
);
3184 pa_assert_se(eb
= p2
->elements
);
3187 if (pa_streq(ea
->alsa_name
, eb
->alsa_name
)) {
3188 if (element_is_subset(ea
, eb
, m
)) {
3191 if ((ea
&& !eb
) || (!ea
&& eb
))
3193 else if (!ea
&& !eb
)
3203 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p
->name
, p2
->name
);
3204 pa_hashmap_remove(ps
->paths
, p
);
3211 static pa_alsa_path
* path_set_find_path_by_name(pa_alsa_path_set
*ps
, const char* name
, pa_alsa_path
*ignore
)
3216 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
)
3217 if (p
!= ignore
&& pa_streq(p
->name
, name
))
3222 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
3223 pa_alsa_path
*p
, *q
;
3224 void *state
, *state2
;
3226 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3230 q
= path_set_find_path_by_name(ps
, p
->name
, p
);
3235 m
= pa_xstrdup(p
->name
);
3237 /* OK, this name is not unique, hence let's rename */
3239 PA_HASHMAP_FOREACH(q
, ps
->paths
, state2
) {
3242 if (!pa_streq(q
->name
, m
))
3245 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
3249 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
3250 pa_xfree(q
->description
);
3251 q
->description
= nd
;
3260 static void mapping_free(pa_alsa_mapping
*m
) {
3264 pa_xfree(m
->description
);
3266 pa_proplist_free(m
->proplist
);
3268 pa_xstrfreev(m
->device_strings
);
3269 pa_xstrfreev(m
->input_path_names
);
3270 pa_xstrfreev(m
->output_path_names
);
3271 pa_xstrfreev(m
->input_element
);
3272 pa_xstrfreev(m
->output_element
);
3273 if (m
->input_path_set
)
3274 pa_alsa_path_set_free(m
->input_path_set
);
3275 if (m
->output_path_set
)
3276 pa_alsa_path_set_free(m
->output_path_set
);
3278 pa_assert(!m
->input_pcm
);
3279 pa_assert(!m
->output_pcm
);
3281 pa_alsa_ucm_mapping_context_free(&m
->ucm_context
);
3286 static void profile_free(pa_alsa_profile
*p
) {
3290 pa_xfree(p
->description
);
3292 pa_xstrfreev(p
->input_mapping_names
);
3293 pa_xstrfreev(p
->output_mapping_names
);
3295 if (p
->input_mappings
)
3296 pa_idxset_free(p
->input_mappings
, NULL
);
3298 if (p
->output_mappings
)
3299 pa_idxset_free(p
->output_mappings
, NULL
);
3304 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
3307 if (ps
->input_paths
)
3308 pa_hashmap_free(ps
->input_paths
, (pa_free_cb_t
) pa_alsa_path_free
);
3310 if (ps
->output_paths
)
3311 pa_hashmap_free(ps
->output_paths
, (pa_free_cb_t
) pa_alsa_path_free
);
3314 pa_hashmap_free(ps
->profiles
, (pa_free_cb_t
) profile_free
);
3317 pa_hashmap_free(ps
->mappings
, (pa_free_cb_t
) mapping_free
);
3319 if (ps
->decibel_fixes
)
3320 pa_hashmap_free(ps
->decibel_fixes
, (pa_free_cb_t
) decibel_fix_free
);
3325 pa_alsa_mapping
*pa_alsa_mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
3328 if (!pa_startswith(name
, "Mapping "))
3333 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
3336 m
= pa_xnew0(pa_alsa_mapping
, 1);
3337 m
->profile_set
= ps
;
3338 m
->name
= pa_xstrdup(name
);
3339 pa_channel_map_init(&m
->channel_map
);
3340 m
->proplist
= pa_proplist_new();
3342 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
3347 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
3350 if (!pa_startswith(name
, "Profile "))
3355 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
3358 p
= pa_xnew0(pa_alsa_profile
, 1);
3359 p
->profile_set
= ps
;
3360 p
->name
= pa_xstrdup(name
);
3362 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3367 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
3368 pa_alsa_decibel_fix
*db_fix
;
3370 if (!pa_startswith(name
, "DecibelFix "))
3375 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
3378 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
3379 db_fix
->profile_set
= ps
;
3380 db_fix
->name
= pa_xstrdup(name
);
3382 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
3387 static int mapping_parse_device_strings(pa_config_parser_state
*state
) {
3388 pa_alsa_profile_set
*ps
;
3393 ps
= state
->userdata
;
3395 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3396 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3400 pa_xstrfreev(m
->device_strings
);
3401 if (!(m
->device_strings
= pa_split_spaces_strv(state
->rvalue
))) {
3402 pa_log("[%s:%u] Device string list empty of '%s'", state
->filename
, state
->lineno
, state
->section
);
3409 static int mapping_parse_channel_map(pa_config_parser_state
*state
) {
3410 pa_alsa_profile_set
*ps
;
3415 ps
= state
->userdata
;
3417 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3418 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3422 if (!(pa_channel_map_parse(&m
->channel_map
, state
->rvalue
))) {
3423 pa_log("[%s:%u] Channel map invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
3430 static int mapping_parse_paths(pa_config_parser_state
*state
) {
3431 pa_alsa_profile_set
*ps
;
3436 ps
= state
->userdata
;
3438 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3439 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3443 if (pa_streq(state
->lvalue
, "paths-input")) {
3444 pa_xstrfreev(m
->input_path_names
);
3445 m
->input_path_names
= pa_split_spaces_strv(state
->rvalue
);
3447 pa_xstrfreev(m
->output_path_names
);
3448 m
->output_path_names
= pa_split_spaces_strv(state
->rvalue
);
3454 static int mapping_parse_element(pa_config_parser_state
*state
) {
3455 pa_alsa_profile_set
*ps
;
3460 ps
= state
->userdata
;
3462 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3463 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3467 if (pa_streq(state
->lvalue
, "element-input")) {
3468 pa_xstrfreev(m
->input_element
);
3469 m
->input_element
= pa_split_spaces_strv(state
->rvalue
);
3471 pa_xstrfreev(m
->output_element
);
3472 m
->output_element
= pa_split_spaces_strv(state
->rvalue
);
3478 static int mapping_parse_direction(pa_config_parser_state
*state
) {
3479 pa_alsa_profile_set
*ps
;
3484 ps
= state
->userdata
;
3486 if (!(m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3487 pa_log("[%s:%u] Section name %s invalid.", state
->filename
, state
->lineno
, state
->section
);
3491 if (pa_streq(state
->rvalue
, "input"))
3492 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3493 else if (pa_streq(state
->rvalue
, "output"))
3494 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3495 else if (pa_streq(state
->rvalue
, "any"))
3496 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3498 pa_log("[%s:%u] Direction %s invalid.", state
->filename
, state
->lineno
, state
->rvalue
);
3505 static int mapping_parse_description(pa_config_parser_state
*state
) {
3506 pa_alsa_profile_set
*ps
;
3512 ps
= state
->userdata
;
3514 if ((m
= pa_alsa_mapping_get(ps
, state
->section
))) {
3515 pa_xfree(m
->description
);
3516 m
->description
= pa_xstrdup(state
->rvalue
);
3517 } else if ((p
= profile_get(ps
, state
->section
))) {
3518 pa_xfree(p
->description
);
3519 p
->description
= pa_xstrdup(state
->rvalue
);
3521 pa_log("[%s:%u] Section name %s invalid.", state
->filename
, state
->lineno
, state
->section
);
3528 static int mapping_parse_priority(pa_config_parser_state
*state
) {
3529 pa_alsa_profile_set
*ps
;
3536 ps
= state
->userdata
;
3538 if (pa_atou(state
->rvalue
, &prio
) < 0) {
3539 pa_log("[%s:%u] Priority invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
3543 if ((m
= pa_alsa_mapping_get(ps
, state
->section
)))
3545 else if ((p
= profile_get(ps
, state
->section
)))
3548 pa_log("[%s:%u] Section name %s invalid.", state
->filename
, state
->lineno
, state
->section
);
3555 static int profile_parse_mappings(pa_config_parser_state
*state
) {
3556 pa_alsa_profile_set
*ps
;
3561 ps
= state
->userdata
;
3563 if (!(p
= profile_get(ps
, state
->section
))) {
3564 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3568 if (pa_streq(state
->lvalue
, "input-mappings")) {
3569 pa_xstrfreev(p
->input_mapping_names
);
3570 p
->input_mapping_names
= pa_split_spaces_strv(state
->rvalue
);
3572 pa_xstrfreev(p
->output_mapping_names
);
3573 p
->output_mapping_names
= pa_split_spaces_strv(state
->rvalue
);
3579 static int profile_parse_skip_probe(pa_config_parser_state
*state
) {
3580 pa_alsa_profile_set
*ps
;
3586 ps
= state
->userdata
;
3588 if (!(p
= profile_get(ps
, state
->section
))) {
3589 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3593 if ((b
= pa_parse_boolean(state
->rvalue
)) < 0) {
3594 pa_log("[%s:%u] Skip probe invalid of '%s'", state
->filename
, state
->lineno
, state
->section
);
3603 static int decibel_fix_parse_db_values(pa_config_parser_state
*state
) {
3604 pa_alsa_profile_set
*ps
;
3605 pa_alsa_decibel_fix
*db_fix
;
3609 unsigned n
= 8; /* Current size of the db_values table. */
3610 unsigned min_step
= 0;
3611 unsigned max_step
= 0;
3612 unsigned i
= 0; /* Index to the items table. */
3613 unsigned prev_step
= 0;
3618 ps
= state
->userdata
;
3620 if (!(db_fix
= decibel_fix_get(ps
, state
->section
))) {
3621 pa_log("[%s:%u] %s invalid in section %s", state
->filename
, state
->lineno
, state
->lvalue
, state
->section
);
3625 if (!(items
= pa_split_spaces_strv(state
->rvalue
))) {
3626 pa_log("[%s:%u] Value missing", state
->filename
, state
->lineno
);
3630 db_values
= pa_xnew(long, n
);
3632 while ((item
= items
[i
++])) {
3633 char *s
= item
; /* Step value string. */
3634 char *d
= item
; /* dB value string. */
3638 /* Move d forward until it points to a colon or to the end of the item. */
3639 for (; *d
&& *d
!= ':'; ++d
);
3642 /* item started with colon. */
3643 pa_log("[%s:%u] No step value found in %s", state
->filename
, state
->lineno
, item
);
3647 if (!*d
|| !*(d
+ 1)) {
3648 /* No colon found, or it was the last character in item. */
3649 pa_log("[%s:%u] No dB value found in %s", state
->filename
, state
->lineno
, item
);
3653 /* pa_atou() needs a null-terminating string. Let's replace the colon
3654 * with a zero byte. */
3657 if (pa_atou(s
, &step
) < 0) {
3658 pa_log("[%s:%u] Invalid step value: %s", state
->filename
, state
->lineno
, s
);
3662 if (pa_atod(d
, &db
) < 0) {
3663 pa_log("[%s:%u] Invalid dB value: %s", state
->filename
, state
->lineno
, d
);
3667 if (step
<= prev_step
&& i
!= 1) {
3668 pa_log("[%s:%u] Step value %u not greater than the previous value %u", state
->filename
, state
->lineno
, step
, prev_step
);
3672 if (db
< prev_db
&& i
!= 1) {
3673 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", state
->filename
, state
->lineno
, db
, prev_db
);
3679 db_values
[0] = (long) (db
* 100.0);
3683 /* Interpolate linearly. */
3684 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3686 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3688 /* Reallocate the db_values table if it's about to overflow. */
3689 if (prev_step
+ 1 - min_step
== n
) {
3691 db_values
= pa_xrenew(long, db_values
, n
);
3694 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3701 db_fix
->min_step
= min_step
;
3702 db_fix
->max_step
= max_step
;
3703 pa_xfree(db_fix
->db_values
);
3704 db_fix
->db_values
= db_values
;
3706 pa_xstrfreev(items
);
3711 pa_xstrfreev(items
);
3712 pa_xfree(db_values
);
3717 static void mapping_paths_probe(pa_alsa_mapping
*m
, pa_alsa_profile
*profile
,
3718 pa_alsa_direction_t direction
) {
3722 snd_pcm_t
*pcm_handle
;
3723 pa_alsa_path_set
*ps
;
3724 snd_mixer_t
*mixer_handle
;
3725 snd_hctl_t
*hctl_handle
;
3727 if (direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3728 if (m
->output_path_set
)
3729 return; /* Already probed */
3730 m
->output_path_set
= ps
= pa_alsa_path_set_new(m
, direction
, NULL
); /* FIXME: Handle paths_dir */
3731 pcm_handle
= m
->output_pcm
;
3733 if (m
->input_path_set
)
3734 return; /* Already probed */
3735 m
->input_path_set
= ps
= pa_alsa_path_set_new(m
, direction
, NULL
); /* FIXME: Handle paths_dir */
3736 pcm_handle
= m
->input_pcm
;
3740 return; /* No paths */
3742 pa_assert(pcm_handle
);
3744 mixer_handle
= pa_alsa_open_mixer_for_pcm(pcm_handle
, NULL
, &hctl_handle
);
3745 if (!mixer_handle
|| !hctl_handle
) {
3746 /* Cannot open mixer, remove all entries */
3747 pa_hashmap_remove_all(ps
->paths
, NULL
);
3752 PA_HASHMAP_FOREACH(p
, ps
->paths
, state
) {
3753 if (pa_alsa_path_probe(p
, mixer_handle
, hctl_handle
, m
->profile_set
->ignore_dB
) < 0) {
3754 pa_hashmap_remove(ps
->paths
, p
);
3758 path_set_condense(ps
, mixer_handle
);
3759 path_set_make_paths_unique(ps
);
3762 snd_mixer_close(mixer_handle
);
3764 pa_log_debug("Available mixer paths (after tidying):");
3765 pa_alsa_path_set_dump(ps
);
3768 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3770 static const struct description_map well_known_descriptions
[] = {
3771 { "analog-mono", N_("Analog Mono") },
3772 { "analog-stereo", N_("Analog Stereo") },
3773 { "analog-surround-21", N_("Analog Surround 2.1") },
3774 { "analog-surround-30", N_("Analog Surround 3.0") },
3775 { "analog-surround-31", N_("Analog Surround 3.1") },
3776 { "analog-surround-40", N_("Analog Surround 4.0") },
3777 { "analog-surround-41", N_("Analog Surround 4.1") },
3778 { "analog-surround-50", N_("Analog Surround 5.0") },
3779 { "analog-surround-51", N_("Analog Surround 5.1") },
3780 { "analog-surround-61", N_("Analog Surround 6.0") },
3781 { "analog-surround-61", N_("Analog Surround 6.1") },
3782 { "analog-surround-70", N_("Analog Surround 7.0") },
3783 { "analog-surround-71", N_("Analog Surround 7.1") },
3784 { "analog-4-channel-input", N_("Analog 4-channel Input") },
3785 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3786 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3787 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3788 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3789 { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
3790 { "hdmi-stereo", N_("Digital Stereo (HDMI)") },
3791 { "hdmi-surround-51", N_("Digital Surround 5.1 (HDMI)") }
3796 if (!pa_channel_map_valid(&m
->channel_map
)) {
3797 pa_log("Mapping %s is missing channel map.", m
->name
);
3801 if (!m
->device_strings
) {
3802 pa_log("Mapping %s is missing device strings.", m
->name
);
3806 if ((m
->input_path_names
&& m
->input_element
) ||
3807 (m
->output_path_names
&& m
->output_element
)) {
3808 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3812 if (!m
->description
)
3813 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3814 well_known_descriptions
,
3815 PA_ELEMENTSOF(well_known_descriptions
)));
3817 if (!m
->description
)
3818 m
->description
= pa_xstrdup(m
->name
);
3821 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3823 else if (m
->channel_map
.channels
== bonus
->channels
)
3830 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3831 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3835 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3837 pa_strnull(m
->description
),
3839 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3840 pa_yes_no(m
->supported
),
3844 static void profile_set_add_auto_pair(
3845 pa_alsa_profile_set
*ps
,
3846 pa_alsa_mapping
*m
, /* output */
3847 pa_alsa_mapping
*n
/* input */) {
3855 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3858 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3862 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3864 name
= pa_sprintf_malloc("output:%s", m
->name
);
3866 name
= pa_sprintf_malloc("input:%s", n
->name
);
3868 if (pa_hashmap_get(ps
->profiles
, name
)) {
3873 p
= pa_xnew0(pa_alsa_profile
, 1);
3874 p
->profile_set
= ps
;
3878 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3879 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3880 p
->priority
+= m
->priority
* 100;
3884 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3885 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3886 p
->priority
+= n
->priority
;
3889 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3892 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3893 pa_alsa_mapping
*m
, *n
;
3894 void *m_state
, *n_state
;
3898 /* The order is important here:
3899 1) try single inputs and outputs before trying their
3900 combination, because if the half-duplex test failed, we don't have
3902 2) try the output right before the input combinations with
3903 that output, because then the output_pcm is not closed between tests.
3905 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3906 profile_set_add_auto_pair(ps
, NULL
, n
);
3908 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3909 profile_set_add_auto_pair(ps
, m
, NULL
);
3911 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3912 profile_set_add_auto_pair(ps
, m
, n
);
3917 static int profile_verify(pa_alsa_profile
*p
) {
3919 static const struct description_map well_known_descriptions
[] = {
3920 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3921 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3922 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3923 { "off", N_("Off") }
3928 /* Replace the output mapping names by the actual mappings */
3929 if (p
->output_mapping_names
) {
3932 pa_assert(!p
->output_mappings
);
3933 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3935 for (name
= p
->output_mapping_names
; *name
; name
++) {
3938 pa_bool_t duplicate
= FALSE
;
3940 for (in
= name
+ 1; *in
; in
++)
3941 if (pa_streq(*name
, *in
)) {
3949 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3950 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p
->name
, *name
);
3954 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3960 pa_xstrfreev(p
->output_mapping_names
);
3961 p
->output_mapping_names
= NULL
;
3964 /* Replace the input mapping names by the actual mappings */
3965 if (p
->input_mapping_names
) {
3968 pa_assert(!p
->input_mappings
);
3969 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3971 for (name
= p
->input_mapping_names
; *name
; name
++) {
3974 pa_bool_t 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_OUTPUT
) {
3986 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p
->name
, *name
);
3990 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3996 pa_xstrfreev(p
->input_mapping_names
);
3997 p
->input_mapping_names
= NULL
;
4000 if (!p
->input_mappings
&& !p
->output_mappings
) {
4001 pa_log("Profile '%s' lacks mappings.", p
->name
);
4005 if (!p
->description
)
4006 p
->description
= pa_xstrdup(lookup_description(p
->name
,
4007 well_known_descriptions
,
4008 PA_ELEMENTSOF(well_known_descriptions
)));
4010 if (!p
->description
) {
4015 sb
= pa_strbuf_new();
4017 if (p
->output_mappings
)
4018 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4019 if (!pa_strbuf_isempty(sb
))
4020 pa_strbuf_puts(sb
, " + ");
4022 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
4025 if (p
->input_mappings
)
4026 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4027 if (!pa_strbuf_isempty(sb
))
4028 pa_strbuf_puts(sb
, " + ");
4030 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
4033 p
->description
= pa_strbuf_tostring_free(sb
);
4039 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
4044 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4046 pa_strnull(p
->description
),
4048 pa_yes_no(p
->supported
),
4049 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
4050 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
4052 if (p
->input_mappings
)
4053 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
4054 pa_log_debug("Input %s", m
->name
);
4056 if (p
->output_mappings
)
4057 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
4058 pa_log_debug("Output %s", m
->name
);
4061 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
4064 /* Check that the dB mapping has been configured. Since "db-values" is
4065 * currently the only option in the DecibelFix section, and decibel fix
4066 * objects don't get created if a DecibelFix section is empty, this is
4067 * actually a redundant check. Having this may prevent future bugs,
4069 if (!db_fix
->db_values
) {
4070 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
4077 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
4078 char *db_values
= NULL
;
4082 if (db_fix
->db_values
) {
4084 unsigned long i
, nsteps
;
4086 pa_assert(db_fix
->min_step
<= db_fix
->max_step
);
4087 nsteps
= db_fix
->max_step
- db_fix
->min_step
+ 1;
4089 buf
= pa_strbuf_new();
4090 for (i
= 0; i
< nsteps
; ++i
)
4091 pa_strbuf_printf(buf
, "[%li]:%0.2f ", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
4093 db_values
= pa_strbuf_tostring_free(buf
);
4096 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4097 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
4099 pa_xfree(db_values
);
4102 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
4103 pa_alsa_profile_set
*ps
;
4106 pa_alsa_decibel_fix
*db_fix
;
4111 static pa_config_item items
[] = {
4113 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
4116 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
4117 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
4118 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
4119 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
4120 { "element-input", mapping_parse_element
, NULL
, NULL
},
4121 { "element-output", mapping_parse_element
, NULL
, NULL
},
4122 { "direction", mapping_parse_direction
, NULL
, NULL
},
4124 /* Shared by [Mapping ...] and [Profile ...] */
4125 { "description", mapping_parse_description
, NULL
, NULL
},
4126 { "priority", mapping_parse_priority
, NULL
, NULL
},
4129 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
4130 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
4131 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
4133 /* [DecibelFix ...] */
4134 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
4135 { NULL
, NULL
, NULL
, NULL
}
4138 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
4139 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4140 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4141 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4142 ps
->input_paths
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4143 ps
->output_paths
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4145 items
[0].data
= &ps
->auto_profiles
;
4148 fname
= "default.conf";
4150 fn
= pa_maybe_prefix_path(fname
,
4151 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
4152 PA_ALSA_PROFILE_SETS_DIR
);
4154 r
= pa_config_parse(fn
, NULL
, items
, NULL
, ps
);
4160 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4161 if (mapping_verify(m
, bonus
) < 0)
4164 if (ps
->auto_profiles
)
4165 profile_set_add_auto(ps
);
4167 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4168 if (profile_verify(p
) < 0)
4171 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4172 if (decibel_fix_verify(db_fix
) < 0)
4178 pa_alsa_profile_set_free(ps
);
4182 static void profile_finalize_probing(pa_alsa_profile
*to_be_finalized
, pa_alsa_profile
*next
) {
4186 if (!to_be_finalized
)
4189 if (to_be_finalized
->output_mappings
)
4190 PA_IDXSET_FOREACH(m
, to_be_finalized
->output_mappings
, idx
) {
4195 if (to_be_finalized
->supported
)
4198 /* If this mapping is also in the next profile, we won't close the
4199 * pcm handle here, because it would get immediately reopened
4201 if (next
&& next
->output_mappings
&& pa_idxset_get_by_data(next
->output_mappings
, m
, NULL
))
4204 snd_pcm_close(m
->output_pcm
);
4205 m
->output_pcm
= NULL
;
4208 if (to_be_finalized
->input_mappings
)
4209 PA_IDXSET_FOREACH(m
, to_be_finalized
->input_mappings
, idx
) {
4214 if (to_be_finalized
->supported
)
4217 /* If this mapping is also in the next profile, we won't close the
4218 * pcm handle here, because it would get immediately reopened
4220 if (next
&& next
->input_mappings
&& pa_idxset_get_by_data(next
->input_mappings
, m
, NULL
))
4223 snd_pcm_close(m
->input_pcm
);
4224 m
->input_pcm
= NULL
;
4228 static snd_pcm_t
* mapping_open_pcm(pa_alsa_mapping
*m
,
4229 const pa_sample_spec
*ss
,
4232 unsigned default_n_fragments
,
4233 unsigned default_fragment_size_msec
) {
4235 pa_sample_spec try_ss
= *ss
;
4236 pa_channel_map try_map
= m
->channel_map
;
4237 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
4239 try_ss
.channels
= try_map
.channels
;
4242 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
4243 pa_frame_size(&try_ss
);
4244 try_buffer_size
= default_n_fragments
* try_period_size
;
4246 return pa_alsa_open_by_template(
4247 m
->device_strings
, dev_id
, NULL
, &try_ss
,
4248 &try_map
, mode
, &try_period_size
,
4249 &try_buffer_size
, 0, NULL
, NULL
, TRUE
);
4252 static void paths_drop_unsupported(pa_hashmap
* h
) {
4259 p
= pa_hashmap_iterate(h
, &state
, &key
);
4261 if (p
->supported
<= 0) {
4262 pa_hashmap_remove(h
, key
);
4263 pa_alsa_path_free(p
);
4265 p
= pa_hashmap_iterate(h
, &state
, &key
);
4269 void pa_alsa_profile_set_probe(
4270 pa_alsa_profile_set
*ps
,
4272 const pa_sample_spec
*ss
,
4273 unsigned default_n_fragments
,
4274 unsigned default_fragment_size_msec
) {
4277 pa_alsa_profile
*p
, *last
= NULL
;
4279 pa_hashmap
*broken_inputs
, *broken_outputs
;
4288 broken_inputs
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
4289 broken_outputs
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
4291 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
4294 /* Skip if this is already marked that it is supported (i.e. from the config file) */
4295 if (!p
->supported
) {
4297 profile_finalize_probing(last
, p
);
4298 p
->supported
= TRUE
;
4300 if (p
->output_mappings
) {
4301 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4302 if (pa_hashmap_get(broken_outputs
, m
) == m
) {
4303 pa_log_debug("Skipping profile %s - will not be able to open output:%s", p
->name
, m
->name
);
4304 p
->supported
= FALSE
;
4310 if (p
->input_mappings
&& p
->supported
) {
4311 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4312 if (pa_hashmap_get(broken_inputs
, m
) == m
) {
4313 pa_log_debug("Skipping profile %s - will not be able to open input:%s", p
->name
, m
->name
);
4314 p
->supported
= FALSE
;
4321 pa_log_debug("Looking at profile %s", p
->name
);
4323 /* Check if we can open all new ones */
4324 if (p
->output_mappings
&& p
->supported
)
4325 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4330 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
4331 if (!(m
->output_pcm
= mapping_open_pcm(m
, ss
, dev_id
,
4332 SND_PCM_STREAM_PLAYBACK
,
4333 default_n_fragments
,
4334 default_fragment_size_msec
))) {
4335 p
->supported
= FALSE
;
4336 if (pa_idxset_size(p
->output_mappings
) == 1 &&
4337 ((!p
->input_mappings
) || pa_idxset_size(p
->input_mappings
) == 0)) {
4338 pa_log_debug("Caching failure to open output:%s", m
->name
);
4339 pa_hashmap_put(broken_outputs
, m
, m
);
4345 if (p
->input_mappings
&& p
->supported
)
4346 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4351 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
4352 if (!(m
->input_pcm
= mapping_open_pcm(m
, ss
, dev_id
,
4353 SND_PCM_STREAM_CAPTURE
,
4354 default_n_fragments
,
4355 default_fragment_size_msec
))) {
4356 p
->supported
= FALSE
;
4357 if (pa_idxset_size(p
->input_mappings
) == 1 &&
4358 ((!p
->output_mappings
) || pa_idxset_size(p
->output_mappings
) == 0)) {
4359 pa_log_debug("Caching failure to open input:%s", m
->name
);
4360 pa_hashmap_put(broken_inputs
, m
, m
);
4372 pa_log_debug("Profile %s supported.", p
->name
);
4374 if (p
->output_mappings
)
4375 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
4377 mapping_paths_probe(m
, p
, PA_ALSA_DIRECTION_OUTPUT
);
4379 if (p
->input_mappings
)
4380 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
4382 mapping_paths_probe(m
, p
, PA_ALSA_DIRECTION_INPUT
);
4386 profile_finalize_probing(last
, NULL
);
4388 pa_alsa_profile_set_drop_unsupported(ps
);
4390 paths_drop_unsupported(ps
->input_paths
);
4391 paths_drop_unsupported(ps
->output_paths
);
4392 pa_hashmap_free(broken_inputs
, NULL
);
4393 pa_hashmap_free(broken_outputs
, NULL
);
4398 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
4401 pa_alsa_decibel_fix
*db_fix
;
4406 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4409 pa_yes_no(ps
->auto_profiles
),
4410 pa_yes_no(ps
->probed
),
4411 pa_hashmap_size(ps
->mappings
),
4412 pa_hashmap_size(ps
->profiles
),
4413 pa_hashmap_size(ps
->decibel_fixes
));
4415 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4416 pa_alsa_mapping_dump(m
);
4418 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4419 pa_alsa_profile_dump(p
);
4421 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4422 pa_alsa_decibel_fix_dump(db_fix
);
4425 void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set
*ps
) {
4430 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
4431 if (!p
->supported
) {
4432 pa_hashmap_remove(ps
->profiles
, p
->name
);
4437 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
) {
4438 if (m
->supported
<= 0) {
4439 pa_hashmap_remove(ps
->mappings
, m
->name
);
4445 static pa_device_port
* device_port_alsa_init(pa_hashmap
*ports
,
4447 const char* description
,
4449 pa_alsa_setting
*setting
,
4450 pa_card_profile
*cp
,
4458 p
= pa_hashmap_get(ports
, name
);
4461 pa_alsa_port_data
*data
;
4463 p
= pa_device_port_new(core
, name
, description
, sizeof(pa_alsa_port_data
));
4465 pa_hashmap_put(ports
, p
->name
, p
);
4466 pa_proplist_update(p
->proplist
, PA_UPDATE_REPLACE
, path
->proplist
);
4468 data
= PA_DEVICE_PORT_DATA(p
);
4470 data
->setting
= setting
;
4474 p
->is_input
|= path
->direction
== PA_ALSA_DIRECTION_ANY
|| path
->direction
== PA_ALSA_DIRECTION_INPUT
;
4475 p
->is_output
|= path
->direction
== PA_ALSA_DIRECTION_ANY
|| path
->direction
== PA_ALSA_DIRECTION_OUTPUT
;
4478 pa_hashmap_put(p
->profiles
, cp
->name
, cp
);
4481 pa_hashmap_put(extra
, p
->name
, p
);
4482 pa_device_port_ref(p
);
4488 void pa_alsa_path_set_add_ports(
4489 pa_alsa_path_set
*ps
,
4490 pa_card_profile
*cp
,
4503 PA_HASHMAP_FOREACH(path
, ps
->paths
, state
) {
4504 if (!path
->settings
|| !path
->settings
->next
) {
4505 /* If there is no or just one setting we only need a
4507 pa_device_port
*port
= device_port_alsa_init(ports
, path
->name
,
4508 path
->description
, path
, path
->settings
, cp
, extra
, core
);
4509 port
->priority
= path
->priority
* 100;
4513 PA_LLIST_FOREACH(s
, path
->settings
) {
4514 pa_device_port
*port
;
4517 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4519 if (s
->description
[0])
4520 d
= pa_sprintf_malloc("%s / %s", path
->description
, s
->description
);
4522 d
= pa_xstrdup(path
->description
);
4524 port
= device_port_alsa_init(ports
, n
, d
, path
, s
, cp
, extra
, core
);
4525 port
->priority
= path
->priority
* 100 + s
->priority
;
4534 void pa_alsa_add_ports(void *sink_or_source_new_data
, pa_alsa_path_set
*ps
, pa_card
*card
) {
4537 pa_assert(sink_or_source_new_data
);
4540 if (ps
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
4541 ports
= ((pa_sink_new_data
*) sink_or_source_new_data
)->ports
;
4543 ports
= ((pa_source_new_data
*) sink_or_source_new_data
)->ports
;
4545 if (ps
->paths
&& pa_hashmap_size(ps
->paths
) > 0) {
4547 pa_alsa_path_set_add_ports(ps
, NULL
, card
->ports
, ports
, card
->core
);
4550 pa_log_debug("Added %u ports", pa_hashmap_size(ports
));