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 /* Alsa lib documentation says for snd_mixer_selem_set_playback_dB() direction argument,
853 * that "-1 = accurate or first below, 0 = accurate, 1 = accurate or first above".
854 * But even with accurate nearest dB volume step is not selected, so that is why we need
855 * this function. Returns 0 and nearest selectable volume in *value_dB on success or
856 * negative error code if fails. */
857 static int element_get_nearest_alsa_dB(snd_mixer_elem_t
*me
, snd_mixer_selem_channel_id_t c
, pa_alsa_direction_t d
, long *value_dB
) {
867 if (d
== PA_ALSA_DIRECTION_OUTPUT
) {
868 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, *value_dB
, +1, &alsa_val
)) >= 0)
869 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value_high
);
874 if (value_high
== *value_dB
)
877 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, *value_dB
, -1, &alsa_val
)) >= 0)
878 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value_low
);
880 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, *value_dB
, +1, &alsa_val
)) >= 0)
881 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value_high
);
886 if (value_high
== *value_dB
)
889 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, *value_dB
, -1, &alsa_val
)) >= 0)
890 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value_low
);
896 if (labs(value_high
- *value_dB
) < labs(value_low
- *value_dB
))
897 *value_dB
= value_high
;
899 *value_dB
= value_low
;
904 static int element_set_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
, pa_bool_t sync_volume
, pa_bool_t write_to_hw
) {
906 snd_mixer_selem_id_t
*sid
;
908 snd_mixer_elem_t
*me
;
909 snd_mixer_selem_channel_id_t c
;
910 pa_channel_position_mask_t mask
= 0;
917 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
919 SELEM_INIT(sid
, e
->alsa_name
);
920 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
921 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
925 pa_cvolume_mute(&rv
, cm
->channels
);
927 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
929 pa_volume_t f
= PA_VOLUME_MUTED
;
930 pa_bool_t found
= FALSE
;
932 for (k
= 0; k
< cm
->channels
; k
++)
933 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
])) {
935 if (v
->values
[k
] > f
)
940 /* Hmm, so this channel does not exist in the volume
941 * struct, so let's bind it to the overall max of the
943 f
= pa_cvolume_max(v
);
947 long value
= to_alsa_dB(f
);
950 if (e
->volume_limit
>= 0 && value
> (e
->max_dB
* 100))
951 value
= e
->max_dB
* 100;
953 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
954 /* If we call set_playback_volume() without checking first
955 * if the channel is available, ALSA behaves very
956 * strangely and doesn't fail the call */
957 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
961 r
= snd_mixer_selem_set_playback_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
963 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
970 if ((r
= element_get_nearest_alsa_dB(me
, c
, PA_ALSA_DIRECTION_OUTPUT
, &value
)) >= 0)
971 r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, 0);
973 if ((r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, rounding
)) >= 0)
974 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
978 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
979 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value
);
985 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
989 r
= snd_mixer_selem_set_capture_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
991 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
998 if ((r
= element_get_nearest_alsa_dB(me
, c
, PA_ALSA_DIRECTION_INPUT
, &value
)) >= 0)
999 r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, 0);
1001 if ((r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, rounding
)) >= 0)
1002 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
1006 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
1007 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value
);
1017 #ifdef HAVE_VALGRIND_MEMCHECK_H
1018 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
1021 f
= from_alsa_dB(value
);
1026 value
= to_alsa_volume(f
, e
->min_volume
, e
->max_volume
);
1028 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1029 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
1030 if ((r
= snd_mixer_selem_set_playback_volume(me
, c
, value
)) >= 0)
1031 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
1035 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
1036 if ((r
= snd_mixer_selem_set_capture_volume(me
, c
, value
)) >= 0)
1037 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
1045 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
1048 for (k
= 0; k
< cm
->channels
; k
++)
1049 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
1050 if (rv
.values
[k
] < f
)
1053 mask
|= e
->masks
[c
][e
->n_channels
-1];
1056 for (k
= 0; k
< cm
->channels
; k
++)
1057 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
1058 rv
.values
[k
] = PA_VOLUME_NORM
;
1064 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 sync_volume
, pa_bool_t write_to_hw
) {
1073 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
1078 rv
= *v
; /* Remaining adjustment */
1079 pa_cvolume_reset(v
, cm
->channels
); /* Adjustment done */
1081 PA_LLIST_FOREACH(e
, p
->elements
) {
1084 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
1087 pa_assert(!p
->has_dB
|| e
->has_dB
);
1090 if (element_set_volume(e
, m
, cm
, &ev
, sync_volume
, write_to_hw
) < 0)
1098 pa_sw_cvolume_multiply(v
, v
, &ev
);
1099 pa_sw_cvolume_divide(&rv
, &rv
, &ev
);
1105 static int element_set_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t b
) {
1106 snd_mixer_elem_t
*me
;
1107 snd_mixer_selem_id_t
*sid
;
1113 SELEM_INIT(sid
, e
->alsa_name
);
1114 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1115 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1119 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1120 r
= snd_mixer_selem_set_playback_switch_all(me
, b
);
1122 r
= snd_mixer_selem_set_capture_switch_all(me
, b
);
1125 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1130 int pa_alsa_path_set_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t muted
) {
1139 PA_LLIST_FOREACH(e
, p
->elements
) {
1141 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
1144 if (element_set_switch(e
, m
, !muted
) < 0)
1151 /* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
1152 * function sets all channels of the volume element to e->min_volume, 0 dB or
1153 * e->constant_volume. */
1154 static int element_set_constant_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1155 snd_mixer_elem_t
*me
= NULL
;
1156 snd_mixer_selem_id_t
*sid
= NULL
;
1159 pa_bool_t volume_set
= FALSE
;
1164 SELEM_INIT(sid
, e
->alsa_name
);
1165 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1166 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1170 switch (e
->volume_use
) {
1171 case PA_ALSA_VOLUME_OFF
:
1172 volume
= e
->min_volume
;
1176 case PA_ALSA_VOLUME_ZERO
:
1180 volume
= decibel_fix_get_step(e
->db_fix
, &dB
, +1);
1185 case PA_ALSA_VOLUME_CONSTANT
:
1186 volume
= e
->constant_volume
;
1191 pa_assert_not_reached();
1195 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1196 r
= snd_mixer_selem_set_playback_volume_all(me
, volume
);
1198 r
= snd_mixer_selem_set_capture_volume_all(me
, volume
);
1200 pa_assert(e
->volume_use
== PA_ALSA_VOLUME_ZERO
);
1201 pa_assert(!e
->db_fix
);
1203 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1204 r
= snd_mixer_selem_set_playback_dB_all(me
, 0, +1);
1206 r
= snd_mixer_selem_set_capture_dB_all(me
, 0, +1);
1210 pa_log_warn("Failed to set volume of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1215 int pa_alsa_path_select(pa_alsa_path
*p
, snd_mixer_t
*m
) {
1222 pa_log_debug("Activating path %s", p
->name
);
1223 pa_alsa_path_dump(p
);
1225 PA_LLIST_FOREACH(e
, p
->elements
) {
1227 switch (e
->switch_use
) {
1228 case PA_ALSA_SWITCH_OFF
:
1229 r
= element_set_switch(e
, m
, FALSE
);
1232 case PA_ALSA_SWITCH_ON
:
1233 r
= element_set_switch(e
, m
, TRUE
);
1236 case PA_ALSA_SWITCH_MUTE
:
1237 case PA_ALSA_SWITCH_IGNORE
:
1238 case PA_ALSA_SWITCH_SELECT
:
1246 switch (e
->volume_use
) {
1247 case PA_ALSA_VOLUME_OFF
:
1248 case PA_ALSA_VOLUME_ZERO
:
1249 case PA_ALSA_VOLUME_CONSTANT
:
1250 r
= element_set_constant_volume(e
, m
);
1253 case PA_ALSA_VOLUME_MERGE
:
1254 case PA_ALSA_VOLUME_IGNORE
:
1266 static int check_required(pa_alsa_element
*e
, snd_mixer_elem_t
*me
) {
1267 pa_bool_t has_switch
;
1268 pa_bool_t has_enumeration
;
1269 pa_bool_t has_volume
;
1274 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1276 snd_mixer_selem_has_playback_switch(me
) ||
1277 (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
));
1280 snd_mixer_selem_has_capture_switch(me
) ||
1281 (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
));
1284 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1286 snd_mixer_selem_has_playback_volume(me
) ||
1287 (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
));
1290 snd_mixer_selem_has_capture_volume(me
) ||
1291 (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
));
1294 has_enumeration
= snd_mixer_selem_is_enumerated(me
);
1296 if ((e
->required
== PA_ALSA_REQUIRED_SWITCH
&& !has_switch
) ||
1297 (e
->required
== PA_ALSA_REQUIRED_VOLUME
&& !has_volume
) ||
1298 (e
->required
== PA_ALSA_REQUIRED_ENUMERATION
&& !has_enumeration
))
1301 if (e
->required
== PA_ALSA_REQUIRED_ANY
&& !(has_switch
|| has_volume
|| has_enumeration
))
1304 if ((e
->required_absent
== PA_ALSA_REQUIRED_SWITCH
&& has_switch
) ||
1305 (e
->required_absent
== PA_ALSA_REQUIRED_VOLUME
&& has_volume
) ||
1306 (e
->required_absent
== PA_ALSA_REQUIRED_ENUMERATION
&& has_enumeration
))
1309 if (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& (has_switch
|| has_volume
|| has_enumeration
))
1312 if (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) {
1313 switch (e
->required_any
) {
1314 case PA_ALSA_REQUIRED_VOLUME
:
1315 e
->path
->req_any_present
|= (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
);
1317 case PA_ALSA_REQUIRED_SWITCH
:
1318 e
->path
->req_any_present
|= (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
);
1320 case PA_ALSA_REQUIRED_ENUMERATION
:
1321 e
->path
->req_any_present
|= (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1323 case PA_ALSA_REQUIRED_ANY
:
1324 e
->path
->req_any_present
|=
1325 (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) ||
1326 (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) ||
1327 (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1330 pa_assert_not_reached();
1334 if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1336 PA_LLIST_FOREACH(o
, e
->options
) {
1337 e
->path
->req_any_present
|= (o
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) &&
1339 if (o
->required
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
< 0)
1341 if (o
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
>= 0)
1349 static int element_probe(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1350 snd_mixer_selem_id_t
*sid
;
1351 snd_mixer_elem_t
*me
;
1357 SELEM_INIT(sid
, e
->alsa_name
);
1359 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1361 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1364 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1365 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1366 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1371 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1372 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1374 if (!snd_mixer_selem_has_playback_switch(me
)) {
1375 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1376 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1378 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1383 if (!snd_mixer_selem_has_capture_switch(me
)) {
1384 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1385 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1387 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1391 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1392 e
->direction_try_other
= FALSE
;
1395 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1397 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1399 if (!snd_mixer_selem_has_playback_volume(me
)) {
1400 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1401 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1403 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1408 if (!snd_mixer_selem_has_capture_volume(me
)) {
1409 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1410 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1412 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1416 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1417 long min_dB
= 0, max_dB
= 0;
1420 e
->direction_try_other
= FALSE
;
1422 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1423 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1425 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1428 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1432 if (e
->min_volume
>= e
->max_volume
) {
1433 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
);
1434 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1436 } else if (e
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&&
1437 (e
->min_volume
> e
->constant_volume
|| e
->max_volume
< e
->constant_volume
)) {
1438 pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
1439 e
->constant_volume
, e
->alsa_name
, e
->min_volume
, e
->max_volume
);
1440 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1444 pa_channel_position_t p
;
1447 ((e
->min_volume
> e
->db_fix
->min_step
) ||
1448 (e
->max_volume
< e
->db_fix
->max_step
))) {
1449 pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1450 "real hardware range (%li-%li). Disabling the decibel fix.", e
->alsa_name
,
1451 e
->db_fix
->min_step
, e
->db_fix
->max_step
,
1452 e
->min_volume
, e
->max_volume
);
1454 decibel_fix_free(e
->db_fix
);
1460 e
->min_volume
= e
->db_fix
->min_step
;
1461 e
->max_volume
= e
->db_fix
->max_step
;
1462 min_dB
= e
->db_fix
->db_values
[0];
1463 max_dB
= e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
];
1464 } else if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1465 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1467 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1469 /* Check that the kernel driver returns consistent limits with
1470 * both _get_*_dB_range() and _ask_*_vol_dB(). */
1471 if (e
->has_dB
&& !e
->db_fix
) {
1472 long min_dB_checked
= 0;
1473 long max_dB_checked
= 0;
1475 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1476 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1478 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1481 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->min_volume
);
1485 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1486 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1488 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1491 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->max_volume
);
1495 if (min_dB
!= min_dB_checked
|| max_dB
!= max_dB_checked
) {
1496 pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
1497 "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
1498 "%0.2f dB at level %li.",
1500 min_dB
/ 100.0, max_dB
/ 100.0,
1501 min_dB_checked
/ 100.0, e
->min_volume
, max_dB_checked
/ 100.0, e
->max_volume
);
1507 #ifdef HAVE_VALGRIND_MEMCHECK_H
1508 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1509 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1512 e
->min_dB
= ((double) min_dB
) / 100.0;
1513 e
->max_dB
= ((double) max_dB
) / 100.0;
1515 if (min_dB
>= max_dB
) {
1516 pa_assert(!e
->db_fix
);
1517 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
);
1522 if (e
->volume_limit
>= 0) {
1523 if (e
->volume_limit
<= e
->min_volume
|| e
->volume_limit
> e
->max_volume
)
1524 pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1525 "%li-%li. The volume limit is ignored.",
1526 e
->alsa_name
, e
->path
->name
, e
->volume_limit
, e
->min_volume
+ 1, e
->max_volume
);
1529 e
->max_volume
= e
->volume_limit
;
1533 e
->db_fix
->max_step
= e
->max_volume
;
1534 e
->max_dB
= ((double) e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
]) / 100.0;
1537 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1538 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB
);
1540 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB
);
1543 pa_log_warn("Failed to get dB value of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1546 e
->max_dB
= ((double) max_dB
) / 100.0;
1552 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1553 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1555 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1560 if (!e
->override_map
) {
1561 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1562 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1565 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1568 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1571 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1574 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1576 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1579 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1580 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1582 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1585 if (e
->n_channels
<= 0) {
1586 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1590 if (e
->n_channels
> 2) {
1591 /* FIXME: In some places code like this is used:
1593 * e->masks[alsa_channel_ids[p]][e->n_channels-1]
1595 * The definition of e->masks is
1597 * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST][2];
1599 * Since the array size is fixed at 2, we obviously
1600 * don't support elements with more than two
1602 pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e
->alsa_name
, e
->n_channels
);
1606 if (!e
->override_map
) {
1607 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1608 pa_bool_t has_channel
;
1610 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1613 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1614 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1616 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1618 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1623 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1624 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1627 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1635 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1638 PA_LLIST_FOREACH(o
, e
->options
)
1639 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1640 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1644 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1645 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1649 PA_LLIST_FOREACH(o
, e
->options
) {
1652 for (i
= 0; i
< n
; i
++) {
1655 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1658 if (!pa_streq(buf
, o
->alsa_name
))
1666 if (check_required(e
, me
) < 0)
1672 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, pa_bool_t prefixed
) {
1679 if (!pa_startswith(section
, "Element "))
1685 /* This is not an element section, but an enum section? */
1686 if (strchr(section
, ':'))
1689 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1690 return p
->last_element
;
1692 PA_LLIST_FOREACH(e
, p
->elements
)
1693 if (pa_streq(e
->alsa_name
, section
))
1696 e
= pa_xnew0(pa_alsa_element
, 1);
1698 e
->alsa_name
= pa_xstrdup(section
);
1699 e
->direction
= p
->direction
;
1700 e
->volume_limit
= -1;
1702 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1705 p
->last_element
= e
;
1709 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1715 if (!pa_startswith(section
, "Option "))
1720 /* This is not an enum section, but an element section? */
1721 if (!(on
= strchr(section
, ':')))
1724 en
= pa_xstrndup(section
, on
- section
);
1727 if (p
->last_option
&&
1728 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1729 pa_streq(p
->last_option
->alsa_name
, on
)) {
1731 return p
->last_option
;
1734 pa_assert_se(e
= element_get(p
, en
, FALSE
));
1737 PA_LLIST_FOREACH(o
, e
->options
)
1738 if (pa_streq(o
->alsa_name
, on
))
1741 o
= pa_xnew0(pa_alsa_option
, 1);
1743 o
->alsa_name
= pa_xstrdup(on
);
1746 if (p
->last_option
&& p
->last_option
->element
== e
)
1747 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1749 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1756 static int element_parse_switch(
1757 const char *filename
,
1759 const char *section
,
1765 pa_alsa_path
*p
= userdata
;
1770 if (!(e
= element_get(p
, section
, TRUE
))) {
1771 pa_log("[%s:%u] Switch makes no sense in '%s'", filename
, line
, section
);
1775 if (pa_streq(rvalue
, "ignore"))
1776 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1777 else if (pa_streq(rvalue
, "mute"))
1778 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1779 else if (pa_streq(rvalue
, "off"))
1780 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1781 else if (pa_streq(rvalue
, "on"))
1782 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1783 else if (pa_streq(rvalue
, "select"))
1784 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1786 pa_log("[%s:%u] Switch invalid of '%s'", filename
, line
, section
);
1793 static int element_parse_volume(
1794 const char *filename
,
1796 const char *section
,
1802 pa_alsa_path
*p
= userdata
;
1807 if (!(e
= element_get(p
, section
, TRUE
))) {
1808 pa_log("[%s:%u] Volume makes no sense in '%s'", filename
, line
, section
);
1812 if (pa_streq(rvalue
, "ignore"))
1813 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1814 else if (pa_streq(rvalue
, "merge"))
1815 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1816 else if (pa_streq(rvalue
, "off"))
1817 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1818 else if (pa_streq(rvalue
, "zero"))
1819 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1823 if (pa_atou(rvalue
, &constant
) >= 0) {
1824 e
->volume_use
= PA_ALSA_VOLUME_CONSTANT
;
1825 e
->constant_volume
= constant
;
1827 pa_log("[%s:%u] Volume invalid of '%s'", filename
, line
, section
);
1835 static int element_parse_enumeration(
1836 const char *filename
,
1838 const char *section
,
1844 pa_alsa_path
*p
= userdata
;
1849 if (!(e
= element_get(p
, section
, TRUE
))) {
1850 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename
, line
, section
);
1854 if (pa_streq(rvalue
, "ignore"))
1855 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1856 else if (pa_streq(rvalue
, "select"))
1857 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1859 pa_log("[%s:%u] Enumeration invalid of '%s'", filename
, line
, section
);
1866 static int option_parse_priority(
1867 const char *filename
,
1869 const char *section
,
1875 pa_alsa_path
*p
= userdata
;
1881 if (!(o
= option_get(p
, section
))) {
1882 pa_log("[%s:%u] Priority makes no sense in '%s'", filename
, line
, section
);
1886 if (pa_atou(rvalue
, &prio
) < 0) {
1887 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
1895 static int option_parse_name(
1896 const char *filename
,
1898 const char *section
,
1904 pa_alsa_path
*p
= userdata
;
1909 if (!(o
= option_get(p
, section
))) {
1910 pa_log("[%s:%u] Name makes no sense in '%s'", filename
, line
, section
);
1915 o
->name
= pa_xstrdup(rvalue
);
1920 static int element_parse_required(
1921 const char *filename
,
1923 const char *section
,
1929 pa_alsa_path
*p
= userdata
;
1932 pa_alsa_required_t req
;
1936 e
= element_get(p
, section
, TRUE
);
1937 o
= option_get(p
, section
);
1939 pa_log("[%s:%u] Required makes no sense in '%s'", filename
, line
, section
);
1943 if (pa_streq(rvalue
, "ignore"))
1944 req
= PA_ALSA_REQUIRED_IGNORE
;
1945 else if (pa_streq(rvalue
, "switch") && e
)
1946 req
= PA_ALSA_REQUIRED_SWITCH
;
1947 else if (pa_streq(rvalue
, "volume") && e
)
1948 req
= PA_ALSA_REQUIRED_VOLUME
;
1949 else if (pa_streq(rvalue
, "enumeration"))
1950 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1951 else if (pa_streq(rvalue
, "any"))
1952 req
= PA_ALSA_REQUIRED_ANY
;
1954 pa_log("[%s:%u] Required invalid of '%s'", filename
, line
, section
);
1958 if (pa_streq(lvalue
, "required-absent")) {
1960 e
->required_absent
= req
;
1962 o
->required_absent
= req
;
1964 else if (pa_streq(lvalue
, "required-any")) {
1966 e
->required_any
= req
;
1967 e
->path
->has_req_any
= TRUE
;
1970 o
->required_any
= req
;
1971 o
->element
->path
->has_req_any
= TRUE
;
1984 static int element_parse_direction(
1985 const char *filename
,
1987 const char *section
,
1993 pa_alsa_path
*p
= userdata
;
1998 if (!(e
= element_get(p
, section
, TRUE
))) {
1999 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
2003 if (pa_streq(rvalue
, "playback"))
2004 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
2005 else if (pa_streq(rvalue
, "capture"))
2006 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
2008 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
2015 static int element_parse_direction_try_other(
2016 const char *filename
,
2018 const char *section
,
2024 pa_alsa_path
*p
= userdata
;
2028 if (!(e
= element_get(p
, section
, TRUE
))) {
2029 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
2033 if ((yes
= pa_parse_boolean(rvalue
)) < 0) {
2034 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
2038 e
->direction_try_other
= !!yes
;
2042 static int element_parse_volume_limit(
2043 const char *filename
,
2045 const char *section
,
2051 pa_alsa_path
*p
= userdata
;
2055 if (!(e
= element_get(p
, section
, TRUE
))) {
2056 pa_log("[%s:%u] volume-limit makes no sense in '%s'", filename
, line
, section
);
2060 if (pa_atol(rvalue
, &volume_limit
) < 0 || volume_limit
< 0) {
2061 pa_log("[%s:%u] Invalid value for volume-limit", filename
, line
);
2065 e
->volume_limit
= volume_limit
;
2069 static pa_channel_position_mask_t
parse_mask(const char *m
) {
2070 pa_channel_position_mask_t v
;
2072 if (pa_streq(m
, "all-left"))
2073 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
2074 else if (pa_streq(m
, "all-right"))
2075 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
2076 else if (pa_streq(m
, "all-center"))
2077 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
2078 else if (pa_streq(m
, "all-front"))
2079 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
2080 else if (pa_streq(m
, "all-rear"))
2081 v
= PA_CHANNEL_POSITION_MASK_REAR
;
2082 else if (pa_streq(m
, "all-side"))
2083 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
2084 else if (pa_streq(m
, "all-top"))
2085 v
= PA_CHANNEL_POSITION_MASK_TOP
;
2086 else if (pa_streq(m
, "all-no-lfe"))
2087 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
2088 else if (pa_streq(m
, "all"))
2089 v
= PA_CHANNEL_POSITION_MASK_ALL
;
2091 pa_channel_position_t p
;
2093 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
2096 v
= PA_CHANNEL_POSITION_MASK(p
);
2102 static int element_parse_override_map(
2103 const char *filename
,
2105 const char *section
,
2111 pa_alsa_path
*p
= userdata
;
2113 const char *state
= NULL
;
2117 if (!(e
= element_get(p
, section
, TRUE
))) {
2118 pa_log("[%s:%u] Override map makes no sense in '%s'", filename
, line
, section
);
2122 while ((n
= pa_split(rvalue
, ",", &state
))) {
2123 pa_channel_position_mask_t m
;
2128 if ((m
= parse_mask(n
)) == 0) {
2129 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename
, line
, n
, section
);
2135 if (pa_streq(lvalue
, "override-map.1"))
2136 e
->masks
[i
++][0] = m
;
2138 e
->masks
[i
++][1] = m
;
2140 /* Later on we might add override-map.3 and so on here ... */
2145 e
->override_map
= TRUE
;
2150 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
2151 snd_mixer_selem_id_t
*sid
;
2152 snd_mixer_elem_t
*me
;
2158 SELEM_INIT(sid
, e
->alsa_name
);
2159 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2160 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2164 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
2166 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
2167 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
2169 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
2172 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2175 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
2177 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
2178 pa_log_warn("Failed to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2184 int pa_alsa_setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
2191 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
2192 element_set_option(o
->element
, m
, o
->alsa_idx
);
2197 static int option_verify(pa_alsa_option
*o
) {
2198 static const struct description_map well_known_descriptions
[] = {
2199 { "input", N_("Input") },
2200 { "input-docking", N_("Docking Station Input") },
2201 { "input-docking-microphone", N_("Docking Station Microphone") },
2202 { "input-docking-linein", N_("Docking Station Line-In") },
2203 { "input-linein", N_("Line-In") },
2204 { "input-microphone", N_("Microphone") },
2205 { "input-microphone-front", N_("Front Microphone") },
2206 { "input-microphone-rear", N_("Rear Microphone") },
2207 { "input-microphone-external", N_("External Microphone") },
2208 { "input-microphone-internal", N_("Internal Microphone") },
2209 { "input-radio", N_("Radio") },
2210 { "input-video", N_("Video") },
2211 { "input-agc-on", N_("Automatic Gain Control") },
2212 { "input-agc-off", N_("No Automatic Gain Control") },
2213 { "input-boost-on", N_("Boost") },
2214 { "input-boost-off", N_("No Boost") },
2215 { "output-amplifier-on", N_("Amplifier") },
2216 { "output-amplifier-off", N_("No Amplifier") },
2217 { "output-bass-boost-on", N_("Bass Boost") },
2218 { "output-bass-boost-off", N_("No Bass Boost") },
2219 { "output-speaker", N_("Speaker") },
2220 { "output-headphones", N_("Headphones") }
2226 pa_log("No name set for option %s", o
->alsa_name
);
2230 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
2231 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
2232 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
2236 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
2237 !pa_streq(o
->alsa_name
, "on") &&
2238 !pa_streq(o
->alsa_name
, "off")) {
2239 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
2243 if (!o
->description
)
2244 o
->description
= pa_xstrdup(lookup_description(o
->name
,
2245 well_known_descriptions
,
2246 PA_ELEMENTSOF(well_known_descriptions
)));
2247 if (!o
->description
)
2248 o
->description
= pa_xstrdup(o
->name
);
2253 static int element_verify(pa_alsa_element
*e
) {
2258 // 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);
2259 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
2260 (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required_any
== e
->required_absent
) ||
2261 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) ||
2262 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
2263 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
2267 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
2268 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
2272 PA_LLIST_FOREACH(o
, e
->options
)
2273 if (option_verify(o
) < 0)
2279 static int path_verify(pa_alsa_path
*p
) {
2280 static const struct description_map well_known_descriptions
[] = {
2281 { "analog-input", N_("Analog Input") },
2282 { "analog-input-microphone", N_("Analog Microphone") },
2283 { "analog-input-microphone-front", N_("Front Microphone") },
2284 { "analog-input-microphone-rear", N_("Rear Microphone") },
2285 { "analog-input-microphone-dock", N_("Docking Station Microphone") },
2286 { "analog-input-microphone-internal", N_("Internal Microphone") },
2287 { "analog-input-linein", N_("Analog Line-In") },
2288 { "analog-input-radio", N_("Analog Radio") },
2289 { "analog-input-video", N_("Analog Video") },
2290 { "analog-output", N_("Analog Output") },
2291 { "analog-output-headphones", N_("Analog Headphones") },
2292 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
2293 { "analog-output-mono", N_("Analog Mono Output") },
2294 { "analog-output-speaker", N_("Analog Speakers") },
2295 { "iec958-stereo-output", N_("Digital Output (IEC958)") },
2296 { "iec958-passthrough-output", N_("Digital Passthrough (IEC958)") }
2303 PA_LLIST_FOREACH(e
, p
->elements
)
2304 if (element_verify(e
) < 0)
2307 if (!p
->description
)
2308 p
->description
= pa_xstrdup(lookup_description(p
->name
,
2309 well_known_descriptions
,
2310 PA_ELEMENTSOF(well_known_descriptions
)));
2312 if (!p
->description
)
2313 p
->description
= pa_xstrdup(p
->name
);
2318 pa_alsa_path
* pa_alsa_path_new(const char *fname
, pa_alsa_direction_t direction
) {
2324 pa_config_item items
[] = {
2326 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
2327 { "description", pa_config_parse_string
, NULL
, "General" },
2328 { "name", pa_config_parse_string
, NULL
, "General" },
2331 { "priority", option_parse_priority
, NULL
, NULL
},
2332 { "name", option_parse_name
, NULL
, NULL
},
2335 { "switch", element_parse_switch
, NULL
, NULL
},
2336 { "volume", element_parse_volume
, NULL
, NULL
},
2337 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
2338 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
2339 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
2340 /* ... later on we might add override-map.3 and so on here ... */
2341 { "required", element_parse_required
, NULL
, NULL
},
2342 { "required-any", element_parse_required
, NULL
, NULL
},
2343 { "required-absent", element_parse_required
, NULL
, NULL
},
2344 { "direction", element_parse_direction
, NULL
, NULL
},
2345 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2346 { "volume-limit", element_parse_volume_limit
, NULL
, NULL
},
2347 { NULL
, NULL
, NULL
, NULL
}
2352 p
= pa_xnew0(pa_alsa_path
, 1);
2353 n
= pa_path_get_filename(fname
);
2354 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2355 p
->direction
= direction
;
2357 items
[0].data
= &p
->priority
;
2358 items
[1].data
= &p
->description
;
2359 items
[2].data
= &p
->name
;
2361 fn
= pa_maybe_prefix_path(fname
,
2362 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/paths/" :
2365 r
= pa_config_parse(fn
, NULL
, items
, p
);
2371 if (path_verify(p
) < 0)
2377 pa_alsa_path_free(p
);
2381 pa_alsa_path
*pa_alsa_path_synthesize(const char *element
, pa_alsa_direction_t direction
) {
2387 p
= pa_xnew0(pa_alsa_path
, 1);
2388 p
->name
= pa_xstrdup(element
);
2389 p
->direction
= direction
;
2391 e
= pa_xnew0(pa_alsa_element
, 1);
2393 e
->alsa_name
= pa_xstrdup(element
);
2394 e
->direction
= direction
;
2395 e
->volume_limit
= -1;
2397 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2398 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2400 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2401 p
->last_element
= e
;
2405 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2406 pa_alsa_option
*o
, *n
;
2410 for (o
= e
->options
; o
; o
= n
) {
2413 if (o
->alsa_idx
< 0) {
2414 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2420 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2421 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2422 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2425 static void path_drop_unsupported(pa_alsa_path
*p
) {
2426 pa_alsa_element
*e
, *n
;
2430 for (e
= p
->elements
; e
; e
= n
) {
2433 if (!element_drop_unsupported(e
)) {
2434 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2440 static void path_make_options_unique(pa_alsa_path
*p
) {
2442 pa_alsa_option
*o
, *u
;
2444 PA_LLIST_FOREACH(e
, p
->elements
) {
2445 PA_LLIST_FOREACH(o
, e
->options
) {
2449 for (u
= o
->next
; u
; u
= u
->next
)
2450 if (pa_streq(u
->name
, o
->name
))
2456 m
= pa_xstrdup(o
->name
);
2458 /* OK, this name is not unique, hence let's rename */
2459 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2462 if (!pa_streq(u
->name
, m
))
2465 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2469 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2470 pa_xfree(u
->description
);
2471 u
->description
= nd
;
2481 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2484 for (; e
; e
= e
->next
)
2485 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2486 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2492 for (o
= e
->options
; o
; o
= o
->next
) {
2496 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2497 s
->options
= pa_idxset_copy(template->options
);
2498 s
->name
= pa_sprintf_malloc(_("%s+%s"), template->name
, o
->name
);
2500 (template->description
[0] && o
->description
[0])
2501 ? pa_sprintf_malloc(_("%s / %s"), template->description
, o
->description
)
2502 : (template->description
[0]
2503 ? pa_xstrdup(template->description
)
2504 : pa_xstrdup(o
->description
));
2506 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2508 s
= pa_xnew0(pa_alsa_setting
, 1);
2509 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2510 s
->name
= pa_xstrdup(o
->name
);
2511 s
->description
= pa_xstrdup(o
->description
);
2512 s
->priority
= o
->priority
;
2515 pa_idxset_put(s
->options
, o
, NULL
);
2517 if (element_create_settings(e
->next
, s
))
2518 /* This is not a leaf, so let's get rid of it */
2521 /* This is a leaf, so let's add it */
2522 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2524 e
->path
->last_setting
= s
;
2531 static void path_create_settings(pa_alsa_path
*p
) {
2534 element_create_settings(p
->elements
, NULL
);
2537 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2539 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2540 pa_channel_position_t t
;
2541 pa_channel_position_mask_t path_volume_channels
= 0;
2552 pa_log_debug("Probing path '%s'", p
->name
);
2554 PA_LLIST_FOREACH(e
, p
->elements
) {
2555 if (element_probe(e
, m
) < 0) {
2556 p
->supported
= FALSE
;
2557 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2560 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
);
2565 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2567 if (!p
->has_volume
) {
2568 p
->min_volume
= e
->min_volume
;
2569 p
->max_volume
= e
->max_volume
;
2573 if (!p
->has_volume
) {
2574 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2575 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2576 min_dB
[t
] = e
->min_dB
;
2577 max_dB
[t
] = e
->max_dB
;
2578 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2585 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2586 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2587 min_dB
[t
] += e
->min_dB
;
2588 max_dB
[t
] += e
->max_dB
;
2589 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2592 /* Hmm, there's another element before us
2593 * which cannot do dB volumes, so we we need
2594 * to 'neutralize' this slider */
2595 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2596 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2599 } else if (p
->has_volume
) {
2600 /* We can't use this volume, so let's ignore it */
2601 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2602 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2604 p
->has_volume
= TRUE
;
2607 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2611 if (p
->has_req_any
&& !p
->req_any_present
) {
2612 p
->supported
= FALSE
;
2613 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2617 path_drop_unsupported(p
);
2618 path_make_options_unique(p
);
2619 path_create_settings(p
);
2621 p
->supported
= TRUE
;
2624 p
->min_dB
= INFINITY
;
2625 p
->max_dB
= -INFINITY
;
2627 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2628 if (path_volume_channels
& PA_CHANNEL_POSITION_MASK(t
)) {
2629 if (p
->min_dB
> min_dB
[t
])
2630 p
->min_dB
= min_dB
[t
];
2632 if (p
->max_dB
< max_dB
[t
])
2633 p
->max_dB
= max_dB
[t
];
2640 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2643 pa_log_debug("Setting %s (%s) priority=%u",
2645 pa_strnull(s
->description
),
2649 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2652 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2654 pa_strnull(o
->name
),
2655 pa_strnull(o
->description
),
2660 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2664 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",
2674 (long long unsigned) e
->merged_mask
,
2676 pa_yes_no(e
->override_map
));
2678 PA_LLIST_FOREACH(o
, e
->options
)
2679 pa_alsa_option_dump(o
);
2682 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2687 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2688 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2690 pa_strnull(p
->description
),
2693 pa_yes_no(p
->probed
),
2694 pa_yes_no(p
->supported
),
2695 pa_yes_no(p
->has_mute
),
2696 pa_yes_no(p
->has_volume
),
2697 pa_yes_no(p
->has_dB
),
2698 p
->min_volume
, p
->max_volume
,
2699 p
->min_dB
, p
->max_dB
);
2701 PA_LLIST_FOREACH(e
, p
->elements
)
2702 pa_alsa_element_dump(e
);
2704 PA_LLIST_FOREACH(s
, p
->settings
)
2705 pa_alsa_setting_dump(s
);
2708 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2709 snd_mixer_selem_id_t
*sid
;
2710 snd_mixer_elem_t
*me
;
2716 SELEM_INIT(sid
, e
->alsa_name
);
2717 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2718 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2722 snd_mixer_elem_set_callback(me
, cb
);
2723 snd_mixer_elem_set_callback_private(me
, userdata
);
2726 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2733 PA_LLIST_FOREACH(e
, p
->elements
)
2734 element_set_callback(e
, m
, cb
, userdata
);
2737 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2744 PA_LLIST_FOREACH(p
, ps
->paths
)
2745 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2748 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
) {
2749 pa_alsa_path_set
*ps
;
2750 char **pn
= NULL
, **en
= NULL
, **ie
;
2751 pa_alsa_decibel_fix
*db_fix
;
2755 pa_assert(m
->profile_set
);
2756 pa_assert(m
->profile_set
->decibel_fixes
);
2757 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2759 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2762 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2763 ps
->direction
= direction
;
2765 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2766 pn
= m
->output_path_names
;
2767 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2768 pn
= m
->input_path_names
;
2773 for (in
= pn
; *in
; in
++) {
2775 pa_bool_t duplicate
= FALSE
;
2778 for (kn
= pn
; kn
< in
; kn
++)
2779 if (pa_streq(*kn
, *in
)) {
2787 fn
= pa_sprintf_malloc("%s.conf", *in
);
2789 if ((p
= pa_alsa_path_new(fn
, direction
))) {
2791 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2801 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2802 en
= m
->output_element
;
2803 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2804 en
= m
->input_element
;
2807 pa_alsa_path_set_free(ps
);
2811 for (ie
= en
; *ie
; ie
++) {
2815 p
= pa_alsa_path_synthesize(*ie
, direction
);
2818 /* Mark all other passed elements for require-absent */
2819 for (je
= en
; *je
; je
++) {
2825 e
= pa_xnew0(pa_alsa_element
, 1);
2827 e
->alsa_name
= pa_xstrdup(*je
);
2828 e
->direction
= direction
;
2829 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2830 e
->volume_limit
= -1;
2832 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2833 p
->last_element
= e
;
2836 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2841 /* Assign decibel fixes to elements. */
2842 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2845 PA_LLIST_FOREACH(p
, ps
->paths
) {
2848 PA_LLIST_FOREACH(e
, p
->elements
) {
2849 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2850 /* The profile set that contains the dB fix may be freed
2851 * before the element, so we have to copy the dB fix
2853 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2854 e
->db_fix
->profile_set
= NULL
;
2855 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2856 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
2865 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2869 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2872 pa_yes_no(ps
->probed
));
2874 PA_LLIST_FOREACH(p
, ps
->paths
)
2875 pa_alsa_path_dump(p
);
2878 static void path_set_unify(pa_alsa_path_set
*ps
) {
2880 pa_bool_t has_dB
= TRUE
, has_volume
= TRUE
, has_mute
= TRUE
;
2883 /* We have issues dealing with paths that vary too wildly. That
2884 * means for now we have to have all paths support volume/mute/dB
2887 PA_LLIST_FOREACH(p
, ps
->paths
) {
2888 pa_assert(p
->probed
);
2892 else if (!p
->has_dB
)
2899 if (!has_volume
|| !has_dB
|| !has_mute
) {
2902 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2904 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2907 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2909 PA_LLIST_FOREACH(p
, ps
->paths
) {
2911 p
->has_volume
= FALSE
;
2916 p
->has_mute
= FALSE
;
2921 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
2922 pa_alsa_path
*p
, *q
;
2924 PA_LLIST_FOREACH(p
, ps
->paths
) {
2928 for (q
= p
->next
; q
; q
= q
->next
)
2929 if (pa_streq(q
->name
, p
->name
))
2935 m
= pa_xstrdup(p
->name
);
2937 /* OK, this name is not unique, hence let's rename */
2938 for (i
= 1, q
= p
; q
; q
= q
->next
) {
2941 if (!pa_streq(q
->name
, m
))
2944 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2948 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
2949 pa_xfree(q
->description
);
2950 q
->description
= nd
;
2959 void pa_alsa_path_set_probe(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2960 pa_alsa_path
*p
, *n
;
2967 for (p
= ps
->paths
; p
; p
= n
) {
2970 if (pa_alsa_path_probe(p
, m
, ignore_dB
) < 0) {
2971 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
2972 pa_alsa_path_free(p
);
2977 path_set_make_paths_unique(ps
);
2981 static void mapping_free(pa_alsa_mapping
*m
) {
2985 pa_xfree(m
->description
);
2987 pa_xstrfreev(m
->device_strings
);
2988 pa_xstrfreev(m
->input_path_names
);
2989 pa_xstrfreev(m
->output_path_names
);
2990 pa_xstrfreev(m
->input_element
);
2991 pa_xstrfreev(m
->output_element
);
2993 pa_assert(!m
->input_pcm
);
2994 pa_assert(!m
->output_pcm
);
2999 static void profile_free(pa_alsa_profile
*p
) {
3003 pa_xfree(p
->description
);
3005 pa_xstrfreev(p
->input_mapping_names
);
3006 pa_xstrfreev(p
->output_mapping_names
);
3008 if (p
->input_mappings
)
3009 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
3011 if (p
->output_mappings
)
3012 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
3017 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
3023 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
3026 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
3032 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
3035 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
3038 if (ps
->decibel_fixes
) {
3039 pa_alsa_decibel_fix
*db_fix
;
3041 while ((db_fix
= pa_hashmap_steal_first(ps
->decibel_fixes
)))
3042 decibel_fix_free(db_fix
);
3044 pa_hashmap_free(ps
->decibel_fixes
, NULL
, NULL
);
3050 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
3053 if (!pa_startswith(name
, "Mapping "))
3058 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
3061 m
= pa_xnew0(pa_alsa_mapping
, 1);
3062 m
->profile_set
= ps
;
3063 m
->name
= pa_xstrdup(name
);
3064 pa_channel_map_init(&m
->channel_map
);
3066 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
3071 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
3074 if (!pa_startswith(name
, "Profile "))
3079 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
3082 p
= pa_xnew0(pa_alsa_profile
, 1);
3083 p
->profile_set
= ps
;
3084 p
->name
= pa_xstrdup(name
);
3086 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3091 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
3092 pa_alsa_decibel_fix
*db_fix
;
3094 if (!pa_startswith(name
, "DecibelFix "))
3099 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
3102 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
3103 db_fix
->profile_set
= ps
;
3104 db_fix
->name
= pa_xstrdup(name
);
3106 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
3111 static int mapping_parse_device_strings(
3112 const char *filename
,
3114 const char *section
,
3120 pa_alsa_profile_set
*ps
= userdata
;
3125 if (!(m
= mapping_get(ps
, section
))) {
3126 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3130 pa_xstrfreev(m
->device_strings
);
3131 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
3132 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
3139 static int mapping_parse_channel_map(
3140 const char *filename
,
3142 const char *section
,
3148 pa_alsa_profile_set
*ps
= userdata
;
3153 if (!(m
= mapping_get(ps
, section
))) {
3154 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3158 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
3159 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
3166 static int mapping_parse_paths(
3167 const char *filename
,
3169 const char *section
,
3175 pa_alsa_profile_set
*ps
= userdata
;
3180 if (!(m
= mapping_get(ps
, section
))) {
3181 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3185 if (pa_streq(lvalue
, "paths-input")) {
3186 pa_xstrfreev(m
->input_path_names
);
3187 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
3189 pa_xstrfreev(m
->output_path_names
);
3190 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
3196 static int mapping_parse_element(
3197 const char *filename
,
3199 const char *section
,
3205 pa_alsa_profile_set
*ps
= userdata
;
3210 if (!(m
= mapping_get(ps
, section
))) {
3211 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3215 if (pa_streq(lvalue
, "element-input")) {
3216 pa_xstrfreev(m
->input_element
);
3217 m
->input_element
= pa_split_spaces_strv(rvalue
);
3219 pa_xstrfreev(m
->output_element
);
3220 m
->output_element
= pa_split_spaces_strv(rvalue
);
3226 static int mapping_parse_direction(
3227 const char *filename
,
3229 const char *section
,
3235 pa_alsa_profile_set
*ps
= userdata
;
3240 if (!(m
= mapping_get(ps
, section
))) {
3241 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3245 if (pa_streq(rvalue
, "input"))
3246 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3247 else if (pa_streq(rvalue
, "output"))
3248 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3249 else if (pa_streq(rvalue
, "any"))
3250 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3252 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
3259 static int mapping_parse_description(
3260 const char *filename
,
3262 const char *section
,
3268 pa_alsa_profile_set
*ps
= userdata
;
3274 if ((m
= mapping_get(ps
, section
))) {
3275 pa_xfree(m
->description
);
3276 m
->description
= pa_xstrdup(rvalue
);
3277 } else if ((p
= profile_get(ps
, section
))) {
3278 pa_xfree(p
->description
);
3279 p
->description
= pa_xstrdup(rvalue
);
3281 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3288 static int mapping_parse_priority(
3289 const char *filename
,
3291 const char *section
,
3297 pa_alsa_profile_set
*ps
= userdata
;
3304 if (pa_atou(rvalue
, &prio
) < 0) {
3305 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
3309 if ((m
= mapping_get(ps
, section
)))
3311 else if ((p
= profile_get(ps
, section
)))
3314 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3321 static int profile_parse_mappings(
3322 const char *filename
,
3324 const char *section
,
3330 pa_alsa_profile_set
*ps
= userdata
;
3335 if (!(p
= profile_get(ps
, section
))) {
3336 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3340 if (pa_streq(lvalue
, "input-mappings")) {
3341 pa_xstrfreev(p
->input_mapping_names
);
3342 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
3344 pa_xstrfreev(p
->output_mapping_names
);
3345 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
3351 static int profile_parse_skip_probe(
3352 const char *filename
,
3354 const char *section
,
3360 pa_alsa_profile_set
*ps
= userdata
;
3366 if (!(p
= profile_get(ps
, section
))) {
3367 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3371 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
3372 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
3381 static int decibel_fix_parse_db_values(
3382 const char *filename
,
3384 const char *section
,
3390 pa_alsa_profile_set
*ps
= userdata
;
3391 pa_alsa_decibel_fix
*db_fix
;
3395 unsigned n
= 8; /* Current size of the db_values table. */
3396 unsigned min_step
= 0;
3397 unsigned max_step
= 0;
3398 unsigned i
= 0; /* Index to the items table. */
3399 unsigned prev_step
= 0;
3402 pa_assert(filename
);
3408 if (!(db_fix
= decibel_fix_get(ps
, section
))) {
3409 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3413 if (!(items
= pa_split_spaces_strv(rvalue
))) {
3414 pa_log("[%s:%u] Value missing", pa_strnull(filename
), line
);
3418 db_values
= pa_xnew(long, n
);
3420 while ((item
= items
[i
++])) {
3421 char *s
= item
; /* Step value string. */
3422 char *d
= item
; /* dB value string. */
3426 /* Move d forward until it points to a colon or to the end of the item. */
3427 for (; *d
&& *d
!= ':'; ++d
);
3430 /* item started with colon. */
3431 pa_log("[%s:%u] No step value found in %s", filename
, line
, item
);
3435 if (!*d
|| !*(d
+ 1)) {
3436 /* No colon found, or it was the last character in item. */
3437 pa_log("[%s:%u] No dB value found in %s", filename
, line
, item
);
3441 /* pa_atou() needs a null-terminating string. Let's replace the colon
3442 * with a zero byte. */
3445 if (pa_atou(s
, &step
) < 0) {
3446 pa_log("[%s:%u] Invalid step value: %s", filename
, line
, s
);
3450 if (pa_atod(d
, &db
) < 0) {
3451 pa_log("[%s:%u] Invalid dB value: %s", filename
, line
, d
);
3455 if (step
<= prev_step
&& i
!= 1) {
3456 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename
, line
, step
, prev_step
);
3460 if (db
< prev_db
&& i
!= 1) {
3461 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename
, line
, db
, prev_db
);
3467 db_values
[0] = (long) (db
* 100.0);
3471 /* Interpolate linearly. */
3472 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3474 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3476 /* Reallocate the db_values table if it's about to overflow. */
3477 if (prev_step
+ 1 - min_step
== n
) {
3479 db_values
= pa_xrenew(long, db_values
, n
);
3482 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3489 db_fix
->min_step
= min_step
;
3490 db_fix
->max_step
= max_step
;
3491 pa_xfree(db_fix
->db_values
);
3492 db_fix
->db_values
= db_values
;
3494 pa_xstrfreev(items
);
3499 pa_xstrfreev(items
);
3500 pa_xfree(db_values
);
3505 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3507 static const struct description_map well_known_descriptions
[] = {
3508 { "analog-mono", N_("Analog Mono") },
3509 { "analog-stereo", N_("Analog Stereo") },
3510 { "analog-surround-21", N_("Analog Surround 2.1") },
3511 { "analog-surround-30", N_("Analog Surround 3.0") },
3512 { "analog-surround-31", N_("Analog Surround 3.1") },
3513 { "analog-surround-40", N_("Analog Surround 4.0") },
3514 { "analog-surround-41", N_("Analog Surround 4.1") },
3515 { "analog-surround-50", N_("Analog Surround 5.0") },
3516 { "analog-surround-51", N_("Analog Surround 5.1") },
3517 { "analog-surround-61", N_("Analog Surround 6.0") },
3518 { "analog-surround-61", N_("Analog Surround 6.1") },
3519 { "analog-surround-70", N_("Analog Surround 7.0") },
3520 { "analog-surround-71", N_("Analog Surround 7.1") },
3521 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3522 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3523 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3524 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3525 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3530 if (!pa_channel_map_valid(&m
->channel_map
)) {
3531 pa_log("Mapping %s is missing channel map.", m
->name
);
3535 if (!m
->device_strings
) {
3536 pa_log("Mapping %s is missing device strings.", m
->name
);
3540 if ((m
->input_path_names
&& m
->input_element
) ||
3541 (m
->output_path_names
&& m
->output_element
)) {
3542 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3546 if (!m
->description
)
3547 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3548 well_known_descriptions
,
3549 PA_ELEMENTSOF(well_known_descriptions
)));
3551 if (!m
->description
)
3552 m
->description
= pa_xstrdup(m
->name
);
3555 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3557 else if (m
->channel_map
.channels
== bonus
->channels
)
3564 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3565 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3569 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3571 pa_strnull(m
->description
),
3573 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3574 pa_yes_no(m
->supported
),
3578 static void profile_set_add_auto_pair(
3579 pa_alsa_profile_set
*ps
,
3580 pa_alsa_mapping
*m
, /* output */
3581 pa_alsa_mapping
*n
/* input */) {
3589 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3592 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3596 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3598 name
= pa_sprintf_malloc("output:%s", m
->name
);
3600 name
= pa_sprintf_malloc("input:%s", n
->name
);
3602 if (pa_hashmap_get(ps
->profiles
, name
)) {
3607 p
= pa_xnew0(pa_alsa_profile
, 1);
3608 p
->profile_set
= ps
;
3612 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3613 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3614 p
->priority
+= m
->priority
* 100;
3618 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3619 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3620 p
->priority
+= n
->priority
;
3623 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3626 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3627 pa_alsa_mapping
*m
, *n
;
3628 void *m_state
, *n_state
;
3632 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3633 profile_set_add_auto_pair(ps
, m
, NULL
);
3635 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3636 profile_set_add_auto_pair(ps
, m
, n
);
3639 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3640 profile_set_add_auto_pair(ps
, NULL
, n
);
3643 static int profile_verify(pa_alsa_profile
*p
) {
3645 static const struct description_map well_known_descriptions
[] = {
3646 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3647 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3648 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3649 { "off", N_("Off") }
3654 /* Replace the output mapping names by the actual mappings */
3655 if (p
->output_mapping_names
) {
3658 pa_assert(!p
->output_mappings
);
3659 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3661 for (name
= p
->output_mapping_names
; *name
; name
++) {
3664 pa_bool_t duplicate
= FALSE
;
3666 for (in
= name
+ 1; *in
; in
++)
3667 if (pa_streq(*name
, *in
)) {
3675 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3676 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3680 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3686 pa_xstrfreev(p
->output_mapping_names
);
3687 p
->output_mapping_names
= NULL
;
3690 /* Replace the input mapping names by the actual mappings */
3691 if (p
->input_mapping_names
) {
3694 pa_assert(!p
->input_mappings
);
3695 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3697 for (name
= p
->input_mapping_names
; *name
; name
++) {
3700 pa_bool_t duplicate
= FALSE
;
3702 for (in
= name
+ 1; *in
; in
++)
3703 if (pa_streq(*name
, *in
)) {
3711 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3712 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3716 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3722 pa_xstrfreev(p
->input_mapping_names
);
3723 p
->input_mapping_names
= NULL
;
3726 if (!p
->input_mappings
&& !p
->output_mappings
) {
3727 pa_log("Profile '%s' lacks mappings.", p
->name
);
3731 if (!p
->description
)
3732 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3733 well_known_descriptions
,
3734 PA_ELEMENTSOF(well_known_descriptions
)));
3736 if (!p
->description
) {
3741 sb
= pa_strbuf_new();
3743 if (p
->output_mappings
)
3744 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3745 if (!pa_strbuf_isempty(sb
))
3746 pa_strbuf_puts(sb
, " + ");
3748 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
3751 if (p
->input_mappings
)
3752 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3753 if (!pa_strbuf_isempty(sb
))
3754 pa_strbuf_puts(sb
, " + ");
3756 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
3759 p
->description
= pa_strbuf_tostring_free(sb
);
3765 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
3770 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3772 pa_strnull(p
->description
),
3774 pa_yes_no(p
->supported
),
3775 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
3776 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
3778 if (p
->input_mappings
)
3779 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
3780 pa_log_debug("Input %s", m
->name
);
3782 if (p
->output_mappings
)
3783 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
3784 pa_log_debug("Output %s", m
->name
);
3787 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
3790 /* Check that the dB mapping has been configured. Since "db-values" is
3791 * currently the only option in the DecibelFix section, and decibel fix
3792 * objects don't get created if a DecibelFix section is empty, this is
3793 * actually a redundant check. Having this may prevent future bugs,
3795 if (!db_fix
->db_values
) {
3796 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
3803 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
3804 char *db_values
= NULL
;
3808 if (db_fix
->db_values
) {
3810 unsigned long i
, nsteps
;
3812 pa_assert(db_fix
->min_step
<= db_fix
->max_step
);
3813 nsteps
= db_fix
->max_step
- db_fix
->min_step
+ 1;
3815 buf
= pa_strbuf_new();
3816 for (i
= 0; i
< nsteps
; ++i
)
3817 pa_strbuf_printf(buf
, "[%li]:%0.2f ", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
3819 db_values
= pa_strbuf_tostring_free(buf
);
3822 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
3823 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
3825 pa_xfree(db_values
);
3828 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
3829 pa_alsa_profile_set
*ps
;
3832 pa_alsa_decibel_fix
*db_fix
;
3837 static pa_config_item items
[] = {
3839 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
3842 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
3843 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
3844 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
3845 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
3846 { "element-input", mapping_parse_element
, NULL
, NULL
},
3847 { "element-output", mapping_parse_element
, NULL
, NULL
},
3848 { "direction", mapping_parse_direction
, NULL
, NULL
},
3850 /* Shared by [Mapping ...] and [Profile ...] */
3851 { "description", mapping_parse_description
, NULL
, NULL
},
3852 { "priority", mapping_parse_priority
, NULL
, NULL
},
3855 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
3856 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
3857 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
3859 /* [DecibelFix ...] */
3860 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
3861 { NULL
, NULL
, NULL
, NULL
}
3864 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
3865 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3866 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3867 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3869 items
[0].data
= &ps
->auto_profiles
;
3872 fname
= "default.conf";
3874 fn
= pa_maybe_prefix_path(fname
,
3875 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
3876 PA_ALSA_PROFILE_SETS_DIR
);
3878 r
= pa_config_parse(fn
, NULL
, items
, ps
);
3884 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3885 if (mapping_verify(m
, bonus
) < 0)
3888 if (ps
->auto_profiles
)
3889 profile_set_add_auto(ps
);
3891 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3892 if (profile_verify(p
) < 0)
3895 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
3896 if (decibel_fix_verify(db_fix
) < 0)
3902 pa_alsa_profile_set_free(ps
);
3906 void pa_alsa_profile_set_probe(
3907 pa_alsa_profile_set
*ps
,
3909 const pa_sample_spec
*ss
,
3910 unsigned default_n_fragments
,
3911 unsigned default_fragment_size_msec
) {
3914 pa_alsa_profile
*p
, *last
= NULL
;
3924 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
3925 pa_sample_spec try_ss
;
3926 pa_channel_map try_map
;
3927 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
3930 /* Is this already marked that it is supported? (i.e. from the config file) */
3934 pa_log_debug("Looking at profile %s", p
->name
);
3936 /* Close PCMs from the last iteration we don't need anymore */
3937 if (last
&& last
->output_mappings
)
3938 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
) {
3943 if (last
->supported
)
3946 if (!p
->output_mappings
|| !pa_idxset_get_by_data(p
->output_mappings
, m
, NULL
)) {
3947 snd_pcm_close(m
->output_pcm
);
3948 m
->output_pcm
= NULL
;
3952 if (last
&& last
->input_mappings
)
3953 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
) {
3958 if (last
->supported
)
3961 if (!p
->input_mappings
|| !pa_idxset_get_by_data(p
->input_mappings
, m
, NULL
)) {
3962 snd_pcm_close(m
->input_pcm
);
3963 m
->input_pcm
= NULL
;
3967 p
->supported
= TRUE
;
3969 /* Check if we can open all new ones */
3970 if (p
->output_mappings
)
3971 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3976 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
3977 try_map
= m
->channel_map
;
3979 try_ss
.channels
= try_map
.channels
;
3982 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
3983 pa_frame_size(&try_ss
);
3984 try_buffer_size
= default_n_fragments
* try_period_size
;
3986 if (!(m
->output_pcm
= pa_alsa_open_by_template(
3991 SND_PCM_STREAM_PLAYBACK
,
3992 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3994 p
->supported
= FALSE
;
3999 if (p
->input_mappings
&& p
->supported
)
4000 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4005 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
4006 try_map
= m
->channel_map
;
4008 try_ss
.channels
= try_map
.channels
;
4011 pa_usec_to_bytes(default_fragment_size_msec
*PA_USEC_PER_MSEC
, &try_ss
) /
4012 pa_frame_size(&try_ss
);
4013 try_buffer_size
= default_n_fragments
* try_period_size
;
4015 if (!(m
->input_pcm
= pa_alsa_open_by_template(
4020 SND_PCM_STREAM_CAPTURE
,
4021 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
4023 p
->supported
= FALSE
;
4031 pa_log_debug("Profile %s supported.", p
->name
);
4038 if (last
->output_mappings
)
4039 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
)
4040 if (m
->output_pcm
) {
4042 if (last
->supported
)
4045 snd_pcm_close(m
->output_pcm
);
4046 m
->output_pcm
= NULL
;
4049 if (last
->input_mappings
)
4050 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
)
4053 if (last
->supported
)
4056 snd_pcm_close(m
->input_pcm
);
4057 m
->input_pcm
= NULL
;
4061 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4062 if (!p
->supported
) {
4063 pa_hashmap_remove(ps
->profiles
, p
->name
);
4067 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4068 if (m
->supported
<= 0) {
4069 pa_hashmap_remove(ps
->mappings
, m
->name
);
4076 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
4079 pa_alsa_decibel_fix
*db_fix
;
4084 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4087 pa_yes_no(ps
->auto_profiles
),
4088 pa_yes_no(ps
->probed
),
4089 pa_hashmap_size(ps
->mappings
),
4090 pa_hashmap_size(ps
->profiles
),
4091 pa_hashmap_size(ps
->decibel_fixes
));
4093 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4094 pa_alsa_mapping_dump(m
);
4096 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4097 pa_alsa_profile_dump(p
);
4099 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4100 pa_alsa_decibel_fix_dump(db_fix
);
4103 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
) {
4110 /* if there is no path, we don't want a port list */
4114 if (!ps
->paths
->next
){
4117 /* If there is only one path, but no or only one setting, then
4118 * we want a port list either */
4119 if (!ps
->paths
->settings
|| !ps
->paths
->settings
->next
)
4122 /* Ok, there is only one path, however with multiple settings,
4123 * so let's create a port for each setting */
4124 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4126 PA_LLIST_FOREACH(s
, ps
->paths
->settings
) {
4127 pa_device_port
*port
;
4128 pa_alsa_port_data
*data
;
4130 port
= pa_device_port_new(s
->name
, s
->description
, sizeof(pa_alsa_port_data
));
4131 port
->priority
= s
->priority
;
4133 data
= PA_DEVICE_PORT_DATA(port
);
4134 data
->path
= ps
->paths
;
4137 pa_hashmap_put(*p
, port
->name
, port
);
4142 /* We have multiple paths, so let's create a port for each
4143 * one, and each of each settings */
4144 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4146 PA_LLIST_FOREACH(path
, ps
->paths
) {
4148 if (!path
->settings
|| !path
->settings
->next
) {
4149 pa_device_port
*port
;
4150 pa_alsa_port_data
*data
;
4152 /* If there is no or just one setting we only need a
4155 port
= pa_device_port_new(path
->name
, path
->description
, sizeof(pa_alsa_port_data
));
4156 port
->priority
= path
->priority
* 100;
4159 data
= PA_DEVICE_PORT_DATA(port
);
4161 data
->setting
= path
->settings
;
4163 pa_hashmap_put(*p
, port
->name
, port
);
4167 PA_LLIST_FOREACH(s
, path
->settings
) {
4168 pa_device_port
*port
;
4169 pa_alsa_port_data
*data
;
4172 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4174 if (s
->description
[0])
4175 d
= pa_sprintf_malloc(_("%s / %s"), path
->description
, s
->description
);
4177 d
= pa_xstrdup(path
->description
);
4179 port
= pa_device_port_new(n
, d
, sizeof(pa_alsa_port_data
));
4180 port
->priority
= path
->priority
* 100 + s
->priority
;
4185 data
= PA_DEVICE_PORT_DATA(port
);
4189 pa_hashmap_put(*p
, port
->name
, port
);
4195 pa_log_debug("Added %u ports", pa_hashmap_size(*p
));