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
;
1923 uint32_t volume_limit
;
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_atou(rvalue
, &volume_limit
) < 0 || volume_limit
> LONG_MAX
) {
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 #if defined(__linux__) && !defined(__OPTIMIZE__)
2233 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/paths/" :
2237 r
= pa_config_parse(fn
, NULL
, items
, p
);
2243 if (path_verify(p
) < 0)
2249 pa_alsa_path_free(p
);
2253 pa_alsa_path
* pa_alsa_path_synthesize(const char*element
, pa_alsa_direction_t direction
) {
2259 p
= pa_xnew0(pa_alsa_path
, 1);
2260 p
->name
= pa_xstrdup(element
);
2261 p
->direction
= direction
;
2263 e
= pa_xnew0(pa_alsa_element
, 1);
2265 e
->alsa_name
= pa_xstrdup(element
);
2266 e
->direction
= direction
;
2267 e
->volume_limit
= -1;
2269 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2270 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2272 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2273 p
->last_element
= e
;
2277 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2278 pa_alsa_option
*o
, *n
;
2282 for (o
= e
->options
; o
; o
= n
) {
2285 if (o
->alsa_idx
< 0) {
2286 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2292 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2293 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2294 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2297 static void path_drop_unsupported(pa_alsa_path
*p
) {
2298 pa_alsa_element
*e
, *n
;
2302 for (e
= p
->elements
; e
; e
= n
) {
2305 if (!element_drop_unsupported(e
)) {
2306 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2312 static void path_make_options_unique(pa_alsa_path
*p
) {
2314 pa_alsa_option
*o
, *u
;
2316 PA_LLIST_FOREACH(e
, p
->elements
) {
2317 PA_LLIST_FOREACH(o
, e
->options
) {
2321 for (u
= o
->next
; u
; u
= u
->next
)
2322 if (pa_streq(u
->name
, o
->name
))
2328 m
= pa_xstrdup(o
->name
);
2330 /* OK, this name is not unique, hence let's rename */
2331 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2334 if (!pa_streq(u
->name
, m
))
2337 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2341 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2342 pa_xfree(u
->description
);
2343 u
->description
= nd
;
2353 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2356 for (; e
; e
= e
->next
)
2357 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2358 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2364 for (o
= e
->options
; o
; o
= o
->next
) {
2368 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2369 s
->options
= pa_idxset_copy(template->options
);
2370 s
->name
= pa_sprintf_malloc(_("%s+%s"), template->name
, o
->name
);
2372 (template->description
[0] && o
->description
[0])
2373 ? pa_sprintf_malloc(_("%s / %s"), template->description
, o
->description
)
2374 : (template->description
[0]
2375 ? pa_xstrdup(template->description
)
2376 : pa_xstrdup(o
->description
));
2378 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2380 s
= pa_xnew0(pa_alsa_setting
, 1);
2381 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2382 s
->name
= pa_xstrdup(o
->name
);
2383 s
->description
= pa_xstrdup(o
->description
);
2384 s
->priority
= o
->priority
;
2387 pa_idxset_put(s
->options
, o
, NULL
);
2389 if (element_create_settings(e
->next
, s
))
2390 /* This is not a leaf, so let's get rid of it */
2393 /* This is a leaf, so let's add it */
2394 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2396 e
->path
->last_setting
= s
;
2403 static void path_create_settings(pa_alsa_path
*p
) {
2406 element_create_settings(p
->elements
, NULL
);
2409 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2411 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2412 pa_channel_position_t t
;
2413 pa_channel_position_mask_t path_volume_channels
= 0;
2424 pa_log_debug("Probing path '%s'", p
->name
);
2426 PA_LLIST_FOREACH(e
, p
->elements
) {
2427 if (element_probe(e
, m
) < 0) {
2428 p
->supported
= FALSE
;
2429 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2432 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
);
2437 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2439 if (!p
->has_volume
) {
2440 p
->min_volume
= e
->min_volume
;
2441 p
->max_volume
= e
->max_volume
;
2445 if (!p
->has_volume
) {
2446 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2447 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2448 min_dB
[t
] = e
->min_dB
;
2449 max_dB
[t
] = e
->max_dB
;
2450 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2457 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2458 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2459 min_dB
[t
] += e
->min_dB
;
2460 max_dB
[t
] += e
->max_dB
;
2461 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2464 /* Hmm, there's another element before us
2465 * which cannot do dB volumes, so we we need
2466 * to 'neutralize' this slider */
2467 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2468 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2471 } else if (p
->has_volume
) {
2472 /* We can't use this volume, so let's ignore it */
2473 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2474 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2476 p
->has_volume
= TRUE
;
2479 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2483 if (p
->has_req_any
&& !p
->req_any_present
) {
2484 p
->supported
= FALSE
;
2485 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2489 path_drop_unsupported(p
);
2490 path_make_options_unique(p
);
2491 path_create_settings(p
);
2493 p
->supported
= TRUE
;
2496 p
->min_dB
= INFINITY
;
2497 p
->max_dB
= -INFINITY
;
2499 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2500 if (path_volume_channels
& PA_CHANNEL_POSITION_MASK(t
)) {
2501 if (p
->min_dB
> min_dB
[t
])
2502 p
->min_dB
= min_dB
[t
];
2504 if (p
->max_dB
< max_dB
[t
])
2505 p
->max_dB
= max_dB
[t
];
2512 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2515 pa_log_debug("Setting %s (%s) priority=%u",
2517 pa_strnull(s
->description
),
2521 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2524 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2526 pa_strnull(o
->name
),
2527 pa_strnull(o
->description
),
2532 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2536 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",
2546 (long long unsigned) e
->merged_mask
,
2548 pa_yes_no(e
->override_map
));
2550 PA_LLIST_FOREACH(o
, e
->options
)
2551 pa_alsa_option_dump(o
);
2554 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2559 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2560 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2562 pa_strnull(p
->description
),
2565 pa_yes_no(p
->probed
),
2566 pa_yes_no(p
->supported
),
2567 pa_yes_no(p
->has_mute
),
2568 pa_yes_no(p
->has_volume
),
2569 pa_yes_no(p
->has_dB
),
2570 p
->min_volume
, p
->max_volume
,
2571 p
->min_dB
, p
->max_dB
);
2573 PA_LLIST_FOREACH(e
, p
->elements
)
2574 pa_alsa_element_dump(e
);
2576 PA_LLIST_FOREACH(s
, p
->settings
)
2577 pa_alsa_setting_dump(s
);
2580 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2581 snd_mixer_selem_id_t
*sid
;
2582 snd_mixer_elem_t
*me
;
2588 SELEM_INIT(sid
, e
->alsa_name
);
2589 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2590 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2594 snd_mixer_elem_set_callback(me
, cb
);
2595 snd_mixer_elem_set_callback_private(me
, userdata
);
2598 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2605 PA_LLIST_FOREACH(e
, p
->elements
)
2606 element_set_callback(e
, m
, cb
, userdata
);
2609 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2616 PA_LLIST_FOREACH(p
, ps
->paths
)
2617 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2620 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
) {
2621 pa_alsa_path_set
*ps
;
2622 char **pn
= NULL
, **en
= NULL
, **ie
;
2623 pa_alsa_decibel_fix
*db_fix
;
2627 pa_assert(m
->profile_set
);
2628 pa_assert(m
->profile_set
->decibel_fixes
);
2629 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2631 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2634 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2635 ps
->direction
= direction
;
2637 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2638 pn
= m
->output_path_names
;
2639 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2640 pn
= m
->input_path_names
;
2645 for (in
= pn
; *in
; in
++) {
2647 pa_bool_t duplicate
= FALSE
;
2650 for (kn
= pn
; kn
< in
; kn
++)
2651 if (pa_streq(*kn
, *in
)) {
2659 fn
= pa_sprintf_malloc("%s.conf", *in
);
2661 if ((p
= pa_alsa_path_new(fn
, direction
))) {
2663 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2673 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2674 en
= m
->output_element
;
2675 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2676 en
= m
->input_element
;
2679 pa_alsa_path_set_free(ps
);
2683 for (ie
= en
; *ie
; ie
++) {
2687 p
= pa_alsa_path_synthesize(*ie
, direction
);
2690 /* Mark all other passed elements for require-absent */
2691 for (je
= en
; *je
; je
++) {
2697 e
= pa_xnew0(pa_alsa_element
, 1);
2699 e
->alsa_name
= pa_xstrdup(*je
);
2700 e
->direction
= direction
;
2701 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2702 e
->volume_limit
= -1;
2704 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2705 p
->last_element
= e
;
2708 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2713 /* Assign decibel fixes to elements. */
2714 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2717 PA_LLIST_FOREACH(p
, ps
->paths
) {
2720 PA_LLIST_FOREACH(e
, p
->elements
) {
2721 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2722 /* The profile set that contains the dB fix may be freed
2723 * before the element, so we have to copy the dB fix
2725 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2726 e
->db_fix
->profile_set
= NULL
;
2727 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2728 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
2737 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2741 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2744 pa_yes_no(ps
->probed
));
2746 PA_LLIST_FOREACH(p
, ps
->paths
)
2747 pa_alsa_path_dump(p
);
2750 static void path_set_unify(pa_alsa_path_set
*ps
) {
2752 pa_bool_t has_dB
= TRUE
, has_volume
= TRUE
, has_mute
= TRUE
;
2755 /* We have issues dealing with paths that vary too wildly. That
2756 * means for now we have to have all paths support volume/mute/dB
2759 PA_LLIST_FOREACH(p
, ps
->paths
) {
2760 pa_assert(p
->probed
);
2764 else if (!p
->has_dB
)
2771 if (!has_volume
|| !has_dB
|| !has_mute
) {
2774 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2776 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2779 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2781 PA_LLIST_FOREACH(p
, ps
->paths
) {
2783 p
->has_volume
= FALSE
;
2788 p
->has_mute
= FALSE
;
2793 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
2794 pa_alsa_path
*p
, *q
;
2796 PA_LLIST_FOREACH(p
, ps
->paths
) {
2800 for (q
= p
->next
; q
; q
= q
->next
)
2801 if (pa_streq(q
->name
, p
->name
))
2807 m
= pa_xstrdup(p
->name
);
2809 /* OK, this name is not unique, hence let's rename */
2810 for (i
= 1, q
= p
; q
; q
= q
->next
) {
2813 if (!pa_streq(q
->name
, m
))
2816 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2820 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
2821 pa_xfree(q
->description
);
2822 q
->description
= nd
;
2831 void pa_alsa_path_set_probe(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2832 pa_alsa_path
*p
, *n
;
2839 for (p
= ps
->paths
; p
; p
= n
) {
2842 if (pa_alsa_path_probe(p
, m
, ignore_dB
) < 0) {
2843 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
2844 pa_alsa_path_free(p
);
2849 path_set_make_paths_unique(ps
);
2853 static void mapping_free(pa_alsa_mapping
*m
) {
2857 pa_xfree(m
->description
);
2859 pa_xstrfreev(m
->device_strings
);
2860 pa_xstrfreev(m
->input_path_names
);
2861 pa_xstrfreev(m
->output_path_names
);
2862 pa_xstrfreev(m
->input_element
);
2863 pa_xstrfreev(m
->output_element
);
2865 pa_assert(!m
->input_pcm
);
2866 pa_assert(!m
->output_pcm
);
2871 static void profile_free(pa_alsa_profile
*p
) {
2875 pa_xfree(p
->description
);
2877 pa_xstrfreev(p
->input_mapping_names
);
2878 pa_xstrfreev(p
->output_mapping_names
);
2880 if (p
->input_mappings
)
2881 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
2883 if (p
->output_mappings
)
2884 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
2889 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
2895 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
2898 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
2904 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
2907 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
2910 if (ps
->decibel_fixes
) {
2911 pa_alsa_decibel_fix
*db_fix
;
2913 while ((db_fix
= pa_hashmap_steal_first(ps
->decibel_fixes
)))
2914 decibel_fix_free(db_fix
);
2916 pa_hashmap_free(ps
->decibel_fixes
, NULL
, NULL
);
2922 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
2925 if (!pa_startswith(name
, "Mapping "))
2930 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
2933 m
= pa_xnew0(pa_alsa_mapping
, 1);
2934 m
->profile_set
= ps
;
2935 m
->name
= pa_xstrdup(name
);
2936 pa_channel_map_init(&m
->channel_map
);
2938 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
2943 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
2946 if (!pa_startswith(name
, "Profile "))
2951 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
2954 p
= pa_xnew0(pa_alsa_profile
, 1);
2955 p
->profile_set
= ps
;
2956 p
->name
= pa_xstrdup(name
);
2958 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
2963 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
2964 pa_alsa_decibel_fix
*db_fix
;
2966 if (!pa_startswith(name
, "DecibelFix "))
2971 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
2974 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
2975 db_fix
->profile_set
= ps
;
2976 db_fix
->name
= pa_xstrdup(name
);
2978 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
2983 static int mapping_parse_device_strings(
2984 const char *filename
,
2986 const char *section
,
2992 pa_alsa_profile_set
*ps
= userdata
;
2997 if (!(m
= mapping_get(ps
, section
))) {
2998 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3002 pa_xstrfreev(m
->device_strings
);
3003 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
3004 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
3011 static int mapping_parse_channel_map(
3012 const char *filename
,
3014 const char *section
,
3020 pa_alsa_profile_set
*ps
= userdata
;
3025 if (!(m
= mapping_get(ps
, section
))) {
3026 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3030 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
3031 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
3038 static int mapping_parse_paths(
3039 const char *filename
,
3041 const char *section
,
3047 pa_alsa_profile_set
*ps
= userdata
;
3052 if (!(m
= mapping_get(ps
, section
))) {
3053 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3057 if (pa_streq(lvalue
, "paths-input")) {
3058 pa_xstrfreev(m
->input_path_names
);
3059 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
3061 pa_xstrfreev(m
->output_path_names
);
3062 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
3068 static int mapping_parse_element(
3069 const char *filename
,
3071 const char *section
,
3077 pa_alsa_profile_set
*ps
= userdata
;
3082 if (!(m
= mapping_get(ps
, section
))) {
3083 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3087 if (pa_streq(lvalue
, "element-input")) {
3088 pa_xstrfreev(m
->input_element
);
3089 m
->input_element
= pa_split_spaces_strv(rvalue
);
3091 pa_xstrfreev(m
->output_element
);
3092 m
->output_element
= pa_split_spaces_strv(rvalue
);
3098 static int mapping_parse_direction(
3099 const char *filename
,
3101 const char *section
,
3107 pa_alsa_profile_set
*ps
= userdata
;
3112 if (!(m
= mapping_get(ps
, section
))) {
3113 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3117 if (pa_streq(rvalue
, "input"))
3118 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3119 else if (pa_streq(rvalue
, "output"))
3120 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3121 else if (pa_streq(rvalue
, "any"))
3122 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3124 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
3131 static int mapping_parse_description(
3132 const char *filename
,
3134 const char *section
,
3140 pa_alsa_profile_set
*ps
= userdata
;
3146 if ((m
= mapping_get(ps
, section
))) {
3147 pa_xfree(m
->description
);
3148 m
->description
= pa_xstrdup(rvalue
);
3149 } else if ((p
= profile_get(ps
, section
))) {
3150 pa_xfree(p
->description
);
3151 p
->description
= pa_xstrdup(rvalue
);
3153 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3160 static int mapping_parse_priority(
3161 const char *filename
,
3163 const char *section
,
3169 pa_alsa_profile_set
*ps
= userdata
;
3176 if (pa_atou(rvalue
, &prio
) < 0) {
3177 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
3181 if ((m
= mapping_get(ps
, section
)))
3183 else if ((p
= profile_get(ps
, section
)))
3186 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3193 static int profile_parse_mappings(
3194 const char *filename
,
3196 const char *section
,
3202 pa_alsa_profile_set
*ps
= userdata
;
3207 if (!(p
= profile_get(ps
, section
))) {
3208 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3212 if (pa_streq(lvalue
, "input-mappings")) {
3213 pa_xstrfreev(p
->input_mapping_names
);
3214 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
3216 pa_xstrfreev(p
->output_mapping_names
);
3217 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
3223 static int profile_parse_skip_probe(
3224 const char *filename
,
3226 const char *section
,
3232 pa_alsa_profile_set
*ps
= userdata
;
3238 if (!(p
= profile_get(ps
, section
))) {
3239 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3243 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
3244 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
3253 static int decibel_fix_parse_db_values(
3254 const char *filename
,
3256 const char *section
,
3262 pa_alsa_profile_set
*ps
= userdata
;
3263 pa_alsa_decibel_fix
*db_fix
;
3267 unsigned n
= 8; /* Current size of the db_values table. */
3268 unsigned min_step
= 0;
3269 unsigned max_step
= 0;
3270 unsigned i
= 0; /* Index to the items table. */
3271 unsigned prev_step
= 0;
3274 pa_assert(filename
);
3280 if (!(db_fix
= decibel_fix_get(ps
, section
))) {
3281 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3285 if (!(items
= pa_split_spaces_strv(rvalue
))) {
3286 pa_log("[%s:%u] Value missing", pa_strnull(filename
), line
);
3290 db_values
= pa_xnew(long, n
);
3292 while ((item
= items
[i
++])) {
3293 char *s
= item
; /* Step value string. */
3294 char *d
= item
; /* dB value string. */
3298 /* Move d forward until it points to a colon or to the end of the item. */
3299 for (; *d
&& *d
!= ':'; ++d
);
3302 /* item started with colon. */
3303 pa_log("[%s:%u] No step value found in %s", filename
, line
, item
);
3307 if (!*d
|| !*(d
+ 1)) {
3308 /* No colon found, or it was the last character in item. */
3309 pa_log("[%s:%u] No dB value found in %s", filename
, line
, item
);
3313 /* pa_atou() needs a null-terminating string. Let's replace the colon
3314 * with a zero byte. */
3317 if (pa_atou(s
, &step
) < 0) {
3318 pa_log("[%s:%u] Invalid step value: %s", filename
, line
, s
);
3322 if (pa_atod(d
, &db
) < 0) {
3323 pa_log("[%s:%u] Invalid dB value: %s", filename
, line
, d
);
3327 if (step
<= prev_step
&& i
!= 1) {
3328 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename
, line
, step
, prev_step
);
3332 if (db
< prev_db
&& i
!= 1) {
3333 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename
, line
, db
, prev_db
);
3339 db_values
[0] = (long) (db
* 100.0);
3343 /* Interpolate linearly. */
3344 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3346 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3348 /* Reallocate the db_values table if it's about to overflow. */
3349 if (prev_step
+ 1 - min_step
== n
) {
3351 db_values
= pa_xrenew(long, db_values
, n
);
3354 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3361 db_fix
->min_step
= min_step
;
3362 db_fix
->max_step
= max_step
;
3363 pa_xfree(db_fix
->db_values
);
3364 db_fix
->db_values
= db_values
;
3366 pa_xstrfreev(items
);
3371 pa_xstrfreev(items
);
3372 pa_xfree(db_values
);
3377 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3379 static const struct description_map well_known_descriptions
[] = {
3380 { "analog-mono", N_("Analog Mono") },
3381 { "analog-stereo", N_("Analog Stereo") },
3382 { "analog-surround-21", N_("Analog Surround 2.1") },
3383 { "analog-surround-30", N_("Analog Surround 3.0") },
3384 { "analog-surround-31", N_("Analog Surround 3.1") },
3385 { "analog-surround-40", N_("Analog Surround 4.0") },
3386 { "analog-surround-41", N_("Analog Surround 4.1") },
3387 { "analog-surround-50", N_("Analog Surround 5.0") },
3388 { "analog-surround-51", N_("Analog Surround 5.1") },
3389 { "analog-surround-61", N_("Analog Surround 6.0") },
3390 { "analog-surround-61", N_("Analog Surround 6.1") },
3391 { "analog-surround-70", N_("Analog Surround 7.0") },
3392 { "analog-surround-71", N_("Analog Surround 7.1") },
3393 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3394 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3395 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3396 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3397 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3402 if (!pa_channel_map_valid(&m
->channel_map
)) {
3403 pa_log("Mapping %s is missing channel map.", m
->name
);
3407 if (!m
->device_strings
) {
3408 pa_log("Mapping %s is missing device strings.", m
->name
);
3412 if ((m
->input_path_names
&& m
->input_element
) ||
3413 (m
->output_path_names
&& m
->output_element
)) {
3414 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3418 if (!m
->description
)
3419 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3420 well_known_descriptions
,
3421 PA_ELEMENTSOF(well_known_descriptions
)));
3423 if (!m
->description
)
3424 m
->description
= pa_xstrdup(m
->name
);
3427 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3429 else if (m
->channel_map
.channels
== bonus
->channels
)
3436 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3437 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3441 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3443 pa_strnull(m
->description
),
3445 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3446 pa_yes_no(m
->supported
),
3450 static void profile_set_add_auto_pair(
3451 pa_alsa_profile_set
*ps
,
3452 pa_alsa_mapping
*m
, /* output */
3453 pa_alsa_mapping
*n
/* input */) {
3461 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3464 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3468 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3470 name
= pa_sprintf_malloc("output:%s", m
->name
);
3472 name
= pa_sprintf_malloc("input:%s", n
->name
);
3474 if (pa_hashmap_get(ps
->profiles
, name
)) {
3479 p
= pa_xnew0(pa_alsa_profile
, 1);
3480 p
->profile_set
= ps
;
3484 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3485 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3486 p
->priority
+= m
->priority
* 100;
3490 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3491 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3492 p
->priority
+= n
->priority
;
3495 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3498 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3499 pa_alsa_mapping
*m
, *n
;
3500 void *m_state
, *n_state
;
3504 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3505 profile_set_add_auto_pair(ps
, m
, NULL
);
3507 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3508 profile_set_add_auto_pair(ps
, m
, n
);
3511 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3512 profile_set_add_auto_pair(ps
, NULL
, n
);
3515 static int profile_verify(pa_alsa_profile
*p
) {
3517 static const struct description_map well_known_descriptions
[] = {
3518 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3519 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3520 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3521 { "off", N_("Off") }
3526 /* Replace the output mapping names by the actual mappings */
3527 if (p
->output_mapping_names
) {
3530 pa_assert(!p
->output_mappings
);
3531 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3533 for (name
= p
->output_mapping_names
; *name
; name
++) {
3536 pa_bool_t duplicate
= FALSE
;
3538 for (in
= name
+ 1; *in
; in
++)
3539 if (pa_streq(*name
, *in
)) {
3547 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3548 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3552 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3558 pa_xstrfreev(p
->output_mapping_names
);
3559 p
->output_mapping_names
= NULL
;
3562 /* Replace the input mapping names by the actual mappings */
3563 if (p
->input_mapping_names
) {
3566 pa_assert(!p
->input_mappings
);
3567 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3569 for (name
= p
->input_mapping_names
; *name
; name
++) {
3572 pa_bool_t duplicate
= FALSE
;
3574 for (in
= name
+ 1; *in
; in
++)
3575 if (pa_streq(*name
, *in
)) {
3583 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3584 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3588 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3594 pa_xstrfreev(p
->input_mapping_names
);
3595 p
->input_mapping_names
= NULL
;
3598 if (!p
->input_mappings
&& !p
->output_mappings
) {
3599 pa_log("Profile '%s' lacks mappings.", p
->name
);
3603 if (!p
->description
)
3604 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3605 well_known_descriptions
,
3606 PA_ELEMENTSOF(well_known_descriptions
)));
3608 if (!p
->description
) {
3613 sb
= pa_strbuf_new();
3615 if (p
->output_mappings
)
3616 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3617 if (!pa_strbuf_isempty(sb
))
3618 pa_strbuf_puts(sb
, " + ");
3620 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
3623 if (p
->input_mappings
)
3624 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3625 if (!pa_strbuf_isempty(sb
))
3626 pa_strbuf_puts(sb
, " + ");
3628 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
3631 p
->description
= pa_strbuf_tostring_free(sb
);
3637 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
3642 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3644 pa_strnull(p
->description
),
3646 pa_yes_no(p
->supported
),
3647 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
3648 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
3650 if (p
->input_mappings
)
3651 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
3652 pa_log_debug("Input %s", m
->name
);
3654 if (p
->output_mappings
)
3655 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
3656 pa_log_debug("Output %s", m
->name
);
3659 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
3662 /* Check that the dB mapping has been configured. Since "db-values" is
3663 * currently the only option in the DecibelFix section, and decibel fix
3664 * objects don't get created if a DecibelFix section is empty, this is
3665 * actually a redundant check. Having this may prevent future bugs,
3667 if (!db_fix
->db_values
) {
3668 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
3675 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
3676 char *db_values
= NULL
;
3680 if (db_fix
->db_values
) {
3682 unsigned long i
, nsteps
;
3684 pa_assert(db_fix
->min_step
<= db_fix
->max_step
);
3685 nsteps
= db_fix
->max_step
- db_fix
->min_step
+ 1;
3687 buf
= pa_strbuf_new();
3688 for (i
= 0; i
< nsteps
; ++i
)
3689 pa_strbuf_printf(buf
, "[%li]:%0.2f ", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
3691 db_values
= pa_strbuf_tostring_free(buf
);
3694 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
3695 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
3697 pa_xfree(db_values
);
3700 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
3701 pa_alsa_profile_set
*ps
;
3704 pa_alsa_decibel_fix
*db_fix
;
3709 static pa_config_item items
[] = {
3711 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
3714 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
3715 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
3716 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
3717 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
3718 { "element-input", mapping_parse_element
, NULL
, NULL
},
3719 { "element-output", mapping_parse_element
, NULL
, NULL
},
3720 { "direction", mapping_parse_direction
, NULL
, NULL
},
3722 /* Shared by [Mapping ...] and [Profile ...] */
3723 { "description", mapping_parse_description
, NULL
, NULL
},
3724 { "priority", mapping_parse_priority
, NULL
, NULL
},
3727 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
3728 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
3729 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
3731 /* [DecibelFix ...] */
3732 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
3733 { NULL
, NULL
, NULL
, NULL
}
3736 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
3737 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3738 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3739 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3741 items
[0].data
= &ps
->auto_profiles
;
3744 fname
= "default.conf";
3746 fn
= pa_maybe_prefix_path(fname
,
3747 #if defined(__linux__) && !defined(__OPTIMIZE__)
3748 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
3750 PA_ALSA_PROFILE_SETS_DIR
);
3752 r
= pa_config_parse(fn
, NULL
, items
, ps
);
3758 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3759 if (mapping_verify(m
, bonus
) < 0)
3762 if (ps
->auto_profiles
)
3763 profile_set_add_auto(ps
);
3765 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3766 if (profile_verify(p
) < 0)
3769 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
3770 if (decibel_fix_verify(db_fix
) < 0)
3776 pa_alsa_profile_set_free(ps
);
3780 void pa_alsa_profile_set_probe(
3781 pa_alsa_profile_set
*ps
,
3783 const pa_sample_spec
*ss
,
3784 unsigned default_n_fragments
,
3785 unsigned default_fragment_size_msec
) {
3788 pa_alsa_profile
*p
, *last
= NULL
;
3798 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
3799 pa_sample_spec try_ss
;
3800 pa_channel_map try_map
;
3801 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
3804 /* Is this already marked that it is supported? (i.e. from the config file) */
3808 pa_log_debug("Looking at profile %s", p
->name
);
3810 /* Close PCMs from the last iteration we don't need anymore */
3811 if (last
&& last
->output_mappings
)
3812 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
) {
3817 if (last
->supported
)
3820 if (!p
->output_mappings
|| !pa_idxset_get_by_data(p
->output_mappings
, m
, NULL
)) {
3821 snd_pcm_close(m
->output_pcm
);
3822 m
->output_pcm
= NULL
;
3826 if (last
&& last
->input_mappings
)
3827 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
) {
3832 if (last
->supported
)
3835 if (!p
->input_mappings
|| !pa_idxset_get_by_data(p
->input_mappings
, m
, NULL
)) {
3836 snd_pcm_close(m
->input_pcm
);
3837 m
->input_pcm
= NULL
;
3841 p
->supported
= TRUE
;
3843 /* Check if we can open all new ones */
3844 if (p
->output_mappings
)
3845 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3850 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
3851 try_map
= m
->channel_map
;
3853 try_ss
.channels
= try_map
.channels
;
3856 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
3857 pa_frame_size(&try_ss
);
3858 try_buffer_size
= default_n_fragments
* try_period_size
;
3860 if (!(m
->output_pcm
= pa_alsa_open_by_template(
3865 SND_PCM_STREAM_PLAYBACK
,
3866 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3868 p
->supported
= FALSE
;
3873 if (p
->input_mappings
&& p
->supported
)
3874 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3879 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
3880 try_map
= m
->channel_map
;
3882 try_ss
.channels
= try_map
.channels
;
3885 pa_usec_to_bytes(default_fragment_size_msec
*PA_USEC_PER_MSEC
, &try_ss
) /
3886 pa_frame_size(&try_ss
);
3887 try_buffer_size
= default_n_fragments
* try_period_size
;
3889 if (!(m
->input_pcm
= pa_alsa_open_by_template(
3894 SND_PCM_STREAM_CAPTURE
,
3895 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3897 p
->supported
= FALSE
;
3905 pa_log_debug("Profile %s supported.", p
->name
);
3912 if (last
->output_mappings
)
3913 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
)
3914 if (m
->output_pcm
) {
3916 if (last
->supported
)
3919 snd_pcm_close(m
->output_pcm
);
3920 m
->output_pcm
= NULL
;
3923 if (last
->input_mappings
)
3924 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
)
3927 if (last
->supported
)
3930 snd_pcm_close(m
->input_pcm
);
3931 m
->input_pcm
= NULL
;
3935 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3936 if (!p
->supported
) {
3937 pa_hashmap_remove(ps
->profiles
, p
->name
);
3941 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3942 if (m
->supported
<= 0) {
3943 pa_hashmap_remove(ps
->mappings
, m
->name
);
3950 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
3953 pa_alsa_decibel_fix
*db_fix
;
3958 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
3961 pa_yes_no(ps
->auto_profiles
),
3962 pa_yes_no(ps
->probed
),
3963 pa_hashmap_size(ps
->mappings
),
3964 pa_hashmap_size(ps
->profiles
),
3965 pa_hashmap_size(ps
->decibel_fixes
));
3967 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3968 pa_alsa_mapping_dump(m
);
3970 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3971 pa_alsa_profile_dump(p
);
3973 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
3974 pa_alsa_decibel_fix_dump(db_fix
);
3977 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
) {
3984 /* if there is no path, we don't want a port list */
3988 if (!ps
->paths
->next
){
3991 /* If there is only one path, but no or only one setting, then
3992 * we want a port list either */
3993 if (!ps
->paths
->settings
|| !ps
->paths
->settings
->next
)
3996 /* Ok, there is only one path, however with multiple settings,
3997 * so let's create a port for each setting */
3998 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4000 PA_LLIST_FOREACH(s
, ps
->paths
->settings
) {
4001 pa_device_port
*port
;
4002 pa_alsa_port_data
*data
;
4004 port
= pa_device_port_new(s
->name
, s
->description
, sizeof(pa_alsa_port_data
));
4005 port
->priority
= s
->priority
;
4007 data
= PA_DEVICE_PORT_DATA(port
);
4008 data
->path
= ps
->paths
;
4011 pa_hashmap_put(*p
, port
->name
, port
);
4016 /* We have multiple paths, so let's create a port for each
4017 * one, and each of each settings */
4018 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4020 PA_LLIST_FOREACH(path
, ps
->paths
) {
4022 if (!path
->settings
|| !path
->settings
->next
) {
4023 pa_device_port
*port
;
4024 pa_alsa_port_data
*data
;
4026 /* If there is no or just one setting we only need a
4029 port
= pa_device_port_new(path
->name
, path
->description
, sizeof(pa_alsa_port_data
));
4030 port
->priority
= path
->priority
* 100;
4033 data
= PA_DEVICE_PORT_DATA(port
);
4035 data
->setting
= path
->settings
;
4037 pa_hashmap_put(*p
, port
->name
, port
);
4041 PA_LLIST_FOREACH(s
, path
->settings
) {
4042 pa_device_port
*port
;
4043 pa_alsa_port_data
*data
;
4046 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4048 if (s
->description
[0])
4049 d
= pa_sprintf_malloc(_("%s / %s"), path
->description
, s
->description
);
4051 d
= pa_xstrdup(path
->description
);
4053 port
= pa_device_port_new(n
, d
, sizeof(pa_alsa_port_data
));
4054 port
->priority
= path
->priority
* 100 + s
->priority
;
4059 data
= PA_DEVICE_PORT_DATA(port
);
4063 pa_hashmap_put(*p
, port
->name
, port
);
4069 pa_log_debug("Added %u ports", pa_hashmap_size(*p
));