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>
29 #include <asoundlib.h>
31 #ifdef HAVE_VALGRIND_MEMCHECK_H
32 #include <valgrind/memcheck.h>
35 #include <pulse/sample.h>
36 #include <pulse/xmalloc.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/i18n.h>
40 #include <pulse/utf8.h>
42 #include <pulsecore/log.h>
43 #include <pulsecore/macro.h>
44 #include <pulsecore/core-util.h>
45 #include <pulsecore/atomic.h>
46 #include <pulsecore/core-error.h>
47 #include <pulsecore/once.h>
48 #include <pulsecore/thread.h>
49 #include <pulsecore/conf-parser.h>
50 #include <pulsecore/strbuf.h>
52 #include "alsa-mixer.h"
53 #include "alsa-util.h"
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
;
79 pa_defer_event
*defer
;
84 void (*cb
)(void *userdata
);
88 static void io_cb(pa_mainloop_api
*a
, pa_io_event
*e
, int fd
, pa_io_event_flags_t events
, void *userdata
) {
90 struct pa_alsa_fdlist
*fdl
= userdata
;
93 unsigned short revents
;
97 pa_assert(fdl
->mixer
);
99 pa_assert(fdl
->work_fds
);
106 memcpy(fdl
->work_fds
, fdl
->fds
, sizeof(struct pollfd
) * fdl
->num_fds
);
108 for (i
= 0; i
< fdl
->num_fds
; i
++) {
109 if (e
== fdl
->ios
[i
]) {
110 if (events
& PA_IO_EVENT_INPUT
)
111 fdl
->work_fds
[i
].revents
|= POLLIN
;
112 if (events
& PA_IO_EVENT_OUTPUT
)
113 fdl
->work_fds
[i
].revents
|= POLLOUT
;
114 if (events
& PA_IO_EVENT_ERROR
)
115 fdl
->work_fds
[i
].revents
|= POLLERR
;
116 if (events
& PA_IO_EVENT_HANGUP
)
117 fdl
->work_fds
[i
].revents
|= POLLHUP
;
122 pa_assert(i
!= fdl
->num_fds
);
124 if ((err
= snd_mixer_poll_descriptors_revents(fdl
->mixer
, fdl
->work_fds
, fdl
->num_fds
, &revents
)) < 0) {
125 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err
));
129 a
->defer_enable(fdl
->defer
, 1);
132 snd_mixer_handle_events(fdl
->mixer
);
135 static void defer_cb(pa_mainloop_api
*a
, pa_defer_event
*e
, void *userdata
) {
136 struct pa_alsa_fdlist
*fdl
= userdata
;
143 pa_assert(fdl
->mixer
);
145 a
->defer_enable(fdl
->defer
, 0);
147 if ((n
= snd_mixer_poll_descriptors_count(fdl
->mixer
)) < 0) {
148 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n
));
151 num_fds
= (unsigned) n
;
153 if (num_fds
!= fdl
->num_fds
) {
157 pa_xfree(fdl
->work_fds
);
158 fdl
->fds
= pa_xnew0(struct pollfd
, num_fds
);
159 fdl
->work_fds
= pa_xnew(struct pollfd
, num_fds
);
162 memset(fdl
->work_fds
, 0, sizeof(struct pollfd
) * num_fds
);
164 if ((err
= snd_mixer_poll_descriptors(fdl
->mixer
, fdl
->work_fds
, num_fds
)) < 0) {
165 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err
));
171 if (memcmp(fdl
->fds
, fdl
->work_fds
, sizeof(struct pollfd
) * num_fds
) == 0)
175 for (i
= 0; i
< fdl
->num_fds
; i
++)
176 a
->io_free(fdl
->ios
[i
]);
178 if (num_fds
!= fdl
->num_fds
) {
185 fdl
->ios
= pa_xnew(pa_io_event
*, num_fds
);
188 temp
= fdl
->work_fds
;
189 fdl
->work_fds
= fdl
->fds
;
192 fdl
->num_fds
= num_fds
;
194 for (i
= 0;i
< num_fds
;i
++)
195 fdl
->ios
[i
] = a
->io_new(a
, fdl
->fds
[i
].fd
,
196 ((fdl
->fds
[i
].events
& POLLIN
) ? PA_IO_EVENT_INPUT
: 0) |
197 ((fdl
->fds
[i
].events
& POLLOUT
) ? PA_IO_EVENT_OUTPUT
: 0),
201 struct pa_alsa_fdlist
*pa_alsa_fdlist_new(void) {
202 struct pa_alsa_fdlist
*fdl
;
204 fdl
= pa_xnew0(struct pa_alsa_fdlist
, 1);
209 void pa_alsa_fdlist_free(struct pa_alsa_fdlist
*fdl
) {
214 fdl
->m
->defer_free(fdl
->defer
);
220 for (i
= 0; i
< fdl
->num_fds
; i
++)
221 fdl
->m
->io_free(fdl
->ios
[i
]);
228 pa_xfree(fdl
->work_fds
);
233 int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist
*fdl
, snd_mixer_t
*mixer_handle
, pa_mainloop_api
*m
) {
235 pa_assert(mixer_handle
);
239 fdl
->mixer
= mixer_handle
;
241 fdl
->defer
= m
->defer_new(m
, defer_cb
, fdl
);
246 struct pa_alsa_mixer_pdata
{
248 pa_rtpoll_item
*poll_item
;
253 struct pa_alsa_mixer_pdata
*pa_alsa_mixer_pdata_new(void) {
254 struct pa_alsa_mixer_pdata
*pd
;
256 pd
= pa_xnew0(struct pa_alsa_mixer_pdata
, 1);
261 void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata
*pd
) {
265 pa_rtpoll_item_free(pd
->poll_item
);
271 static int rtpoll_work_cb(pa_rtpoll_item
*i
) {
272 struct pa_alsa_mixer_pdata
*pd
;
275 unsigned short revents
= 0;
278 pd
= pa_rtpoll_item_get_userdata(i
);
280 pa_assert_fp(i
== pd
->poll_item
);
282 p
= pa_rtpoll_item_get_pollfd(i
, &n_fds
);
284 if ((err
= snd_mixer_poll_descriptors_revents(pd
->mixer
, p
, n_fds
, &revents
)) < 0) {
285 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err
));
286 pa_rtpoll_item_free(i
);
291 snd_mixer_handle_events(pd
->mixer
);
292 pa_rtpoll_item_free(i
);
293 pa_alsa_set_mixer_rtpoll(pd
, pd
->mixer
, pd
->rtpoll
);
299 int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata
*pd
, snd_mixer_t
*mixer
, pa_rtpoll
*rtp
) {
308 if ((n
= snd_mixer_poll_descriptors_count(mixer
)) < 0) {
309 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n
));
313 i
= pa_rtpoll_item_new(rtp
, PA_RTPOLL_LATE
, (unsigned) n
);
315 p
= pa_rtpoll_item_get_pollfd(i
, NULL
);
317 memset(p
, 0, sizeof(struct pollfd
) * n
);
319 if ((err
= snd_mixer_poll_descriptors(mixer
, p
, (unsigned) n
)) < 0) {
320 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err
));
321 pa_rtpoll_item_free(i
);
329 pa_rtpoll_item_set_userdata(i
, pd
);
330 pa_rtpoll_item_set_work_callback(i
, rtpoll_work_cb
);
335 static int prepare_mixer(snd_mixer_t
*mixer
, const char *dev
) {
341 if ((err
= snd_mixer_attach(mixer
, dev
)) < 0) {
342 pa_log_info("Unable to attach to mixer %s: %s", dev
, pa_alsa_strerror(err
));
346 if ((err
= snd_mixer_selem_register(mixer
, NULL
, NULL
)) < 0) {
347 pa_log_warn("Unable to register mixer: %s", pa_alsa_strerror(err
));
351 if ((err
= snd_mixer_load(mixer
)) < 0) {
352 pa_log_warn("Unable to load mixer: %s", pa_alsa_strerror(err
));
356 pa_log_info("Successfully attached to mixer '%s'", dev
);
360 snd_mixer_t
*pa_alsa_open_mixer_for_pcm(snd_pcm_t
*pcm
, char **ctl_device
) {
364 snd_pcm_info_t
* info
;
365 snd_pcm_info_alloca(&info
);
369 if ((err
= snd_mixer_open(&m
, 0)) < 0) {
370 pa_log("Error opening mixer: %s", pa_alsa_strerror(err
));
374 /* First, try by name */
375 if ((dev
= snd_pcm_name(pcm
)))
376 if (prepare_mixer(m
, dev
) >= 0) {
378 *ctl_device
= pa_xstrdup(dev
);
383 /* Then, try by card index */
384 if (snd_pcm_info(pcm
, info
) >= 0) {
388 if ((card_idx
= snd_pcm_info_get_card(info
)) >= 0) {
390 md
= pa_sprintf_malloc("hw:%i", card_idx
);
392 if (!dev
|| !pa_streq(dev
, md
))
393 if (prepare_mixer(m
, md
) >= 0) {
411 static const snd_mixer_selem_channel_id_t alsa_channel_ids
[PA_CHANNEL_POSITION_MAX
] = {
412 [PA_CHANNEL_POSITION_MONO
] = SND_MIXER_SCHN_MONO
, /* The ALSA name is just an alias! */
414 [PA_CHANNEL_POSITION_FRONT_CENTER
] = SND_MIXER_SCHN_FRONT_CENTER
,
415 [PA_CHANNEL_POSITION_FRONT_LEFT
] = SND_MIXER_SCHN_FRONT_LEFT
,
416 [PA_CHANNEL_POSITION_FRONT_RIGHT
] = SND_MIXER_SCHN_FRONT_RIGHT
,
418 [PA_CHANNEL_POSITION_REAR_CENTER
] = SND_MIXER_SCHN_REAR_CENTER
,
419 [PA_CHANNEL_POSITION_REAR_LEFT
] = SND_MIXER_SCHN_REAR_LEFT
,
420 [PA_CHANNEL_POSITION_REAR_RIGHT
] = SND_MIXER_SCHN_REAR_RIGHT
,
422 [PA_CHANNEL_POSITION_LFE
] = SND_MIXER_SCHN_WOOFER
,
424 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
425 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
427 [PA_CHANNEL_POSITION_SIDE_LEFT
] = SND_MIXER_SCHN_SIDE_LEFT
,
428 [PA_CHANNEL_POSITION_SIDE_RIGHT
] = SND_MIXER_SCHN_SIDE_RIGHT
,
430 [PA_CHANNEL_POSITION_AUX0
] = SND_MIXER_SCHN_UNKNOWN
,
431 [PA_CHANNEL_POSITION_AUX1
] = SND_MIXER_SCHN_UNKNOWN
,
432 [PA_CHANNEL_POSITION_AUX2
] = SND_MIXER_SCHN_UNKNOWN
,
433 [PA_CHANNEL_POSITION_AUX3
] = SND_MIXER_SCHN_UNKNOWN
,
434 [PA_CHANNEL_POSITION_AUX4
] = SND_MIXER_SCHN_UNKNOWN
,
435 [PA_CHANNEL_POSITION_AUX5
] = SND_MIXER_SCHN_UNKNOWN
,
436 [PA_CHANNEL_POSITION_AUX6
] = SND_MIXER_SCHN_UNKNOWN
,
437 [PA_CHANNEL_POSITION_AUX7
] = SND_MIXER_SCHN_UNKNOWN
,
438 [PA_CHANNEL_POSITION_AUX8
] = SND_MIXER_SCHN_UNKNOWN
,
439 [PA_CHANNEL_POSITION_AUX9
] = SND_MIXER_SCHN_UNKNOWN
,
440 [PA_CHANNEL_POSITION_AUX10
] = SND_MIXER_SCHN_UNKNOWN
,
441 [PA_CHANNEL_POSITION_AUX11
] = SND_MIXER_SCHN_UNKNOWN
,
442 [PA_CHANNEL_POSITION_AUX12
] = SND_MIXER_SCHN_UNKNOWN
,
443 [PA_CHANNEL_POSITION_AUX13
] = SND_MIXER_SCHN_UNKNOWN
,
444 [PA_CHANNEL_POSITION_AUX14
] = SND_MIXER_SCHN_UNKNOWN
,
445 [PA_CHANNEL_POSITION_AUX15
] = SND_MIXER_SCHN_UNKNOWN
,
446 [PA_CHANNEL_POSITION_AUX16
] = SND_MIXER_SCHN_UNKNOWN
,
447 [PA_CHANNEL_POSITION_AUX17
] = SND_MIXER_SCHN_UNKNOWN
,
448 [PA_CHANNEL_POSITION_AUX18
] = SND_MIXER_SCHN_UNKNOWN
,
449 [PA_CHANNEL_POSITION_AUX19
] = SND_MIXER_SCHN_UNKNOWN
,
450 [PA_CHANNEL_POSITION_AUX20
] = SND_MIXER_SCHN_UNKNOWN
,
451 [PA_CHANNEL_POSITION_AUX21
] = SND_MIXER_SCHN_UNKNOWN
,
452 [PA_CHANNEL_POSITION_AUX22
] = SND_MIXER_SCHN_UNKNOWN
,
453 [PA_CHANNEL_POSITION_AUX23
] = SND_MIXER_SCHN_UNKNOWN
,
454 [PA_CHANNEL_POSITION_AUX24
] = SND_MIXER_SCHN_UNKNOWN
,
455 [PA_CHANNEL_POSITION_AUX25
] = SND_MIXER_SCHN_UNKNOWN
,
456 [PA_CHANNEL_POSITION_AUX26
] = SND_MIXER_SCHN_UNKNOWN
,
457 [PA_CHANNEL_POSITION_AUX27
] = SND_MIXER_SCHN_UNKNOWN
,
458 [PA_CHANNEL_POSITION_AUX28
] = SND_MIXER_SCHN_UNKNOWN
,
459 [PA_CHANNEL_POSITION_AUX29
] = SND_MIXER_SCHN_UNKNOWN
,
460 [PA_CHANNEL_POSITION_AUX30
] = SND_MIXER_SCHN_UNKNOWN
,
461 [PA_CHANNEL_POSITION_AUX31
] = SND_MIXER_SCHN_UNKNOWN
,
463 [PA_CHANNEL_POSITION_TOP_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
465 [PA_CHANNEL_POSITION_TOP_FRONT_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
466 [PA_CHANNEL_POSITION_TOP_FRONT_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
467 [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
,
469 [PA_CHANNEL_POSITION_TOP_REAR_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
470 [PA_CHANNEL_POSITION_TOP_REAR_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
471 [PA_CHANNEL_POSITION_TOP_REAR_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
474 static void setting_free(pa_alsa_setting
*s
) {
478 pa_idxset_free(s
->options
, NULL
, NULL
);
481 pa_xfree(s
->description
);
485 static void option_free(pa_alsa_option
*o
) {
488 pa_xfree(o
->alsa_name
);
490 pa_xfree(o
->description
);
494 static void decibel_fix_free(pa_alsa_decibel_fix
*db_fix
) {
497 pa_xfree(db_fix
->name
);
498 pa_xfree(db_fix
->db_values
);
503 static void element_free(pa_alsa_element
*e
) {
507 while ((o
= e
->options
)) {
508 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
513 decibel_fix_free(e
->db_fix
);
515 pa_xfree(e
->alsa_name
);
519 void pa_alsa_path_free(pa_alsa_path
*p
) {
525 while ((e
= p
->elements
)) {
526 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
530 while ((s
= p
->settings
)) {
531 PA_LLIST_REMOVE(pa_alsa_setting
, p
->settings
, s
);
536 pa_xfree(p
->description
);
540 void pa_alsa_path_set_free(pa_alsa_path_set
*ps
) {
544 while ((p
= ps
->paths
)) {
545 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
546 pa_alsa_path_free(p
);
552 static long to_alsa_dB(pa_volume_t v
) {
553 return (long) (pa_sw_volume_to_dB(v
) * 100.0);
556 static pa_volume_t
from_alsa_dB(long v
) {
557 return pa_sw_volume_from_dB((double) v
/ 100.0);
560 static long to_alsa_volume(pa_volume_t v
, long min
, long max
) {
563 w
= (long) round(((double) v
* (double) (max
- min
)) / PA_VOLUME_NORM
) + min
;
564 return PA_CLAMP_UNLIKELY(w
, min
, max
);
567 static pa_volume_t
from_alsa_volume(long v
, long min
, long max
) {
568 return (pa_volume_t
) round(((double) (v
- min
) * PA_VOLUME_NORM
) / (double) (max
- min
));
571 #define SELEM_INIT(sid, name) \
573 snd_mixer_selem_id_alloca(&(sid)); \
574 snd_mixer_selem_id_set_name((sid), (name)); \
575 snd_mixer_selem_id_set_index((sid), 0); \
578 static int element_get_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
579 snd_mixer_selem_id_t
*sid
;
580 snd_mixer_elem_t
*me
;
581 snd_mixer_selem_channel_id_t c
;
582 pa_channel_position_mask_t mask
= 0;
590 SELEM_INIT(sid
, e
->alsa_name
);
591 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
592 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
596 pa_cvolume_mute(v
, cm
->channels
);
598 /* We take the highest volume of all channels that match */
600 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
607 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
608 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
610 if ((r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
)) >= 0) {
611 /* If the channel volume is outside the limits set
612 * by the dB fix, we clamp the hw volume to be
613 * within the limits. */
614 if (value
< e
->db_fix
->min_step
) {
615 value
= e
->db_fix
->min_step
;
616 snd_mixer_selem_set_playback_volume(me
, c
, value
);
617 pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
618 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
619 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
620 } else if (value
> e
->db_fix
->max_step
) {
621 value
= e
->db_fix
->max_step
;
622 snd_mixer_selem_set_playback_volume(me
, c
, value
);
623 pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
624 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
625 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
628 /* Volume step -> dB value conversion. */
629 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
632 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
636 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
638 if ((r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
)) >= 0) {
639 /* If the channel volume is outside the limits set
640 * by the dB fix, we clamp the hw volume to be
641 * within the limits. */
642 if (value
< e
->db_fix
->min_step
) {
643 value
= e
->db_fix
->min_step
;
644 snd_mixer_selem_set_capture_volume(me
, c
, value
);
645 pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
646 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
647 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
648 } else if (value
> e
->db_fix
->max_step
) {
649 value
= e
->db_fix
->max_step
;
650 snd_mixer_selem_set_capture_volume(me
, c
, value
);
651 pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
652 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
653 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
656 /* Volume step -> dB value conversion. */
657 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
660 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
668 #ifdef HAVE_VALGRIND_MEMCHECK_H
669 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
672 f
= from_alsa_dB(value
);
677 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
678 if (snd_mixer_selem_has_playback_channel(me
, c
))
679 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
683 if (snd_mixer_selem_has_capture_channel(me
, c
))
684 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
692 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
695 for (k
= 0; k
< cm
->channels
; k
++)
696 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
697 if (v
->values
[k
] < f
)
700 mask
|= e
->masks
[c
][e
->n_channels
-1];
703 for (k
= 0; k
< cm
->channels
; k
++)
704 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
705 v
->values
[k
] = PA_VOLUME_NORM
;
710 int pa_alsa_path_get_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
721 pa_cvolume_reset(v
, cm
->channels
);
723 PA_LLIST_FOREACH(e
, p
->elements
) {
726 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
729 pa_assert(!p
->has_dB
|| e
->has_dB
);
731 if (element_get_volume(e
, m
, cm
, &ev
) < 0)
734 /* If we have no dB information all we can do is take the first element and leave */
740 pa_sw_cvolume_multiply(v
, v
, &ev
);
746 static int element_get_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t
*b
) {
747 snd_mixer_selem_id_t
*sid
;
748 snd_mixer_elem_t
*me
;
749 snd_mixer_selem_channel_id_t c
;
755 SELEM_INIT(sid
, e
->alsa_name
);
756 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
757 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
761 /* We return muted if at least one channel is muted */
763 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
767 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
768 if (snd_mixer_selem_has_playback_channel(me
, c
))
769 r
= snd_mixer_selem_get_playback_switch(me
, c
, &value
);
773 if (snd_mixer_selem_has_capture_channel(me
, c
))
774 r
= snd_mixer_selem_get_capture_switch(me
, c
, &value
);
792 int pa_alsa_path_get_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t
*muted
) {
802 PA_LLIST_FOREACH(e
, p
->elements
) {
805 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
808 if (element_get_switch(e
, m
, &b
) < 0)
821 /* Finds the closest item in db_fix->db_values and returns the corresponding
822 * step. *db_value is replaced with the value from the db_values table.
823 * Rounding is done based on the rounding parameter: -1 means rounding down and
824 * +1 means rounding up. */
825 static long decibel_fix_get_step(pa_alsa_decibel_fix
*db_fix
, long *db_value
, int rounding
) {
831 pa_assert(rounding
!= 0);
833 max_i
= db_fix
->max_step
- db_fix
->min_step
;
836 for (i
= 0; i
< max_i
; i
++) {
837 if (db_fix
->db_values
[i
] >= *db_value
)
841 for (i
= 0; i
< max_i
; i
++) {
842 if (db_fix
->db_values
[i
+ 1] > *db_value
)
847 *db_value
= db_fix
->db_values
[i
];
849 return i
+ db_fix
->min_step
;
852 static int element_set_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
, pa_bool_t write_to_hw
) {
854 snd_mixer_selem_id_t
*sid
;
856 snd_mixer_elem_t
*me
;
857 snd_mixer_selem_channel_id_t c
;
858 pa_channel_position_mask_t mask
= 0;
865 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
867 SELEM_INIT(sid
, e
->alsa_name
);
868 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
869 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
873 pa_cvolume_mute(&rv
, cm
->channels
);
875 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
877 pa_volume_t f
= PA_VOLUME_MUTED
;
878 pa_bool_t found
= FALSE
;
880 for (k
= 0; k
< cm
->channels
; k
++)
881 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
])) {
883 if (v
->values
[k
] > f
)
888 /* Hmm, so this channel does not exist in the volume
889 * struct, so let's bind it to the overall max of the
891 f
= pa_cvolume_max(v
);
895 long value
= to_alsa_dB(f
);
896 int rounding
= value
> 0 ? -1 : +1;
898 if (e
->volume_limit
>= 0 && value
> (e
->max_dB
* 100))
899 value
= e
->max_dB
* 100;
901 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
902 /* If we call set_playback_volume() without checking first
903 * if the channel is available, ALSA behaves very
904 * strangely and doesn't fail the call */
905 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
908 r
= snd_mixer_selem_set_playback_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
910 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
916 if ((r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, rounding
)) >= 0)
917 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
920 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
921 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value
);
927 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
930 r
= snd_mixer_selem_set_capture_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
932 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
938 if ((r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, rounding
)) >= 0)
939 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
942 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
943 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value
);
953 #ifdef HAVE_VALGRIND_MEMCHECK_H
954 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
957 f
= from_alsa_dB(value
);
962 value
= to_alsa_volume(f
, e
->min_volume
, e
->max_volume
);
964 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
965 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
966 if ((r
= snd_mixer_selem_set_playback_volume(me
, c
, value
)) >= 0)
967 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
971 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
972 if ((r
= snd_mixer_selem_set_capture_volume(me
, c
, value
)) >= 0)
973 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
981 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
984 for (k
= 0; k
< cm
->channels
; k
++)
985 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
986 if (rv
.values
[k
] < f
)
989 mask
|= e
->masks
[c
][e
->n_channels
-1];
992 for (k
= 0; k
< cm
->channels
; k
++)
993 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
994 rv
.values
[k
] = PA_VOLUME_NORM
;
1000 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 write_to_hw
) {
1009 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
1014 rv
= *v
; /* Remaining adjustment */
1015 pa_cvolume_reset(v
, cm
->channels
); /* Adjustment done */
1017 PA_LLIST_FOREACH(e
, p
->elements
) {
1020 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
1023 pa_assert(!p
->has_dB
|| e
->has_dB
);
1026 if (element_set_volume(e
, m
, cm
, &ev
, write_to_hw
) < 0)
1034 pa_sw_cvolume_multiply(v
, v
, &ev
);
1035 pa_sw_cvolume_divide(&rv
, &rv
, &ev
);
1041 static int element_set_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t b
) {
1042 snd_mixer_elem_t
*me
;
1043 snd_mixer_selem_id_t
*sid
;
1049 SELEM_INIT(sid
, e
->alsa_name
);
1050 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1051 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1055 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1056 r
= snd_mixer_selem_set_playback_switch_all(me
, b
);
1058 r
= snd_mixer_selem_set_capture_switch_all(me
, b
);
1061 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1066 int pa_alsa_path_set_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t muted
) {
1075 PA_LLIST_FOREACH(e
, p
->elements
) {
1077 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
1080 if (element_set_switch(e
, m
, !muted
) < 0)
1087 /* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
1088 * function sets all channels of the volume element to e->min_volume, 0 dB or
1089 * e->constant_volume. */
1090 static int element_set_constant_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1091 snd_mixer_elem_t
*me
= NULL
;
1092 snd_mixer_selem_id_t
*sid
= NULL
;
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 switch (e
->volume_use
) {
1106 case PA_ALSA_VOLUME_OFF
:
1107 volume
= e
->min_volume
;
1110 case PA_ALSA_VOLUME_ZERO
:
1114 volume
= decibel_fix_get_step(e
->db_fix
, &dB
, +1);
1118 case PA_ALSA_VOLUME_CONSTANT
:
1119 volume
= e
->constant_volume
;
1123 pa_assert_not_reached();
1127 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1128 r
= snd_mixer_selem_set_playback_volume_all(me
, volume
);
1130 r
= snd_mixer_selem_set_capture_volume_all(me
, volume
);
1132 pa_assert(e
->volume_use
== PA_ALSA_VOLUME_ZERO
);
1133 pa_assert(!e
->db_fix
);
1135 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1136 r
= snd_mixer_selem_set_playback_dB_all(me
, 0, +1);
1138 r
= snd_mixer_selem_set_capture_dB_all(me
, 0, +1);
1142 pa_log_warn("Failed to set volume of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1147 int pa_alsa_path_select(pa_alsa_path
*p
, snd_mixer_t
*m
) {
1154 pa_log_debug("Activating path %s", p
->name
);
1155 pa_alsa_path_dump(p
);
1157 PA_LLIST_FOREACH(e
, p
->elements
) {
1159 switch (e
->switch_use
) {
1160 case PA_ALSA_SWITCH_OFF
:
1161 r
= element_set_switch(e
, m
, FALSE
);
1164 case PA_ALSA_SWITCH_ON
:
1165 r
= element_set_switch(e
, m
, TRUE
);
1168 case PA_ALSA_SWITCH_MUTE
:
1169 case PA_ALSA_SWITCH_IGNORE
:
1170 case PA_ALSA_SWITCH_SELECT
:
1178 switch (e
->volume_use
) {
1179 case PA_ALSA_VOLUME_OFF
:
1180 case PA_ALSA_VOLUME_ZERO
:
1181 case PA_ALSA_VOLUME_CONSTANT
:
1182 r
= element_set_constant_volume(e
, m
);
1185 case PA_ALSA_VOLUME_MERGE
:
1186 case PA_ALSA_VOLUME_IGNORE
:
1198 static int check_required(pa_alsa_element
*e
, snd_mixer_elem_t
*me
) {
1199 pa_bool_t has_switch
;
1200 pa_bool_t has_enumeration
;
1201 pa_bool_t has_volume
;
1206 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1208 snd_mixer_selem_has_playback_switch(me
) ||
1209 (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
));
1212 snd_mixer_selem_has_capture_switch(me
) ||
1213 (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
));
1216 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1218 snd_mixer_selem_has_playback_volume(me
) ||
1219 (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
));
1222 snd_mixer_selem_has_capture_volume(me
) ||
1223 (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
));
1226 has_enumeration
= snd_mixer_selem_is_enumerated(me
);
1228 if ((e
->required
== PA_ALSA_REQUIRED_SWITCH
&& !has_switch
) ||
1229 (e
->required
== PA_ALSA_REQUIRED_VOLUME
&& !has_volume
) ||
1230 (e
->required
== PA_ALSA_REQUIRED_ENUMERATION
&& !has_enumeration
))
1233 if (e
->required
== PA_ALSA_REQUIRED_ANY
&& !(has_switch
|| has_volume
|| has_enumeration
))
1236 if ((e
->required_absent
== PA_ALSA_REQUIRED_SWITCH
&& has_switch
) ||
1237 (e
->required_absent
== PA_ALSA_REQUIRED_VOLUME
&& has_volume
) ||
1238 (e
->required_absent
== PA_ALSA_REQUIRED_ENUMERATION
&& has_enumeration
))
1241 if (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& (has_switch
|| has_volume
|| has_enumeration
))
1244 if (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) {
1245 switch (e
->required_any
) {
1246 case PA_ALSA_REQUIRED_VOLUME
:
1247 e
->path
->req_any_present
|= (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
);
1249 case PA_ALSA_REQUIRED_SWITCH
:
1250 e
->path
->req_any_present
|= (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
);
1252 case PA_ALSA_REQUIRED_ENUMERATION
:
1253 e
->path
->req_any_present
|= (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1255 case PA_ALSA_REQUIRED_ANY
:
1256 e
->path
->req_any_present
|=
1257 (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) ||
1258 (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) ||
1259 (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1262 pa_assert_not_reached();
1266 if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1268 PA_LLIST_FOREACH(o
, e
->options
) {
1269 e
->path
->req_any_present
|= (o
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) &&
1271 if (o
->required
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
< 0)
1273 if (o
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
>= 0)
1281 static int element_probe(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1282 snd_mixer_selem_id_t
*sid
;
1283 snd_mixer_elem_t
*me
;
1289 SELEM_INIT(sid
, e
->alsa_name
);
1291 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1293 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1296 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1297 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1298 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1303 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1304 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1306 if (!snd_mixer_selem_has_playback_switch(me
)) {
1307 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1308 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1310 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1315 if (!snd_mixer_selem_has_capture_switch(me
)) {
1316 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1317 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1319 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1323 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1324 e
->direction_try_other
= FALSE
;
1327 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1329 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1331 if (!snd_mixer_selem_has_playback_volume(me
)) {
1332 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1333 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1335 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1340 if (!snd_mixer_selem_has_capture_volume(me
)) {
1341 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1342 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1344 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1348 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1349 long min_dB
= 0, max_dB
= 0;
1352 e
->direction_try_other
= FALSE
;
1354 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1355 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1357 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1360 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1364 if (e
->min_volume
>= e
->max_volume
) {
1365 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
);
1366 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1368 } else if (e
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&&
1369 (e
->min_volume
> e
->constant_volume
|| e
->max_volume
< e
->constant_volume
)) {
1370 pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
1371 e
->constant_volume
, e
->alsa_name
, e
->min_volume
, e
->max_volume
);
1372 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1376 pa_channel_position_t p
;
1379 ((e
->min_volume
> e
->db_fix
->min_step
) ||
1380 (e
->max_volume
< e
->db_fix
->max_step
))) {
1381 pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1382 "real hardware range (%li-%li). Disabling the decibel fix.", e
->alsa_name
,
1383 e
->db_fix
->min_step
, e
->db_fix
->max_step
,
1384 e
->min_volume
, e
->max_volume
);
1386 decibel_fix_free(e
->db_fix
);
1392 e
->min_volume
= e
->db_fix
->min_step
;
1393 e
->max_volume
= e
->db_fix
->max_step
;
1394 min_dB
= e
->db_fix
->db_values
[0];
1395 max_dB
= e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
];
1396 } else if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1397 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1399 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1402 #ifdef HAVE_VALGRIND_MEMCHECK_H
1403 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1404 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1407 e
->min_dB
= ((double) min_dB
) / 100.0;
1408 e
->max_dB
= ((double) max_dB
) / 100.0;
1410 if (min_dB
>= max_dB
) {
1411 pa_assert(!e
->db_fix
);
1412 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
);
1417 if (e
->volume_limit
>= 0) {
1418 if (e
->volume_limit
<= e
->min_volume
|| e
->volume_limit
> e
->max_volume
)
1419 pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1420 "%li-%li. The volume limit is ignored.",
1421 e
->alsa_name
, e
->path
->name
, e
->volume_limit
, e
->min_volume
+ 1, e
->max_volume
);
1424 e
->max_volume
= e
->volume_limit
;
1428 e
->db_fix
->max_step
= e
->max_volume
;
1429 e
->max_dB
= ((double) e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
]) / 100.0;
1432 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1433 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB
);
1435 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB
);
1438 pa_log_warn("Failed to get dB value of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1441 e
->max_dB
= ((double) max_dB
) / 100.0;
1447 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1448 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1450 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1455 if (!e
->override_map
) {
1456 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++)
1457 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1458 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1461 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1464 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1466 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1469 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1470 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1472 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1475 if (e
->n_channels
<= 0) {
1476 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1480 if (e
->n_channels
> 2) {
1481 /* FIXME: In some places code like this is used:
1483 * e->masks[alsa_channel_ids[p]][e->n_channels-1]
1485 * The definition of e->masks is
1487 * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST][2];
1489 * Since the array size is fixed at 2, we obviously
1490 * don't support elements with more than two
1492 pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e
->alsa_name
, e
->n_channels
);
1496 if (!e
->override_map
) {
1497 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1498 pa_bool_t has_channel
;
1500 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1503 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1504 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1506 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1508 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1513 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++)
1514 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1521 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1524 PA_LLIST_FOREACH(o
, e
->options
)
1525 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1526 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1530 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1531 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1535 PA_LLIST_FOREACH(o
, e
->options
) {
1538 for (i
= 0; i
< n
; i
++) {
1541 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1544 if (!pa_streq(buf
, o
->alsa_name
))
1552 if (check_required(e
, me
) < 0)
1558 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, pa_bool_t prefixed
) {
1565 if (!pa_startswith(section
, "Element "))
1571 /* This is not an element section, but an enum section? */
1572 if (strchr(section
, ':'))
1575 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1576 return p
->last_element
;
1578 PA_LLIST_FOREACH(e
, p
->elements
)
1579 if (pa_streq(e
->alsa_name
, section
))
1582 e
= pa_xnew0(pa_alsa_element
, 1);
1584 e
->alsa_name
= pa_xstrdup(section
);
1585 e
->direction
= p
->direction
;
1586 e
->volume_limit
= -1;
1588 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1591 p
->last_element
= e
;
1595 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1601 if (!pa_startswith(section
, "Option "))
1606 /* This is not an enum section, but an element section? */
1607 if (!(on
= strchr(section
, ':')))
1610 en
= pa_xstrndup(section
, on
- section
);
1613 if (p
->last_option
&&
1614 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1615 pa_streq(p
->last_option
->alsa_name
, on
)) {
1617 return p
->last_option
;
1620 pa_assert_se(e
= element_get(p
, en
, FALSE
));
1623 PA_LLIST_FOREACH(o
, e
->options
)
1624 if (pa_streq(o
->alsa_name
, on
))
1627 o
= pa_xnew0(pa_alsa_option
, 1);
1629 o
->alsa_name
= pa_xstrdup(on
);
1632 if (p
->last_option
&& p
->last_option
->element
== e
)
1633 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1635 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1642 static int element_parse_switch(
1643 const char *filename
,
1645 const char *section
,
1651 pa_alsa_path
*p
= userdata
;
1656 if (!(e
= element_get(p
, section
, TRUE
))) {
1657 pa_log("[%s:%u] Switch makes no sense in '%s'", filename
, line
, section
);
1661 if (pa_streq(rvalue
, "ignore"))
1662 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1663 else if (pa_streq(rvalue
, "mute"))
1664 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1665 else if (pa_streq(rvalue
, "off"))
1666 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1667 else if (pa_streq(rvalue
, "on"))
1668 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1669 else if (pa_streq(rvalue
, "select"))
1670 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1672 pa_log("[%s:%u] Switch invalid of '%s'", filename
, line
, section
);
1679 static int element_parse_volume(
1680 const char *filename
,
1682 const char *section
,
1688 pa_alsa_path
*p
= userdata
;
1693 if (!(e
= element_get(p
, section
, TRUE
))) {
1694 pa_log("[%s:%u] Volume makes no sense in '%s'", filename
, line
, section
);
1698 if (pa_streq(rvalue
, "ignore"))
1699 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1700 else if (pa_streq(rvalue
, "merge"))
1701 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1702 else if (pa_streq(rvalue
, "off"))
1703 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1704 else if (pa_streq(rvalue
, "zero"))
1705 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1709 if (pa_atou(rvalue
, &constant
) >= 0) {
1710 e
->volume_use
= PA_ALSA_VOLUME_CONSTANT
;
1711 e
->constant_volume
= constant
;
1713 pa_log("[%s:%u] Volume invalid of '%s'", filename
, line
, section
);
1721 static int element_parse_enumeration(
1722 const char *filename
,
1724 const char *section
,
1730 pa_alsa_path
*p
= userdata
;
1735 if (!(e
= element_get(p
, section
, TRUE
))) {
1736 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename
, line
, section
);
1740 if (pa_streq(rvalue
, "ignore"))
1741 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1742 else if (pa_streq(rvalue
, "select"))
1743 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1745 pa_log("[%s:%u] Enumeration invalid of '%s'", filename
, line
, section
);
1752 static int option_parse_priority(
1753 const char *filename
,
1755 const char *section
,
1761 pa_alsa_path
*p
= userdata
;
1767 if (!(o
= option_get(p
, section
))) {
1768 pa_log("[%s:%u] Priority makes no sense in '%s'", filename
, line
, section
);
1772 if (pa_atou(rvalue
, &prio
) < 0) {
1773 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
1781 static int option_parse_name(
1782 const char *filename
,
1784 const char *section
,
1790 pa_alsa_path
*p
= userdata
;
1795 if (!(o
= option_get(p
, section
))) {
1796 pa_log("[%s:%u] Name makes no sense in '%s'", filename
, line
, section
);
1801 o
->name
= pa_xstrdup(rvalue
);
1806 static int element_parse_required(
1807 const char *filename
,
1809 const char *section
,
1815 pa_alsa_path
*p
= userdata
;
1818 pa_alsa_required_t req
;
1822 e
= element_get(p
, section
, TRUE
);
1823 o
= option_get(p
, section
);
1825 pa_log("[%s:%u] Required makes no sense in '%s'", filename
, line
, section
);
1829 if (pa_streq(rvalue
, "ignore"))
1830 req
= PA_ALSA_REQUIRED_IGNORE
;
1831 else if (pa_streq(rvalue
, "switch") && e
)
1832 req
= PA_ALSA_REQUIRED_SWITCH
;
1833 else if (pa_streq(rvalue
, "volume") && e
)
1834 req
= PA_ALSA_REQUIRED_VOLUME
;
1835 else if (pa_streq(rvalue
, "enumeration"))
1836 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1837 else if (pa_streq(rvalue
, "any"))
1838 req
= PA_ALSA_REQUIRED_ANY
;
1840 pa_log("[%s:%u] Required invalid of '%s'", filename
, line
, section
);
1844 if (pa_streq(lvalue
, "required-absent")) {
1846 e
->required_absent
= req
;
1848 o
->required_absent
= req
;
1850 else if (pa_streq(lvalue
, "required-any")) {
1852 e
->required_any
= req
;
1853 e
->path
->has_req_any
= TRUE
;
1856 o
->required_any
= req
;
1857 o
->element
->path
->has_req_any
= TRUE
;
1870 static int element_parse_direction(
1871 const char *filename
,
1873 const char *section
,
1879 pa_alsa_path
*p
= userdata
;
1884 if (!(e
= element_get(p
, section
, TRUE
))) {
1885 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1889 if (pa_streq(rvalue
, "playback"))
1890 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1891 else if (pa_streq(rvalue
, "capture"))
1892 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1894 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1901 static int element_parse_direction_try_other(
1902 const char *filename
,
1904 const char *section
,
1910 pa_alsa_path
*p
= userdata
;
1914 if (!(e
= element_get(p
, section
, TRUE
))) {
1915 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1919 if ((yes
= pa_parse_boolean(rvalue
)) < 0) {
1920 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1924 e
->direction_try_other
= !!yes
;
1928 static int element_parse_volume_limit(
1929 const char *filename
,
1931 const char *section
,
1937 pa_alsa_path
*p
= userdata
;
1941 if (!(e
= element_get(p
, section
, TRUE
))) {
1942 pa_log("[%s:%u] volume-limit makes no sense in '%s'", filename
, line
, section
);
1946 if (pa_atol(rvalue
, &volume_limit
) < 0 || volume_limit
< 0) {
1947 pa_log("[%s:%u] Invalid value for volume-limit", filename
, line
);
1951 e
->volume_limit
= volume_limit
;
1955 static pa_channel_position_mask_t
parse_mask(const char *m
) {
1956 pa_channel_position_mask_t v
;
1958 if (pa_streq(m
, "all-left"))
1959 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
1960 else if (pa_streq(m
, "all-right"))
1961 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
1962 else if (pa_streq(m
, "all-center"))
1963 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
1964 else if (pa_streq(m
, "all-front"))
1965 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
1966 else if (pa_streq(m
, "all-rear"))
1967 v
= PA_CHANNEL_POSITION_MASK_REAR
;
1968 else if (pa_streq(m
, "all-side"))
1969 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
1970 else if (pa_streq(m
, "all-top"))
1971 v
= PA_CHANNEL_POSITION_MASK_TOP
;
1972 else if (pa_streq(m
, "all-no-lfe"))
1973 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
1974 else if (pa_streq(m
, "all"))
1975 v
= PA_CHANNEL_POSITION_MASK_ALL
;
1977 pa_channel_position_t p
;
1979 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
1982 v
= PA_CHANNEL_POSITION_MASK(p
);
1988 static int element_parse_override_map(
1989 const char *filename
,
1991 const char *section
,
1997 pa_alsa_path
*p
= userdata
;
1999 const char *state
= NULL
;
2003 if (!(e
= element_get(p
, section
, TRUE
))) {
2004 pa_log("[%s:%u] Override map makes no sense in '%s'", filename
, line
, section
);
2008 while ((n
= pa_split(rvalue
, ",", &state
))) {
2009 pa_channel_position_mask_t m
;
2014 if ((m
= parse_mask(n
)) == 0) {
2015 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename
, line
, n
, section
);
2021 if (pa_streq(lvalue
, "override-map.1"))
2022 e
->masks
[i
++][0] = m
;
2024 e
->masks
[i
++][1] = m
;
2026 /* Later on we might add override-map.3 and so on here ... */
2031 e
->override_map
= TRUE
;
2036 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
2037 snd_mixer_selem_id_t
*sid
;
2038 snd_mixer_elem_t
*me
;
2044 SELEM_INIT(sid
, e
->alsa_name
);
2045 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2046 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2050 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
2052 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
2053 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
2055 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
2058 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2061 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
2063 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
2064 pa_log_warn("Failed to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2070 int pa_alsa_setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
2077 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
2078 element_set_option(o
->element
, m
, o
->alsa_idx
);
2083 static int option_verify(pa_alsa_option
*o
) {
2084 static const struct description_map well_known_descriptions
[] = {
2085 { "input", N_("Input") },
2086 { "input-docking", N_("Docking Station Input") },
2087 { "input-docking-microphone", N_("Docking Station Microphone") },
2088 { "input-docking-linein", N_("Docking Station Line-In") },
2089 { "input-linein", N_("Line-In") },
2090 { "input-microphone", N_("Microphone") },
2091 { "input-microphone-front", N_("Front Microphone") },
2092 { "input-microphone-rear", N_("Rear Microphone") },
2093 { "input-microphone-external", N_("External Microphone") },
2094 { "input-microphone-internal", N_("Internal Microphone") },
2095 { "input-radio", N_("Radio") },
2096 { "input-video", N_("Video") },
2097 { "input-agc-on", N_("Automatic Gain Control") },
2098 { "input-agc-off", N_("No Automatic Gain Control") },
2099 { "input-boost-on", N_("Boost") },
2100 { "input-boost-off", N_("No Boost") },
2101 { "output-amplifier-on", N_("Amplifier") },
2102 { "output-amplifier-off", N_("No Amplifier") },
2103 { "output-bass-boost-on", N_("Bass Boost") },
2104 { "output-bass-boost-off", N_("No Bass Boost") },
2105 { "output-speaker", N_("Speaker") },
2106 { "output-headphones", N_("Headphones") }
2112 pa_log("No name set for option %s", o
->alsa_name
);
2116 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
2117 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
2118 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
2122 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
2123 !pa_streq(o
->alsa_name
, "on") &&
2124 !pa_streq(o
->alsa_name
, "off")) {
2125 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
2129 if (!o
->description
)
2130 o
->description
= pa_xstrdup(lookup_description(o
->name
,
2131 well_known_descriptions
,
2132 PA_ELEMENTSOF(well_known_descriptions
)));
2133 if (!o
->description
)
2134 o
->description
= pa_xstrdup(o
->name
);
2139 static int element_verify(pa_alsa_element
*e
) {
2144 // 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);
2145 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
2146 (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required_any
== e
->required_absent
) ||
2147 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) ||
2148 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
2149 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
2153 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
2154 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
2158 PA_LLIST_FOREACH(o
, e
->options
)
2159 if (option_verify(o
) < 0)
2165 static int path_verify(pa_alsa_path
*p
) {
2166 static const struct description_map well_known_descriptions
[] = {
2167 { "analog-input", N_("Analog Input") },
2168 { "analog-input-microphone", N_("Analog Microphone") },
2169 { "analog-input-microphone-front", N_("Front Microphone") },
2170 { "analog-input-microphone-rear", N_("Rear Microphone") },
2171 { "analog-input-microphone-dock", N_("Docking Station Microphone") },
2172 { "analog-input-microphone-internal", N_("Internal Microphone") },
2173 { "analog-input-linein", N_("Analog Line-In") },
2174 { "analog-input-radio", N_("Analog Radio") },
2175 { "analog-input-video", N_("Analog Video") },
2176 { "analog-output", N_("Analog Output") },
2177 { "analog-output-headphones", N_("Analog Headphones") },
2178 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
2179 { "analog-output-mono", N_("Analog Mono Output") },
2180 { "analog-output-speaker", N_("Analog Speakers") },
2181 { "iec958-stereo-output", N_("Digital Output (IEC958)") },
2182 { "iec958-passthrough-output", N_("Digital Passthrough (IEC958)") }
2189 PA_LLIST_FOREACH(e
, p
->elements
)
2190 if (element_verify(e
) < 0)
2193 if (!p
->description
)
2194 p
->description
= pa_xstrdup(lookup_description(p
->name
,
2195 well_known_descriptions
,
2196 PA_ELEMENTSOF(well_known_descriptions
)));
2198 if (!p
->description
)
2199 p
->description
= pa_xstrdup(p
->name
);
2204 pa_alsa_path
* pa_alsa_path_new(const char *fname
, pa_alsa_direction_t direction
) {
2210 pa_config_item items
[] = {
2212 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
2213 { "description", pa_config_parse_string
, NULL
, "General" },
2214 { "name", pa_config_parse_string
, NULL
, "General" },
2217 { "priority", option_parse_priority
, NULL
, NULL
},
2218 { "name", option_parse_name
, NULL
, NULL
},
2221 { "switch", element_parse_switch
, NULL
, NULL
},
2222 { "volume", element_parse_volume
, NULL
, NULL
},
2223 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
2224 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
2225 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
2226 /* ... later on we might add override-map.3 and so on here ... */
2227 { "required", element_parse_required
, NULL
, NULL
},
2228 { "required-any", element_parse_required
, NULL
, NULL
},
2229 { "required-absent", element_parse_required
, NULL
, NULL
},
2230 { "direction", element_parse_direction
, NULL
, NULL
},
2231 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2232 { "volume-limit", element_parse_volume_limit
, NULL
, NULL
},
2233 { NULL
, NULL
, NULL
, NULL
}
2238 p
= pa_xnew0(pa_alsa_path
, 1);
2239 n
= pa_path_get_filename(fname
);
2240 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2241 p
->direction
= direction
;
2243 items
[0].data
= &p
->priority
;
2244 items
[1].data
= &p
->description
;
2245 items
[2].data
= &p
->name
;
2247 fn
= pa_maybe_prefix_path(fname
,
2248 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/paths/" :
2251 r
= pa_config_parse(fn
, NULL
, items
, p
);
2257 if (path_verify(p
) < 0)
2263 pa_alsa_path_free(p
);
2267 pa_alsa_path
*pa_alsa_path_synthesize(const char *element
, pa_alsa_direction_t direction
) {
2273 p
= pa_xnew0(pa_alsa_path
, 1);
2274 p
->name
= pa_xstrdup(element
);
2275 p
->direction
= direction
;
2277 e
= pa_xnew0(pa_alsa_element
, 1);
2279 e
->alsa_name
= pa_xstrdup(element
);
2280 e
->direction
= direction
;
2281 e
->volume_limit
= -1;
2283 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2284 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2286 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2287 p
->last_element
= e
;
2291 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2292 pa_alsa_option
*o
, *n
;
2296 for (o
= e
->options
; o
; o
= n
) {
2299 if (o
->alsa_idx
< 0) {
2300 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2306 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2307 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2308 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2311 static void path_drop_unsupported(pa_alsa_path
*p
) {
2312 pa_alsa_element
*e
, *n
;
2316 for (e
= p
->elements
; e
; e
= n
) {
2319 if (!element_drop_unsupported(e
)) {
2320 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2326 static void path_make_options_unique(pa_alsa_path
*p
) {
2328 pa_alsa_option
*o
, *u
;
2330 PA_LLIST_FOREACH(e
, p
->elements
) {
2331 PA_LLIST_FOREACH(o
, e
->options
) {
2335 for (u
= o
->next
; u
; u
= u
->next
)
2336 if (pa_streq(u
->name
, o
->name
))
2342 m
= pa_xstrdup(o
->name
);
2344 /* OK, this name is not unique, hence let's rename */
2345 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2348 if (!pa_streq(u
->name
, m
))
2351 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2355 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2356 pa_xfree(u
->description
);
2357 u
->description
= nd
;
2367 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2370 for (; e
; e
= e
->next
)
2371 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2372 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2378 for (o
= e
->options
; o
; o
= o
->next
) {
2382 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2383 s
->options
= pa_idxset_copy(template->options
);
2384 s
->name
= pa_sprintf_malloc(_("%s+%s"), template->name
, o
->name
);
2386 (template->description
[0] && o
->description
[0])
2387 ? pa_sprintf_malloc(_("%s / %s"), template->description
, o
->description
)
2388 : (template->description
[0]
2389 ? pa_xstrdup(template->description
)
2390 : pa_xstrdup(o
->description
));
2392 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2394 s
= pa_xnew0(pa_alsa_setting
, 1);
2395 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2396 s
->name
= pa_xstrdup(o
->name
);
2397 s
->description
= pa_xstrdup(o
->description
);
2398 s
->priority
= o
->priority
;
2401 pa_idxset_put(s
->options
, o
, NULL
);
2403 if (element_create_settings(e
->next
, s
))
2404 /* This is not a leaf, so let's get rid of it */
2407 /* This is a leaf, so let's add it */
2408 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2410 e
->path
->last_setting
= s
;
2417 static void path_create_settings(pa_alsa_path
*p
) {
2420 element_create_settings(p
->elements
, NULL
);
2423 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2425 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2426 pa_channel_position_t t
;
2427 pa_channel_position_mask_t path_volume_channels
= 0;
2438 pa_log_debug("Probing path '%s'", p
->name
);
2440 PA_LLIST_FOREACH(e
, p
->elements
) {
2441 if (element_probe(e
, m
) < 0) {
2442 p
->supported
= FALSE
;
2443 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2446 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
);
2451 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2453 if (!p
->has_volume
) {
2454 p
->min_volume
= e
->min_volume
;
2455 p
->max_volume
= e
->max_volume
;
2459 if (!p
->has_volume
) {
2460 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2461 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2462 min_dB
[t
] = e
->min_dB
;
2463 max_dB
[t
] = e
->max_dB
;
2464 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2471 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2472 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2473 min_dB
[t
] += e
->min_dB
;
2474 max_dB
[t
] += e
->max_dB
;
2475 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2478 /* Hmm, there's another element before us
2479 * which cannot do dB volumes, so we we need
2480 * to 'neutralize' this slider */
2481 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2482 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2485 } else if (p
->has_volume
) {
2486 /* We can't use this volume, so let's ignore it */
2487 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2488 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2490 p
->has_volume
= TRUE
;
2493 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2497 if (p
->has_req_any
&& !p
->req_any_present
) {
2498 p
->supported
= FALSE
;
2499 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2503 path_drop_unsupported(p
);
2504 path_make_options_unique(p
);
2505 path_create_settings(p
);
2507 p
->supported
= TRUE
;
2510 p
->min_dB
= INFINITY
;
2511 p
->max_dB
= -INFINITY
;
2513 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2514 if (path_volume_channels
& PA_CHANNEL_POSITION_MASK(t
)) {
2515 if (p
->min_dB
> min_dB
[t
])
2516 p
->min_dB
= min_dB
[t
];
2518 if (p
->max_dB
< max_dB
[t
])
2519 p
->max_dB
= max_dB
[t
];
2526 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2529 pa_log_debug("Setting %s (%s) priority=%u",
2531 pa_strnull(s
->description
),
2535 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2538 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2540 pa_strnull(o
->name
),
2541 pa_strnull(o
->description
),
2546 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2550 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",
2560 (long long unsigned) e
->merged_mask
,
2562 pa_yes_no(e
->override_map
));
2564 PA_LLIST_FOREACH(o
, e
->options
)
2565 pa_alsa_option_dump(o
);
2568 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2573 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2574 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2576 pa_strnull(p
->description
),
2579 pa_yes_no(p
->probed
),
2580 pa_yes_no(p
->supported
),
2581 pa_yes_no(p
->has_mute
),
2582 pa_yes_no(p
->has_volume
),
2583 pa_yes_no(p
->has_dB
),
2584 p
->min_volume
, p
->max_volume
,
2585 p
->min_dB
, p
->max_dB
);
2587 PA_LLIST_FOREACH(e
, p
->elements
)
2588 pa_alsa_element_dump(e
);
2590 PA_LLIST_FOREACH(s
, p
->settings
)
2591 pa_alsa_setting_dump(s
);
2594 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2595 snd_mixer_selem_id_t
*sid
;
2596 snd_mixer_elem_t
*me
;
2602 SELEM_INIT(sid
, e
->alsa_name
);
2603 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2604 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2608 snd_mixer_elem_set_callback(me
, cb
);
2609 snd_mixer_elem_set_callback_private(me
, userdata
);
2612 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2619 PA_LLIST_FOREACH(e
, p
->elements
)
2620 element_set_callback(e
, m
, cb
, userdata
);
2623 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2630 PA_LLIST_FOREACH(p
, ps
->paths
)
2631 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2634 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
) {
2635 pa_alsa_path_set
*ps
;
2636 char **pn
= NULL
, **en
= NULL
, **ie
;
2637 pa_alsa_decibel_fix
*db_fix
;
2641 pa_assert(m
->profile_set
);
2642 pa_assert(m
->profile_set
->decibel_fixes
);
2643 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2645 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2648 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2649 ps
->direction
= direction
;
2651 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2652 pn
= m
->output_path_names
;
2653 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2654 pn
= m
->input_path_names
;
2659 for (in
= pn
; *in
; in
++) {
2661 pa_bool_t duplicate
= FALSE
;
2664 for (kn
= pn
; kn
< in
; kn
++)
2665 if (pa_streq(*kn
, *in
)) {
2673 fn
= pa_sprintf_malloc("%s.conf", *in
);
2675 if ((p
= pa_alsa_path_new(fn
, direction
))) {
2677 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2687 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2688 en
= m
->output_element
;
2689 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2690 en
= m
->input_element
;
2693 pa_alsa_path_set_free(ps
);
2697 for (ie
= en
; *ie
; ie
++) {
2701 p
= pa_alsa_path_synthesize(*ie
, direction
);
2704 /* Mark all other passed elements for require-absent */
2705 for (je
= en
; *je
; je
++) {
2711 e
= pa_xnew0(pa_alsa_element
, 1);
2713 e
->alsa_name
= pa_xstrdup(*je
);
2714 e
->direction
= direction
;
2715 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2716 e
->volume_limit
= -1;
2718 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2719 p
->last_element
= e
;
2722 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2727 /* Assign decibel fixes to elements. */
2728 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2731 PA_LLIST_FOREACH(p
, ps
->paths
) {
2734 PA_LLIST_FOREACH(e
, p
->elements
) {
2735 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2736 /* The profile set that contains the dB fix may be freed
2737 * before the element, so we have to copy the dB fix
2739 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2740 e
->db_fix
->profile_set
= NULL
;
2741 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2742 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
2751 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2755 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2758 pa_yes_no(ps
->probed
));
2760 PA_LLIST_FOREACH(p
, ps
->paths
)
2761 pa_alsa_path_dump(p
);
2764 static void path_set_unify(pa_alsa_path_set
*ps
) {
2766 pa_bool_t has_dB
= TRUE
, has_volume
= TRUE
, has_mute
= TRUE
;
2769 /* We have issues dealing with paths that vary too wildly. That
2770 * means for now we have to have all paths support volume/mute/dB
2773 PA_LLIST_FOREACH(p
, ps
->paths
) {
2774 pa_assert(p
->probed
);
2778 else if (!p
->has_dB
)
2785 if (!has_volume
|| !has_dB
|| !has_mute
) {
2788 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2790 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2793 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2795 PA_LLIST_FOREACH(p
, ps
->paths
) {
2797 p
->has_volume
= FALSE
;
2802 p
->has_mute
= FALSE
;
2807 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
2808 pa_alsa_path
*p
, *q
;
2810 PA_LLIST_FOREACH(p
, ps
->paths
) {
2814 for (q
= p
->next
; q
; q
= q
->next
)
2815 if (pa_streq(q
->name
, p
->name
))
2821 m
= pa_xstrdup(p
->name
);
2823 /* OK, this name is not unique, hence let's rename */
2824 for (i
= 1, q
= p
; q
; q
= q
->next
) {
2827 if (!pa_streq(q
->name
, m
))
2830 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2834 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
2835 pa_xfree(q
->description
);
2836 q
->description
= nd
;
2845 void pa_alsa_path_set_probe(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2846 pa_alsa_path
*p
, *n
;
2853 for (p
= ps
->paths
; p
; p
= n
) {
2856 if (pa_alsa_path_probe(p
, m
, ignore_dB
) < 0) {
2857 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
2858 pa_alsa_path_free(p
);
2863 path_set_make_paths_unique(ps
);
2867 static void mapping_free(pa_alsa_mapping
*m
) {
2871 pa_xfree(m
->description
);
2873 pa_xstrfreev(m
->device_strings
);
2874 pa_xstrfreev(m
->input_path_names
);
2875 pa_xstrfreev(m
->output_path_names
);
2876 pa_xstrfreev(m
->input_element
);
2877 pa_xstrfreev(m
->output_element
);
2879 pa_assert(!m
->input_pcm
);
2880 pa_assert(!m
->output_pcm
);
2885 static void profile_free(pa_alsa_profile
*p
) {
2889 pa_xfree(p
->description
);
2891 pa_xstrfreev(p
->input_mapping_names
);
2892 pa_xstrfreev(p
->output_mapping_names
);
2894 if (p
->input_mappings
)
2895 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
2897 if (p
->output_mappings
)
2898 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
2903 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
2909 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
2912 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
2918 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
2921 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
2924 if (ps
->decibel_fixes
) {
2925 pa_alsa_decibel_fix
*db_fix
;
2927 while ((db_fix
= pa_hashmap_steal_first(ps
->decibel_fixes
)))
2928 decibel_fix_free(db_fix
);
2930 pa_hashmap_free(ps
->decibel_fixes
, NULL
, NULL
);
2936 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
2939 if (!pa_startswith(name
, "Mapping "))
2944 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
2947 m
= pa_xnew0(pa_alsa_mapping
, 1);
2948 m
->profile_set
= ps
;
2949 m
->name
= pa_xstrdup(name
);
2950 pa_channel_map_init(&m
->channel_map
);
2952 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
2957 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
2960 if (!pa_startswith(name
, "Profile "))
2965 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
2968 p
= pa_xnew0(pa_alsa_profile
, 1);
2969 p
->profile_set
= ps
;
2970 p
->name
= pa_xstrdup(name
);
2972 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
2977 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
2978 pa_alsa_decibel_fix
*db_fix
;
2980 if (!pa_startswith(name
, "DecibelFix "))
2985 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
2988 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
2989 db_fix
->profile_set
= ps
;
2990 db_fix
->name
= pa_xstrdup(name
);
2992 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
2997 static int mapping_parse_device_strings(
2998 const char *filename
,
3000 const char *section
,
3006 pa_alsa_profile_set
*ps
= userdata
;
3011 if (!(m
= mapping_get(ps
, section
))) {
3012 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3016 pa_xstrfreev(m
->device_strings
);
3017 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
3018 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
3025 static int mapping_parse_channel_map(
3026 const char *filename
,
3028 const char *section
,
3034 pa_alsa_profile_set
*ps
= userdata
;
3039 if (!(m
= mapping_get(ps
, section
))) {
3040 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3044 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
3045 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
3052 static int mapping_parse_paths(
3053 const char *filename
,
3055 const char *section
,
3061 pa_alsa_profile_set
*ps
= userdata
;
3066 if (!(m
= mapping_get(ps
, section
))) {
3067 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3071 if (pa_streq(lvalue
, "paths-input")) {
3072 pa_xstrfreev(m
->input_path_names
);
3073 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
3075 pa_xstrfreev(m
->output_path_names
);
3076 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
3082 static int mapping_parse_element(
3083 const char *filename
,
3085 const char *section
,
3091 pa_alsa_profile_set
*ps
= userdata
;
3096 if (!(m
= mapping_get(ps
, section
))) {
3097 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3101 if (pa_streq(lvalue
, "element-input")) {
3102 pa_xstrfreev(m
->input_element
);
3103 m
->input_element
= pa_split_spaces_strv(rvalue
);
3105 pa_xstrfreev(m
->output_element
);
3106 m
->output_element
= pa_split_spaces_strv(rvalue
);
3112 static int mapping_parse_direction(
3113 const char *filename
,
3115 const char *section
,
3121 pa_alsa_profile_set
*ps
= userdata
;
3126 if (!(m
= mapping_get(ps
, section
))) {
3127 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3131 if (pa_streq(rvalue
, "input"))
3132 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3133 else if (pa_streq(rvalue
, "output"))
3134 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3135 else if (pa_streq(rvalue
, "any"))
3136 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3138 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
3145 static int mapping_parse_description(
3146 const char *filename
,
3148 const char *section
,
3154 pa_alsa_profile_set
*ps
= userdata
;
3160 if ((m
= mapping_get(ps
, section
))) {
3161 pa_xfree(m
->description
);
3162 m
->description
= pa_xstrdup(rvalue
);
3163 } else if ((p
= profile_get(ps
, section
))) {
3164 pa_xfree(p
->description
);
3165 p
->description
= pa_xstrdup(rvalue
);
3167 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3174 static int mapping_parse_priority(
3175 const char *filename
,
3177 const char *section
,
3183 pa_alsa_profile_set
*ps
= userdata
;
3190 if (pa_atou(rvalue
, &prio
) < 0) {
3191 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
3195 if ((m
= mapping_get(ps
, section
)))
3197 else if ((p
= profile_get(ps
, section
)))
3200 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3207 static int profile_parse_mappings(
3208 const char *filename
,
3210 const char *section
,
3216 pa_alsa_profile_set
*ps
= userdata
;
3221 if (!(p
= profile_get(ps
, section
))) {
3222 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3226 if (pa_streq(lvalue
, "input-mappings")) {
3227 pa_xstrfreev(p
->input_mapping_names
);
3228 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
3230 pa_xstrfreev(p
->output_mapping_names
);
3231 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
3237 static int profile_parse_skip_probe(
3238 const char *filename
,
3240 const char *section
,
3246 pa_alsa_profile_set
*ps
= userdata
;
3252 if (!(p
= profile_get(ps
, section
))) {
3253 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3257 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
3258 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
3267 static int decibel_fix_parse_db_values(
3268 const char *filename
,
3270 const char *section
,
3276 pa_alsa_profile_set
*ps
= userdata
;
3277 pa_alsa_decibel_fix
*db_fix
;
3281 unsigned n
= 8; /* Current size of the db_values table. */
3282 unsigned min_step
= 0;
3283 unsigned max_step
= 0;
3284 unsigned i
= 0; /* Index to the items table. */
3285 unsigned prev_step
= 0;
3288 pa_assert(filename
);
3294 if (!(db_fix
= decibel_fix_get(ps
, section
))) {
3295 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3299 if (!(items
= pa_split_spaces_strv(rvalue
))) {
3300 pa_log("[%s:%u] Value missing", pa_strnull(filename
), line
);
3304 db_values
= pa_xnew(long, n
);
3306 while ((item
= items
[i
++])) {
3307 char *s
= item
; /* Step value string. */
3308 char *d
= item
; /* dB value string. */
3312 /* Move d forward until it points to a colon or to the end of the item. */
3313 for (; *d
&& *d
!= ':'; ++d
);
3316 /* item started with colon. */
3317 pa_log("[%s:%u] No step value found in %s", filename
, line
, item
);
3321 if (!*d
|| !*(d
+ 1)) {
3322 /* No colon found, or it was the last character in item. */
3323 pa_log("[%s:%u] No dB value found in %s", filename
, line
, item
);
3327 /* pa_atou() needs a null-terminating string. Let's replace the colon
3328 * with a zero byte. */
3331 if (pa_atou(s
, &step
) < 0) {
3332 pa_log("[%s:%u] Invalid step value: %s", filename
, line
, s
);
3336 if (pa_atod(d
, &db
) < 0) {
3337 pa_log("[%s:%u] Invalid dB value: %s", filename
, line
, d
);
3341 if (step
<= prev_step
&& i
!= 1) {
3342 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename
, line
, step
, prev_step
);
3346 if (db
< prev_db
&& i
!= 1) {
3347 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename
, line
, db
, prev_db
);
3353 db_values
[0] = (long) (db
* 100.0);
3357 /* Interpolate linearly. */
3358 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3360 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3362 /* Reallocate the db_values table if it's about to overflow. */
3363 if (prev_step
+ 1 - min_step
== n
) {
3365 db_values
= pa_xrenew(long, db_values
, n
);
3368 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3375 db_fix
->min_step
= min_step
;
3376 db_fix
->max_step
= max_step
;
3377 pa_xfree(db_fix
->db_values
);
3378 db_fix
->db_values
= db_values
;
3380 pa_xstrfreev(items
);
3385 pa_xstrfreev(items
);
3386 pa_xfree(db_values
);
3391 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3393 static const struct description_map well_known_descriptions
[] = {
3394 { "analog-mono", N_("Analog Mono") },
3395 { "analog-stereo", N_("Analog Stereo") },
3396 { "analog-surround-21", N_("Analog Surround 2.1") },
3397 { "analog-surround-30", N_("Analog Surround 3.0") },
3398 { "analog-surround-31", N_("Analog Surround 3.1") },
3399 { "analog-surround-40", N_("Analog Surround 4.0") },
3400 { "analog-surround-41", N_("Analog Surround 4.1") },
3401 { "analog-surround-50", N_("Analog Surround 5.0") },
3402 { "analog-surround-51", N_("Analog Surround 5.1") },
3403 { "analog-surround-61", N_("Analog Surround 6.0") },
3404 { "analog-surround-61", N_("Analog Surround 6.1") },
3405 { "analog-surround-70", N_("Analog Surround 7.0") },
3406 { "analog-surround-71", N_("Analog Surround 7.1") },
3407 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3408 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3409 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3410 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3411 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3416 if (!pa_channel_map_valid(&m
->channel_map
)) {
3417 pa_log("Mapping %s is missing channel map.", m
->name
);
3421 if (!m
->device_strings
) {
3422 pa_log("Mapping %s is missing device strings.", m
->name
);
3426 if ((m
->input_path_names
&& m
->input_element
) ||
3427 (m
->output_path_names
&& m
->output_element
)) {
3428 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3432 if (!m
->description
)
3433 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3434 well_known_descriptions
,
3435 PA_ELEMENTSOF(well_known_descriptions
)));
3437 if (!m
->description
)
3438 m
->description
= pa_xstrdup(m
->name
);
3441 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3443 else if (m
->channel_map
.channels
== bonus
->channels
)
3450 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3451 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3455 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3457 pa_strnull(m
->description
),
3459 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3460 pa_yes_no(m
->supported
),
3464 static void profile_set_add_auto_pair(
3465 pa_alsa_profile_set
*ps
,
3466 pa_alsa_mapping
*m
, /* output */
3467 pa_alsa_mapping
*n
/* input */) {
3475 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3478 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3482 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3484 name
= pa_sprintf_malloc("output:%s", m
->name
);
3486 name
= pa_sprintf_malloc("input:%s", n
->name
);
3488 if (pa_hashmap_get(ps
->profiles
, name
)) {
3493 p
= pa_xnew0(pa_alsa_profile
, 1);
3494 p
->profile_set
= ps
;
3498 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3499 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3500 p
->priority
+= m
->priority
* 100;
3504 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3505 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3506 p
->priority
+= n
->priority
;
3509 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3512 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3513 pa_alsa_mapping
*m
, *n
;
3514 void *m_state
, *n_state
;
3518 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3519 profile_set_add_auto_pair(ps
, m
, NULL
);
3521 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3522 profile_set_add_auto_pair(ps
, m
, n
);
3525 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3526 profile_set_add_auto_pair(ps
, NULL
, n
);
3529 static int profile_verify(pa_alsa_profile
*p
) {
3531 static const struct description_map well_known_descriptions
[] = {
3532 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3533 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3534 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3535 { "off", N_("Off") }
3540 /* Replace the output mapping names by the actual mappings */
3541 if (p
->output_mapping_names
) {
3544 pa_assert(!p
->output_mappings
);
3545 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3547 for (name
= p
->output_mapping_names
; *name
; name
++) {
3550 pa_bool_t duplicate
= FALSE
;
3552 for (in
= name
+ 1; *in
; in
++)
3553 if (pa_streq(*name
, *in
)) {
3561 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3562 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3566 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3572 pa_xstrfreev(p
->output_mapping_names
);
3573 p
->output_mapping_names
= NULL
;
3576 /* Replace the input mapping names by the actual mappings */
3577 if (p
->input_mapping_names
) {
3580 pa_assert(!p
->input_mappings
);
3581 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3583 for (name
= p
->input_mapping_names
; *name
; name
++) {
3586 pa_bool_t duplicate
= FALSE
;
3588 for (in
= name
+ 1; *in
; in
++)
3589 if (pa_streq(*name
, *in
)) {
3597 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3598 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3602 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3608 pa_xstrfreev(p
->input_mapping_names
);
3609 p
->input_mapping_names
= NULL
;
3612 if (!p
->input_mappings
&& !p
->output_mappings
) {
3613 pa_log("Profile '%s' lacks mappings.", p
->name
);
3617 if (!p
->description
)
3618 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3619 well_known_descriptions
,
3620 PA_ELEMENTSOF(well_known_descriptions
)));
3622 if (!p
->description
) {
3627 sb
= pa_strbuf_new();
3629 if (p
->output_mappings
)
3630 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3631 if (!pa_strbuf_isempty(sb
))
3632 pa_strbuf_puts(sb
, " + ");
3634 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
3637 if (p
->input_mappings
)
3638 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3639 if (!pa_strbuf_isempty(sb
))
3640 pa_strbuf_puts(sb
, " + ");
3642 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
3645 p
->description
= pa_strbuf_tostring_free(sb
);
3651 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
3656 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3658 pa_strnull(p
->description
),
3660 pa_yes_no(p
->supported
),
3661 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
3662 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
3664 if (p
->input_mappings
)
3665 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
3666 pa_log_debug("Input %s", m
->name
);
3668 if (p
->output_mappings
)
3669 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
3670 pa_log_debug("Output %s", m
->name
);
3673 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
3676 /* Check that the dB mapping has been configured. Since "db-values" is
3677 * currently the only option in the DecibelFix section, and decibel fix
3678 * objects don't get created if a DecibelFix section is empty, this is
3679 * actually a redundant check. Having this may prevent future bugs,
3681 if (!db_fix
->db_values
) {
3682 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
3689 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
3690 char *db_values
= NULL
;
3694 if (db_fix
->db_values
) {
3696 unsigned long i
, nsteps
;
3698 pa_assert(db_fix
->min_step
<= db_fix
->max_step
);
3699 nsteps
= db_fix
->max_step
- db_fix
->min_step
+ 1;
3701 buf
= pa_strbuf_new();
3702 for (i
= 0; i
< nsteps
; ++i
)
3703 pa_strbuf_printf(buf
, "[%li]:%0.2f ", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
3705 db_values
= pa_strbuf_tostring_free(buf
);
3708 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
3709 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
3711 pa_xfree(db_values
);
3714 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
3715 pa_alsa_profile_set
*ps
;
3718 pa_alsa_decibel_fix
*db_fix
;
3723 static pa_config_item items
[] = {
3725 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
3728 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
3729 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
3730 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
3731 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
3732 { "element-input", mapping_parse_element
, NULL
, NULL
},
3733 { "element-output", mapping_parse_element
, NULL
, NULL
},
3734 { "direction", mapping_parse_direction
, NULL
, NULL
},
3736 /* Shared by [Mapping ...] and [Profile ...] */
3737 { "description", mapping_parse_description
, NULL
, NULL
},
3738 { "priority", mapping_parse_priority
, NULL
, NULL
},
3741 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
3742 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
3743 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
3745 /* [DecibelFix ...] */
3746 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
3747 { NULL
, NULL
, NULL
, NULL
}
3750 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
3751 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3752 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3753 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3755 items
[0].data
= &ps
->auto_profiles
;
3758 fname
= "default.conf";
3760 fn
= pa_maybe_prefix_path(fname
,
3761 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
3762 PA_ALSA_PROFILE_SETS_DIR
);
3764 r
= pa_config_parse(fn
, NULL
, items
, ps
);
3770 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3771 if (mapping_verify(m
, bonus
) < 0)
3774 if (ps
->auto_profiles
)
3775 profile_set_add_auto(ps
);
3777 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3778 if (profile_verify(p
) < 0)
3781 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
3782 if (decibel_fix_verify(db_fix
) < 0)
3788 pa_alsa_profile_set_free(ps
);
3792 void pa_alsa_profile_set_probe(
3793 pa_alsa_profile_set
*ps
,
3795 const pa_sample_spec
*ss
,
3796 unsigned default_n_fragments
,
3797 unsigned default_fragment_size_msec
) {
3800 pa_alsa_profile
*p
, *last
= NULL
;
3810 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
3811 pa_sample_spec try_ss
;
3812 pa_channel_map try_map
;
3813 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
3816 /* Is this already marked that it is supported? (i.e. from the config file) */
3820 pa_log_debug("Looking at profile %s", p
->name
);
3822 /* Close PCMs from the last iteration we don't need anymore */
3823 if (last
&& last
->output_mappings
)
3824 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
) {
3829 if (last
->supported
)
3832 if (!p
->output_mappings
|| !pa_idxset_get_by_data(p
->output_mappings
, m
, NULL
)) {
3833 snd_pcm_close(m
->output_pcm
);
3834 m
->output_pcm
= NULL
;
3838 if (last
&& last
->input_mappings
)
3839 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
) {
3844 if (last
->supported
)
3847 if (!p
->input_mappings
|| !pa_idxset_get_by_data(p
->input_mappings
, m
, NULL
)) {
3848 snd_pcm_close(m
->input_pcm
);
3849 m
->input_pcm
= NULL
;
3853 p
->supported
= TRUE
;
3855 /* Check if we can open all new ones */
3856 if (p
->output_mappings
)
3857 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3862 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
3863 try_map
= m
->channel_map
;
3865 try_ss
.channels
= try_map
.channels
;
3868 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
3869 pa_frame_size(&try_ss
);
3870 try_buffer_size
= default_n_fragments
* try_period_size
;
3872 if (!(m
->output_pcm
= pa_alsa_open_by_template(
3877 SND_PCM_STREAM_PLAYBACK
,
3878 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3880 p
->supported
= FALSE
;
3885 if (p
->input_mappings
&& p
->supported
)
3886 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3891 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
3892 try_map
= m
->channel_map
;
3894 try_ss
.channels
= try_map
.channels
;
3897 pa_usec_to_bytes(default_fragment_size_msec
*PA_USEC_PER_MSEC
, &try_ss
) /
3898 pa_frame_size(&try_ss
);
3899 try_buffer_size
= default_n_fragments
* try_period_size
;
3901 if (!(m
->input_pcm
= pa_alsa_open_by_template(
3906 SND_PCM_STREAM_CAPTURE
,
3907 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3909 p
->supported
= FALSE
;
3917 pa_log_debug("Profile %s supported.", p
->name
);
3924 if (last
->output_mappings
)
3925 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
)
3926 if (m
->output_pcm
) {
3928 if (last
->supported
)
3931 snd_pcm_close(m
->output_pcm
);
3932 m
->output_pcm
= NULL
;
3935 if (last
->input_mappings
)
3936 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
)
3939 if (last
->supported
)
3942 snd_pcm_close(m
->input_pcm
);
3943 m
->input_pcm
= NULL
;
3947 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3948 if (!p
->supported
) {
3949 pa_hashmap_remove(ps
->profiles
, p
->name
);
3953 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3954 if (m
->supported
<= 0) {
3955 pa_hashmap_remove(ps
->mappings
, m
->name
);
3962 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
3965 pa_alsa_decibel_fix
*db_fix
;
3970 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
3973 pa_yes_no(ps
->auto_profiles
),
3974 pa_yes_no(ps
->probed
),
3975 pa_hashmap_size(ps
->mappings
),
3976 pa_hashmap_size(ps
->profiles
),
3977 pa_hashmap_size(ps
->decibel_fixes
));
3979 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3980 pa_alsa_mapping_dump(m
);
3982 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3983 pa_alsa_profile_dump(p
);
3985 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
3986 pa_alsa_decibel_fix_dump(db_fix
);
3989 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
) {
3996 /* if there is no path, we don't want a port list */
4000 if (!ps
->paths
->next
){
4003 /* If there is only one path, but no or only one setting, then
4004 * we want a port list either */
4005 if (!ps
->paths
->settings
|| !ps
->paths
->settings
->next
)
4008 /* Ok, there is only one path, however with multiple settings,
4009 * so let's create a port for each setting */
4010 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4012 PA_LLIST_FOREACH(s
, ps
->paths
->settings
) {
4013 pa_device_port
*port
;
4014 pa_alsa_port_data
*data
;
4016 port
= pa_device_port_new(s
->name
, s
->description
, sizeof(pa_alsa_port_data
));
4017 port
->priority
= s
->priority
;
4019 data
= PA_DEVICE_PORT_DATA(port
);
4020 data
->path
= ps
->paths
;
4023 pa_hashmap_put(*p
, port
->name
, port
);
4028 /* We have multiple paths, so let's create a port for each
4029 * one, and each of each settings */
4030 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4032 PA_LLIST_FOREACH(path
, ps
->paths
) {
4034 if (!path
->settings
|| !path
->settings
->next
) {
4035 pa_device_port
*port
;
4036 pa_alsa_port_data
*data
;
4038 /* If there is no or just one setting we only need a
4041 port
= pa_device_port_new(path
->name
, path
->description
, sizeof(pa_alsa_port_data
));
4042 port
->priority
= path
->priority
* 100;
4045 data
= PA_DEVICE_PORT_DATA(port
);
4047 data
->setting
= path
->settings
;
4049 pa_hashmap_put(*p
, port
->name
, port
);
4053 PA_LLIST_FOREACH(s
, path
->settings
) {
4054 pa_device_port
*port
;
4055 pa_alsa_port_data
*data
;
4058 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4060 if (s
->description
[0])
4061 d
= pa_sprintf_malloc(_("%s / %s"), path
->description
, s
->description
);
4063 d
= pa_xstrdup(path
->description
);
4065 port
= pa_device_port_new(n
, d
, sizeof(pa_alsa_port_data
));
4066 port
->priority
= path
->priority
* 100 + s
->priority
;
4071 data
= PA_DEVICE_PORT_DATA(port
);
4075 pa_hashmap_put(*p
, port
->name
, port
);
4081 pa_log_debug("Added %u ports", pa_hashmap_size(*p
));