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
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
899 /* If we call set_playback_volume() without checking first
900 * if the channel is available, ALSA behaves very
901 * strangely and doesn't fail the call */
902 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
905 r
= snd_mixer_selem_set_playback_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
907 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
913 if ((r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, rounding
)) >= 0)
914 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
917 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
918 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value
);
924 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
927 r
= snd_mixer_selem_set_capture_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
929 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
935 if ((r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, rounding
)) >= 0)
936 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
939 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
940 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value
);
950 #ifdef HAVE_VALGRIND_MEMCHECK_H
951 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
954 f
= from_alsa_dB(value
);
959 value
= to_alsa_volume(f
, e
->min_volume
, e
->max_volume
);
961 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
962 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
963 if ((r
= snd_mixer_selem_set_playback_volume(me
, c
, value
)) >= 0)
964 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
968 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
969 if ((r
= snd_mixer_selem_set_capture_volume(me
, c
, value
)) >= 0)
970 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
978 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
981 for (k
= 0; k
< cm
->channels
; k
++)
982 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
983 if (rv
.values
[k
] < f
)
986 mask
|= e
->masks
[c
][e
->n_channels
-1];
989 for (k
= 0; k
< cm
->channels
; k
++)
990 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
991 rv
.values
[k
] = PA_VOLUME_NORM
;
997 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
) {
1006 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
1011 rv
= *v
; /* Remaining adjustment */
1012 pa_cvolume_reset(v
, cm
->channels
); /* Adjustment done */
1014 PA_LLIST_FOREACH(e
, p
->elements
) {
1017 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
1020 pa_assert(!p
->has_dB
|| e
->has_dB
);
1023 if (element_set_volume(e
, m
, cm
, &ev
, write_to_hw
) < 0)
1031 pa_sw_cvolume_multiply(v
, v
, &ev
);
1032 pa_sw_cvolume_divide(&rv
, &rv
, &ev
);
1038 static int element_set_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t b
) {
1039 snd_mixer_elem_t
*me
;
1040 snd_mixer_selem_id_t
*sid
;
1046 SELEM_INIT(sid
, e
->alsa_name
);
1047 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1048 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1052 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1053 r
= snd_mixer_selem_set_playback_switch_all(me
, b
);
1055 r
= snd_mixer_selem_set_capture_switch_all(me
, b
);
1058 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1063 int pa_alsa_path_set_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t muted
) {
1072 PA_LLIST_FOREACH(e
, p
->elements
) {
1074 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
1077 if (element_set_switch(e
, m
, !muted
) < 0)
1084 static int element_mute_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1085 snd_mixer_elem_t
*me
;
1086 snd_mixer_selem_id_t
*sid
;
1092 SELEM_INIT(sid
, e
->alsa_name
);
1093 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1094 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1098 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1099 r
= snd_mixer_selem_set_playback_volume_all(me
, e
->min_volume
);
1101 r
= snd_mixer_selem_set_capture_volume_all(me
, e
->min_volume
);
1104 pa_log_warn("Failed to set volume to muted of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1109 /* The volume to 0dB */
1110 static int element_zero_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1111 snd_mixer_elem_t
*me
;
1112 snd_mixer_selem_id_t
*sid
;
1118 SELEM_INIT(sid
, e
->alsa_name
);
1119 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1120 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1124 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1128 r
= snd_mixer_selem_set_playback_volume_all(me
, decibel_fix_get_step(e
->db_fix
, &value
, +1));
1130 r
= snd_mixer_selem_set_playback_dB_all(me
, 0, +1);
1135 r
= snd_mixer_selem_set_capture_volume_all(me
, decibel_fix_get_step(e
->db_fix
, &value
, +1));
1137 r
= snd_mixer_selem_set_capture_dB_all(me
, 0, +1);
1140 pa_log_warn("Failed to set volume to 0dB of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1145 int pa_alsa_path_select(pa_alsa_path
*p
, snd_mixer_t
*m
) {
1152 pa_log_debug("Activating path %s", p
->name
);
1153 pa_alsa_path_dump(p
);
1155 PA_LLIST_FOREACH(e
, p
->elements
) {
1157 switch (e
->switch_use
) {
1158 case PA_ALSA_SWITCH_OFF
:
1159 r
= element_set_switch(e
, m
, FALSE
);
1162 case PA_ALSA_SWITCH_ON
:
1163 r
= element_set_switch(e
, m
, TRUE
);
1166 case PA_ALSA_SWITCH_MUTE
:
1167 case PA_ALSA_SWITCH_IGNORE
:
1168 case PA_ALSA_SWITCH_SELECT
:
1176 switch (e
->volume_use
) {
1177 case PA_ALSA_VOLUME_OFF
:
1178 r
= element_mute_volume(e
, m
);
1181 case PA_ALSA_VOLUME_ZERO
:
1182 r
= element_zero_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
;
1288 SELEM_INIT(sid
, e
->alsa_name
);
1290 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1292 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1295 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1296 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1297 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1302 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1303 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1305 if (!snd_mixer_selem_has_playback_switch(me
)) {
1306 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1307 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1309 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1314 if (!snd_mixer_selem_has_capture_switch(me
)) {
1315 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1316 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1318 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1322 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1323 e
->direction_try_other
= FALSE
;
1326 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1328 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1330 if (!snd_mixer_selem_has_playback_volume(me
)) {
1331 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1332 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1334 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1339 if (!snd_mixer_selem_has_capture_volume(me
)) {
1340 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1341 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1343 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1347 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1348 long min_dB
= 0, max_dB
= 0;
1351 e
->direction_try_other
= FALSE
;
1353 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1354 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1356 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1359 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1363 if (e
->min_volume
>= e
->max_volume
) {
1364 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
);
1365 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1369 pa_channel_position_t p
;
1372 ((e
->min_volume
> e
->db_fix
->min_step
) ||
1373 (e
->max_volume
< e
->db_fix
->max_step
))) {
1374 pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1375 "real hardware range (%li-%li). Disabling the decibel fix.", e
->alsa_name
,
1376 e
->db_fix
->min_step
, e
->db_fix
->max_step
,
1377 e
->min_volume
, e
->max_volume
);
1379 decibel_fix_free(e
->db_fix
);
1385 e
->min_volume
= e
->db_fix
->min_step
;
1386 e
->max_volume
= e
->db_fix
->max_step
;
1387 min_dB
= e
->db_fix
->db_values
[0];
1388 max_dB
= e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
];
1389 } else if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1390 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1392 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1395 #ifdef HAVE_VALGRIND_MEMCHECK_H
1396 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1397 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1400 e
->min_dB
= ((double) min_dB
) / 100.0;
1401 e
->max_dB
= ((double) max_dB
) / 100.0;
1403 if (min_dB
>= max_dB
) {
1404 pa_assert(!e
->db_fix
);
1405 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
);
1410 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1411 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1413 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1418 if (!e
->override_map
) {
1419 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++)
1420 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1421 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1424 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1427 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1429 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1432 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1433 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1435 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1438 if (e
->n_channels
<= 0) {
1439 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1443 if (!e
->override_map
) {
1444 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1445 pa_bool_t has_channel
;
1447 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1450 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1451 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1453 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1455 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1460 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++)
1461 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1468 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1471 PA_LLIST_FOREACH(o
, e
->options
)
1472 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1473 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1477 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1478 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1482 PA_LLIST_FOREACH(o
, e
->options
) {
1485 for (i
= 0; i
< n
; i
++) {
1488 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1491 if (!pa_streq(buf
, o
->alsa_name
))
1499 if (check_required(e
, me
) < 0)
1505 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, pa_bool_t prefixed
) {
1512 if (!pa_startswith(section
, "Element "))
1518 /* This is not an element section, but an enum section? */
1519 if (strchr(section
, ':'))
1522 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1523 return p
->last_element
;
1525 PA_LLIST_FOREACH(e
, p
->elements
)
1526 if (pa_streq(e
->alsa_name
, section
))
1529 e
= pa_xnew0(pa_alsa_element
, 1);
1531 e
->alsa_name
= pa_xstrdup(section
);
1532 e
->direction
= p
->direction
;
1534 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1537 p
->last_element
= e
;
1541 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1547 if (!pa_startswith(section
, "Option "))
1552 /* This is not an enum section, but an element section? */
1553 if (!(on
= strchr(section
, ':')))
1556 en
= pa_xstrndup(section
, on
- section
);
1559 if (p
->last_option
&&
1560 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1561 pa_streq(p
->last_option
->alsa_name
, on
)) {
1563 return p
->last_option
;
1566 pa_assert_se(e
= element_get(p
, en
, FALSE
));
1569 PA_LLIST_FOREACH(o
, e
->options
)
1570 if (pa_streq(o
->alsa_name
, on
))
1573 o
= pa_xnew0(pa_alsa_option
, 1);
1575 o
->alsa_name
= pa_xstrdup(on
);
1578 if (p
->last_option
&& p
->last_option
->element
== e
)
1579 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1581 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1588 static int element_parse_switch(
1589 const char *filename
,
1591 const char *section
,
1597 pa_alsa_path
*p
= userdata
;
1602 if (!(e
= element_get(p
, section
, TRUE
))) {
1603 pa_log("[%s:%u] Switch makes no sense in '%s'", filename
, line
, section
);
1607 if (pa_streq(rvalue
, "ignore"))
1608 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1609 else if (pa_streq(rvalue
, "mute"))
1610 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1611 else if (pa_streq(rvalue
, "off"))
1612 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1613 else if (pa_streq(rvalue
, "on"))
1614 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1615 else if (pa_streq(rvalue
, "select"))
1616 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1618 pa_log("[%s:%u] Switch invalid of '%s'", filename
, line
, section
);
1625 static int element_parse_volume(
1626 const char *filename
,
1628 const char *section
,
1634 pa_alsa_path
*p
= userdata
;
1639 if (!(e
= element_get(p
, section
, TRUE
))) {
1640 pa_log("[%s:%u] Volume makes no sense in '%s'", filename
, line
, section
);
1644 if (pa_streq(rvalue
, "ignore"))
1645 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1646 else if (pa_streq(rvalue
, "merge"))
1647 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1648 else if (pa_streq(rvalue
, "off"))
1649 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1650 else if (pa_streq(rvalue
, "zero"))
1651 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1653 pa_log("[%s:%u] Volume invalid of '%s'", filename
, line
, section
);
1660 static int element_parse_enumeration(
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] Enumeration makes no sense in '%s'", filename
, line
, section
);
1679 if (pa_streq(rvalue
, "ignore"))
1680 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1681 else if (pa_streq(rvalue
, "select"))
1682 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1684 pa_log("[%s:%u] Enumeration invalid of '%s'", filename
, line
, section
);
1691 static int option_parse_priority(
1692 const char *filename
,
1694 const char *section
,
1700 pa_alsa_path
*p
= userdata
;
1706 if (!(o
= option_get(p
, section
))) {
1707 pa_log("[%s:%u] Priority makes no sense in '%s'", filename
, line
, section
);
1711 if (pa_atou(rvalue
, &prio
) < 0) {
1712 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
1720 static int option_parse_name(
1721 const char *filename
,
1723 const char *section
,
1729 pa_alsa_path
*p
= userdata
;
1734 if (!(o
= option_get(p
, section
))) {
1735 pa_log("[%s:%u] Name makes no sense in '%s'", filename
, line
, section
);
1740 o
->name
= pa_xstrdup(rvalue
);
1745 static int element_parse_required(
1746 const char *filename
,
1748 const char *section
,
1754 pa_alsa_path
*p
= userdata
;
1757 pa_alsa_required_t req
;
1761 e
= element_get(p
, section
, TRUE
);
1762 o
= option_get(p
, section
);
1764 pa_log("[%s:%u] Required makes no sense in '%s'", filename
, line
, section
);
1768 if (pa_streq(rvalue
, "ignore"))
1769 req
= PA_ALSA_REQUIRED_IGNORE
;
1770 else if (pa_streq(rvalue
, "switch") && e
)
1771 req
= PA_ALSA_REQUIRED_SWITCH
;
1772 else if (pa_streq(rvalue
, "volume") && e
)
1773 req
= PA_ALSA_REQUIRED_VOLUME
;
1774 else if (pa_streq(rvalue
, "enumeration"))
1775 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1776 else if (pa_streq(rvalue
, "any"))
1777 req
= PA_ALSA_REQUIRED_ANY
;
1779 pa_log("[%s:%u] Required invalid of '%s'", filename
, line
, section
);
1783 if (pa_streq(lvalue
, "required-absent")) {
1785 e
->required_absent
= req
;
1787 o
->required_absent
= req
;
1789 else if (pa_streq(lvalue
, "required-any")) {
1791 e
->required_any
= req
;
1792 e
->path
->has_req_any
= TRUE
;
1795 o
->required_any
= req
;
1796 o
->element
->path
->has_req_any
= TRUE
;
1809 static int element_parse_direction(
1810 const char *filename
,
1812 const char *section
,
1818 pa_alsa_path
*p
= userdata
;
1823 if (!(e
= element_get(p
, section
, TRUE
))) {
1824 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1828 if (pa_streq(rvalue
, "playback"))
1829 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1830 else if (pa_streq(rvalue
, "capture"))
1831 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1833 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1840 static int element_parse_direction_try_other(
1841 const char *filename
,
1843 const char *section
,
1849 pa_alsa_path
*p
= userdata
;
1853 if (!(e
= element_get(p
, section
, TRUE
))) {
1854 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1858 if ((yes
= pa_parse_boolean(rvalue
)) < 0) {
1859 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1863 e
->direction_try_other
= !!yes
;
1867 static pa_channel_position_mask_t
parse_mask(const char *m
) {
1868 pa_channel_position_mask_t v
;
1870 if (pa_streq(m
, "all-left"))
1871 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
1872 else if (pa_streq(m
, "all-right"))
1873 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
1874 else if (pa_streq(m
, "all-center"))
1875 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
1876 else if (pa_streq(m
, "all-front"))
1877 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
1878 else if (pa_streq(m
, "all-rear"))
1879 v
= PA_CHANNEL_POSITION_MASK_REAR
;
1880 else if (pa_streq(m
, "all-side"))
1881 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
1882 else if (pa_streq(m
, "all-top"))
1883 v
= PA_CHANNEL_POSITION_MASK_TOP
;
1884 else if (pa_streq(m
, "all-no-lfe"))
1885 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
1886 else if (pa_streq(m
, "all"))
1887 v
= PA_CHANNEL_POSITION_MASK_ALL
;
1889 pa_channel_position_t p
;
1891 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
1894 v
= PA_CHANNEL_POSITION_MASK(p
);
1900 static int element_parse_override_map(
1901 const char *filename
,
1903 const char *section
,
1909 pa_alsa_path
*p
= userdata
;
1911 const char *state
= NULL
;
1915 if (!(e
= element_get(p
, section
, TRUE
))) {
1916 pa_log("[%s:%u] Override map makes no sense in '%s'", filename
, line
, section
);
1920 while ((n
= pa_split(rvalue
, ",", &state
))) {
1921 pa_channel_position_mask_t m
;
1926 if ((m
= parse_mask(n
)) == 0) {
1927 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename
, line
, n
, section
);
1933 if (pa_streq(lvalue
, "override-map.1"))
1934 e
->masks
[i
++][0] = m
;
1936 e
->masks
[i
++][1] = m
;
1938 /* Later on we might add override-map.3 and so on here ... */
1943 e
->override_map
= TRUE
;
1948 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
1949 snd_mixer_selem_id_t
*sid
;
1950 snd_mixer_elem_t
*me
;
1956 SELEM_INIT(sid
, e
->alsa_name
);
1957 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1958 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1962 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1964 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1965 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
1967 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
1970 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1973 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
1975 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
1976 pa_log_warn("Failed to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1982 int pa_alsa_setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
1989 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
1990 element_set_option(o
->element
, m
, o
->alsa_idx
);
1995 static int option_verify(pa_alsa_option
*o
) {
1996 static const struct description_map well_known_descriptions
[] = {
1997 { "input", N_("Input") },
1998 { "input-docking", N_("Docking Station Input") },
1999 { "input-docking-microphone", N_("Docking Station Microphone") },
2000 { "input-docking-linein", N_("Docking Station Line-In") },
2001 { "input-linein", N_("Line-In") },
2002 { "input-microphone", N_("Microphone") },
2003 { "input-microphone-front", N_("Front Microphone") },
2004 { "input-microphone-rear", N_("Rear Microphone") },
2005 { "input-microphone-external", N_("External Microphone") },
2006 { "input-microphone-internal", N_("Internal Microphone") },
2007 { "input-radio", N_("Radio") },
2008 { "input-video", N_("Video") },
2009 { "input-agc-on", N_("Automatic Gain Control") },
2010 { "input-agc-off", N_("No Automatic Gain Control") },
2011 { "input-boost-on", N_("Boost") },
2012 { "input-boost-off", N_("No Boost") },
2013 { "output-amplifier-on", N_("Amplifier") },
2014 { "output-amplifier-off", N_("No Amplifier") },
2015 { "output-bass-boost-on", N_("Bass Boost") },
2016 { "output-bass-boost-off", N_("No Bass Boost") },
2017 { "output-speaker", N_("Speaker") },
2018 { "output-headphones", N_("Headphones") }
2024 pa_log("No name set for option %s", o
->alsa_name
);
2028 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
2029 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
2030 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
2034 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
2035 !pa_streq(o
->alsa_name
, "on") &&
2036 !pa_streq(o
->alsa_name
, "off")) {
2037 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
2041 if (!o
->description
)
2042 o
->description
= pa_xstrdup(lookup_description(o
->name
,
2043 well_known_descriptions
,
2044 PA_ELEMENTSOF(well_known_descriptions
)));
2045 if (!o
->description
)
2046 o
->description
= pa_xstrdup(o
->name
);
2051 static int element_verify(pa_alsa_element
*e
) {
2056 // 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);
2057 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
2058 (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required_any
== e
->required_absent
) ||
2059 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) ||
2060 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
2061 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
2065 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
2066 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
2070 PA_LLIST_FOREACH(o
, e
->options
)
2071 if (option_verify(o
) < 0)
2077 static int path_verify(pa_alsa_path
*p
) {
2078 static const struct description_map well_known_descriptions
[] = {
2079 { "analog-input", N_("Analog Input") },
2080 { "analog-input-microphone", N_("Analog Microphone") },
2081 { "analog-input-microphone-front", N_("Front Microphone") },
2082 { "analog-input-microphone-rear", N_("Rear Microphone") },
2083 { "analog-input-microphone-dock", N_("Docking Station Microphone") },
2084 { "analog-input-microphone-internal", N_("Internal Microphone") },
2085 { "analog-input-linein", N_("Analog Line-In") },
2086 { "analog-input-radio", N_("Analog Radio") },
2087 { "analog-input-video", N_("Analog Video") },
2088 { "analog-output", N_("Analog Output") },
2089 { "analog-output-headphones", N_("Analog Headphones") },
2090 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
2091 { "analog-output-mono", N_("Analog Mono Output") },
2092 { "analog-output-speaker", N_("Analog Speakers") },
2093 { "iec958-stereo-output", N_("Digital Output (IEC958)") },
2094 { "iec958-passthrough-output", N_("Digital Passthrough (IEC958)") }
2101 PA_LLIST_FOREACH(e
, p
->elements
)
2102 if (element_verify(e
) < 0)
2105 if (!p
->description
)
2106 p
->description
= pa_xstrdup(lookup_description(p
->name
,
2107 well_known_descriptions
,
2108 PA_ELEMENTSOF(well_known_descriptions
)));
2110 if (!p
->description
)
2111 p
->description
= pa_xstrdup(p
->name
);
2116 pa_alsa_path
* pa_alsa_path_new(const char *fname
, pa_alsa_direction_t direction
) {
2122 pa_config_item items
[] = {
2124 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
2125 { "description", pa_config_parse_string
, NULL
, "General" },
2126 { "name", pa_config_parse_string
, NULL
, "General" },
2129 { "priority", option_parse_priority
, NULL
, NULL
},
2130 { "name", option_parse_name
, NULL
, NULL
},
2133 { "switch", element_parse_switch
, NULL
, NULL
},
2134 { "volume", element_parse_volume
, NULL
, NULL
},
2135 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
2136 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
2137 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
2138 /* ... later on we might add override-map.3 and so on here ... */
2139 { "required", element_parse_required
, NULL
, NULL
},
2140 { "required-any", element_parse_required
, NULL
, NULL
},
2141 { "required-absent", element_parse_required
, NULL
, NULL
},
2142 { "direction", element_parse_direction
, NULL
, NULL
},
2143 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2144 { NULL
, NULL
, NULL
, NULL
}
2149 p
= pa_xnew0(pa_alsa_path
, 1);
2150 n
= pa_path_get_filename(fname
);
2151 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2152 p
->direction
= direction
;
2154 items
[0].data
= &p
->priority
;
2155 items
[1].data
= &p
->description
;
2156 items
[2].data
= &p
->name
;
2158 fn
= pa_maybe_prefix_path(fname
,
2159 #if defined(__linux__) && !defined(__OPTIMIZE__)
2160 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/paths/" :
2164 r
= pa_config_parse(fn
, NULL
, items
, p
);
2170 if (path_verify(p
) < 0)
2176 pa_alsa_path_free(p
);
2180 pa_alsa_path
* pa_alsa_path_synthesize(const char*element
, pa_alsa_direction_t direction
) {
2186 p
= pa_xnew0(pa_alsa_path
, 1);
2187 p
->name
= pa_xstrdup(element
);
2188 p
->direction
= direction
;
2190 e
= pa_xnew0(pa_alsa_element
, 1);
2192 e
->alsa_name
= pa_xstrdup(element
);
2193 e
->direction
= direction
;
2195 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2196 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2198 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2199 p
->last_element
= e
;
2203 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2204 pa_alsa_option
*o
, *n
;
2208 for (o
= e
->options
; o
; o
= n
) {
2211 if (o
->alsa_idx
< 0) {
2212 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2218 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2219 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2220 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2223 static void path_drop_unsupported(pa_alsa_path
*p
) {
2224 pa_alsa_element
*e
, *n
;
2228 for (e
= p
->elements
; e
; e
= n
) {
2231 if (!element_drop_unsupported(e
)) {
2232 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2238 static void path_make_options_unique(pa_alsa_path
*p
) {
2240 pa_alsa_option
*o
, *u
;
2242 PA_LLIST_FOREACH(e
, p
->elements
) {
2243 PA_LLIST_FOREACH(o
, e
->options
) {
2247 for (u
= o
->next
; u
; u
= u
->next
)
2248 if (pa_streq(u
->name
, o
->name
))
2254 m
= pa_xstrdup(o
->name
);
2256 /* OK, this name is not unique, hence let's rename */
2257 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2260 if (!pa_streq(u
->name
, m
))
2263 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2267 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2268 pa_xfree(u
->description
);
2269 u
->description
= nd
;
2279 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2282 for (; e
; e
= e
->next
)
2283 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2284 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2290 for (o
= e
->options
; o
; o
= o
->next
) {
2294 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2295 s
->options
= pa_idxset_copy(template->options
);
2296 s
->name
= pa_sprintf_malloc(_("%s+%s"), template->name
, o
->name
);
2298 (template->description
[0] && o
->description
[0])
2299 ? pa_sprintf_malloc(_("%s / %s"), template->description
, o
->description
)
2300 : (template->description
[0]
2301 ? pa_xstrdup(template->description
)
2302 : pa_xstrdup(o
->description
));
2304 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2306 s
= pa_xnew0(pa_alsa_setting
, 1);
2307 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2308 s
->name
= pa_xstrdup(o
->name
);
2309 s
->description
= pa_xstrdup(o
->description
);
2310 s
->priority
= o
->priority
;
2313 pa_idxset_put(s
->options
, o
, NULL
);
2315 if (element_create_settings(e
->next
, s
))
2316 /* This is not a leaf, so let's get rid of it */
2319 /* This is a leaf, so let's add it */
2320 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2322 e
->path
->last_setting
= s
;
2329 static void path_create_settings(pa_alsa_path
*p
) {
2332 element_create_settings(p
->elements
, NULL
);
2335 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2337 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2338 pa_channel_position_t t
;
2349 pa_log_debug("Probing path '%s'", p
->name
);
2351 PA_LLIST_FOREACH(e
, p
->elements
) {
2352 if (element_probe(e
, m
) < 0) {
2353 p
->supported
= FALSE
;
2354 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2357 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
);
2362 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2364 if (!p
->has_volume
) {
2365 p
->min_volume
= e
->min_volume
;
2366 p
->max_volume
= e
->max_volume
;
2370 if (!p
->has_volume
) {
2371 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2372 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2373 min_dB
[t
] = e
->min_dB
;
2374 max_dB
[t
] = e
->max_dB
;
2381 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2382 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2383 min_dB
[t
] += e
->min_dB
;
2384 max_dB
[t
] += e
->max_dB
;
2387 /* Hmm, there's another element before us
2388 * which cannot do dB volumes, so we we need
2389 * to 'neutralize' this slider */
2390 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2391 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2394 } else if (p
->has_volume
) {
2395 /* We can't use this volume, so let's ignore it */
2396 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2397 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2399 p
->has_volume
= TRUE
;
2402 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2406 if (p
->has_req_any
&& !p
->req_any_present
) {
2407 p
->supported
= FALSE
;
2408 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2412 path_drop_unsupported(p
);
2413 path_make_options_unique(p
);
2414 path_create_settings(p
);
2416 p
->supported
= TRUE
;
2419 p
->min_dB
= INFINITY
;
2420 p
->max_dB
= -INFINITY
;
2422 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2423 if (p
->min_dB
> min_dB
[t
])
2424 p
->min_dB
= min_dB
[t
];
2426 if (p
->max_dB
< max_dB
[t
])
2427 p
->max_dB
= max_dB
[t
];
2433 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2436 pa_log_debug("Setting %s (%s) priority=%u",
2438 pa_strnull(s
->description
),
2442 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2445 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2447 pa_strnull(o
->name
),
2448 pa_strnull(o
->description
),
2453 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2457 pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s",
2466 (long long unsigned) e
->merged_mask
,
2468 pa_yes_no(e
->override_map
));
2470 PA_LLIST_FOREACH(o
, e
->options
)
2471 pa_alsa_option_dump(o
);
2474 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2479 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2480 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2482 pa_strnull(p
->description
),
2485 pa_yes_no(p
->probed
),
2486 pa_yes_no(p
->supported
),
2487 pa_yes_no(p
->has_mute
),
2488 pa_yes_no(p
->has_volume
),
2489 pa_yes_no(p
->has_dB
),
2490 p
->min_volume
, p
->max_volume
,
2491 p
->min_dB
, p
->max_dB
);
2493 PA_LLIST_FOREACH(e
, p
->elements
)
2494 pa_alsa_element_dump(e
);
2496 PA_LLIST_FOREACH(s
, p
->settings
)
2497 pa_alsa_setting_dump(s
);
2500 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2501 snd_mixer_selem_id_t
*sid
;
2502 snd_mixer_elem_t
*me
;
2508 SELEM_INIT(sid
, e
->alsa_name
);
2509 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2510 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2514 snd_mixer_elem_set_callback(me
, cb
);
2515 snd_mixer_elem_set_callback_private(me
, userdata
);
2518 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2525 PA_LLIST_FOREACH(e
, p
->elements
)
2526 element_set_callback(e
, m
, cb
, userdata
);
2529 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2536 PA_LLIST_FOREACH(p
, ps
->paths
)
2537 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2540 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
) {
2541 pa_alsa_path_set
*ps
;
2542 char **pn
= NULL
, **en
= NULL
, **ie
;
2543 pa_alsa_decibel_fix
*db_fix
;
2547 pa_assert(m
->profile_set
);
2548 pa_assert(m
->profile_set
->decibel_fixes
);
2549 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2551 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2554 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2555 ps
->direction
= direction
;
2557 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2558 pn
= m
->output_path_names
;
2559 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2560 pn
= m
->input_path_names
;
2565 for (in
= pn
; *in
; in
++) {
2567 pa_bool_t duplicate
= FALSE
;
2570 for (kn
= pn
; kn
!= in
; kn
++)
2571 if (pa_streq(*kn
, *in
)) {
2579 fn
= pa_sprintf_malloc("%s.conf", *in
);
2581 if ((p
= pa_alsa_path_new(fn
, direction
))) {
2583 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2593 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2594 en
= m
->output_element
;
2595 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2596 en
= m
->input_element
;
2599 pa_alsa_path_set_free(ps
);
2603 for (ie
= en
; *ie
; ie
++) {
2607 p
= pa_alsa_path_synthesize(*ie
, direction
);
2610 /* Mark all other passed elements for require-absent */
2611 for (je
= en
; *je
; je
++) {
2617 e
= pa_xnew0(pa_alsa_element
, 1);
2619 e
->alsa_name
= pa_xstrdup(*je
);
2620 e
->direction
= direction
;
2621 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2623 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2624 p
->last_element
= e
;
2627 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2632 /* Assign decibel fixes to elements. */
2633 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2636 PA_LLIST_FOREACH(p
, ps
->paths
) {
2639 PA_LLIST_FOREACH(e
, p
->elements
) {
2640 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2641 /* The profile set that contains the dB fix may be freed
2642 * before the element, so we have to copy the dB fix
2644 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2645 e
->db_fix
->profile_set
= NULL
;
2646 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2647 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
2656 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2660 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2663 pa_yes_no(ps
->probed
));
2665 PA_LLIST_FOREACH(p
, ps
->paths
)
2666 pa_alsa_path_dump(p
);
2669 static void path_set_unify(pa_alsa_path_set
*ps
) {
2671 pa_bool_t has_dB
= TRUE
, has_volume
= TRUE
, has_mute
= TRUE
;
2674 /* We have issues dealing with paths that vary too wildly. That
2675 * means for now we have to have all paths support volume/mute/dB
2678 PA_LLIST_FOREACH(p
, ps
->paths
) {
2679 pa_assert(p
->probed
);
2683 else if (!p
->has_dB
)
2690 if (!has_volume
|| !has_dB
|| !has_mute
) {
2693 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2695 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2698 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2700 PA_LLIST_FOREACH(p
, ps
->paths
) {
2702 p
->has_volume
= FALSE
;
2707 p
->has_mute
= FALSE
;
2712 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
2713 pa_alsa_path
*p
, *q
;
2715 PA_LLIST_FOREACH(p
, ps
->paths
) {
2719 for (q
= p
->next
; q
; q
= q
->next
)
2720 if (pa_streq(q
->name
, p
->name
))
2726 m
= pa_xstrdup(p
->name
);
2728 /* OK, this name is not unique, hence let's rename */
2729 for (i
= 1, q
= p
; q
; q
= q
->next
) {
2732 if (!pa_streq(q
->name
, m
))
2735 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2739 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
2740 pa_xfree(q
->description
);
2741 q
->description
= nd
;
2750 void pa_alsa_path_set_probe(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2751 pa_alsa_path
*p
, *n
;
2758 for (p
= ps
->paths
; p
; p
= n
) {
2761 if (pa_alsa_path_probe(p
, m
, ignore_dB
) < 0) {
2762 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
2763 pa_alsa_path_free(p
);
2768 path_set_make_paths_unique(ps
);
2772 static void mapping_free(pa_alsa_mapping
*m
) {
2776 pa_xfree(m
->description
);
2778 pa_xstrfreev(m
->device_strings
);
2779 pa_xstrfreev(m
->input_path_names
);
2780 pa_xstrfreev(m
->output_path_names
);
2781 pa_xstrfreev(m
->input_element
);
2782 pa_xstrfreev(m
->output_element
);
2784 pa_assert(!m
->input_pcm
);
2785 pa_assert(!m
->output_pcm
);
2790 static void profile_free(pa_alsa_profile
*p
) {
2794 pa_xfree(p
->description
);
2796 pa_xstrfreev(p
->input_mapping_names
);
2797 pa_xstrfreev(p
->output_mapping_names
);
2799 if (p
->input_mappings
)
2800 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
2802 if (p
->output_mappings
)
2803 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
2808 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
2814 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
2817 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
2823 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
2826 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
2829 if (ps
->decibel_fixes
) {
2830 pa_alsa_decibel_fix
*db_fix
;
2832 while ((db_fix
= pa_hashmap_steal_first(ps
->decibel_fixes
)))
2833 decibel_fix_free(db_fix
);
2835 pa_hashmap_free(ps
->decibel_fixes
, NULL
, NULL
);
2841 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
2844 if (!pa_startswith(name
, "Mapping "))
2849 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
2852 m
= pa_xnew0(pa_alsa_mapping
, 1);
2853 m
->profile_set
= ps
;
2854 m
->name
= pa_xstrdup(name
);
2855 pa_channel_map_init(&m
->channel_map
);
2857 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
2862 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
2865 if (!pa_startswith(name
, "Profile "))
2870 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
2873 p
= pa_xnew0(pa_alsa_profile
, 1);
2874 p
->profile_set
= ps
;
2875 p
->name
= pa_xstrdup(name
);
2877 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
2882 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
2883 pa_alsa_decibel_fix
*db_fix
;
2885 if (!pa_startswith(name
, "DecibelFix "))
2890 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
2893 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
2894 db_fix
->profile_set
= ps
;
2895 db_fix
->name
= pa_xstrdup(name
);
2897 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
2902 static int mapping_parse_device_strings(
2903 const char *filename
,
2905 const char *section
,
2911 pa_alsa_profile_set
*ps
= userdata
;
2916 if (!(m
= mapping_get(ps
, section
))) {
2917 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2921 pa_xstrfreev(m
->device_strings
);
2922 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
2923 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
2930 static int mapping_parse_channel_map(
2931 const char *filename
,
2933 const char *section
,
2939 pa_alsa_profile_set
*ps
= userdata
;
2944 if (!(m
= mapping_get(ps
, section
))) {
2945 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2949 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
2950 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
2957 static int mapping_parse_paths(
2958 const char *filename
,
2960 const char *section
,
2966 pa_alsa_profile_set
*ps
= userdata
;
2971 if (!(m
= mapping_get(ps
, section
))) {
2972 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2976 if (pa_streq(lvalue
, "paths-input")) {
2977 pa_xstrfreev(m
->input_path_names
);
2978 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
2980 pa_xstrfreev(m
->output_path_names
);
2981 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
2987 static int mapping_parse_element(
2988 const char *filename
,
2990 const char *section
,
2996 pa_alsa_profile_set
*ps
= userdata
;
3001 if (!(m
= mapping_get(ps
, section
))) {
3002 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3006 if (pa_streq(lvalue
, "element-input")) {
3007 pa_xstrfreev(m
->input_element
);
3008 m
->input_element
= pa_split_spaces_strv(rvalue
);
3010 pa_xstrfreev(m
->output_element
);
3011 m
->output_element
= pa_split_spaces_strv(rvalue
);
3017 static int mapping_parse_direction(
3018 const char *filename
,
3020 const char *section
,
3026 pa_alsa_profile_set
*ps
= userdata
;
3031 if (!(m
= mapping_get(ps
, section
))) {
3032 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3036 if (pa_streq(rvalue
, "input"))
3037 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3038 else if (pa_streq(rvalue
, "output"))
3039 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3040 else if (pa_streq(rvalue
, "any"))
3041 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3043 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
3050 static int mapping_parse_description(
3051 const char *filename
,
3053 const char *section
,
3059 pa_alsa_profile_set
*ps
= userdata
;
3065 if ((m
= mapping_get(ps
, section
))) {
3066 pa_xfree(m
->description
);
3067 m
->description
= pa_xstrdup(rvalue
);
3068 } else if ((p
= profile_get(ps
, section
))) {
3069 pa_xfree(p
->description
);
3070 p
->description
= pa_xstrdup(rvalue
);
3072 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3079 static int mapping_parse_priority(
3080 const char *filename
,
3082 const char *section
,
3088 pa_alsa_profile_set
*ps
= userdata
;
3095 if (pa_atou(rvalue
, &prio
) < 0) {
3096 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
3100 if ((m
= mapping_get(ps
, section
)))
3102 else if ((p
= profile_get(ps
, section
)))
3105 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3112 static int profile_parse_mappings(
3113 const char *filename
,
3115 const char *section
,
3121 pa_alsa_profile_set
*ps
= userdata
;
3126 if (!(p
= profile_get(ps
, section
))) {
3127 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3131 if (pa_streq(lvalue
, "input-mappings")) {
3132 pa_xstrfreev(p
->input_mapping_names
);
3133 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
3135 pa_xstrfreev(p
->output_mapping_names
);
3136 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
3142 static int profile_parse_skip_probe(
3143 const char *filename
,
3145 const char *section
,
3151 pa_alsa_profile_set
*ps
= userdata
;
3157 if (!(p
= profile_get(ps
, section
))) {
3158 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3162 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
3163 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
3172 static int decibel_fix_parse_db_values(
3173 const char *filename
,
3175 const char *section
,
3181 pa_alsa_profile_set
*ps
= userdata
;
3182 pa_alsa_decibel_fix
*db_fix
;
3186 unsigned n
= 8; /* Current size of the db_values table. */
3187 unsigned min_step
= 0;
3188 unsigned max_step
= 0;
3189 unsigned i
= 0; /* Index to the items table. */
3190 unsigned prev_step
= 0;
3193 pa_assert(filename
);
3199 if (!(db_fix
= decibel_fix_get(ps
, section
))) {
3200 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3204 if (!(items
= pa_split_spaces_strv(rvalue
))) {
3205 pa_log("[%s:%u] Value missing", pa_strnull(filename
), line
);
3209 db_values
= pa_xnew(long, n
);
3211 while ((item
= items
[i
++])) {
3212 char *s
= item
; /* Step value string. */
3213 char *d
= item
; /* dB value string. */
3217 /* Move d forward until it points to a colon or to the end of the item. */
3218 for (; *d
&& *d
!= ':'; ++d
);
3221 /* item started with colon. */
3222 pa_log("[%s:%u] No step value found in %s", filename
, line
, item
);
3226 if (!*d
|| !*(d
+ 1)) {
3227 /* No colon found, or it was the last character in item. */
3228 pa_log("[%s:%u] No dB value found in %s", filename
, line
, item
);
3232 /* pa_atou() needs a null-terminating string. Let's replace the colon
3233 * with a zero byte. */
3236 if (pa_atou(s
, &step
) < 0) {
3237 pa_log("[%s:%u] Invalid step value: %s", filename
, line
, s
);
3241 if (pa_atod(d
, &db
) < 0) {
3242 pa_log("[%s:%u] Invalid dB value: %s", filename
, line
, d
);
3246 if (step
<= prev_step
&& i
!= 1) {
3247 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename
, line
, step
, prev_step
);
3251 if (db
< prev_db
&& i
!= 1) {
3252 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename
, line
, db
, prev_db
);
3258 db_values
[0] = (long) (db
* 100.0);
3262 /* Interpolate linearly. */
3263 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3265 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3267 /* Reallocate the db_values table if it's about to overflow. */
3268 if (prev_step
+ 1 - min_step
== n
) {
3270 db_values
= pa_xrenew(long, db_values
, n
);
3273 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3280 db_fix
->min_step
= min_step
;
3281 db_fix
->max_step
= max_step
;
3282 pa_xfree(db_fix
->db_values
);
3283 db_fix
->db_values
= db_values
;
3285 pa_xstrfreev(items
);
3290 pa_xstrfreev(items
);
3291 pa_xfree(db_values
);
3296 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3298 static const struct description_map well_known_descriptions
[] = {
3299 { "analog-mono", N_("Analog Mono") },
3300 { "analog-stereo", N_("Analog Stereo") },
3301 { "analog-surround-21", N_("Analog Surround 2.1") },
3302 { "analog-surround-30", N_("Analog Surround 3.0") },
3303 { "analog-surround-31", N_("Analog Surround 3.1") },
3304 { "analog-surround-40", N_("Analog Surround 4.0") },
3305 { "analog-surround-41", N_("Analog Surround 4.1") },
3306 { "analog-surround-50", N_("Analog Surround 5.0") },
3307 { "analog-surround-51", N_("Analog Surround 5.1") },
3308 { "analog-surround-61", N_("Analog Surround 6.0") },
3309 { "analog-surround-61", N_("Analog Surround 6.1") },
3310 { "analog-surround-70", N_("Analog Surround 7.0") },
3311 { "analog-surround-71", N_("Analog Surround 7.1") },
3312 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3313 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3314 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3315 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3316 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3321 if (!pa_channel_map_valid(&m
->channel_map
)) {
3322 pa_log("Mapping %s is missing channel map.", m
->name
);
3326 if (!m
->device_strings
) {
3327 pa_log("Mapping %s is missing device strings.", m
->name
);
3331 if ((m
->input_path_names
&& m
->input_element
) ||
3332 (m
->output_path_names
&& m
->output_element
)) {
3333 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3337 if (!m
->description
)
3338 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3339 well_known_descriptions
,
3340 PA_ELEMENTSOF(well_known_descriptions
)));
3342 if (!m
->description
)
3343 m
->description
= pa_xstrdup(m
->name
);
3346 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3348 else if (m
->channel_map
.channels
== bonus
->channels
)
3355 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3356 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3360 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3362 pa_strnull(m
->description
),
3364 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3365 pa_yes_no(m
->supported
),
3369 static void profile_set_add_auto_pair(
3370 pa_alsa_profile_set
*ps
,
3371 pa_alsa_mapping
*m
, /* output */
3372 pa_alsa_mapping
*n
/* input */) {
3380 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3383 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3387 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3389 name
= pa_sprintf_malloc("output:%s", m
->name
);
3391 name
= pa_sprintf_malloc("input:%s", n
->name
);
3393 if (pa_hashmap_get(ps
->profiles
, name
)) {
3398 p
= pa_xnew0(pa_alsa_profile
, 1);
3399 p
->profile_set
= ps
;
3403 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3404 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3405 p
->priority
+= m
->priority
* 100;
3409 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3410 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3411 p
->priority
+= n
->priority
;
3414 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3417 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3418 pa_alsa_mapping
*m
, *n
;
3419 void *m_state
, *n_state
;
3423 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3424 profile_set_add_auto_pair(ps
, m
, NULL
);
3426 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3427 profile_set_add_auto_pair(ps
, m
, n
);
3430 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3431 profile_set_add_auto_pair(ps
, NULL
, n
);
3434 static int profile_verify(pa_alsa_profile
*p
) {
3436 static const struct description_map well_known_descriptions
[] = {
3437 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3438 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3439 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3440 { "off", N_("Off") }
3445 /* Replace the output mapping names by the actual mappings */
3446 if (p
->output_mapping_names
) {
3449 pa_assert(!p
->output_mappings
);
3450 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3452 for (name
= p
->output_mapping_names
; *name
; name
++) {
3455 pa_bool_t duplicate
= FALSE
;
3457 for (in
= name
+ 1; *in
; in
++)
3458 if (pa_streq(*name
, *in
)) {
3466 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3467 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3471 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3477 pa_xstrfreev(p
->output_mapping_names
);
3478 p
->output_mapping_names
= NULL
;
3481 /* Replace the input mapping names by the actual mappings */
3482 if (p
->input_mapping_names
) {
3485 pa_assert(!p
->input_mappings
);
3486 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3488 for (name
= p
->input_mapping_names
; *name
; name
++) {
3491 pa_bool_t duplicate
= FALSE
;
3493 for (in
= name
+ 1; *in
; in
++)
3494 if (pa_streq(*name
, *in
)) {
3502 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3503 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3507 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3513 pa_xstrfreev(p
->input_mapping_names
);
3514 p
->input_mapping_names
= NULL
;
3517 if (!p
->input_mappings
&& !p
->output_mappings
) {
3518 pa_log("Profile '%s' lacks mappings.", p
->name
);
3522 if (!p
->description
)
3523 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3524 well_known_descriptions
,
3525 PA_ELEMENTSOF(well_known_descriptions
)));
3527 if (!p
->description
) {
3532 sb
= pa_strbuf_new();
3534 if (p
->output_mappings
)
3535 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3536 if (!pa_strbuf_isempty(sb
))
3537 pa_strbuf_puts(sb
, " + ");
3539 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
3542 if (p
->input_mappings
)
3543 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3544 if (!pa_strbuf_isempty(sb
))
3545 pa_strbuf_puts(sb
, " + ");
3547 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
3550 p
->description
= pa_strbuf_tostring_free(sb
);
3556 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
3561 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3563 pa_strnull(p
->description
),
3565 pa_yes_no(p
->supported
),
3566 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
3567 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
3569 if (p
->input_mappings
)
3570 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
3571 pa_log_debug("Input %s", m
->name
);
3573 if (p
->output_mappings
)
3574 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
3575 pa_log_debug("Output %s", m
->name
);
3578 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
3581 /* Check that the dB mapping has been configured. Since "db-values" is
3582 * currently the only option in the DecibelFix section, and decibel fix
3583 * objects don't get created if a DecibelFix section is empty, this is
3584 * actually a redundant check. Having this may prevent future bugs,
3586 if (!db_fix
->db_values
) {
3587 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
3594 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
3595 char *db_values
= NULL
;
3599 if (db_fix
->db_values
) {
3602 long max_i
= db_fix
->max_step
- db_fix
->min_step
;
3604 buf
= pa_strbuf_new();
3605 pa_strbuf_printf(buf
, "[%li]:%0.2f", db_fix
->min_step
, db_fix
->db_values
[0] / 100.0);
3607 for (i
= 1; i
<= max_i
; ++i
)
3608 pa_strbuf_printf(buf
, " [%li]:%0.2f", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
3610 db_values
= pa_strbuf_tostring_free(buf
);
3613 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
3614 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
3616 pa_xfree(db_values
);
3619 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
3620 pa_alsa_profile_set
*ps
;
3623 pa_alsa_decibel_fix
*db_fix
;
3628 static pa_config_item items
[] = {
3630 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
3633 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
3634 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
3635 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
3636 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
3637 { "element-input", mapping_parse_element
, NULL
, NULL
},
3638 { "element-output", mapping_parse_element
, NULL
, NULL
},
3639 { "direction", mapping_parse_direction
, NULL
, NULL
},
3641 /* Shared by [Mapping ...] and [Profile ...] */
3642 { "description", mapping_parse_description
, NULL
, NULL
},
3643 { "priority", mapping_parse_priority
, NULL
, NULL
},
3646 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
3647 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
3648 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
3650 /* [DecibelFix ...] */
3651 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
3652 { NULL
, NULL
, NULL
, NULL
}
3655 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
3656 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3657 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3658 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3660 items
[0].data
= &ps
->auto_profiles
;
3663 fname
= "default.conf";
3665 fn
= pa_maybe_prefix_path(fname
,
3666 #if defined(__linux__) && !defined(__OPTIMIZE__)
3667 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
3669 PA_ALSA_PROFILE_SETS_DIR
);
3671 r
= pa_config_parse(fn
, NULL
, items
, ps
);
3677 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3678 if (mapping_verify(m
, bonus
) < 0)
3681 if (ps
->auto_profiles
)
3682 profile_set_add_auto(ps
);
3684 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3685 if (profile_verify(p
) < 0)
3688 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
3689 if (decibel_fix_verify(db_fix
) < 0)
3695 pa_alsa_profile_set_free(ps
);
3699 void pa_alsa_profile_set_probe(
3700 pa_alsa_profile_set
*ps
,
3702 const pa_sample_spec
*ss
,
3703 unsigned default_n_fragments
,
3704 unsigned default_fragment_size_msec
) {
3707 pa_alsa_profile
*p
, *last
= NULL
;
3717 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
3718 pa_sample_spec try_ss
;
3719 pa_channel_map try_map
;
3720 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
3723 /* Is this already marked that it is supported? (i.e. from the config file) */
3727 pa_log_debug("Looking at profile %s", p
->name
);
3729 /* Close PCMs from the last iteration we don't need anymore */
3730 if (last
&& last
->output_mappings
)
3731 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
) {
3736 if (last
->supported
)
3739 if (!p
->output_mappings
|| !pa_idxset_get_by_data(p
->output_mappings
, m
, NULL
)) {
3740 snd_pcm_close(m
->output_pcm
);
3741 m
->output_pcm
= NULL
;
3745 if (last
&& last
->input_mappings
)
3746 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
) {
3751 if (last
->supported
)
3754 if (!p
->input_mappings
|| !pa_idxset_get_by_data(p
->input_mappings
, m
, NULL
)) {
3755 snd_pcm_close(m
->input_pcm
);
3756 m
->input_pcm
= NULL
;
3760 p
->supported
= TRUE
;
3762 /* Check if we can open all new ones */
3763 if (p
->output_mappings
)
3764 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3769 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
3770 try_map
= m
->channel_map
;
3772 try_ss
.channels
= try_map
.channels
;
3775 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
3776 pa_frame_size(&try_ss
);
3777 try_buffer_size
= default_n_fragments
* try_period_size
;
3779 if (!(m
->output_pcm
= pa_alsa_open_by_template(
3784 SND_PCM_STREAM_PLAYBACK
,
3785 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3787 p
->supported
= FALSE
;
3792 if (p
->input_mappings
&& p
->supported
)
3793 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3798 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
3799 try_map
= m
->channel_map
;
3801 try_ss
.channels
= try_map
.channels
;
3804 pa_usec_to_bytes(default_fragment_size_msec
*PA_USEC_PER_MSEC
, &try_ss
) /
3805 pa_frame_size(&try_ss
);
3806 try_buffer_size
= default_n_fragments
* try_period_size
;
3808 if (!(m
->input_pcm
= pa_alsa_open_by_template(
3813 SND_PCM_STREAM_CAPTURE
,
3814 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3816 p
->supported
= FALSE
;
3824 pa_log_debug("Profile %s supported.", p
->name
);
3831 if (last
->output_mappings
)
3832 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
)
3833 if (m
->output_pcm
) {
3835 if (last
->supported
)
3838 snd_pcm_close(m
->output_pcm
);
3839 m
->output_pcm
= NULL
;
3842 if (last
->input_mappings
)
3843 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
)
3846 if (last
->supported
)
3849 snd_pcm_close(m
->input_pcm
);
3850 m
->input_pcm
= NULL
;
3854 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3855 if (!p
->supported
) {
3856 pa_hashmap_remove(ps
->profiles
, p
->name
);
3860 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3861 if (m
->supported
<= 0) {
3862 pa_hashmap_remove(ps
->mappings
, m
->name
);
3869 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
3872 pa_alsa_decibel_fix
*db_fix
;
3877 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
3880 pa_yes_no(ps
->auto_profiles
),
3881 pa_yes_no(ps
->probed
),
3882 pa_hashmap_size(ps
->mappings
),
3883 pa_hashmap_size(ps
->profiles
),
3884 pa_hashmap_size(ps
->decibel_fixes
));
3886 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3887 pa_alsa_mapping_dump(m
);
3889 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3890 pa_alsa_profile_dump(p
);
3892 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
3893 pa_alsa_decibel_fix_dump(db_fix
);
3896 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
) {
3903 /* if there is no path, we don't want a port list */
3907 if (!ps
->paths
->next
){
3910 /* If there is only one path, but no or only one setting, then
3911 * we want a port list either */
3912 if (!ps
->paths
->settings
|| !ps
->paths
->settings
->next
)
3915 /* Ok, there is only one path, however with multiple settings,
3916 * so let's create a port for each setting */
3917 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3919 PA_LLIST_FOREACH(s
, ps
->paths
->settings
) {
3920 pa_device_port
*port
;
3921 pa_alsa_port_data
*data
;
3923 port
= pa_device_port_new(s
->name
, s
->description
, sizeof(pa_alsa_port_data
));
3924 port
->priority
= s
->priority
;
3926 data
= PA_DEVICE_PORT_DATA(port
);
3927 data
->path
= ps
->paths
;
3930 pa_hashmap_put(*p
, port
->name
, port
);
3935 /* We have multiple paths, so let's create a port for each
3936 * one, and each of each settings */
3937 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3939 PA_LLIST_FOREACH(path
, ps
->paths
) {
3941 if (!path
->settings
|| !path
->settings
->next
) {
3942 pa_device_port
*port
;
3943 pa_alsa_port_data
*data
;
3945 /* If there is no or just one setting we only need a
3948 port
= pa_device_port_new(path
->name
, path
->description
, sizeof(pa_alsa_port_data
));
3949 port
->priority
= path
->priority
* 100;
3952 data
= PA_DEVICE_PORT_DATA(port
);
3954 data
->setting
= path
->settings
;
3956 pa_hashmap_put(*p
, port
->name
, port
);
3960 PA_LLIST_FOREACH(s
, path
->settings
) {
3961 pa_device_port
*port
;
3962 pa_alsa_port_data
*data
;
3965 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
3967 if (s
->description
[0])
3968 d
= pa_sprintf_malloc(_("%s / %s"), path
->description
, s
->description
);
3970 d
= pa_xstrdup(path
->description
);
3972 port
= pa_device_port_new(n
, d
, sizeof(pa_alsa_port_data
));
3973 port
->priority
= path
->priority
* 100 + s
->priority
;
3978 data
= PA_DEVICE_PORT_DATA(port
);
3982 pa_hashmap_put(*p
, port
->name
, port
);
3988 pa_log_debug("Added %u ports", pa_hashmap_size(*p
));