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
->override_map
) {
1481 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1482 pa_bool_t has_channel
;
1484 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1487 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1488 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1490 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1492 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1497 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++)
1498 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1505 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1508 PA_LLIST_FOREACH(o
, e
->options
)
1509 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1510 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1514 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1515 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1519 PA_LLIST_FOREACH(o
, e
->options
) {
1522 for (i
= 0; i
< n
; i
++) {
1525 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1528 if (!pa_streq(buf
, o
->alsa_name
))
1536 if (check_required(e
, me
) < 0)
1542 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, pa_bool_t prefixed
) {
1549 if (!pa_startswith(section
, "Element "))
1555 /* This is not an element section, but an enum section? */
1556 if (strchr(section
, ':'))
1559 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1560 return p
->last_element
;
1562 PA_LLIST_FOREACH(e
, p
->elements
)
1563 if (pa_streq(e
->alsa_name
, section
))
1566 e
= pa_xnew0(pa_alsa_element
, 1);
1568 e
->alsa_name
= pa_xstrdup(section
);
1569 e
->direction
= p
->direction
;
1570 e
->volume_limit
= -1;
1572 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1575 p
->last_element
= e
;
1579 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1585 if (!pa_startswith(section
, "Option "))
1590 /* This is not an enum section, but an element section? */
1591 if (!(on
= strchr(section
, ':')))
1594 en
= pa_xstrndup(section
, on
- section
);
1597 if (p
->last_option
&&
1598 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1599 pa_streq(p
->last_option
->alsa_name
, on
)) {
1601 return p
->last_option
;
1604 pa_assert_se(e
= element_get(p
, en
, FALSE
));
1607 PA_LLIST_FOREACH(o
, e
->options
)
1608 if (pa_streq(o
->alsa_name
, on
))
1611 o
= pa_xnew0(pa_alsa_option
, 1);
1613 o
->alsa_name
= pa_xstrdup(on
);
1616 if (p
->last_option
&& p
->last_option
->element
== e
)
1617 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1619 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1626 static int element_parse_switch(
1627 const char *filename
,
1629 const char *section
,
1635 pa_alsa_path
*p
= userdata
;
1640 if (!(e
= element_get(p
, section
, TRUE
))) {
1641 pa_log("[%s:%u] Switch makes no sense in '%s'", filename
, line
, section
);
1645 if (pa_streq(rvalue
, "ignore"))
1646 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1647 else if (pa_streq(rvalue
, "mute"))
1648 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1649 else if (pa_streq(rvalue
, "off"))
1650 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1651 else if (pa_streq(rvalue
, "on"))
1652 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1653 else if (pa_streq(rvalue
, "select"))
1654 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1656 pa_log("[%s:%u] Switch invalid of '%s'", filename
, line
, section
);
1663 static int element_parse_volume(
1664 const char *filename
,
1666 const char *section
,
1672 pa_alsa_path
*p
= userdata
;
1677 if (!(e
= element_get(p
, section
, TRUE
))) {
1678 pa_log("[%s:%u] Volume makes no sense in '%s'", filename
, line
, section
);
1682 if (pa_streq(rvalue
, "ignore"))
1683 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1684 else if (pa_streq(rvalue
, "merge"))
1685 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1686 else if (pa_streq(rvalue
, "off"))
1687 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1688 else if (pa_streq(rvalue
, "zero"))
1689 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1693 if (pa_atou(rvalue
, &constant
) >= 0) {
1694 e
->volume_use
= PA_ALSA_VOLUME_CONSTANT
;
1695 e
->constant_volume
= constant
;
1697 pa_log("[%s:%u] Volume invalid of '%s'", filename
, line
, section
);
1705 static int element_parse_enumeration(
1706 const char *filename
,
1708 const char *section
,
1714 pa_alsa_path
*p
= userdata
;
1719 if (!(e
= element_get(p
, section
, TRUE
))) {
1720 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename
, line
, section
);
1724 if (pa_streq(rvalue
, "ignore"))
1725 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1726 else if (pa_streq(rvalue
, "select"))
1727 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1729 pa_log("[%s:%u] Enumeration invalid of '%s'", filename
, line
, section
);
1736 static int option_parse_priority(
1737 const char *filename
,
1739 const char *section
,
1745 pa_alsa_path
*p
= userdata
;
1751 if (!(o
= option_get(p
, section
))) {
1752 pa_log("[%s:%u] Priority makes no sense in '%s'", filename
, line
, section
);
1756 if (pa_atou(rvalue
, &prio
) < 0) {
1757 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
1765 static int option_parse_name(
1766 const char *filename
,
1768 const char *section
,
1774 pa_alsa_path
*p
= userdata
;
1779 if (!(o
= option_get(p
, section
))) {
1780 pa_log("[%s:%u] Name makes no sense in '%s'", filename
, line
, section
);
1785 o
->name
= pa_xstrdup(rvalue
);
1790 static int element_parse_required(
1791 const char *filename
,
1793 const char *section
,
1799 pa_alsa_path
*p
= userdata
;
1802 pa_alsa_required_t req
;
1806 e
= element_get(p
, section
, TRUE
);
1807 o
= option_get(p
, section
);
1809 pa_log("[%s:%u] Required makes no sense in '%s'", filename
, line
, section
);
1813 if (pa_streq(rvalue
, "ignore"))
1814 req
= PA_ALSA_REQUIRED_IGNORE
;
1815 else if (pa_streq(rvalue
, "switch") && e
)
1816 req
= PA_ALSA_REQUIRED_SWITCH
;
1817 else if (pa_streq(rvalue
, "volume") && e
)
1818 req
= PA_ALSA_REQUIRED_VOLUME
;
1819 else if (pa_streq(rvalue
, "enumeration"))
1820 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1821 else if (pa_streq(rvalue
, "any"))
1822 req
= PA_ALSA_REQUIRED_ANY
;
1824 pa_log("[%s:%u] Required invalid of '%s'", filename
, line
, section
);
1828 if (pa_streq(lvalue
, "required-absent")) {
1830 e
->required_absent
= req
;
1832 o
->required_absent
= req
;
1834 else if (pa_streq(lvalue
, "required-any")) {
1836 e
->required_any
= req
;
1837 e
->path
->has_req_any
= TRUE
;
1840 o
->required_any
= req
;
1841 o
->element
->path
->has_req_any
= TRUE
;
1854 static int element_parse_direction(
1855 const char *filename
,
1857 const char *section
,
1863 pa_alsa_path
*p
= userdata
;
1868 if (!(e
= element_get(p
, section
, TRUE
))) {
1869 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1873 if (pa_streq(rvalue
, "playback"))
1874 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1875 else if (pa_streq(rvalue
, "capture"))
1876 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1878 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1885 static int element_parse_direction_try_other(
1886 const char *filename
,
1888 const char *section
,
1894 pa_alsa_path
*p
= userdata
;
1898 if (!(e
= element_get(p
, section
, TRUE
))) {
1899 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1903 if ((yes
= pa_parse_boolean(rvalue
)) < 0) {
1904 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1908 e
->direction_try_other
= !!yes
;
1912 static int element_parse_volume_limit(
1913 const char *filename
,
1915 const char *section
,
1921 pa_alsa_path
*p
= userdata
;
1925 if (!(e
= element_get(p
, section
, TRUE
))) {
1926 pa_log("[%s:%u] volume-limit makes no sense in '%s'", filename
, line
, section
);
1930 if (pa_atol(rvalue
, &volume_limit
) < 0 || volume_limit
< 0) {
1931 pa_log("[%s:%u] Invalid value for volume-limit", filename
, line
);
1935 e
->volume_limit
= volume_limit
;
1939 static pa_channel_position_mask_t
parse_mask(const char *m
) {
1940 pa_channel_position_mask_t v
;
1942 if (pa_streq(m
, "all-left"))
1943 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
1944 else if (pa_streq(m
, "all-right"))
1945 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
1946 else if (pa_streq(m
, "all-center"))
1947 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
1948 else if (pa_streq(m
, "all-front"))
1949 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
1950 else if (pa_streq(m
, "all-rear"))
1951 v
= PA_CHANNEL_POSITION_MASK_REAR
;
1952 else if (pa_streq(m
, "all-side"))
1953 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
1954 else if (pa_streq(m
, "all-top"))
1955 v
= PA_CHANNEL_POSITION_MASK_TOP
;
1956 else if (pa_streq(m
, "all-no-lfe"))
1957 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
1958 else if (pa_streq(m
, "all"))
1959 v
= PA_CHANNEL_POSITION_MASK_ALL
;
1961 pa_channel_position_t p
;
1963 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
1966 v
= PA_CHANNEL_POSITION_MASK(p
);
1972 static int element_parse_override_map(
1973 const char *filename
,
1975 const char *section
,
1981 pa_alsa_path
*p
= userdata
;
1983 const char *state
= NULL
;
1987 if (!(e
= element_get(p
, section
, TRUE
))) {
1988 pa_log("[%s:%u] Override map makes no sense in '%s'", filename
, line
, section
);
1992 while ((n
= pa_split(rvalue
, ",", &state
))) {
1993 pa_channel_position_mask_t m
;
1998 if ((m
= parse_mask(n
)) == 0) {
1999 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename
, line
, n
, section
);
2005 if (pa_streq(lvalue
, "override-map.1"))
2006 e
->masks
[i
++][0] = m
;
2008 e
->masks
[i
++][1] = m
;
2010 /* Later on we might add override-map.3 and so on here ... */
2015 e
->override_map
= TRUE
;
2020 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
2021 snd_mixer_selem_id_t
*sid
;
2022 snd_mixer_elem_t
*me
;
2028 SELEM_INIT(sid
, e
->alsa_name
);
2029 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2030 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2034 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
2036 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
2037 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
2039 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
2042 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2045 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
2047 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
2048 pa_log_warn("Failed to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2054 int pa_alsa_setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
2061 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
2062 element_set_option(o
->element
, m
, o
->alsa_idx
);
2067 static int option_verify(pa_alsa_option
*o
) {
2068 static const struct description_map well_known_descriptions
[] = {
2069 { "input", N_("Input") },
2070 { "input-docking", N_("Docking Station Input") },
2071 { "input-docking-microphone", N_("Docking Station Microphone") },
2072 { "input-docking-linein", N_("Docking Station Line-In") },
2073 { "input-linein", N_("Line-In") },
2074 { "input-microphone", N_("Microphone") },
2075 { "input-microphone-front", N_("Front Microphone") },
2076 { "input-microphone-rear", N_("Rear Microphone") },
2077 { "input-microphone-external", N_("External Microphone") },
2078 { "input-microphone-internal", N_("Internal Microphone") },
2079 { "input-radio", N_("Radio") },
2080 { "input-video", N_("Video") },
2081 { "input-agc-on", N_("Automatic Gain Control") },
2082 { "input-agc-off", N_("No Automatic Gain Control") },
2083 { "input-boost-on", N_("Boost") },
2084 { "input-boost-off", N_("No Boost") },
2085 { "output-amplifier-on", N_("Amplifier") },
2086 { "output-amplifier-off", N_("No Amplifier") },
2087 { "output-bass-boost-on", N_("Bass Boost") },
2088 { "output-bass-boost-off", N_("No Bass Boost") },
2089 { "output-speaker", N_("Speaker") },
2090 { "output-headphones", N_("Headphones") }
2096 pa_log("No name set for option %s", o
->alsa_name
);
2100 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
2101 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
2102 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
2106 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
2107 !pa_streq(o
->alsa_name
, "on") &&
2108 !pa_streq(o
->alsa_name
, "off")) {
2109 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
2113 if (!o
->description
)
2114 o
->description
= pa_xstrdup(lookup_description(o
->name
,
2115 well_known_descriptions
,
2116 PA_ELEMENTSOF(well_known_descriptions
)));
2117 if (!o
->description
)
2118 o
->description
= pa_xstrdup(o
->name
);
2123 static int element_verify(pa_alsa_element
*e
) {
2128 // 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);
2129 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
2130 (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required_any
== e
->required_absent
) ||
2131 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) ||
2132 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
2133 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
2137 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
2138 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
2142 PA_LLIST_FOREACH(o
, e
->options
)
2143 if (option_verify(o
) < 0)
2149 static int path_verify(pa_alsa_path
*p
) {
2150 static const struct description_map well_known_descriptions
[] = {
2151 { "analog-input", N_("Analog Input") },
2152 { "analog-input-microphone", N_("Analog Microphone") },
2153 { "analog-input-microphone-front", N_("Front Microphone") },
2154 { "analog-input-microphone-rear", N_("Rear Microphone") },
2155 { "analog-input-microphone-dock", N_("Docking Station Microphone") },
2156 { "analog-input-microphone-internal", N_("Internal Microphone") },
2157 { "analog-input-linein", N_("Analog Line-In") },
2158 { "analog-input-radio", N_("Analog Radio") },
2159 { "analog-input-video", N_("Analog Video") },
2160 { "analog-output", N_("Analog Output") },
2161 { "analog-output-headphones", N_("Analog Headphones") },
2162 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
2163 { "analog-output-mono", N_("Analog Mono Output") },
2164 { "analog-output-speaker", N_("Analog Speakers") },
2165 { "iec958-stereo-output", N_("Digital Output (IEC958)") },
2166 { "iec958-passthrough-output", N_("Digital Passthrough (IEC958)") }
2173 PA_LLIST_FOREACH(e
, p
->elements
)
2174 if (element_verify(e
) < 0)
2177 if (!p
->description
)
2178 p
->description
= pa_xstrdup(lookup_description(p
->name
,
2179 well_known_descriptions
,
2180 PA_ELEMENTSOF(well_known_descriptions
)));
2182 if (!p
->description
)
2183 p
->description
= pa_xstrdup(p
->name
);
2188 pa_alsa_path
* pa_alsa_path_new(const char *fname
, pa_alsa_direction_t direction
) {
2194 pa_config_item items
[] = {
2196 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
2197 { "description", pa_config_parse_string
, NULL
, "General" },
2198 { "name", pa_config_parse_string
, NULL
, "General" },
2201 { "priority", option_parse_priority
, NULL
, NULL
},
2202 { "name", option_parse_name
, NULL
, NULL
},
2205 { "switch", element_parse_switch
, NULL
, NULL
},
2206 { "volume", element_parse_volume
, NULL
, NULL
},
2207 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
2208 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
2209 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
2210 /* ... later on we might add override-map.3 and so on here ... */
2211 { "required", element_parse_required
, NULL
, NULL
},
2212 { "required-any", element_parse_required
, NULL
, NULL
},
2213 { "required-absent", element_parse_required
, NULL
, NULL
},
2214 { "direction", element_parse_direction
, NULL
, NULL
},
2215 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2216 { "volume-limit", element_parse_volume_limit
, NULL
, NULL
},
2217 { NULL
, NULL
, NULL
, NULL
}
2222 p
= pa_xnew0(pa_alsa_path
, 1);
2223 n
= pa_path_get_filename(fname
);
2224 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2225 p
->direction
= direction
;
2227 items
[0].data
= &p
->priority
;
2228 items
[1].data
= &p
->description
;
2229 items
[2].data
= &p
->name
;
2231 fn
= pa_maybe_prefix_path(fname
,
2232 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/paths/" :
2235 r
= pa_config_parse(fn
, NULL
, items
, p
);
2241 if (path_verify(p
) < 0)
2247 pa_alsa_path_free(p
);
2251 pa_alsa_path
*pa_alsa_path_synthesize(const char *element
, pa_alsa_direction_t direction
) {
2257 p
= pa_xnew0(pa_alsa_path
, 1);
2258 p
->name
= pa_xstrdup(element
);
2259 p
->direction
= direction
;
2261 e
= pa_xnew0(pa_alsa_element
, 1);
2263 e
->alsa_name
= pa_xstrdup(element
);
2264 e
->direction
= direction
;
2265 e
->volume_limit
= -1;
2267 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2268 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2270 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2271 p
->last_element
= e
;
2275 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2276 pa_alsa_option
*o
, *n
;
2280 for (o
= e
->options
; o
; o
= n
) {
2283 if (o
->alsa_idx
< 0) {
2284 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2290 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2291 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2292 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2295 static void path_drop_unsupported(pa_alsa_path
*p
) {
2296 pa_alsa_element
*e
, *n
;
2300 for (e
= p
->elements
; e
; e
= n
) {
2303 if (!element_drop_unsupported(e
)) {
2304 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2310 static void path_make_options_unique(pa_alsa_path
*p
) {
2312 pa_alsa_option
*o
, *u
;
2314 PA_LLIST_FOREACH(e
, p
->elements
) {
2315 PA_LLIST_FOREACH(o
, e
->options
) {
2319 for (u
= o
->next
; u
; u
= u
->next
)
2320 if (pa_streq(u
->name
, o
->name
))
2326 m
= pa_xstrdup(o
->name
);
2328 /* OK, this name is not unique, hence let's rename */
2329 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2332 if (!pa_streq(u
->name
, m
))
2335 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2339 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2340 pa_xfree(u
->description
);
2341 u
->description
= nd
;
2351 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2354 for (; e
; e
= e
->next
)
2355 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2356 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2362 for (o
= e
->options
; o
; o
= o
->next
) {
2366 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2367 s
->options
= pa_idxset_copy(template->options
);
2368 s
->name
= pa_sprintf_malloc(_("%s+%s"), template->name
, o
->name
);
2370 (template->description
[0] && o
->description
[0])
2371 ? pa_sprintf_malloc(_("%s / %s"), template->description
, o
->description
)
2372 : (template->description
[0]
2373 ? pa_xstrdup(template->description
)
2374 : pa_xstrdup(o
->description
));
2376 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2378 s
= pa_xnew0(pa_alsa_setting
, 1);
2379 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2380 s
->name
= pa_xstrdup(o
->name
);
2381 s
->description
= pa_xstrdup(o
->description
);
2382 s
->priority
= o
->priority
;
2385 pa_idxset_put(s
->options
, o
, NULL
);
2387 if (element_create_settings(e
->next
, s
))
2388 /* This is not a leaf, so let's get rid of it */
2391 /* This is a leaf, so let's add it */
2392 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2394 e
->path
->last_setting
= s
;
2401 static void path_create_settings(pa_alsa_path
*p
) {
2404 element_create_settings(p
->elements
, NULL
);
2407 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2409 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2410 pa_channel_position_t t
;
2411 pa_channel_position_mask_t path_volume_channels
= 0;
2422 pa_log_debug("Probing path '%s'", p
->name
);
2424 PA_LLIST_FOREACH(e
, p
->elements
) {
2425 if (element_probe(e
, m
) < 0) {
2426 p
->supported
= FALSE
;
2427 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2430 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
);
2435 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2437 if (!p
->has_volume
) {
2438 p
->min_volume
= e
->min_volume
;
2439 p
->max_volume
= e
->max_volume
;
2443 if (!p
->has_volume
) {
2444 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2445 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2446 min_dB
[t
] = e
->min_dB
;
2447 max_dB
[t
] = e
->max_dB
;
2448 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2455 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2456 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2457 min_dB
[t
] += e
->min_dB
;
2458 max_dB
[t
] += e
->max_dB
;
2459 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2462 /* Hmm, there's another element before us
2463 * which cannot do dB volumes, so we we need
2464 * to 'neutralize' this slider */
2465 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2466 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2469 } else if (p
->has_volume
) {
2470 /* We can't use this volume, so let's ignore it */
2471 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2472 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2474 p
->has_volume
= TRUE
;
2477 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2481 if (p
->has_req_any
&& !p
->req_any_present
) {
2482 p
->supported
= FALSE
;
2483 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2487 path_drop_unsupported(p
);
2488 path_make_options_unique(p
);
2489 path_create_settings(p
);
2491 p
->supported
= TRUE
;
2494 p
->min_dB
= INFINITY
;
2495 p
->max_dB
= -INFINITY
;
2497 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2498 if (path_volume_channels
& PA_CHANNEL_POSITION_MASK(t
)) {
2499 if (p
->min_dB
> min_dB
[t
])
2500 p
->min_dB
= min_dB
[t
];
2502 if (p
->max_dB
< max_dB
[t
])
2503 p
->max_dB
= max_dB
[t
];
2510 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2513 pa_log_debug("Setting %s (%s) priority=%u",
2515 pa_strnull(s
->description
),
2519 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2522 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2524 pa_strnull(o
->name
),
2525 pa_strnull(o
->description
),
2530 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2534 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",
2544 (long long unsigned) e
->merged_mask
,
2546 pa_yes_no(e
->override_map
));
2548 PA_LLIST_FOREACH(o
, e
->options
)
2549 pa_alsa_option_dump(o
);
2552 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2557 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2558 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2560 pa_strnull(p
->description
),
2563 pa_yes_no(p
->probed
),
2564 pa_yes_no(p
->supported
),
2565 pa_yes_no(p
->has_mute
),
2566 pa_yes_no(p
->has_volume
),
2567 pa_yes_no(p
->has_dB
),
2568 p
->min_volume
, p
->max_volume
,
2569 p
->min_dB
, p
->max_dB
);
2571 PA_LLIST_FOREACH(e
, p
->elements
)
2572 pa_alsa_element_dump(e
);
2574 PA_LLIST_FOREACH(s
, p
->settings
)
2575 pa_alsa_setting_dump(s
);
2578 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2579 snd_mixer_selem_id_t
*sid
;
2580 snd_mixer_elem_t
*me
;
2586 SELEM_INIT(sid
, e
->alsa_name
);
2587 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2588 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2592 snd_mixer_elem_set_callback(me
, cb
);
2593 snd_mixer_elem_set_callback_private(me
, userdata
);
2596 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2603 PA_LLIST_FOREACH(e
, p
->elements
)
2604 element_set_callback(e
, m
, cb
, userdata
);
2607 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2614 PA_LLIST_FOREACH(p
, ps
->paths
)
2615 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2618 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
) {
2619 pa_alsa_path_set
*ps
;
2620 char **pn
= NULL
, **en
= NULL
, **ie
;
2621 pa_alsa_decibel_fix
*db_fix
;
2625 pa_assert(m
->profile_set
);
2626 pa_assert(m
->profile_set
->decibel_fixes
);
2627 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2629 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2632 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2633 ps
->direction
= direction
;
2635 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2636 pn
= m
->output_path_names
;
2637 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2638 pn
= m
->input_path_names
;
2643 for (in
= pn
; *in
; in
++) {
2645 pa_bool_t duplicate
= FALSE
;
2648 for (kn
= pn
; kn
< in
; kn
++)
2649 if (pa_streq(*kn
, *in
)) {
2657 fn
= pa_sprintf_malloc("%s.conf", *in
);
2659 if ((p
= pa_alsa_path_new(fn
, direction
))) {
2661 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2671 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2672 en
= m
->output_element
;
2673 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2674 en
= m
->input_element
;
2677 pa_alsa_path_set_free(ps
);
2681 for (ie
= en
; *ie
; ie
++) {
2685 p
= pa_alsa_path_synthesize(*ie
, direction
);
2688 /* Mark all other passed elements for require-absent */
2689 for (je
= en
; *je
; je
++) {
2695 e
= pa_xnew0(pa_alsa_element
, 1);
2697 e
->alsa_name
= pa_xstrdup(*je
);
2698 e
->direction
= direction
;
2699 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2700 e
->volume_limit
= -1;
2702 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2703 p
->last_element
= e
;
2706 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2711 /* Assign decibel fixes to elements. */
2712 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2715 PA_LLIST_FOREACH(p
, ps
->paths
) {
2718 PA_LLIST_FOREACH(e
, p
->elements
) {
2719 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2720 /* The profile set that contains the dB fix may be freed
2721 * before the element, so we have to copy the dB fix
2723 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2724 e
->db_fix
->profile_set
= NULL
;
2725 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2726 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
2735 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2739 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2742 pa_yes_no(ps
->probed
));
2744 PA_LLIST_FOREACH(p
, ps
->paths
)
2745 pa_alsa_path_dump(p
);
2748 static void path_set_unify(pa_alsa_path_set
*ps
) {
2750 pa_bool_t has_dB
= TRUE
, has_volume
= TRUE
, has_mute
= TRUE
;
2753 /* We have issues dealing with paths that vary too wildly. That
2754 * means for now we have to have all paths support volume/mute/dB
2757 PA_LLIST_FOREACH(p
, ps
->paths
) {
2758 pa_assert(p
->probed
);
2762 else if (!p
->has_dB
)
2769 if (!has_volume
|| !has_dB
|| !has_mute
) {
2772 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2774 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2777 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2779 PA_LLIST_FOREACH(p
, ps
->paths
) {
2781 p
->has_volume
= FALSE
;
2786 p
->has_mute
= FALSE
;
2791 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
2792 pa_alsa_path
*p
, *q
;
2794 PA_LLIST_FOREACH(p
, ps
->paths
) {
2798 for (q
= p
->next
; q
; q
= q
->next
)
2799 if (pa_streq(q
->name
, p
->name
))
2805 m
= pa_xstrdup(p
->name
);
2807 /* OK, this name is not unique, hence let's rename */
2808 for (i
= 1, q
= p
; q
; q
= q
->next
) {
2811 if (!pa_streq(q
->name
, m
))
2814 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2818 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
2819 pa_xfree(q
->description
);
2820 q
->description
= nd
;
2829 void pa_alsa_path_set_probe(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2830 pa_alsa_path
*p
, *n
;
2837 for (p
= ps
->paths
; p
; p
= n
) {
2840 if (pa_alsa_path_probe(p
, m
, ignore_dB
) < 0) {
2841 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
2842 pa_alsa_path_free(p
);
2847 path_set_make_paths_unique(ps
);
2851 static void mapping_free(pa_alsa_mapping
*m
) {
2855 pa_xfree(m
->description
);
2857 pa_xstrfreev(m
->device_strings
);
2858 pa_xstrfreev(m
->input_path_names
);
2859 pa_xstrfreev(m
->output_path_names
);
2860 pa_xstrfreev(m
->input_element
);
2861 pa_xstrfreev(m
->output_element
);
2863 pa_assert(!m
->input_pcm
);
2864 pa_assert(!m
->output_pcm
);
2869 static void profile_free(pa_alsa_profile
*p
) {
2873 pa_xfree(p
->description
);
2875 pa_xstrfreev(p
->input_mapping_names
);
2876 pa_xstrfreev(p
->output_mapping_names
);
2878 if (p
->input_mappings
)
2879 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
2881 if (p
->output_mappings
)
2882 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
2887 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
2893 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
2896 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
2902 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
2905 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
2908 if (ps
->decibel_fixes
) {
2909 pa_alsa_decibel_fix
*db_fix
;
2911 while ((db_fix
= pa_hashmap_steal_first(ps
->decibel_fixes
)))
2912 decibel_fix_free(db_fix
);
2914 pa_hashmap_free(ps
->decibel_fixes
, NULL
, NULL
);
2920 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
2923 if (!pa_startswith(name
, "Mapping "))
2928 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
2931 m
= pa_xnew0(pa_alsa_mapping
, 1);
2932 m
->profile_set
= ps
;
2933 m
->name
= pa_xstrdup(name
);
2934 pa_channel_map_init(&m
->channel_map
);
2936 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
2941 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
2944 if (!pa_startswith(name
, "Profile "))
2949 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
2952 p
= pa_xnew0(pa_alsa_profile
, 1);
2953 p
->profile_set
= ps
;
2954 p
->name
= pa_xstrdup(name
);
2956 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
2961 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
2962 pa_alsa_decibel_fix
*db_fix
;
2964 if (!pa_startswith(name
, "DecibelFix "))
2969 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
2972 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
2973 db_fix
->profile_set
= ps
;
2974 db_fix
->name
= pa_xstrdup(name
);
2976 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
2981 static int mapping_parse_device_strings(
2982 const char *filename
,
2984 const char *section
,
2990 pa_alsa_profile_set
*ps
= userdata
;
2995 if (!(m
= mapping_get(ps
, section
))) {
2996 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3000 pa_xstrfreev(m
->device_strings
);
3001 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
3002 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
3009 static int mapping_parse_channel_map(
3010 const char *filename
,
3012 const char *section
,
3018 pa_alsa_profile_set
*ps
= userdata
;
3023 if (!(m
= mapping_get(ps
, section
))) {
3024 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3028 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
3029 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
3036 static int mapping_parse_paths(
3037 const char *filename
,
3039 const char *section
,
3045 pa_alsa_profile_set
*ps
= userdata
;
3050 if (!(m
= mapping_get(ps
, section
))) {
3051 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3055 if (pa_streq(lvalue
, "paths-input")) {
3056 pa_xstrfreev(m
->input_path_names
);
3057 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
3059 pa_xstrfreev(m
->output_path_names
);
3060 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
3066 static int mapping_parse_element(
3067 const char *filename
,
3069 const char *section
,
3075 pa_alsa_profile_set
*ps
= userdata
;
3080 if (!(m
= mapping_get(ps
, section
))) {
3081 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3085 if (pa_streq(lvalue
, "element-input")) {
3086 pa_xstrfreev(m
->input_element
);
3087 m
->input_element
= pa_split_spaces_strv(rvalue
);
3089 pa_xstrfreev(m
->output_element
);
3090 m
->output_element
= pa_split_spaces_strv(rvalue
);
3096 static int mapping_parse_direction(
3097 const char *filename
,
3099 const char *section
,
3105 pa_alsa_profile_set
*ps
= userdata
;
3110 if (!(m
= mapping_get(ps
, section
))) {
3111 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3115 if (pa_streq(rvalue
, "input"))
3116 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3117 else if (pa_streq(rvalue
, "output"))
3118 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3119 else if (pa_streq(rvalue
, "any"))
3120 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3122 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
3129 static int mapping_parse_description(
3130 const char *filename
,
3132 const char *section
,
3138 pa_alsa_profile_set
*ps
= userdata
;
3144 if ((m
= mapping_get(ps
, section
))) {
3145 pa_xfree(m
->description
);
3146 m
->description
= pa_xstrdup(rvalue
);
3147 } else if ((p
= profile_get(ps
, section
))) {
3148 pa_xfree(p
->description
);
3149 p
->description
= pa_xstrdup(rvalue
);
3151 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3158 static int mapping_parse_priority(
3159 const char *filename
,
3161 const char *section
,
3167 pa_alsa_profile_set
*ps
= userdata
;
3174 if (pa_atou(rvalue
, &prio
) < 0) {
3175 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
3179 if ((m
= mapping_get(ps
, section
)))
3181 else if ((p
= profile_get(ps
, section
)))
3184 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3191 static int profile_parse_mappings(
3192 const char *filename
,
3194 const char *section
,
3200 pa_alsa_profile_set
*ps
= userdata
;
3205 if (!(p
= profile_get(ps
, section
))) {
3206 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3210 if (pa_streq(lvalue
, "input-mappings")) {
3211 pa_xstrfreev(p
->input_mapping_names
);
3212 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
3214 pa_xstrfreev(p
->output_mapping_names
);
3215 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
3221 static int profile_parse_skip_probe(
3222 const char *filename
,
3224 const char *section
,
3230 pa_alsa_profile_set
*ps
= userdata
;
3236 if (!(p
= profile_get(ps
, section
))) {
3237 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3241 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
3242 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
3251 static int decibel_fix_parse_db_values(
3252 const char *filename
,
3254 const char *section
,
3260 pa_alsa_profile_set
*ps
= userdata
;
3261 pa_alsa_decibel_fix
*db_fix
;
3265 unsigned n
= 8; /* Current size of the db_values table. */
3266 unsigned min_step
= 0;
3267 unsigned max_step
= 0;
3268 unsigned i
= 0; /* Index to the items table. */
3269 unsigned prev_step
= 0;
3272 pa_assert(filename
);
3278 if (!(db_fix
= decibel_fix_get(ps
, section
))) {
3279 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3283 if (!(items
= pa_split_spaces_strv(rvalue
))) {
3284 pa_log("[%s:%u] Value missing", pa_strnull(filename
), line
);
3288 db_values
= pa_xnew(long, n
);
3290 while ((item
= items
[i
++])) {
3291 char *s
= item
; /* Step value string. */
3292 char *d
= item
; /* dB value string. */
3296 /* Move d forward until it points to a colon or to the end of the item. */
3297 for (; *d
&& *d
!= ':'; ++d
);
3300 /* item started with colon. */
3301 pa_log("[%s:%u] No step value found in %s", filename
, line
, item
);
3305 if (!*d
|| !*(d
+ 1)) {
3306 /* No colon found, or it was the last character in item. */
3307 pa_log("[%s:%u] No dB value found in %s", filename
, line
, item
);
3311 /* pa_atou() needs a null-terminating string. Let's replace the colon
3312 * with a zero byte. */
3315 if (pa_atou(s
, &step
) < 0) {
3316 pa_log("[%s:%u] Invalid step value: %s", filename
, line
, s
);
3320 if (pa_atod(d
, &db
) < 0) {
3321 pa_log("[%s:%u] Invalid dB value: %s", filename
, line
, d
);
3325 if (step
<= prev_step
&& i
!= 1) {
3326 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename
, line
, step
, prev_step
);
3330 if (db
< prev_db
&& i
!= 1) {
3331 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename
, line
, db
, prev_db
);
3337 db_values
[0] = (long) (db
* 100.0);
3341 /* Interpolate linearly. */
3342 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3344 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3346 /* Reallocate the db_values table if it's about to overflow. */
3347 if (prev_step
+ 1 - min_step
== n
) {
3349 db_values
= pa_xrenew(long, db_values
, n
);
3352 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3359 db_fix
->min_step
= min_step
;
3360 db_fix
->max_step
= max_step
;
3361 pa_xfree(db_fix
->db_values
);
3362 db_fix
->db_values
= db_values
;
3364 pa_xstrfreev(items
);
3369 pa_xstrfreev(items
);
3370 pa_xfree(db_values
);
3375 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3377 static const struct description_map well_known_descriptions
[] = {
3378 { "analog-mono", N_("Analog Mono") },
3379 { "analog-stereo", N_("Analog Stereo") },
3380 { "analog-surround-21", N_("Analog Surround 2.1") },
3381 { "analog-surround-30", N_("Analog Surround 3.0") },
3382 { "analog-surround-31", N_("Analog Surround 3.1") },
3383 { "analog-surround-40", N_("Analog Surround 4.0") },
3384 { "analog-surround-41", N_("Analog Surround 4.1") },
3385 { "analog-surround-50", N_("Analog Surround 5.0") },
3386 { "analog-surround-51", N_("Analog Surround 5.1") },
3387 { "analog-surround-61", N_("Analog Surround 6.0") },
3388 { "analog-surround-61", N_("Analog Surround 6.1") },
3389 { "analog-surround-70", N_("Analog Surround 7.0") },
3390 { "analog-surround-71", N_("Analog Surround 7.1") },
3391 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3392 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3393 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3394 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3395 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3400 if (!pa_channel_map_valid(&m
->channel_map
)) {
3401 pa_log("Mapping %s is missing channel map.", m
->name
);
3405 if (!m
->device_strings
) {
3406 pa_log("Mapping %s is missing device strings.", m
->name
);
3410 if ((m
->input_path_names
&& m
->input_element
) ||
3411 (m
->output_path_names
&& m
->output_element
)) {
3412 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3416 if (!m
->description
)
3417 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3418 well_known_descriptions
,
3419 PA_ELEMENTSOF(well_known_descriptions
)));
3421 if (!m
->description
)
3422 m
->description
= pa_xstrdup(m
->name
);
3425 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3427 else if (m
->channel_map
.channels
== bonus
->channels
)
3434 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3435 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3439 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3441 pa_strnull(m
->description
),
3443 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3444 pa_yes_no(m
->supported
),
3448 static void profile_set_add_auto_pair(
3449 pa_alsa_profile_set
*ps
,
3450 pa_alsa_mapping
*m
, /* output */
3451 pa_alsa_mapping
*n
/* input */) {
3459 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3462 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3466 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3468 name
= pa_sprintf_malloc("output:%s", m
->name
);
3470 name
= pa_sprintf_malloc("input:%s", n
->name
);
3472 if (pa_hashmap_get(ps
->profiles
, name
)) {
3477 p
= pa_xnew0(pa_alsa_profile
, 1);
3478 p
->profile_set
= ps
;
3482 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3483 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3484 p
->priority
+= m
->priority
* 100;
3488 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3489 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3490 p
->priority
+= n
->priority
;
3493 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3496 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3497 pa_alsa_mapping
*m
, *n
;
3498 void *m_state
, *n_state
;
3502 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3503 profile_set_add_auto_pair(ps
, m
, NULL
);
3505 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3506 profile_set_add_auto_pair(ps
, m
, n
);
3509 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3510 profile_set_add_auto_pair(ps
, NULL
, n
);
3513 static int profile_verify(pa_alsa_profile
*p
) {
3515 static const struct description_map well_known_descriptions
[] = {
3516 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3517 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3518 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3519 { "off", N_("Off") }
3524 /* Replace the output mapping names by the actual mappings */
3525 if (p
->output_mapping_names
) {
3528 pa_assert(!p
->output_mappings
);
3529 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3531 for (name
= p
->output_mapping_names
; *name
; name
++) {
3534 pa_bool_t duplicate
= FALSE
;
3536 for (in
= name
+ 1; *in
; in
++)
3537 if (pa_streq(*name
, *in
)) {
3545 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3546 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3550 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3556 pa_xstrfreev(p
->output_mapping_names
);
3557 p
->output_mapping_names
= NULL
;
3560 /* Replace the input mapping names by the actual mappings */
3561 if (p
->input_mapping_names
) {
3564 pa_assert(!p
->input_mappings
);
3565 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3567 for (name
= p
->input_mapping_names
; *name
; name
++) {
3570 pa_bool_t duplicate
= FALSE
;
3572 for (in
= name
+ 1; *in
; in
++)
3573 if (pa_streq(*name
, *in
)) {
3581 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3582 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3586 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3592 pa_xstrfreev(p
->input_mapping_names
);
3593 p
->input_mapping_names
= NULL
;
3596 if (!p
->input_mappings
&& !p
->output_mappings
) {
3597 pa_log("Profile '%s' lacks mappings.", p
->name
);
3601 if (!p
->description
)
3602 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3603 well_known_descriptions
,
3604 PA_ELEMENTSOF(well_known_descriptions
)));
3606 if (!p
->description
) {
3611 sb
= pa_strbuf_new();
3613 if (p
->output_mappings
)
3614 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3615 if (!pa_strbuf_isempty(sb
))
3616 pa_strbuf_puts(sb
, " + ");
3618 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
3621 if (p
->input_mappings
)
3622 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3623 if (!pa_strbuf_isempty(sb
))
3624 pa_strbuf_puts(sb
, " + ");
3626 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
3629 p
->description
= pa_strbuf_tostring_free(sb
);
3635 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
3640 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3642 pa_strnull(p
->description
),
3644 pa_yes_no(p
->supported
),
3645 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
3646 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
3648 if (p
->input_mappings
)
3649 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
3650 pa_log_debug("Input %s", m
->name
);
3652 if (p
->output_mappings
)
3653 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
3654 pa_log_debug("Output %s", m
->name
);
3657 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
3660 /* Check that the dB mapping has been configured. Since "db-values" is
3661 * currently the only option in the DecibelFix section, and decibel fix
3662 * objects don't get created if a DecibelFix section is empty, this is
3663 * actually a redundant check. Having this may prevent future bugs,
3665 if (!db_fix
->db_values
) {
3666 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
3673 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
3674 char *db_values
= NULL
;
3678 if (db_fix
->db_values
) {
3680 unsigned long i
, nsteps
;
3682 pa_assert(db_fix
->min_step
<= db_fix
->max_step
);
3683 nsteps
= db_fix
->max_step
- db_fix
->min_step
+ 1;
3685 buf
= pa_strbuf_new();
3686 for (i
= 0; i
< nsteps
; ++i
)
3687 pa_strbuf_printf(buf
, "[%li]:%0.2f ", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
3689 db_values
= pa_strbuf_tostring_free(buf
);
3692 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
3693 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
3695 pa_xfree(db_values
);
3698 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
3699 pa_alsa_profile_set
*ps
;
3702 pa_alsa_decibel_fix
*db_fix
;
3707 static pa_config_item items
[] = {
3709 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
3712 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
3713 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
3714 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
3715 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
3716 { "element-input", mapping_parse_element
, NULL
, NULL
},
3717 { "element-output", mapping_parse_element
, NULL
, NULL
},
3718 { "direction", mapping_parse_direction
, NULL
, NULL
},
3720 /* Shared by [Mapping ...] and [Profile ...] */
3721 { "description", mapping_parse_description
, NULL
, NULL
},
3722 { "priority", mapping_parse_priority
, NULL
, NULL
},
3725 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
3726 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
3727 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
3729 /* [DecibelFix ...] */
3730 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
3731 { NULL
, NULL
, NULL
, NULL
}
3734 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
3735 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3736 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3737 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3739 items
[0].data
= &ps
->auto_profiles
;
3742 fname
= "default.conf";
3744 fn
= pa_maybe_prefix_path(fname
,
3745 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
3746 PA_ALSA_PROFILE_SETS_DIR
);
3748 r
= pa_config_parse(fn
, NULL
, items
, ps
);
3754 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3755 if (mapping_verify(m
, bonus
) < 0)
3758 if (ps
->auto_profiles
)
3759 profile_set_add_auto(ps
);
3761 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3762 if (profile_verify(p
) < 0)
3765 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
3766 if (decibel_fix_verify(db_fix
) < 0)
3772 pa_alsa_profile_set_free(ps
);
3776 void pa_alsa_profile_set_probe(
3777 pa_alsa_profile_set
*ps
,
3779 const pa_sample_spec
*ss
,
3780 unsigned default_n_fragments
,
3781 unsigned default_fragment_size_msec
) {
3784 pa_alsa_profile
*p
, *last
= NULL
;
3794 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
3795 pa_sample_spec try_ss
;
3796 pa_channel_map try_map
;
3797 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
3800 /* Is this already marked that it is supported? (i.e. from the config file) */
3804 pa_log_debug("Looking at profile %s", p
->name
);
3806 /* Close PCMs from the last iteration we don't need anymore */
3807 if (last
&& last
->output_mappings
)
3808 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
) {
3813 if (last
->supported
)
3816 if (!p
->output_mappings
|| !pa_idxset_get_by_data(p
->output_mappings
, m
, NULL
)) {
3817 snd_pcm_close(m
->output_pcm
);
3818 m
->output_pcm
= NULL
;
3822 if (last
&& last
->input_mappings
)
3823 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
) {
3828 if (last
->supported
)
3831 if (!p
->input_mappings
|| !pa_idxset_get_by_data(p
->input_mappings
, m
, NULL
)) {
3832 snd_pcm_close(m
->input_pcm
);
3833 m
->input_pcm
= NULL
;
3837 p
->supported
= TRUE
;
3839 /* Check if we can open all new ones */
3840 if (p
->output_mappings
)
3841 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3846 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
3847 try_map
= m
->channel_map
;
3849 try_ss
.channels
= try_map
.channels
;
3852 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
3853 pa_frame_size(&try_ss
);
3854 try_buffer_size
= default_n_fragments
* try_period_size
;
3856 if (!(m
->output_pcm
= pa_alsa_open_by_template(
3861 SND_PCM_STREAM_PLAYBACK
,
3862 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3864 p
->supported
= FALSE
;
3869 if (p
->input_mappings
&& p
->supported
)
3870 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3875 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
3876 try_map
= m
->channel_map
;
3878 try_ss
.channels
= try_map
.channels
;
3881 pa_usec_to_bytes(default_fragment_size_msec
*PA_USEC_PER_MSEC
, &try_ss
) /
3882 pa_frame_size(&try_ss
);
3883 try_buffer_size
= default_n_fragments
* try_period_size
;
3885 if (!(m
->input_pcm
= pa_alsa_open_by_template(
3890 SND_PCM_STREAM_CAPTURE
,
3891 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3893 p
->supported
= FALSE
;
3901 pa_log_debug("Profile %s supported.", p
->name
);
3908 if (last
->output_mappings
)
3909 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
)
3910 if (m
->output_pcm
) {
3912 if (last
->supported
)
3915 snd_pcm_close(m
->output_pcm
);
3916 m
->output_pcm
= NULL
;
3919 if (last
->input_mappings
)
3920 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
)
3923 if (last
->supported
)
3926 snd_pcm_close(m
->input_pcm
);
3927 m
->input_pcm
= NULL
;
3931 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3932 if (!p
->supported
) {
3933 pa_hashmap_remove(ps
->profiles
, p
->name
);
3937 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3938 if (m
->supported
<= 0) {
3939 pa_hashmap_remove(ps
->mappings
, m
->name
);
3946 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
3949 pa_alsa_decibel_fix
*db_fix
;
3954 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
3957 pa_yes_no(ps
->auto_profiles
),
3958 pa_yes_no(ps
->probed
),
3959 pa_hashmap_size(ps
->mappings
),
3960 pa_hashmap_size(ps
->profiles
),
3961 pa_hashmap_size(ps
->decibel_fixes
));
3963 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3964 pa_alsa_mapping_dump(m
);
3966 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3967 pa_alsa_profile_dump(p
);
3969 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
3970 pa_alsa_decibel_fix_dump(db_fix
);
3973 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
) {
3980 /* if there is no path, we don't want a port list */
3984 if (!ps
->paths
->next
){
3987 /* If there is only one path, but no or only one setting, then
3988 * we want a port list either */
3989 if (!ps
->paths
->settings
|| !ps
->paths
->settings
->next
)
3992 /* Ok, there is only one path, however with multiple settings,
3993 * so let's create a port for each setting */
3994 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3996 PA_LLIST_FOREACH(s
, ps
->paths
->settings
) {
3997 pa_device_port
*port
;
3998 pa_alsa_port_data
*data
;
4000 port
= pa_device_port_new(s
->name
, s
->description
, sizeof(pa_alsa_port_data
));
4001 port
->priority
= s
->priority
;
4003 data
= PA_DEVICE_PORT_DATA(port
);
4004 data
->path
= ps
->paths
;
4007 pa_hashmap_put(*p
, port
->name
, port
);
4012 /* We have multiple paths, so let's create a port for each
4013 * one, and each of each settings */
4014 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4016 PA_LLIST_FOREACH(path
, ps
->paths
) {
4018 if (!path
->settings
|| !path
->settings
->next
) {
4019 pa_device_port
*port
;
4020 pa_alsa_port_data
*data
;
4022 /* If there is no or just one setting we only need a
4025 port
= pa_device_port_new(path
->name
, path
->description
, sizeof(pa_alsa_port_data
));
4026 port
->priority
= path
->priority
* 100;
4029 data
= PA_DEVICE_PORT_DATA(port
);
4031 data
->setting
= path
->settings
;
4033 pa_hashmap_put(*p
, port
->name
, port
);
4037 PA_LLIST_FOREACH(s
, path
->settings
) {
4038 pa_device_port
*port
;
4039 pa_alsa_port_data
*data
;
4042 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4044 if (s
->description
[0])
4045 d
= pa_sprintf_malloc(_("%s / %s"), path
->description
, s
->description
);
4047 d
= pa_xstrdup(path
->description
);
4049 port
= pa_device_port_new(n
, d
, sizeof(pa_alsa_port_data
));
4050 port
->priority
= path
->priority
* 100 + s
->priority
;
4055 data
= PA_DEVICE_PORT_DATA(port
);
4059 pa_hashmap_put(*p
, port
->name
, port
);
4065 pa_log_debug("Added %u ports", pa_hashmap_size(*p
));