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
;
2413 pa_log_debug("Probing path '%s'", p
->name
);
2415 PA_LLIST_FOREACH(e
, p
->elements
) {
2416 if (element_probe(e
, m
) < 0) {
2417 p
->supported
= FALSE
;
2418 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2421 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
);
2426 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2428 if (!p
->has_volume
) {
2429 p
->min_volume
= e
->min_volume
;
2430 p
->max_volume
= e
->max_volume
;
2434 if (!p
->has_volume
) {
2435 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2436 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2437 min_dB
[t
] = e
->min_dB
;
2438 max_dB
[t
] = e
->max_dB
;
2445 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2446 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2447 min_dB
[t
] += e
->min_dB
;
2448 max_dB
[t
] += e
->max_dB
;
2451 /* Hmm, there's another element before us
2452 * which cannot do dB volumes, so we we need
2453 * to 'neutralize' this slider */
2454 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2455 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2458 } else if (p
->has_volume
) {
2459 /* We can't use this volume, so let's ignore it */
2460 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2461 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2463 p
->has_volume
= TRUE
;
2466 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2470 if (p
->has_req_any
&& !p
->req_any_present
) {
2471 p
->supported
= FALSE
;
2472 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2476 path_drop_unsupported(p
);
2477 path_make_options_unique(p
);
2478 path_create_settings(p
);
2480 p
->supported
= TRUE
;
2483 p
->min_dB
= INFINITY
;
2484 p
->max_dB
= -INFINITY
;
2486 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2487 if (p
->min_dB
> min_dB
[t
])
2488 p
->min_dB
= min_dB
[t
];
2490 if (p
->max_dB
< max_dB
[t
])
2491 p
->max_dB
= max_dB
[t
];
2497 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2500 pa_log_debug("Setting %s (%s) priority=%u",
2502 pa_strnull(s
->description
),
2506 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2509 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2511 pa_strnull(o
->name
),
2512 pa_strnull(o
->description
),
2517 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2521 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",
2531 (long long unsigned) e
->merged_mask
,
2533 pa_yes_no(e
->override_map
));
2535 PA_LLIST_FOREACH(o
, e
->options
)
2536 pa_alsa_option_dump(o
);
2539 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2544 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2545 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2547 pa_strnull(p
->description
),
2550 pa_yes_no(p
->probed
),
2551 pa_yes_no(p
->supported
),
2552 pa_yes_no(p
->has_mute
),
2553 pa_yes_no(p
->has_volume
),
2554 pa_yes_no(p
->has_dB
),
2555 p
->min_volume
, p
->max_volume
,
2556 p
->min_dB
, p
->max_dB
);
2558 PA_LLIST_FOREACH(e
, p
->elements
)
2559 pa_alsa_element_dump(e
);
2561 PA_LLIST_FOREACH(s
, p
->settings
)
2562 pa_alsa_setting_dump(s
);
2565 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2566 snd_mixer_selem_id_t
*sid
;
2567 snd_mixer_elem_t
*me
;
2573 SELEM_INIT(sid
, e
->alsa_name
);
2574 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2575 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2579 snd_mixer_elem_set_callback(me
, cb
);
2580 snd_mixer_elem_set_callback_private(me
, userdata
);
2583 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2590 PA_LLIST_FOREACH(e
, p
->elements
)
2591 element_set_callback(e
, m
, cb
, userdata
);
2594 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2601 PA_LLIST_FOREACH(p
, ps
->paths
)
2602 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2605 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
) {
2606 pa_alsa_path_set
*ps
;
2607 char **pn
= NULL
, **en
= NULL
, **ie
;
2608 pa_alsa_decibel_fix
*db_fix
;
2612 pa_assert(m
->profile_set
);
2613 pa_assert(m
->profile_set
->decibel_fixes
);
2614 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2616 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2619 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2620 ps
->direction
= direction
;
2622 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2623 pn
= m
->output_path_names
;
2624 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2625 pn
= m
->input_path_names
;
2630 for (in
= pn
; *in
; in
++) {
2632 pa_bool_t duplicate
= FALSE
;
2635 for (kn
= pn
; kn
!= in
; kn
++)
2636 if (pa_streq(*kn
, *in
)) {
2644 fn
= pa_sprintf_malloc("%s.conf", *in
);
2646 if ((p
= pa_alsa_path_new(fn
, direction
))) {
2648 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2658 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2659 en
= m
->output_element
;
2660 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2661 en
= m
->input_element
;
2664 pa_alsa_path_set_free(ps
);
2668 for (ie
= en
; *ie
; ie
++) {
2672 p
= pa_alsa_path_synthesize(*ie
, direction
);
2675 /* Mark all other passed elements for require-absent */
2676 for (je
= en
; *je
; je
++) {
2682 e
= pa_xnew0(pa_alsa_element
, 1);
2684 e
->alsa_name
= pa_xstrdup(*je
);
2685 e
->direction
= direction
;
2686 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2687 e
->volume_limit
= -1;
2689 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2690 p
->last_element
= e
;
2693 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2698 /* Assign decibel fixes to elements. */
2699 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2702 PA_LLIST_FOREACH(p
, ps
->paths
) {
2705 PA_LLIST_FOREACH(e
, p
->elements
) {
2706 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2707 /* The profile set that contains the dB fix may be freed
2708 * before the element, so we have to copy the dB fix
2710 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2711 e
->db_fix
->profile_set
= NULL
;
2712 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2713 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
2722 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2726 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2729 pa_yes_no(ps
->probed
));
2731 PA_LLIST_FOREACH(p
, ps
->paths
)
2732 pa_alsa_path_dump(p
);
2735 static void path_set_unify(pa_alsa_path_set
*ps
) {
2737 pa_bool_t has_dB
= TRUE
, has_volume
= TRUE
, has_mute
= TRUE
;
2740 /* We have issues dealing with paths that vary too wildly. That
2741 * means for now we have to have all paths support volume/mute/dB
2744 PA_LLIST_FOREACH(p
, ps
->paths
) {
2745 pa_assert(p
->probed
);
2749 else if (!p
->has_dB
)
2756 if (!has_volume
|| !has_dB
|| !has_mute
) {
2759 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2761 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2764 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2766 PA_LLIST_FOREACH(p
, ps
->paths
) {
2768 p
->has_volume
= FALSE
;
2773 p
->has_mute
= FALSE
;
2778 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
2779 pa_alsa_path
*p
, *q
;
2781 PA_LLIST_FOREACH(p
, ps
->paths
) {
2785 for (q
= p
->next
; q
; q
= q
->next
)
2786 if (pa_streq(q
->name
, p
->name
))
2792 m
= pa_xstrdup(p
->name
);
2794 /* OK, this name is not unique, hence let's rename */
2795 for (i
= 1, q
= p
; q
; q
= q
->next
) {
2798 if (!pa_streq(q
->name
, m
))
2801 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2805 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
2806 pa_xfree(q
->description
);
2807 q
->description
= nd
;
2816 void pa_alsa_path_set_probe(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2817 pa_alsa_path
*p
, *n
;
2824 for (p
= ps
->paths
; p
; p
= n
) {
2827 if (pa_alsa_path_probe(p
, m
, ignore_dB
) < 0) {
2828 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
2829 pa_alsa_path_free(p
);
2834 path_set_make_paths_unique(ps
);
2838 static void mapping_free(pa_alsa_mapping
*m
) {
2842 pa_xfree(m
->description
);
2844 pa_xstrfreev(m
->device_strings
);
2845 pa_xstrfreev(m
->input_path_names
);
2846 pa_xstrfreev(m
->output_path_names
);
2847 pa_xstrfreev(m
->input_element
);
2848 pa_xstrfreev(m
->output_element
);
2850 pa_assert(!m
->input_pcm
);
2851 pa_assert(!m
->output_pcm
);
2856 static void profile_free(pa_alsa_profile
*p
) {
2860 pa_xfree(p
->description
);
2862 pa_xstrfreev(p
->input_mapping_names
);
2863 pa_xstrfreev(p
->output_mapping_names
);
2865 if (p
->input_mappings
)
2866 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
2868 if (p
->output_mappings
)
2869 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
2874 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
2880 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
2883 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
2889 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
2892 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
2895 if (ps
->decibel_fixes
) {
2896 pa_alsa_decibel_fix
*db_fix
;
2898 while ((db_fix
= pa_hashmap_steal_first(ps
->decibel_fixes
)))
2899 decibel_fix_free(db_fix
);
2901 pa_hashmap_free(ps
->decibel_fixes
, NULL
, NULL
);
2907 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
2910 if (!pa_startswith(name
, "Mapping "))
2915 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
2918 m
= pa_xnew0(pa_alsa_mapping
, 1);
2919 m
->profile_set
= ps
;
2920 m
->name
= pa_xstrdup(name
);
2921 pa_channel_map_init(&m
->channel_map
);
2923 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
2928 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
2931 if (!pa_startswith(name
, "Profile "))
2936 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
2939 p
= pa_xnew0(pa_alsa_profile
, 1);
2940 p
->profile_set
= ps
;
2941 p
->name
= pa_xstrdup(name
);
2943 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
2948 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
2949 pa_alsa_decibel_fix
*db_fix
;
2951 if (!pa_startswith(name
, "DecibelFix "))
2956 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
2959 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
2960 db_fix
->profile_set
= ps
;
2961 db_fix
->name
= pa_xstrdup(name
);
2963 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
2968 static int mapping_parse_device_strings(
2969 const char *filename
,
2971 const char *section
,
2977 pa_alsa_profile_set
*ps
= userdata
;
2982 if (!(m
= mapping_get(ps
, section
))) {
2983 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2987 pa_xstrfreev(m
->device_strings
);
2988 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
2989 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
2996 static int mapping_parse_channel_map(
2997 const char *filename
,
2999 const char *section
,
3005 pa_alsa_profile_set
*ps
= userdata
;
3010 if (!(m
= mapping_get(ps
, section
))) {
3011 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3015 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
3016 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
3023 static int mapping_parse_paths(
3024 const char *filename
,
3026 const char *section
,
3032 pa_alsa_profile_set
*ps
= userdata
;
3037 if (!(m
= mapping_get(ps
, section
))) {
3038 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3042 if (pa_streq(lvalue
, "paths-input")) {
3043 pa_xstrfreev(m
->input_path_names
);
3044 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
3046 pa_xstrfreev(m
->output_path_names
);
3047 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
3053 static int mapping_parse_element(
3054 const char *filename
,
3056 const char *section
,
3062 pa_alsa_profile_set
*ps
= userdata
;
3067 if (!(m
= mapping_get(ps
, section
))) {
3068 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3072 if (pa_streq(lvalue
, "element-input")) {
3073 pa_xstrfreev(m
->input_element
);
3074 m
->input_element
= pa_split_spaces_strv(rvalue
);
3076 pa_xstrfreev(m
->output_element
);
3077 m
->output_element
= pa_split_spaces_strv(rvalue
);
3083 static int mapping_parse_direction(
3084 const char *filename
,
3086 const char *section
,
3092 pa_alsa_profile_set
*ps
= userdata
;
3097 if (!(m
= mapping_get(ps
, section
))) {
3098 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3102 if (pa_streq(rvalue
, "input"))
3103 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3104 else if (pa_streq(rvalue
, "output"))
3105 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3106 else if (pa_streq(rvalue
, "any"))
3107 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3109 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
3116 static int mapping_parse_description(
3117 const char *filename
,
3119 const char *section
,
3125 pa_alsa_profile_set
*ps
= userdata
;
3131 if ((m
= mapping_get(ps
, section
))) {
3132 pa_xfree(m
->description
);
3133 m
->description
= pa_xstrdup(rvalue
);
3134 } else if ((p
= profile_get(ps
, section
))) {
3135 pa_xfree(p
->description
);
3136 p
->description
= pa_xstrdup(rvalue
);
3138 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3145 static int mapping_parse_priority(
3146 const char *filename
,
3148 const char *section
,
3154 pa_alsa_profile_set
*ps
= userdata
;
3161 if (pa_atou(rvalue
, &prio
) < 0) {
3162 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
3166 if ((m
= mapping_get(ps
, section
)))
3168 else if ((p
= profile_get(ps
, section
)))
3171 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3178 static int profile_parse_mappings(
3179 const char *filename
,
3181 const char *section
,
3187 pa_alsa_profile_set
*ps
= userdata
;
3192 if (!(p
= profile_get(ps
, section
))) {
3193 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3197 if (pa_streq(lvalue
, "input-mappings")) {
3198 pa_xstrfreev(p
->input_mapping_names
);
3199 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
3201 pa_xstrfreev(p
->output_mapping_names
);
3202 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
3208 static int profile_parse_skip_probe(
3209 const char *filename
,
3211 const char *section
,
3217 pa_alsa_profile_set
*ps
= userdata
;
3223 if (!(p
= profile_get(ps
, section
))) {
3224 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3228 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
3229 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
3238 static int decibel_fix_parse_db_values(
3239 const char *filename
,
3241 const char *section
,
3247 pa_alsa_profile_set
*ps
= userdata
;
3248 pa_alsa_decibel_fix
*db_fix
;
3252 unsigned n
= 8; /* Current size of the db_values table. */
3253 unsigned min_step
= 0;
3254 unsigned max_step
= 0;
3255 unsigned i
= 0; /* Index to the items table. */
3256 unsigned prev_step
= 0;
3259 pa_assert(filename
);
3265 if (!(db_fix
= decibel_fix_get(ps
, section
))) {
3266 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3270 if (!(items
= pa_split_spaces_strv(rvalue
))) {
3271 pa_log("[%s:%u] Value missing", pa_strnull(filename
), line
);
3275 db_values
= pa_xnew(long, n
);
3277 while ((item
= items
[i
++])) {
3278 char *s
= item
; /* Step value string. */
3279 char *d
= item
; /* dB value string. */
3283 /* Move d forward until it points to a colon or to the end of the item. */
3284 for (; *d
&& *d
!= ':'; ++d
);
3287 /* item started with colon. */
3288 pa_log("[%s:%u] No step value found in %s", filename
, line
, item
);
3292 if (!*d
|| !*(d
+ 1)) {
3293 /* No colon found, or it was the last character in item. */
3294 pa_log("[%s:%u] No dB value found in %s", filename
, line
, item
);
3298 /* pa_atou() needs a null-terminating string. Let's replace the colon
3299 * with a zero byte. */
3302 if (pa_atou(s
, &step
) < 0) {
3303 pa_log("[%s:%u] Invalid step value: %s", filename
, line
, s
);
3307 if (pa_atod(d
, &db
) < 0) {
3308 pa_log("[%s:%u] Invalid dB value: %s", filename
, line
, d
);
3312 if (step
<= prev_step
&& i
!= 1) {
3313 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename
, line
, step
, prev_step
);
3317 if (db
< prev_db
&& i
!= 1) {
3318 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename
, line
, db
, prev_db
);
3324 db_values
[0] = (long) (db
* 100.0);
3328 /* Interpolate linearly. */
3329 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3331 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3333 /* Reallocate the db_values table if it's about to overflow. */
3334 if (prev_step
+ 1 - min_step
== n
) {
3336 db_values
= pa_xrenew(long, db_values
, n
);
3339 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3346 db_fix
->min_step
= min_step
;
3347 db_fix
->max_step
= max_step
;
3348 pa_xfree(db_fix
->db_values
);
3349 db_fix
->db_values
= db_values
;
3351 pa_xstrfreev(items
);
3356 pa_xstrfreev(items
);
3357 pa_xfree(db_values
);
3362 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3364 static const struct description_map well_known_descriptions
[] = {
3365 { "analog-mono", N_("Analog Mono") },
3366 { "analog-stereo", N_("Analog Stereo") },
3367 { "analog-surround-21", N_("Analog Surround 2.1") },
3368 { "analog-surround-30", N_("Analog Surround 3.0") },
3369 { "analog-surround-31", N_("Analog Surround 3.1") },
3370 { "analog-surround-40", N_("Analog Surround 4.0") },
3371 { "analog-surround-41", N_("Analog Surround 4.1") },
3372 { "analog-surround-50", N_("Analog Surround 5.0") },
3373 { "analog-surround-51", N_("Analog Surround 5.1") },
3374 { "analog-surround-61", N_("Analog Surround 6.0") },
3375 { "analog-surround-61", N_("Analog Surround 6.1") },
3376 { "analog-surround-70", N_("Analog Surround 7.0") },
3377 { "analog-surround-71", N_("Analog Surround 7.1") },
3378 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3379 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3380 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3381 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3382 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3387 if (!pa_channel_map_valid(&m
->channel_map
)) {
3388 pa_log("Mapping %s is missing channel map.", m
->name
);
3392 if (!m
->device_strings
) {
3393 pa_log("Mapping %s is missing device strings.", m
->name
);
3397 if ((m
->input_path_names
&& m
->input_element
) ||
3398 (m
->output_path_names
&& m
->output_element
)) {
3399 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3403 if (!m
->description
)
3404 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3405 well_known_descriptions
,
3406 PA_ELEMENTSOF(well_known_descriptions
)));
3408 if (!m
->description
)
3409 m
->description
= pa_xstrdup(m
->name
);
3412 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3414 else if (m
->channel_map
.channels
== bonus
->channels
)
3421 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3422 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3426 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3428 pa_strnull(m
->description
),
3430 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3431 pa_yes_no(m
->supported
),
3435 static void profile_set_add_auto_pair(
3436 pa_alsa_profile_set
*ps
,
3437 pa_alsa_mapping
*m
, /* output */
3438 pa_alsa_mapping
*n
/* input */) {
3446 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3449 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3453 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3455 name
= pa_sprintf_malloc("output:%s", m
->name
);
3457 name
= pa_sprintf_malloc("input:%s", n
->name
);
3459 if (pa_hashmap_get(ps
->profiles
, name
)) {
3464 p
= pa_xnew0(pa_alsa_profile
, 1);
3465 p
->profile_set
= ps
;
3469 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3470 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3471 p
->priority
+= m
->priority
* 100;
3475 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3476 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3477 p
->priority
+= n
->priority
;
3480 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3483 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3484 pa_alsa_mapping
*m
, *n
;
3485 void *m_state
, *n_state
;
3489 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3490 profile_set_add_auto_pair(ps
, m
, NULL
);
3492 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3493 profile_set_add_auto_pair(ps
, m
, n
);
3496 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3497 profile_set_add_auto_pair(ps
, NULL
, n
);
3500 static int profile_verify(pa_alsa_profile
*p
) {
3502 static const struct description_map well_known_descriptions
[] = {
3503 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3504 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3505 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3506 { "off", N_("Off") }
3511 /* Replace the output mapping names by the actual mappings */
3512 if (p
->output_mapping_names
) {
3515 pa_assert(!p
->output_mappings
);
3516 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3518 for (name
= p
->output_mapping_names
; *name
; name
++) {
3521 pa_bool_t duplicate
= FALSE
;
3523 for (in
= name
+ 1; *in
; in
++)
3524 if (pa_streq(*name
, *in
)) {
3532 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3533 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3537 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3543 pa_xstrfreev(p
->output_mapping_names
);
3544 p
->output_mapping_names
= NULL
;
3547 /* Replace the input mapping names by the actual mappings */
3548 if (p
->input_mapping_names
) {
3551 pa_assert(!p
->input_mappings
);
3552 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3554 for (name
= p
->input_mapping_names
; *name
; name
++) {
3557 pa_bool_t duplicate
= FALSE
;
3559 for (in
= name
+ 1; *in
; in
++)
3560 if (pa_streq(*name
, *in
)) {
3568 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3569 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3573 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3579 pa_xstrfreev(p
->input_mapping_names
);
3580 p
->input_mapping_names
= NULL
;
3583 if (!p
->input_mappings
&& !p
->output_mappings
) {
3584 pa_log("Profile '%s' lacks mappings.", p
->name
);
3588 if (!p
->description
)
3589 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3590 well_known_descriptions
,
3591 PA_ELEMENTSOF(well_known_descriptions
)));
3593 if (!p
->description
) {
3598 sb
= pa_strbuf_new();
3600 if (p
->output_mappings
)
3601 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3602 if (!pa_strbuf_isempty(sb
))
3603 pa_strbuf_puts(sb
, " + ");
3605 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
3608 if (p
->input_mappings
)
3609 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3610 if (!pa_strbuf_isempty(sb
))
3611 pa_strbuf_puts(sb
, " + ");
3613 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
3616 p
->description
= pa_strbuf_tostring_free(sb
);
3622 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
3627 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3629 pa_strnull(p
->description
),
3631 pa_yes_no(p
->supported
),
3632 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
3633 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
3635 if (p
->input_mappings
)
3636 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
3637 pa_log_debug("Input %s", m
->name
);
3639 if (p
->output_mappings
)
3640 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
3641 pa_log_debug("Output %s", m
->name
);
3644 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
3647 /* Check that the dB mapping has been configured. Since "db-values" is
3648 * currently the only option in the DecibelFix section, and decibel fix
3649 * objects don't get created if a DecibelFix section is empty, this is
3650 * actually a redundant check. Having this may prevent future bugs,
3652 if (!db_fix
->db_values
) {
3653 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
3660 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
3661 char *db_values
= NULL
;
3665 if (db_fix
->db_values
) {
3668 long max_i
= db_fix
->max_step
- db_fix
->min_step
;
3670 buf
= pa_strbuf_new();
3671 pa_strbuf_printf(buf
, "[%li]:%0.2f", db_fix
->min_step
, db_fix
->db_values
[0] / 100.0);
3673 for (i
= 1; i
<= max_i
; ++i
)
3674 pa_strbuf_printf(buf
, " [%li]:%0.2f", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
3676 db_values
= pa_strbuf_tostring_free(buf
);
3679 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
3680 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
3682 pa_xfree(db_values
);
3685 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
3686 pa_alsa_profile_set
*ps
;
3689 pa_alsa_decibel_fix
*db_fix
;
3694 static pa_config_item items
[] = {
3696 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
3699 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
3700 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
3701 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
3702 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
3703 { "element-input", mapping_parse_element
, NULL
, NULL
},
3704 { "element-output", mapping_parse_element
, NULL
, NULL
},
3705 { "direction", mapping_parse_direction
, NULL
, NULL
},
3707 /* Shared by [Mapping ...] and [Profile ...] */
3708 { "description", mapping_parse_description
, NULL
, NULL
},
3709 { "priority", mapping_parse_priority
, NULL
, NULL
},
3712 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
3713 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
3714 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
3716 /* [DecibelFix ...] */
3717 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
3718 { NULL
, NULL
, NULL
, NULL
}
3721 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
3722 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3723 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3724 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3726 items
[0].data
= &ps
->auto_profiles
;
3729 fname
= "default.conf";
3731 fn
= pa_maybe_prefix_path(fname
,
3732 #if defined(__linux__) && !defined(__OPTIMIZE__)
3733 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
3735 PA_ALSA_PROFILE_SETS_DIR
);
3737 r
= pa_config_parse(fn
, NULL
, items
, ps
);
3743 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3744 if (mapping_verify(m
, bonus
) < 0)
3747 if (ps
->auto_profiles
)
3748 profile_set_add_auto(ps
);
3750 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3751 if (profile_verify(p
) < 0)
3754 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
3755 if (decibel_fix_verify(db_fix
) < 0)
3761 pa_alsa_profile_set_free(ps
);
3765 void pa_alsa_profile_set_probe(
3766 pa_alsa_profile_set
*ps
,
3768 const pa_sample_spec
*ss
,
3769 unsigned default_n_fragments
,
3770 unsigned default_fragment_size_msec
) {
3773 pa_alsa_profile
*p
, *last
= NULL
;
3783 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
3784 pa_sample_spec try_ss
;
3785 pa_channel_map try_map
;
3786 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
3789 /* Is this already marked that it is supported? (i.e. from the config file) */
3793 pa_log_debug("Looking at profile %s", p
->name
);
3795 /* Close PCMs from the last iteration we don't need anymore */
3796 if (last
&& last
->output_mappings
)
3797 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
) {
3802 if (last
->supported
)
3805 if (!p
->output_mappings
|| !pa_idxset_get_by_data(p
->output_mappings
, m
, NULL
)) {
3806 snd_pcm_close(m
->output_pcm
);
3807 m
->output_pcm
= NULL
;
3811 if (last
&& last
->input_mappings
)
3812 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
) {
3817 if (last
->supported
)
3820 if (!p
->input_mappings
|| !pa_idxset_get_by_data(p
->input_mappings
, m
, NULL
)) {
3821 snd_pcm_close(m
->input_pcm
);
3822 m
->input_pcm
= NULL
;
3826 p
->supported
= TRUE
;
3828 /* Check if we can open all new ones */
3829 if (p
->output_mappings
)
3830 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3835 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
3836 try_map
= m
->channel_map
;
3838 try_ss
.channels
= try_map
.channels
;
3841 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
3842 pa_frame_size(&try_ss
);
3843 try_buffer_size
= default_n_fragments
* try_period_size
;
3845 if (!(m
->output_pcm
= pa_alsa_open_by_template(
3850 SND_PCM_STREAM_PLAYBACK
,
3851 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3853 p
->supported
= FALSE
;
3858 if (p
->input_mappings
&& p
->supported
)
3859 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3864 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
3865 try_map
= m
->channel_map
;
3867 try_ss
.channels
= try_map
.channels
;
3870 pa_usec_to_bytes(default_fragment_size_msec
*PA_USEC_PER_MSEC
, &try_ss
) /
3871 pa_frame_size(&try_ss
);
3872 try_buffer_size
= default_n_fragments
* try_period_size
;
3874 if (!(m
->input_pcm
= pa_alsa_open_by_template(
3879 SND_PCM_STREAM_CAPTURE
,
3880 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3882 p
->supported
= FALSE
;
3890 pa_log_debug("Profile %s supported.", p
->name
);
3897 if (last
->output_mappings
)
3898 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
)
3899 if (m
->output_pcm
) {
3901 if (last
->supported
)
3904 snd_pcm_close(m
->output_pcm
);
3905 m
->output_pcm
= NULL
;
3908 if (last
->input_mappings
)
3909 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
)
3912 if (last
->supported
)
3915 snd_pcm_close(m
->input_pcm
);
3916 m
->input_pcm
= NULL
;
3920 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3921 if (!p
->supported
) {
3922 pa_hashmap_remove(ps
->profiles
, p
->name
);
3926 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3927 if (m
->supported
<= 0) {
3928 pa_hashmap_remove(ps
->mappings
, m
->name
);
3935 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
3938 pa_alsa_decibel_fix
*db_fix
;
3943 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
3946 pa_yes_no(ps
->auto_profiles
),
3947 pa_yes_no(ps
->probed
),
3948 pa_hashmap_size(ps
->mappings
),
3949 pa_hashmap_size(ps
->profiles
),
3950 pa_hashmap_size(ps
->decibel_fixes
));
3952 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3953 pa_alsa_mapping_dump(m
);
3955 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3956 pa_alsa_profile_dump(p
);
3958 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
3959 pa_alsa_decibel_fix_dump(db_fix
);
3962 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
) {
3969 /* if there is no path, we don't want a port list */
3973 if (!ps
->paths
->next
){
3976 /* If there is only one path, but no or only one setting, then
3977 * we want a port list either */
3978 if (!ps
->paths
->settings
|| !ps
->paths
->settings
->next
)
3981 /* Ok, there is only one path, however with multiple settings,
3982 * so let's create a port for each setting */
3983 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3985 PA_LLIST_FOREACH(s
, ps
->paths
->settings
) {
3986 pa_device_port
*port
;
3987 pa_alsa_port_data
*data
;
3989 port
= pa_device_port_new(s
->name
, s
->description
, sizeof(pa_alsa_port_data
));
3990 port
->priority
= s
->priority
;
3992 data
= PA_DEVICE_PORT_DATA(port
);
3993 data
->path
= ps
->paths
;
3996 pa_hashmap_put(*p
, port
->name
, port
);
4001 /* We have multiple paths, so let's create a port for each
4002 * one, and each of each settings */
4003 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4005 PA_LLIST_FOREACH(path
, ps
->paths
) {
4007 if (!path
->settings
|| !path
->settings
->next
) {
4008 pa_device_port
*port
;
4009 pa_alsa_port_data
*data
;
4011 /* If there is no or just one setting we only need a
4014 port
= pa_device_port_new(path
->name
, path
->description
, sizeof(pa_alsa_port_data
));
4015 port
->priority
= path
->priority
* 100;
4018 data
= PA_DEVICE_PORT_DATA(port
);
4020 data
->setting
= path
->settings
;
4022 pa_hashmap_put(*p
, port
->name
, port
);
4026 PA_LLIST_FOREACH(s
, path
->settings
) {
4027 pa_device_port
*port
;
4028 pa_alsa_port_data
*data
;
4031 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4033 if (s
->description
[0])
4034 d
= pa_sprintf_malloc(_("%s / %s"), path
->description
, s
->description
);
4036 d
= pa_xstrdup(path
->description
);
4038 port
= pa_device_port_new(n
, d
, sizeof(pa_alsa_port_data
));
4039 port
->priority
= path
->priority
* 100 + s
->priority
;
4044 data
= PA_DEVICE_PORT_DATA(port
);
4048 pa_hashmap_put(*p
, port
->name
, port
);
4054 pa_log_debug("Added %u ports", pa_hashmap_size(*p
));