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 static int element_mute_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1088 snd_mixer_elem_t
*me
;
1089 snd_mixer_selem_id_t
*sid
;
1095 SELEM_INIT(sid
, e
->alsa_name
);
1096 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1097 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1101 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1102 r
= snd_mixer_selem_set_playback_volume_all(me
, e
->min_volume
);
1104 r
= snd_mixer_selem_set_capture_volume_all(me
, e
->min_volume
);
1107 pa_log_warn("Failed to set volume to muted of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1112 /* The volume to 0dB */
1113 static int element_zero_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1114 snd_mixer_elem_t
*me
;
1115 snd_mixer_selem_id_t
*sid
;
1121 SELEM_INIT(sid
, e
->alsa_name
);
1122 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1123 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1127 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1131 r
= snd_mixer_selem_set_playback_volume_all(me
, decibel_fix_get_step(e
->db_fix
, &value
, +1));
1133 r
= snd_mixer_selem_set_playback_dB_all(me
, 0, +1);
1138 r
= snd_mixer_selem_set_capture_volume_all(me
, decibel_fix_get_step(e
->db_fix
, &value
, +1));
1140 r
= snd_mixer_selem_set_capture_dB_all(me
, 0, +1);
1143 pa_log_warn("Failed to set volume to 0dB of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1148 int pa_alsa_path_select(pa_alsa_path
*p
, snd_mixer_t
*m
) {
1155 pa_log_debug("Activating path %s", p
->name
);
1156 pa_alsa_path_dump(p
);
1158 PA_LLIST_FOREACH(e
, p
->elements
) {
1160 switch (e
->switch_use
) {
1161 case PA_ALSA_SWITCH_OFF
:
1162 r
= element_set_switch(e
, m
, FALSE
);
1165 case PA_ALSA_SWITCH_ON
:
1166 r
= element_set_switch(e
, m
, TRUE
);
1169 case PA_ALSA_SWITCH_MUTE
:
1170 case PA_ALSA_SWITCH_IGNORE
:
1171 case PA_ALSA_SWITCH_SELECT
:
1179 switch (e
->volume_use
) {
1180 case PA_ALSA_VOLUME_OFF
:
1181 r
= element_mute_volume(e
, m
);
1184 case PA_ALSA_VOLUME_ZERO
:
1185 r
= element_zero_volume(e
, m
);
1188 case PA_ALSA_VOLUME_MERGE
:
1189 case PA_ALSA_VOLUME_IGNORE
:
1201 static int check_required(pa_alsa_element
*e
, snd_mixer_elem_t
*me
) {
1202 pa_bool_t has_switch
;
1203 pa_bool_t has_enumeration
;
1204 pa_bool_t has_volume
;
1209 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1211 snd_mixer_selem_has_playback_switch(me
) ||
1212 (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
));
1215 snd_mixer_selem_has_capture_switch(me
) ||
1216 (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
));
1219 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1221 snd_mixer_selem_has_playback_volume(me
) ||
1222 (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
));
1225 snd_mixer_selem_has_capture_volume(me
) ||
1226 (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
));
1229 has_enumeration
= snd_mixer_selem_is_enumerated(me
);
1231 if ((e
->required
== PA_ALSA_REQUIRED_SWITCH
&& !has_switch
) ||
1232 (e
->required
== PA_ALSA_REQUIRED_VOLUME
&& !has_volume
) ||
1233 (e
->required
== PA_ALSA_REQUIRED_ENUMERATION
&& !has_enumeration
))
1236 if (e
->required
== PA_ALSA_REQUIRED_ANY
&& !(has_switch
|| has_volume
|| has_enumeration
))
1239 if ((e
->required_absent
== PA_ALSA_REQUIRED_SWITCH
&& has_switch
) ||
1240 (e
->required_absent
== PA_ALSA_REQUIRED_VOLUME
&& has_volume
) ||
1241 (e
->required_absent
== PA_ALSA_REQUIRED_ENUMERATION
&& has_enumeration
))
1244 if (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& (has_switch
|| has_volume
|| has_enumeration
))
1247 if (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) {
1248 switch (e
->required_any
) {
1249 case PA_ALSA_REQUIRED_VOLUME
:
1250 e
->path
->req_any_present
|= (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
);
1252 case PA_ALSA_REQUIRED_SWITCH
:
1253 e
->path
->req_any_present
|= (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
);
1255 case PA_ALSA_REQUIRED_ENUMERATION
:
1256 e
->path
->req_any_present
|= (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1258 case PA_ALSA_REQUIRED_ANY
:
1259 e
->path
->req_any_present
|=
1260 (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) ||
1261 (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) ||
1262 (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1265 pa_assert_not_reached();
1269 if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1271 PA_LLIST_FOREACH(o
, e
->options
) {
1272 e
->path
->req_any_present
|= (o
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) &&
1274 if (o
->required
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
< 0)
1276 if (o
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
>= 0)
1284 static int element_probe(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1285 snd_mixer_selem_id_t
*sid
;
1286 snd_mixer_elem_t
*me
;
1292 SELEM_INIT(sid
, e
->alsa_name
);
1294 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1296 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1299 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1300 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1301 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1306 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1307 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1309 if (!snd_mixer_selem_has_playback_switch(me
)) {
1310 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1311 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1313 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1318 if (!snd_mixer_selem_has_capture_switch(me
)) {
1319 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1320 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1322 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1326 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1327 e
->direction_try_other
= FALSE
;
1330 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1332 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1334 if (!snd_mixer_selem_has_playback_volume(me
)) {
1335 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1336 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1338 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1343 if (!snd_mixer_selem_has_capture_volume(me
)) {
1344 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1345 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1347 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1351 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1352 long min_dB
= 0, max_dB
= 0;
1355 e
->direction_try_other
= FALSE
;
1357 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1358 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1360 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1363 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1367 if (e
->min_volume
>= e
->max_volume
) {
1368 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
);
1369 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1373 pa_channel_position_t p
;
1376 ((e
->min_volume
> e
->db_fix
->min_step
) ||
1377 (e
->max_volume
< e
->db_fix
->max_step
))) {
1378 pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1379 "real hardware range (%li-%li). Disabling the decibel fix.", e
->alsa_name
,
1380 e
->db_fix
->min_step
, e
->db_fix
->max_step
,
1381 e
->min_volume
, e
->max_volume
);
1383 decibel_fix_free(e
->db_fix
);
1389 e
->min_volume
= e
->db_fix
->min_step
;
1390 e
->max_volume
= e
->db_fix
->max_step
;
1391 min_dB
= e
->db_fix
->db_values
[0];
1392 max_dB
= e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
];
1393 } else if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1394 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1396 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1399 #ifdef HAVE_VALGRIND_MEMCHECK_H
1400 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1401 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1404 e
->min_dB
= ((double) min_dB
) / 100.0;
1405 e
->max_dB
= ((double) max_dB
) / 100.0;
1407 if (min_dB
>= max_dB
) {
1408 pa_assert(!e
->db_fix
);
1409 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
);
1414 if (e
->volume_limit
>= 0) {
1415 if (e
->volume_limit
<= e
->min_volume
|| e
->volume_limit
> e
->max_volume
)
1416 pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1417 "%li-%li. The volume limit is ignored.",
1418 e
->alsa_name
, e
->path
->name
, e
->volume_limit
, e
->min_volume
+ 1, e
->max_volume
);
1421 e
->max_volume
= e
->volume_limit
;
1425 e
->db_fix
->max_step
= e
->max_volume
;
1426 e
->max_dB
= ((double) e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
]) / 100.0;
1429 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1430 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB
);
1432 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB
);
1435 pa_log_warn("Failed to get dB value of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1438 e
->max_dB
= ((double) max_dB
) / 100.0;
1444 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1445 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1447 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1452 if (!e
->override_map
) {
1453 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++)
1454 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1455 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1458 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1461 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1463 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1466 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1467 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1469 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1472 if (e
->n_channels
<= 0) {
1473 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1477 if (!e
->override_map
) {
1478 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1479 pa_bool_t has_channel
;
1481 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1484 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1485 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1487 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1489 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1494 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++)
1495 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1502 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1505 PA_LLIST_FOREACH(o
, e
->options
)
1506 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1507 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1511 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1512 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1516 PA_LLIST_FOREACH(o
, e
->options
) {
1519 for (i
= 0; i
< n
; i
++) {
1522 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1525 if (!pa_streq(buf
, o
->alsa_name
))
1533 if (check_required(e
, me
) < 0)
1539 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, pa_bool_t prefixed
) {
1546 if (!pa_startswith(section
, "Element "))
1552 /* This is not an element section, but an enum section? */
1553 if (strchr(section
, ':'))
1556 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1557 return p
->last_element
;
1559 PA_LLIST_FOREACH(e
, p
->elements
)
1560 if (pa_streq(e
->alsa_name
, section
))
1563 e
= pa_xnew0(pa_alsa_element
, 1);
1565 e
->alsa_name
= pa_xstrdup(section
);
1566 e
->direction
= p
->direction
;
1567 e
->volume_limit
= -1;
1569 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1572 p
->last_element
= e
;
1576 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1582 if (!pa_startswith(section
, "Option "))
1587 /* This is not an enum section, but an element section? */
1588 if (!(on
= strchr(section
, ':')))
1591 en
= pa_xstrndup(section
, on
- section
);
1594 if (p
->last_option
&&
1595 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1596 pa_streq(p
->last_option
->alsa_name
, on
)) {
1598 return p
->last_option
;
1601 pa_assert_se(e
= element_get(p
, en
, FALSE
));
1604 PA_LLIST_FOREACH(o
, e
->options
)
1605 if (pa_streq(o
->alsa_name
, on
))
1608 o
= pa_xnew0(pa_alsa_option
, 1);
1610 o
->alsa_name
= pa_xstrdup(on
);
1613 if (p
->last_option
&& p
->last_option
->element
== e
)
1614 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1616 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1623 static int element_parse_switch(
1624 const char *filename
,
1626 const char *section
,
1632 pa_alsa_path
*p
= userdata
;
1637 if (!(e
= element_get(p
, section
, TRUE
))) {
1638 pa_log("[%s:%u] Switch makes no sense in '%s'", filename
, line
, section
);
1642 if (pa_streq(rvalue
, "ignore"))
1643 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1644 else if (pa_streq(rvalue
, "mute"))
1645 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1646 else if (pa_streq(rvalue
, "off"))
1647 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1648 else if (pa_streq(rvalue
, "on"))
1649 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1650 else if (pa_streq(rvalue
, "select"))
1651 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1653 pa_log("[%s:%u] Switch invalid of '%s'", filename
, line
, section
);
1660 static int element_parse_volume(
1661 const char *filename
,
1663 const char *section
,
1669 pa_alsa_path
*p
= userdata
;
1674 if (!(e
= element_get(p
, section
, TRUE
))) {
1675 pa_log("[%s:%u] Volume makes no sense in '%s'", filename
, line
, section
);
1679 if (pa_streq(rvalue
, "ignore"))
1680 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1681 else if (pa_streq(rvalue
, "merge"))
1682 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1683 else if (pa_streq(rvalue
, "off"))
1684 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1685 else if (pa_streq(rvalue
, "zero"))
1686 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1688 pa_log("[%s:%u] Volume invalid of '%s'", filename
, line
, section
);
1695 static int element_parse_enumeration(
1696 const char *filename
,
1698 const char *section
,
1704 pa_alsa_path
*p
= userdata
;
1709 if (!(e
= element_get(p
, section
, TRUE
))) {
1710 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename
, line
, section
);
1714 if (pa_streq(rvalue
, "ignore"))
1715 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1716 else if (pa_streq(rvalue
, "select"))
1717 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1719 pa_log("[%s:%u] Enumeration invalid of '%s'", filename
, line
, section
);
1726 static int option_parse_priority(
1727 const char *filename
,
1729 const char *section
,
1735 pa_alsa_path
*p
= userdata
;
1741 if (!(o
= option_get(p
, section
))) {
1742 pa_log("[%s:%u] Priority makes no sense in '%s'", filename
, line
, section
);
1746 if (pa_atou(rvalue
, &prio
) < 0) {
1747 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
1755 static int option_parse_name(
1756 const char *filename
,
1758 const char *section
,
1764 pa_alsa_path
*p
= userdata
;
1769 if (!(o
= option_get(p
, section
))) {
1770 pa_log("[%s:%u] Name makes no sense in '%s'", filename
, line
, section
);
1775 o
->name
= pa_xstrdup(rvalue
);
1780 static int element_parse_required(
1781 const char *filename
,
1783 const char *section
,
1789 pa_alsa_path
*p
= userdata
;
1792 pa_alsa_required_t req
;
1796 e
= element_get(p
, section
, TRUE
);
1797 o
= option_get(p
, section
);
1799 pa_log("[%s:%u] Required makes no sense in '%s'", filename
, line
, section
);
1803 if (pa_streq(rvalue
, "ignore"))
1804 req
= PA_ALSA_REQUIRED_IGNORE
;
1805 else if (pa_streq(rvalue
, "switch") && e
)
1806 req
= PA_ALSA_REQUIRED_SWITCH
;
1807 else if (pa_streq(rvalue
, "volume") && e
)
1808 req
= PA_ALSA_REQUIRED_VOLUME
;
1809 else if (pa_streq(rvalue
, "enumeration"))
1810 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1811 else if (pa_streq(rvalue
, "any"))
1812 req
= PA_ALSA_REQUIRED_ANY
;
1814 pa_log("[%s:%u] Required invalid of '%s'", filename
, line
, section
);
1818 if (pa_streq(lvalue
, "required-absent")) {
1820 e
->required_absent
= req
;
1822 o
->required_absent
= req
;
1824 else if (pa_streq(lvalue
, "required-any")) {
1826 e
->required_any
= req
;
1827 e
->path
->has_req_any
= TRUE
;
1830 o
->required_any
= req
;
1831 o
->element
->path
->has_req_any
= TRUE
;
1844 static int element_parse_direction(
1845 const char *filename
,
1847 const char *section
,
1853 pa_alsa_path
*p
= userdata
;
1858 if (!(e
= element_get(p
, section
, TRUE
))) {
1859 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1863 if (pa_streq(rvalue
, "playback"))
1864 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1865 else if (pa_streq(rvalue
, "capture"))
1866 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1868 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1875 static int element_parse_direction_try_other(
1876 const char *filename
,
1878 const char *section
,
1884 pa_alsa_path
*p
= userdata
;
1888 if (!(e
= element_get(p
, section
, TRUE
))) {
1889 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1893 if ((yes
= pa_parse_boolean(rvalue
)) < 0) {
1894 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1898 e
->direction_try_other
= !!yes
;
1902 static int element_parse_volume_limit(
1903 const char *filename
,
1905 const char *section
,
1911 pa_alsa_path
*p
= userdata
;
1913 uint32_t volume_limit
;
1915 if (!(e
= element_get(p
, section
, TRUE
))) {
1916 pa_log("[%s:%u] volume-limit makes no sense in '%s'", filename
, line
, section
);
1920 if (pa_atou(rvalue
, &volume_limit
) < 0 || volume_limit
> LONG_MAX
) {
1921 pa_log("[%s:%u] Invalid value for volume-limit", filename
, line
);
1925 e
->volume_limit
= volume_limit
;
1929 static pa_channel_position_mask_t
parse_mask(const char *m
) {
1930 pa_channel_position_mask_t v
;
1932 if (pa_streq(m
, "all-left"))
1933 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
1934 else if (pa_streq(m
, "all-right"))
1935 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
1936 else if (pa_streq(m
, "all-center"))
1937 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
1938 else if (pa_streq(m
, "all-front"))
1939 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
1940 else if (pa_streq(m
, "all-rear"))
1941 v
= PA_CHANNEL_POSITION_MASK_REAR
;
1942 else if (pa_streq(m
, "all-side"))
1943 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
1944 else if (pa_streq(m
, "all-top"))
1945 v
= PA_CHANNEL_POSITION_MASK_TOP
;
1946 else if (pa_streq(m
, "all-no-lfe"))
1947 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
1948 else if (pa_streq(m
, "all"))
1949 v
= PA_CHANNEL_POSITION_MASK_ALL
;
1951 pa_channel_position_t p
;
1953 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
1956 v
= PA_CHANNEL_POSITION_MASK(p
);
1962 static int element_parse_override_map(
1963 const char *filename
,
1965 const char *section
,
1971 pa_alsa_path
*p
= userdata
;
1973 const char *state
= NULL
;
1977 if (!(e
= element_get(p
, section
, TRUE
))) {
1978 pa_log("[%s:%u] Override map makes no sense in '%s'", filename
, line
, section
);
1982 while ((n
= pa_split(rvalue
, ",", &state
))) {
1983 pa_channel_position_mask_t m
;
1988 if ((m
= parse_mask(n
)) == 0) {
1989 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename
, line
, n
, section
);
1995 if (pa_streq(lvalue
, "override-map.1"))
1996 e
->masks
[i
++][0] = m
;
1998 e
->masks
[i
++][1] = m
;
2000 /* Later on we might add override-map.3 and so on here ... */
2005 e
->override_map
= TRUE
;
2010 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
2011 snd_mixer_selem_id_t
*sid
;
2012 snd_mixer_elem_t
*me
;
2018 SELEM_INIT(sid
, e
->alsa_name
);
2019 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2020 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2024 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
2026 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
2027 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
2029 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
2032 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2035 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
2037 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
2038 pa_log_warn("Failed to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2044 int pa_alsa_setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
2051 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
2052 element_set_option(o
->element
, m
, o
->alsa_idx
);
2057 static int option_verify(pa_alsa_option
*o
) {
2058 static const struct description_map well_known_descriptions
[] = {
2059 { "input", N_("Input") },
2060 { "input-docking", N_("Docking Station Input") },
2061 { "input-docking-microphone", N_("Docking Station Microphone") },
2062 { "input-docking-linein", N_("Docking Station Line-In") },
2063 { "input-linein", N_("Line-In") },
2064 { "input-microphone", N_("Microphone") },
2065 { "input-microphone-front", N_("Front Microphone") },
2066 { "input-microphone-rear", N_("Rear Microphone") },
2067 { "input-microphone-external", N_("External Microphone") },
2068 { "input-microphone-internal", N_("Internal Microphone") },
2069 { "input-radio", N_("Radio") },
2070 { "input-video", N_("Video") },
2071 { "input-agc-on", N_("Automatic Gain Control") },
2072 { "input-agc-off", N_("No Automatic Gain Control") },
2073 { "input-boost-on", N_("Boost") },
2074 { "input-boost-off", N_("No Boost") },
2075 { "output-amplifier-on", N_("Amplifier") },
2076 { "output-amplifier-off", N_("No Amplifier") },
2077 { "output-bass-boost-on", N_("Bass Boost") },
2078 { "output-bass-boost-off", N_("No Bass Boost") },
2079 { "output-speaker", N_("Speaker") },
2080 { "output-headphones", N_("Headphones") }
2086 pa_log("No name set for option %s", o
->alsa_name
);
2090 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
2091 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
2092 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
2096 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
2097 !pa_streq(o
->alsa_name
, "on") &&
2098 !pa_streq(o
->alsa_name
, "off")) {
2099 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
2103 if (!o
->description
)
2104 o
->description
= pa_xstrdup(lookup_description(o
->name
,
2105 well_known_descriptions
,
2106 PA_ELEMENTSOF(well_known_descriptions
)));
2107 if (!o
->description
)
2108 o
->description
= pa_xstrdup(o
->name
);
2113 static int element_verify(pa_alsa_element
*e
) {
2118 // 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);
2119 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
2120 (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required_any
== e
->required_absent
) ||
2121 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) ||
2122 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
2123 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
2127 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
2128 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
2132 PA_LLIST_FOREACH(o
, e
->options
)
2133 if (option_verify(o
) < 0)
2139 static int path_verify(pa_alsa_path
*p
) {
2140 static const struct description_map well_known_descriptions
[] = {
2141 { "analog-input", N_("Analog Input") },
2142 { "analog-input-microphone", N_("Analog Microphone") },
2143 { "analog-input-microphone-front", N_("Front Microphone") },
2144 { "analog-input-microphone-rear", N_("Rear Microphone") },
2145 { "analog-input-microphone-dock", N_("Docking Station Microphone") },
2146 { "analog-input-microphone-internal", N_("Internal Microphone") },
2147 { "analog-input-linein", N_("Analog Line-In") },
2148 { "analog-input-radio", N_("Analog Radio") },
2149 { "analog-input-video", N_("Analog Video") },
2150 { "analog-output", N_("Analog Output") },
2151 { "analog-output-headphones", N_("Analog Headphones") },
2152 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
2153 { "analog-output-mono", N_("Analog Mono Output") },
2154 { "analog-output-speaker", N_("Analog Speakers") },
2155 { "iec958-stereo-output", N_("Digital Output (IEC958)") },
2156 { "iec958-passthrough-output", N_("Digital Passthrough (IEC958)") }
2163 PA_LLIST_FOREACH(e
, p
->elements
)
2164 if (element_verify(e
) < 0)
2167 if (!p
->description
)
2168 p
->description
= pa_xstrdup(lookup_description(p
->name
,
2169 well_known_descriptions
,
2170 PA_ELEMENTSOF(well_known_descriptions
)));
2172 if (!p
->description
)
2173 p
->description
= pa_xstrdup(p
->name
);
2178 pa_alsa_path
* pa_alsa_path_new(const char *fname
, pa_alsa_direction_t direction
) {
2184 pa_config_item items
[] = {
2186 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
2187 { "description", pa_config_parse_string
, NULL
, "General" },
2188 { "name", pa_config_parse_string
, NULL
, "General" },
2191 { "priority", option_parse_priority
, NULL
, NULL
},
2192 { "name", option_parse_name
, NULL
, NULL
},
2195 { "switch", element_parse_switch
, NULL
, NULL
},
2196 { "volume", element_parse_volume
, NULL
, NULL
},
2197 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
2198 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
2199 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
2200 /* ... later on we might add override-map.3 and so on here ... */
2201 { "required", element_parse_required
, NULL
, NULL
},
2202 { "required-any", element_parse_required
, NULL
, NULL
},
2203 { "required-absent", element_parse_required
, NULL
, NULL
},
2204 { "direction", element_parse_direction
, NULL
, NULL
},
2205 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2206 { "volume-limit", element_parse_volume_limit
, NULL
, NULL
},
2207 { NULL
, NULL
, NULL
, NULL
}
2212 p
= pa_xnew0(pa_alsa_path
, 1);
2213 n
= pa_path_get_filename(fname
);
2214 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2215 p
->direction
= direction
;
2217 items
[0].data
= &p
->priority
;
2218 items
[1].data
= &p
->description
;
2219 items
[2].data
= &p
->name
;
2221 fn
= pa_maybe_prefix_path(fname
,
2222 #if defined(__linux__) && !defined(__OPTIMIZE__)
2223 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/paths/" :
2227 r
= pa_config_parse(fn
, NULL
, items
, p
);
2233 if (path_verify(p
) < 0)
2239 pa_alsa_path_free(p
);
2243 pa_alsa_path
* pa_alsa_path_synthesize(const char*element
, pa_alsa_direction_t direction
) {
2249 p
= pa_xnew0(pa_alsa_path
, 1);
2250 p
->name
= pa_xstrdup(element
);
2251 p
->direction
= direction
;
2253 e
= pa_xnew0(pa_alsa_element
, 1);
2255 e
->alsa_name
= pa_xstrdup(element
);
2256 e
->direction
= direction
;
2257 e
->volume_limit
= -1;
2259 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2260 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2262 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2263 p
->last_element
= e
;
2267 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2268 pa_alsa_option
*o
, *n
;
2272 for (o
= e
->options
; o
; o
= n
) {
2275 if (o
->alsa_idx
< 0) {
2276 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2282 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2283 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2284 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2287 static void path_drop_unsupported(pa_alsa_path
*p
) {
2288 pa_alsa_element
*e
, *n
;
2292 for (e
= p
->elements
; e
; e
= n
) {
2295 if (!element_drop_unsupported(e
)) {
2296 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2302 static void path_make_options_unique(pa_alsa_path
*p
) {
2304 pa_alsa_option
*o
, *u
;
2306 PA_LLIST_FOREACH(e
, p
->elements
) {
2307 PA_LLIST_FOREACH(o
, e
->options
) {
2311 for (u
= o
->next
; u
; u
= u
->next
)
2312 if (pa_streq(u
->name
, o
->name
))
2318 m
= pa_xstrdup(o
->name
);
2320 /* OK, this name is not unique, hence let's rename */
2321 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2324 if (!pa_streq(u
->name
, m
))
2327 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2331 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2332 pa_xfree(u
->description
);
2333 u
->description
= nd
;
2343 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2346 for (; e
; e
= e
->next
)
2347 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2348 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2354 for (o
= e
->options
; o
; o
= o
->next
) {
2358 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2359 s
->options
= pa_idxset_copy(template->options
);
2360 s
->name
= pa_sprintf_malloc(_("%s+%s"), template->name
, o
->name
);
2362 (template->description
[0] && o
->description
[0])
2363 ? pa_sprintf_malloc(_("%s / %s"), template->description
, o
->description
)
2364 : (template->description
[0]
2365 ? pa_xstrdup(template->description
)
2366 : pa_xstrdup(o
->description
));
2368 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2370 s
= pa_xnew0(pa_alsa_setting
, 1);
2371 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2372 s
->name
= pa_xstrdup(o
->name
);
2373 s
->description
= pa_xstrdup(o
->description
);
2374 s
->priority
= o
->priority
;
2377 pa_idxset_put(s
->options
, o
, NULL
);
2379 if (element_create_settings(e
->next
, s
))
2380 /* This is not a leaf, so let's get rid of it */
2383 /* This is a leaf, so let's add it */
2384 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2386 e
->path
->last_setting
= s
;
2393 static void path_create_settings(pa_alsa_path
*p
) {
2396 element_create_settings(p
->elements
, NULL
);
2399 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2401 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2402 pa_channel_position_t t
;
2403 pa_channel_position_mask_t path_volume_channels
= 0;
2414 pa_log_debug("Probing path '%s'", p
->name
);
2416 PA_LLIST_FOREACH(e
, p
->elements
) {
2417 if (element_probe(e
, m
) < 0) {
2418 p
->supported
= FALSE
;
2419 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2422 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
);
2427 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2429 if (!p
->has_volume
) {
2430 p
->min_volume
= e
->min_volume
;
2431 p
->max_volume
= e
->max_volume
;
2435 if (!p
->has_volume
) {
2436 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2437 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2438 min_dB
[t
] = e
->min_dB
;
2439 max_dB
[t
] = e
->max_dB
;
2440 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2447 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2448 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2449 min_dB
[t
] += e
->min_dB
;
2450 max_dB
[t
] += e
->max_dB
;
2451 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2454 /* Hmm, there's another element before us
2455 * which cannot do dB volumes, so we we need
2456 * to 'neutralize' this slider */
2457 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2458 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2461 } else if (p
->has_volume
) {
2462 /* We can't use this volume, so let's ignore it */
2463 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2464 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2466 p
->has_volume
= TRUE
;
2469 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2473 if (p
->has_req_any
&& !p
->req_any_present
) {
2474 p
->supported
= FALSE
;
2475 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2479 path_drop_unsupported(p
);
2480 path_make_options_unique(p
);
2481 path_create_settings(p
);
2483 p
->supported
= TRUE
;
2486 p
->min_dB
= INFINITY
;
2487 p
->max_dB
= -INFINITY
;
2489 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2490 if (path_volume_channels
& PA_CHANNEL_POSITION_MASK(t
)) {
2491 if (p
->min_dB
> min_dB
[t
])
2492 p
->min_dB
= min_dB
[t
];
2494 if (p
->max_dB
< max_dB
[t
])
2495 p
->max_dB
= max_dB
[t
];
2502 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2505 pa_log_debug("Setting %s (%s) priority=%u",
2507 pa_strnull(s
->description
),
2511 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2514 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2516 pa_strnull(o
->name
),
2517 pa_strnull(o
->description
),
2522 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2526 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",
2536 (long long unsigned) e
->merged_mask
,
2538 pa_yes_no(e
->override_map
));
2540 PA_LLIST_FOREACH(o
, e
->options
)
2541 pa_alsa_option_dump(o
);
2544 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2549 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2550 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2552 pa_strnull(p
->description
),
2555 pa_yes_no(p
->probed
),
2556 pa_yes_no(p
->supported
),
2557 pa_yes_no(p
->has_mute
),
2558 pa_yes_no(p
->has_volume
),
2559 pa_yes_no(p
->has_dB
),
2560 p
->min_volume
, p
->max_volume
,
2561 p
->min_dB
, p
->max_dB
);
2563 PA_LLIST_FOREACH(e
, p
->elements
)
2564 pa_alsa_element_dump(e
);
2566 PA_LLIST_FOREACH(s
, p
->settings
)
2567 pa_alsa_setting_dump(s
);
2570 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2571 snd_mixer_selem_id_t
*sid
;
2572 snd_mixer_elem_t
*me
;
2578 SELEM_INIT(sid
, e
->alsa_name
);
2579 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2580 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2584 snd_mixer_elem_set_callback(me
, cb
);
2585 snd_mixer_elem_set_callback_private(me
, userdata
);
2588 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2595 PA_LLIST_FOREACH(e
, p
->elements
)
2596 element_set_callback(e
, m
, cb
, userdata
);
2599 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2606 PA_LLIST_FOREACH(p
, ps
->paths
)
2607 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2610 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
) {
2611 pa_alsa_path_set
*ps
;
2612 char **pn
= NULL
, **en
= NULL
, **ie
;
2613 pa_alsa_decibel_fix
*db_fix
;
2617 pa_assert(m
->profile_set
);
2618 pa_assert(m
->profile_set
->decibel_fixes
);
2619 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2621 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2624 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2625 ps
->direction
= direction
;
2627 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2628 pn
= m
->output_path_names
;
2629 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2630 pn
= m
->input_path_names
;
2635 for (in
= pn
; *in
; in
++) {
2637 pa_bool_t duplicate
= FALSE
;
2640 for (kn
= pn
; kn
!= in
; kn
++)
2641 if (pa_streq(*kn
, *in
)) {
2649 fn
= pa_sprintf_malloc("%s.conf", *in
);
2651 if ((p
= pa_alsa_path_new(fn
, direction
))) {
2653 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2663 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2664 en
= m
->output_element
;
2665 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2666 en
= m
->input_element
;
2669 pa_alsa_path_set_free(ps
);
2673 for (ie
= en
; *ie
; ie
++) {
2677 p
= pa_alsa_path_synthesize(*ie
, direction
);
2680 /* Mark all other passed elements for require-absent */
2681 for (je
= en
; *je
; je
++) {
2687 e
= pa_xnew0(pa_alsa_element
, 1);
2689 e
->alsa_name
= pa_xstrdup(*je
);
2690 e
->direction
= direction
;
2691 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2692 e
->volume_limit
= -1;
2694 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2695 p
->last_element
= e
;
2698 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2703 /* Assign decibel fixes to elements. */
2704 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2707 PA_LLIST_FOREACH(p
, ps
->paths
) {
2710 PA_LLIST_FOREACH(e
, p
->elements
) {
2711 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2712 /* The profile set that contains the dB fix may be freed
2713 * before the element, so we have to copy the dB fix
2715 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2716 e
->db_fix
->profile_set
= NULL
;
2717 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2718 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
2727 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2731 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2734 pa_yes_no(ps
->probed
));
2736 PA_LLIST_FOREACH(p
, ps
->paths
)
2737 pa_alsa_path_dump(p
);
2740 static void path_set_unify(pa_alsa_path_set
*ps
) {
2742 pa_bool_t has_dB
= TRUE
, has_volume
= TRUE
, has_mute
= TRUE
;
2745 /* We have issues dealing with paths that vary too wildly. That
2746 * means for now we have to have all paths support volume/mute/dB
2749 PA_LLIST_FOREACH(p
, ps
->paths
) {
2750 pa_assert(p
->probed
);
2754 else if (!p
->has_dB
)
2761 if (!has_volume
|| !has_dB
|| !has_mute
) {
2764 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2766 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2769 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2771 PA_LLIST_FOREACH(p
, ps
->paths
) {
2773 p
->has_volume
= FALSE
;
2778 p
->has_mute
= FALSE
;
2783 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
2784 pa_alsa_path
*p
, *q
;
2786 PA_LLIST_FOREACH(p
, ps
->paths
) {
2790 for (q
= p
->next
; q
; q
= q
->next
)
2791 if (pa_streq(q
->name
, p
->name
))
2797 m
= pa_xstrdup(p
->name
);
2799 /* OK, this name is not unique, hence let's rename */
2800 for (i
= 1, q
= p
; q
; q
= q
->next
) {
2803 if (!pa_streq(q
->name
, m
))
2806 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2810 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
2811 pa_xfree(q
->description
);
2812 q
->description
= nd
;
2821 void pa_alsa_path_set_probe(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2822 pa_alsa_path
*p
, *n
;
2829 for (p
= ps
->paths
; p
; p
= n
) {
2832 if (pa_alsa_path_probe(p
, m
, ignore_dB
) < 0) {
2833 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
2834 pa_alsa_path_free(p
);
2839 path_set_make_paths_unique(ps
);
2843 static void mapping_free(pa_alsa_mapping
*m
) {
2847 pa_xfree(m
->description
);
2849 pa_xstrfreev(m
->device_strings
);
2850 pa_xstrfreev(m
->input_path_names
);
2851 pa_xstrfreev(m
->output_path_names
);
2852 pa_xstrfreev(m
->input_element
);
2853 pa_xstrfreev(m
->output_element
);
2855 pa_assert(!m
->input_pcm
);
2856 pa_assert(!m
->output_pcm
);
2861 static void profile_free(pa_alsa_profile
*p
) {
2865 pa_xfree(p
->description
);
2867 pa_xstrfreev(p
->input_mapping_names
);
2868 pa_xstrfreev(p
->output_mapping_names
);
2870 if (p
->input_mappings
)
2871 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
2873 if (p
->output_mappings
)
2874 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
2879 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
2885 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
2888 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
2894 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
2897 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
2900 if (ps
->decibel_fixes
) {
2901 pa_alsa_decibel_fix
*db_fix
;
2903 while ((db_fix
= pa_hashmap_steal_first(ps
->decibel_fixes
)))
2904 decibel_fix_free(db_fix
);
2906 pa_hashmap_free(ps
->decibel_fixes
, NULL
, NULL
);
2912 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
2915 if (!pa_startswith(name
, "Mapping "))
2920 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
2923 m
= pa_xnew0(pa_alsa_mapping
, 1);
2924 m
->profile_set
= ps
;
2925 m
->name
= pa_xstrdup(name
);
2926 pa_channel_map_init(&m
->channel_map
);
2928 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
2933 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
2936 if (!pa_startswith(name
, "Profile "))
2941 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
2944 p
= pa_xnew0(pa_alsa_profile
, 1);
2945 p
->profile_set
= ps
;
2946 p
->name
= pa_xstrdup(name
);
2948 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
2953 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
2954 pa_alsa_decibel_fix
*db_fix
;
2956 if (!pa_startswith(name
, "DecibelFix "))
2961 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
2964 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
2965 db_fix
->profile_set
= ps
;
2966 db_fix
->name
= pa_xstrdup(name
);
2968 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
2973 static int mapping_parse_device_strings(
2974 const char *filename
,
2976 const char *section
,
2982 pa_alsa_profile_set
*ps
= userdata
;
2987 if (!(m
= mapping_get(ps
, section
))) {
2988 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2992 pa_xstrfreev(m
->device_strings
);
2993 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
2994 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
3001 static int mapping_parse_channel_map(
3002 const char *filename
,
3004 const char *section
,
3010 pa_alsa_profile_set
*ps
= userdata
;
3015 if (!(m
= mapping_get(ps
, section
))) {
3016 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3020 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
3021 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
3028 static int mapping_parse_paths(
3029 const char *filename
,
3031 const char *section
,
3037 pa_alsa_profile_set
*ps
= userdata
;
3042 if (!(m
= mapping_get(ps
, section
))) {
3043 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3047 if (pa_streq(lvalue
, "paths-input")) {
3048 pa_xstrfreev(m
->input_path_names
);
3049 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
3051 pa_xstrfreev(m
->output_path_names
);
3052 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
3058 static int mapping_parse_element(
3059 const char *filename
,
3061 const char *section
,
3067 pa_alsa_profile_set
*ps
= userdata
;
3072 if (!(m
= mapping_get(ps
, section
))) {
3073 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3077 if (pa_streq(lvalue
, "element-input")) {
3078 pa_xstrfreev(m
->input_element
);
3079 m
->input_element
= pa_split_spaces_strv(rvalue
);
3081 pa_xstrfreev(m
->output_element
);
3082 m
->output_element
= pa_split_spaces_strv(rvalue
);
3088 static int mapping_parse_direction(
3089 const char *filename
,
3091 const char *section
,
3097 pa_alsa_profile_set
*ps
= userdata
;
3102 if (!(m
= mapping_get(ps
, section
))) {
3103 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3107 if (pa_streq(rvalue
, "input"))
3108 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3109 else if (pa_streq(rvalue
, "output"))
3110 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3111 else if (pa_streq(rvalue
, "any"))
3112 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3114 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
3121 static int mapping_parse_description(
3122 const char *filename
,
3124 const char *section
,
3130 pa_alsa_profile_set
*ps
= userdata
;
3136 if ((m
= mapping_get(ps
, section
))) {
3137 pa_xfree(m
->description
);
3138 m
->description
= pa_xstrdup(rvalue
);
3139 } else if ((p
= profile_get(ps
, section
))) {
3140 pa_xfree(p
->description
);
3141 p
->description
= pa_xstrdup(rvalue
);
3143 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3150 static int mapping_parse_priority(
3151 const char *filename
,
3153 const char *section
,
3159 pa_alsa_profile_set
*ps
= userdata
;
3166 if (pa_atou(rvalue
, &prio
) < 0) {
3167 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
3171 if ((m
= mapping_get(ps
, section
)))
3173 else if ((p
= profile_get(ps
, section
)))
3176 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3183 static int profile_parse_mappings(
3184 const char *filename
,
3186 const char *section
,
3192 pa_alsa_profile_set
*ps
= userdata
;
3197 if (!(p
= profile_get(ps
, section
))) {
3198 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3202 if (pa_streq(lvalue
, "input-mappings")) {
3203 pa_xstrfreev(p
->input_mapping_names
);
3204 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
3206 pa_xstrfreev(p
->output_mapping_names
);
3207 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
3213 static int profile_parse_skip_probe(
3214 const char *filename
,
3216 const char *section
,
3222 pa_alsa_profile_set
*ps
= userdata
;
3228 if (!(p
= profile_get(ps
, section
))) {
3229 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3233 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
3234 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
3243 static int decibel_fix_parse_db_values(
3244 const char *filename
,
3246 const char *section
,
3252 pa_alsa_profile_set
*ps
= userdata
;
3253 pa_alsa_decibel_fix
*db_fix
;
3257 unsigned n
= 8; /* Current size of the db_values table. */
3258 unsigned min_step
= 0;
3259 unsigned max_step
= 0;
3260 unsigned i
= 0; /* Index to the items table. */
3261 unsigned prev_step
= 0;
3264 pa_assert(filename
);
3270 if (!(db_fix
= decibel_fix_get(ps
, section
))) {
3271 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3275 if (!(items
= pa_split_spaces_strv(rvalue
))) {
3276 pa_log("[%s:%u] Value missing", pa_strnull(filename
), line
);
3280 db_values
= pa_xnew(long, n
);
3282 while ((item
= items
[i
++])) {
3283 char *s
= item
; /* Step value string. */
3284 char *d
= item
; /* dB value string. */
3288 /* Move d forward until it points to a colon or to the end of the item. */
3289 for (; *d
&& *d
!= ':'; ++d
);
3292 /* item started with colon. */
3293 pa_log("[%s:%u] No step value found in %s", filename
, line
, item
);
3297 if (!*d
|| !*(d
+ 1)) {
3298 /* No colon found, or it was the last character in item. */
3299 pa_log("[%s:%u] No dB value found in %s", filename
, line
, item
);
3303 /* pa_atou() needs a null-terminating string. Let's replace the colon
3304 * with a zero byte. */
3307 if (pa_atou(s
, &step
) < 0) {
3308 pa_log("[%s:%u] Invalid step value: %s", filename
, line
, s
);
3312 if (pa_atod(d
, &db
) < 0) {
3313 pa_log("[%s:%u] Invalid dB value: %s", filename
, line
, d
);
3317 if (step
<= prev_step
&& i
!= 1) {
3318 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename
, line
, step
, prev_step
);
3322 if (db
< prev_db
&& i
!= 1) {
3323 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename
, line
, db
, prev_db
);
3329 db_values
[0] = (long) (db
* 100.0);
3333 /* Interpolate linearly. */
3334 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3336 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3338 /* Reallocate the db_values table if it's about to overflow. */
3339 if (prev_step
+ 1 - min_step
== n
) {
3341 db_values
= pa_xrenew(long, db_values
, n
);
3344 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3351 db_fix
->min_step
= min_step
;
3352 db_fix
->max_step
= max_step
;
3353 pa_xfree(db_fix
->db_values
);
3354 db_fix
->db_values
= db_values
;
3356 pa_xstrfreev(items
);
3361 pa_xstrfreev(items
);
3362 pa_xfree(db_values
);
3367 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3369 static const struct description_map well_known_descriptions
[] = {
3370 { "analog-mono", N_("Analog Mono") },
3371 { "analog-stereo", N_("Analog Stereo") },
3372 { "analog-surround-21", N_("Analog Surround 2.1") },
3373 { "analog-surround-30", N_("Analog Surround 3.0") },
3374 { "analog-surround-31", N_("Analog Surround 3.1") },
3375 { "analog-surround-40", N_("Analog Surround 4.0") },
3376 { "analog-surround-41", N_("Analog Surround 4.1") },
3377 { "analog-surround-50", N_("Analog Surround 5.0") },
3378 { "analog-surround-51", N_("Analog Surround 5.1") },
3379 { "analog-surround-61", N_("Analog Surround 6.0") },
3380 { "analog-surround-61", N_("Analog Surround 6.1") },
3381 { "analog-surround-70", N_("Analog Surround 7.0") },
3382 { "analog-surround-71", N_("Analog Surround 7.1") },
3383 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3384 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3385 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3386 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3387 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3392 if (!pa_channel_map_valid(&m
->channel_map
)) {
3393 pa_log("Mapping %s is missing channel map.", m
->name
);
3397 if (!m
->device_strings
) {
3398 pa_log("Mapping %s is missing device strings.", m
->name
);
3402 if ((m
->input_path_names
&& m
->input_element
) ||
3403 (m
->output_path_names
&& m
->output_element
)) {
3404 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3408 if (!m
->description
)
3409 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3410 well_known_descriptions
,
3411 PA_ELEMENTSOF(well_known_descriptions
)));
3413 if (!m
->description
)
3414 m
->description
= pa_xstrdup(m
->name
);
3417 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3419 else if (m
->channel_map
.channels
== bonus
->channels
)
3426 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3427 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3431 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3433 pa_strnull(m
->description
),
3435 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3436 pa_yes_no(m
->supported
),
3440 static void profile_set_add_auto_pair(
3441 pa_alsa_profile_set
*ps
,
3442 pa_alsa_mapping
*m
, /* output */
3443 pa_alsa_mapping
*n
/* input */) {
3451 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3454 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3458 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3460 name
= pa_sprintf_malloc("output:%s", m
->name
);
3462 name
= pa_sprintf_malloc("input:%s", n
->name
);
3464 if (pa_hashmap_get(ps
->profiles
, name
)) {
3469 p
= pa_xnew0(pa_alsa_profile
, 1);
3470 p
->profile_set
= ps
;
3474 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3475 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3476 p
->priority
+= m
->priority
* 100;
3480 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3481 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3482 p
->priority
+= n
->priority
;
3485 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3488 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3489 pa_alsa_mapping
*m
, *n
;
3490 void *m_state
, *n_state
;
3494 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3495 profile_set_add_auto_pair(ps
, m
, NULL
);
3497 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3498 profile_set_add_auto_pair(ps
, m
, n
);
3501 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3502 profile_set_add_auto_pair(ps
, NULL
, n
);
3505 static int profile_verify(pa_alsa_profile
*p
) {
3507 static const struct description_map well_known_descriptions
[] = {
3508 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3509 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3510 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3511 { "off", N_("Off") }
3516 /* Replace the output mapping names by the actual mappings */
3517 if (p
->output_mapping_names
) {
3520 pa_assert(!p
->output_mappings
);
3521 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3523 for (name
= p
->output_mapping_names
; *name
; name
++) {
3526 pa_bool_t duplicate
= FALSE
;
3528 for (in
= name
+ 1; *in
; in
++)
3529 if (pa_streq(*name
, *in
)) {
3537 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3538 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3542 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3548 pa_xstrfreev(p
->output_mapping_names
);
3549 p
->output_mapping_names
= NULL
;
3552 /* Replace the input mapping names by the actual mappings */
3553 if (p
->input_mapping_names
) {
3556 pa_assert(!p
->input_mappings
);
3557 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3559 for (name
= p
->input_mapping_names
; *name
; name
++) {
3562 pa_bool_t duplicate
= FALSE
;
3564 for (in
= name
+ 1; *in
; in
++)
3565 if (pa_streq(*name
, *in
)) {
3573 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3574 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3578 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3584 pa_xstrfreev(p
->input_mapping_names
);
3585 p
->input_mapping_names
= NULL
;
3588 if (!p
->input_mappings
&& !p
->output_mappings
) {
3589 pa_log("Profile '%s' lacks mappings.", p
->name
);
3593 if (!p
->description
)
3594 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3595 well_known_descriptions
,
3596 PA_ELEMENTSOF(well_known_descriptions
)));
3598 if (!p
->description
) {
3603 sb
= pa_strbuf_new();
3605 if (p
->output_mappings
)
3606 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3607 if (!pa_strbuf_isempty(sb
))
3608 pa_strbuf_puts(sb
, " + ");
3610 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
3613 if (p
->input_mappings
)
3614 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3615 if (!pa_strbuf_isempty(sb
))
3616 pa_strbuf_puts(sb
, " + ");
3618 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
3621 p
->description
= pa_strbuf_tostring_free(sb
);
3627 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
3632 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3634 pa_strnull(p
->description
),
3636 pa_yes_no(p
->supported
),
3637 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
3638 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
3640 if (p
->input_mappings
)
3641 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
3642 pa_log_debug("Input %s", m
->name
);
3644 if (p
->output_mappings
)
3645 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
3646 pa_log_debug("Output %s", m
->name
);
3649 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
3652 /* Check that the dB mapping has been configured. Since "db-values" is
3653 * currently the only option in the DecibelFix section, and decibel fix
3654 * objects don't get created if a DecibelFix section is empty, this is
3655 * actually a redundant check. Having this may prevent future bugs,
3657 if (!db_fix
->db_values
) {
3658 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
3665 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
3666 char *db_values
= NULL
;
3670 if (db_fix
->db_values
) {
3673 long max_i
= db_fix
->max_step
- db_fix
->min_step
;
3675 buf
= pa_strbuf_new();
3676 pa_strbuf_printf(buf
, "[%li]:%0.2f", db_fix
->min_step
, db_fix
->db_values
[0] / 100.0);
3678 for (i
= 1; i
<= max_i
; ++i
)
3679 pa_strbuf_printf(buf
, " [%li]:%0.2f", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
3681 db_values
= pa_strbuf_tostring_free(buf
);
3684 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
3685 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
3687 pa_xfree(db_values
);
3690 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
3691 pa_alsa_profile_set
*ps
;
3694 pa_alsa_decibel_fix
*db_fix
;
3699 static pa_config_item items
[] = {
3701 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
3704 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
3705 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
3706 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
3707 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
3708 { "element-input", mapping_parse_element
, NULL
, NULL
},
3709 { "element-output", mapping_parse_element
, NULL
, NULL
},
3710 { "direction", mapping_parse_direction
, NULL
, NULL
},
3712 /* Shared by [Mapping ...] and [Profile ...] */
3713 { "description", mapping_parse_description
, NULL
, NULL
},
3714 { "priority", mapping_parse_priority
, NULL
, NULL
},
3717 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
3718 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
3719 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
3721 /* [DecibelFix ...] */
3722 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
3723 { NULL
, NULL
, NULL
, NULL
}
3726 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
3727 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3728 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3729 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3731 items
[0].data
= &ps
->auto_profiles
;
3734 fname
= "default.conf";
3736 fn
= pa_maybe_prefix_path(fname
,
3737 #if defined(__linux__) && !defined(__OPTIMIZE__)
3738 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
3740 PA_ALSA_PROFILE_SETS_DIR
);
3742 r
= pa_config_parse(fn
, NULL
, items
, ps
);
3748 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3749 if (mapping_verify(m
, bonus
) < 0)
3752 if (ps
->auto_profiles
)
3753 profile_set_add_auto(ps
);
3755 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3756 if (profile_verify(p
) < 0)
3759 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
3760 if (decibel_fix_verify(db_fix
) < 0)
3766 pa_alsa_profile_set_free(ps
);
3770 void pa_alsa_profile_set_probe(
3771 pa_alsa_profile_set
*ps
,
3773 const pa_sample_spec
*ss
,
3774 unsigned default_n_fragments
,
3775 unsigned default_fragment_size_msec
) {
3778 pa_alsa_profile
*p
, *last
= NULL
;
3788 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
3789 pa_sample_spec try_ss
;
3790 pa_channel_map try_map
;
3791 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
3794 /* Is this already marked that it is supported? (i.e. from the config file) */
3798 pa_log_debug("Looking at profile %s", p
->name
);
3800 /* Close PCMs from the last iteration we don't need anymore */
3801 if (last
&& last
->output_mappings
)
3802 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
) {
3807 if (last
->supported
)
3810 if (!p
->output_mappings
|| !pa_idxset_get_by_data(p
->output_mappings
, m
, NULL
)) {
3811 snd_pcm_close(m
->output_pcm
);
3812 m
->output_pcm
= NULL
;
3816 if (last
&& last
->input_mappings
)
3817 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
) {
3822 if (last
->supported
)
3825 if (!p
->input_mappings
|| !pa_idxset_get_by_data(p
->input_mappings
, m
, NULL
)) {
3826 snd_pcm_close(m
->input_pcm
);
3827 m
->input_pcm
= NULL
;
3831 p
->supported
= TRUE
;
3833 /* Check if we can open all new ones */
3834 if (p
->output_mappings
)
3835 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3840 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
3841 try_map
= m
->channel_map
;
3843 try_ss
.channels
= try_map
.channels
;
3846 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
3847 pa_frame_size(&try_ss
);
3848 try_buffer_size
= default_n_fragments
* try_period_size
;
3850 if (!(m
->output_pcm
= pa_alsa_open_by_template(
3855 SND_PCM_STREAM_PLAYBACK
,
3856 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3858 p
->supported
= FALSE
;
3863 if (p
->input_mappings
&& p
->supported
)
3864 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3869 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
3870 try_map
= m
->channel_map
;
3872 try_ss
.channels
= try_map
.channels
;
3875 pa_usec_to_bytes(default_fragment_size_msec
*PA_USEC_PER_MSEC
, &try_ss
) /
3876 pa_frame_size(&try_ss
);
3877 try_buffer_size
= default_n_fragments
* try_period_size
;
3879 if (!(m
->input_pcm
= pa_alsa_open_by_template(
3884 SND_PCM_STREAM_CAPTURE
,
3885 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3887 p
->supported
= FALSE
;
3895 pa_log_debug("Profile %s supported.", p
->name
);
3902 if (last
->output_mappings
)
3903 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
)
3904 if (m
->output_pcm
) {
3906 if (last
->supported
)
3909 snd_pcm_close(m
->output_pcm
);
3910 m
->output_pcm
= NULL
;
3913 if (last
->input_mappings
)
3914 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
)
3917 if (last
->supported
)
3920 snd_pcm_close(m
->input_pcm
);
3921 m
->input_pcm
= NULL
;
3925 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3926 if (!p
->supported
) {
3927 pa_hashmap_remove(ps
->profiles
, p
->name
);
3931 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3932 if (m
->supported
<= 0) {
3933 pa_hashmap_remove(ps
->mappings
, m
->name
);
3940 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
3943 pa_alsa_decibel_fix
*db_fix
;
3948 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
3951 pa_yes_no(ps
->auto_profiles
),
3952 pa_yes_no(ps
->probed
),
3953 pa_hashmap_size(ps
->mappings
),
3954 pa_hashmap_size(ps
->profiles
),
3955 pa_hashmap_size(ps
->decibel_fixes
));
3957 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3958 pa_alsa_mapping_dump(m
);
3960 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3961 pa_alsa_profile_dump(p
);
3963 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
3964 pa_alsa_decibel_fix_dump(db_fix
);
3967 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
) {
3974 /* if there is no path, we don't want a port list */
3978 if (!ps
->paths
->next
){
3981 /* If there is only one path, but no or only one setting, then
3982 * we want a port list either */
3983 if (!ps
->paths
->settings
|| !ps
->paths
->settings
->next
)
3986 /* Ok, there is only one path, however with multiple settings,
3987 * so let's create a port for each setting */
3988 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3990 PA_LLIST_FOREACH(s
, ps
->paths
->settings
) {
3991 pa_device_port
*port
;
3992 pa_alsa_port_data
*data
;
3994 port
= pa_device_port_new(s
->name
, s
->description
, sizeof(pa_alsa_port_data
));
3995 port
->priority
= s
->priority
;
3997 data
= PA_DEVICE_PORT_DATA(port
);
3998 data
->path
= ps
->paths
;
4001 pa_hashmap_put(*p
, port
->name
, port
);
4006 /* We have multiple paths, so let's create a port for each
4007 * one, and each of each settings */
4008 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4010 PA_LLIST_FOREACH(path
, ps
->paths
) {
4012 if (!path
->settings
|| !path
->settings
->next
) {
4013 pa_device_port
*port
;
4014 pa_alsa_port_data
*data
;
4016 /* If there is no or just one setting we only need a
4019 port
= pa_device_port_new(path
->name
, path
->description
, sizeof(pa_alsa_port_data
));
4020 port
->priority
= path
->priority
* 100;
4023 data
= PA_DEVICE_PORT_DATA(port
);
4025 data
->setting
= path
->settings
;
4027 pa_hashmap_put(*p
, port
->name
, port
);
4031 PA_LLIST_FOREACH(s
, path
->settings
) {
4032 pa_device_port
*port
;
4033 pa_alsa_port_data
*data
;
4036 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4038 if (s
->description
[0])
4039 d
= pa_sprintf_malloc(_("%s / %s"), path
->description
, s
->description
);
4041 d
= pa_xstrdup(path
->description
);
4043 port
= pa_device_port_new(n
, d
, sizeof(pa_alsa_port_data
));
4044 port
->priority
= path
->priority
* 100 + s
->priority
;
4049 data
= PA_DEVICE_PORT_DATA(port
);
4053 pa_hashmap_put(*p
, port
->name
, port
);
4059 pa_log_debug("Added %u ports", pa_hashmap_size(*p
));