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>
28 #include <asoundlib.h>
31 #ifdef HAVE_VALGRIND_MEMCHECK_H
32 #include <valgrind/memcheck.h>
35 #include <pulse/mainloop-api.h>
36 #include <pulse/sample.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/volume.h>
40 #include <pulse/xmalloc.h>
41 #include <pulse/i18n.h>
42 #include <pulse/utf8.h>
44 #include <pulsecore/log.h>
45 #include <pulsecore/macro.h>
46 #include <pulsecore/core-util.h>
47 #include <pulsecore/conf-parser.h>
48 #include <pulsecore/strbuf.h>
50 #include "alsa-mixer.h"
51 #include "alsa-util.h"
53 struct description_map
{
55 const char *description
;
58 static const char *lookup_description(const char *name
, const struct description_map dm
[], unsigned n
) {
61 for (i
= 0; i
< n
; i
++)
62 if (pa_streq(dm
[i
].name
, name
))
63 return _(dm
[i
].description
);
68 struct pa_alsa_fdlist
{
71 /* This is a temporary buffer used to avoid lots of mallocs */
72 struct pollfd
*work_fds
;
77 pa_defer_event
*defer
;
82 void (*cb
)(void *userdata
);
86 static void io_cb(pa_mainloop_api
*a
, pa_io_event
*e
, int fd
, pa_io_event_flags_t events
, void *userdata
) {
88 struct pa_alsa_fdlist
*fdl
= userdata
;
91 unsigned short revents
;
95 pa_assert(fdl
->mixer
);
97 pa_assert(fdl
->work_fds
);
104 memcpy(fdl
->work_fds
, fdl
->fds
, sizeof(struct pollfd
) * fdl
->num_fds
);
106 for (i
= 0; i
< fdl
->num_fds
; i
++) {
107 if (e
== fdl
->ios
[i
]) {
108 if (events
& PA_IO_EVENT_INPUT
)
109 fdl
->work_fds
[i
].revents
|= POLLIN
;
110 if (events
& PA_IO_EVENT_OUTPUT
)
111 fdl
->work_fds
[i
].revents
|= POLLOUT
;
112 if (events
& PA_IO_EVENT_ERROR
)
113 fdl
->work_fds
[i
].revents
|= POLLERR
;
114 if (events
& PA_IO_EVENT_HANGUP
)
115 fdl
->work_fds
[i
].revents
|= POLLHUP
;
120 pa_assert(i
!= fdl
->num_fds
);
122 if ((err
= snd_mixer_poll_descriptors_revents(fdl
->mixer
, fdl
->work_fds
, fdl
->num_fds
, &revents
)) < 0) {
123 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err
));
127 a
->defer_enable(fdl
->defer
, 1);
130 snd_mixer_handle_events(fdl
->mixer
);
133 static void defer_cb(pa_mainloop_api
*a
, pa_defer_event
*e
, void *userdata
) {
134 struct pa_alsa_fdlist
*fdl
= userdata
;
141 pa_assert(fdl
->mixer
);
143 a
->defer_enable(fdl
->defer
, 0);
145 if ((n
= snd_mixer_poll_descriptors_count(fdl
->mixer
)) < 0) {
146 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n
));
149 num_fds
= (unsigned) n
;
151 if (num_fds
!= fdl
->num_fds
) {
155 pa_xfree(fdl
->work_fds
);
156 fdl
->fds
= pa_xnew0(struct pollfd
, num_fds
);
157 fdl
->work_fds
= pa_xnew(struct pollfd
, num_fds
);
160 memset(fdl
->work_fds
, 0, sizeof(struct pollfd
) * num_fds
);
162 if ((err
= snd_mixer_poll_descriptors(fdl
->mixer
, fdl
->work_fds
, num_fds
)) < 0) {
163 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err
));
169 if (memcmp(fdl
->fds
, fdl
->work_fds
, sizeof(struct pollfd
) * num_fds
) == 0)
173 for (i
= 0; i
< fdl
->num_fds
; i
++)
174 a
->io_free(fdl
->ios
[i
]);
176 if (num_fds
!= fdl
->num_fds
) {
183 fdl
->ios
= pa_xnew(pa_io_event
*, num_fds
);
186 temp
= fdl
->work_fds
;
187 fdl
->work_fds
= fdl
->fds
;
190 fdl
->num_fds
= num_fds
;
192 for (i
= 0;i
< num_fds
;i
++)
193 fdl
->ios
[i
] = a
->io_new(a
, fdl
->fds
[i
].fd
,
194 ((fdl
->fds
[i
].events
& POLLIN
) ? PA_IO_EVENT_INPUT
: 0) |
195 ((fdl
->fds
[i
].events
& POLLOUT
) ? PA_IO_EVENT_OUTPUT
: 0),
199 struct pa_alsa_fdlist
*pa_alsa_fdlist_new(void) {
200 struct pa_alsa_fdlist
*fdl
;
202 fdl
= pa_xnew0(struct pa_alsa_fdlist
, 1);
207 void pa_alsa_fdlist_free(struct pa_alsa_fdlist
*fdl
) {
212 fdl
->m
->defer_free(fdl
->defer
);
218 for (i
= 0; i
< fdl
->num_fds
; i
++)
219 fdl
->m
->io_free(fdl
->ios
[i
]);
226 pa_xfree(fdl
->work_fds
);
231 int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist
*fdl
, snd_mixer_t
*mixer_handle
, pa_mainloop_api
*m
) {
233 pa_assert(mixer_handle
);
237 fdl
->mixer
= mixer_handle
;
239 fdl
->defer
= m
->defer_new(m
, defer_cb
, fdl
);
244 struct pa_alsa_mixer_pdata
{
246 pa_rtpoll_item
*poll_item
;
251 struct pa_alsa_mixer_pdata
*pa_alsa_mixer_pdata_new(void) {
252 struct pa_alsa_mixer_pdata
*pd
;
254 pd
= pa_xnew0(struct pa_alsa_mixer_pdata
, 1);
259 void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata
*pd
) {
263 pa_rtpoll_item_free(pd
->poll_item
);
269 static int rtpoll_work_cb(pa_rtpoll_item
*i
) {
270 struct pa_alsa_mixer_pdata
*pd
;
273 unsigned short revents
= 0;
276 pd
= pa_rtpoll_item_get_userdata(i
);
278 pa_assert_fp(i
== pd
->poll_item
);
280 p
= pa_rtpoll_item_get_pollfd(i
, &n_fds
);
282 if ((err
= snd_mixer_poll_descriptors_revents(pd
->mixer
, p
, n_fds
, &revents
)) < 0) {
283 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err
));
284 pa_rtpoll_item_free(i
);
289 snd_mixer_handle_events(pd
->mixer
);
290 pa_rtpoll_item_free(i
);
291 pa_alsa_set_mixer_rtpoll(pd
, pd
->mixer
, pd
->rtpoll
);
297 int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata
*pd
, snd_mixer_t
*mixer
, pa_rtpoll
*rtp
) {
306 if ((n
= snd_mixer_poll_descriptors_count(mixer
)) < 0) {
307 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n
));
311 i
= pa_rtpoll_item_new(rtp
, PA_RTPOLL_LATE
, (unsigned) n
);
313 p
= pa_rtpoll_item_get_pollfd(i
, NULL
);
315 memset(p
, 0, sizeof(struct pollfd
) * n
);
317 if ((err
= snd_mixer_poll_descriptors(mixer
, p
, (unsigned) n
)) < 0) {
318 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err
));
319 pa_rtpoll_item_free(i
);
327 pa_rtpoll_item_set_userdata(i
, pd
);
328 pa_rtpoll_item_set_work_callback(i
, rtpoll_work_cb
);
333 static int prepare_mixer(snd_mixer_t
*mixer
, const char *dev
) {
339 if ((err
= snd_mixer_attach(mixer
, dev
)) < 0) {
340 pa_log_info("Unable to attach to mixer %s: %s", dev
, pa_alsa_strerror(err
));
344 if ((err
= snd_mixer_selem_register(mixer
, NULL
, NULL
)) < 0) {
345 pa_log_warn("Unable to register mixer: %s", pa_alsa_strerror(err
));
349 if ((err
= snd_mixer_load(mixer
)) < 0) {
350 pa_log_warn("Unable to load mixer: %s", pa_alsa_strerror(err
));
354 pa_log_info("Successfully attached to mixer '%s'", dev
);
358 snd_mixer_t
*pa_alsa_open_mixer_for_pcm(snd_pcm_t
*pcm
, char **ctl_device
) {
362 snd_pcm_info_t
* info
;
363 snd_pcm_info_alloca(&info
);
367 if ((err
= snd_mixer_open(&m
, 0)) < 0) {
368 pa_log("Error opening mixer: %s", pa_alsa_strerror(err
));
372 /* First, try by name */
373 if ((dev
= snd_pcm_name(pcm
)))
374 if (prepare_mixer(m
, dev
) >= 0) {
376 *ctl_device
= pa_xstrdup(dev
);
381 /* Then, try by card index */
382 if (snd_pcm_info(pcm
, info
) >= 0) {
386 if ((card_idx
= snd_pcm_info_get_card(info
)) >= 0) {
388 md
= pa_sprintf_malloc("hw:%i", card_idx
);
390 if (!dev
|| !pa_streq(dev
, md
))
391 if (prepare_mixer(m
, md
) >= 0) {
409 static const snd_mixer_selem_channel_id_t alsa_channel_ids
[PA_CHANNEL_POSITION_MAX
] = {
410 [PA_CHANNEL_POSITION_MONO
] = SND_MIXER_SCHN_MONO
, /* The ALSA name is just an alias! */
412 [PA_CHANNEL_POSITION_FRONT_CENTER
] = SND_MIXER_SCHN_FRONT_CENTER
,
413 [PA_CHANNEL_POSITION_FRONT_LEFT
] = SND_MIXER_SCHN_FRONT_LEFT
,
414 [PA_CHANNEL_POSITION_FRONT_RIGHT
] = SND_MIXER_SCHN_FRONT_RIGHT
,
416 [PA_CHANNEL_POSITION_REAR_CENTER
] = SND_MIXER_SCHN_REAR_CENTER
,
417 [PA_CHANNEL_POSITION_REAR_LEFT
] = SND_MIXER_SCHN_REAR_LEFT
,
418 [PA_CHANNEL_POSITION_REAR_RIGHT
] = SND_MIXER_SCHN_REAR_RIGHT
,
420 [PA_CHANNEL_POSITION_LFE
] = SND_MIXER_SCHN_WOOFER
,
422 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
423 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
425 [PA_CHANNEL_POSITION_SIDE_LEFT
] = SND_MIXER_SCHN_SIDE_LEFT
,
426 [PA_CHANNEL_POSITION_SIDE_RIGHT
] = SND_MIXER_SCHN_SIDE_RIGHT
,
428 [PA_CHANNEL_POSITION_AUX0
] = SND_MIXER_SCHN_UNKNOWN
,
429 [PA_CHANNEL_POSITION_AUX1
] = SND_MIXER_SCHN_UNKNOWN
,
430 [PA_CHANNEL_POSITION_AUX2
] = SND_MIXER_SCHN_UNKNOWN
,
431 [PA_CHANNEL_POSITION_AUX3
] = SND_MIXER_SCHN_UNKNOWN
,
432 [PA_CHANNEL_POSITION_AUX4
] = SND_MIXER_SCHN_UNKNOWN
,
433 [PA_CHANNEL_POSITION_AUX5
] = SND_MIXER_SCHN_UNKNOWN
,
434 [PA_CHANNEL_POSITION_AUX6
] = SND_MIXER_SCHN_UNKNOWN
,
435 [PA_CHANNEL_POSITION_AUX7
] = SND_MIXER_SCHN_UNKNOWN
,
436 [PA_CHANNEL_POSITION_AUX8
] = SND_MIXER_SCHN_UNKNOWN
,
437 [PA_CHANNEL_POSITION_AUX9
] = SND_MIXER_SCHN_UNKNOWN
,
438 [PA_CHANNEL_POSITION_AUX10
] = SND_MIXER_SCHN_UNKNOWN
,
439 [PA_CHANNEL_POSITION_AUX11
] = SND_MIXER_SCHN_UNKNOWN
,
440 [PA_CHANNEL_POSITION_AUX12
] = SND_MIXER_SCHN_UNKNOWN
,
441 [PA_CHANNEL_POSITION_AUX13
] = SND_MIXER_SCHN_UNKNOWN
,
442 [PA_CHANNEL_POSITION_AUX14
] = SND_MIXER_SCHN_UNKNOWN
,
443 [PA_CHANNEL_POSITION_AUX15
] = SND_MIXER_SCHN_UNKNOWN
,
444 [PA_CHANNEL_POSITION_AUX16
] = SND_MIXER_SCHN_UNKNOWN
,
445 [PA_CHANNEL_POSITION_AUX17
] = SND_MIXER_SCHN_UNKNOWN
,
446 [PA_CHANNEL_POSITION_AUX18
] = SND_MIXER_SCHN_UNKNOWN
,
447 [PA_CHANNEL_POSITION_AUX19
] = SND_MIXER_SCHN_UNKNOWN
,
448 [PA_CHANNEL_POSITION_AUX20
] = SND_MIXER_SCHN_UNKNOWN
,
449 [PA_CHANNEL_POSITION_AUX21
] = SND_MIXER_SCHN_UNKNOWN
,
450 [PA_CHANNEL_POSITION_AUX22
] = SND_MIXER_SCHN_UNKNOWN
,
451 [PA_CHANNEL_POSITION_AUX23
] = SND_MIXER_SCHN_UNKNOWN
,
452 [PA_CHANNEL_POSITION_AUX24
] = SND_MIXER_SCHN_UNKNOWN
,
453 [PA_CHANNEL_POSITION_AUX25
] = SND_MIXER_SCHN_UNKNOWN
,
454 [PA_CHANNEL_POSITION_AUX26
] = SND_MIXER_SCHN_UNKNOWN
,
455 [PA_CHANNEL_POSITION_AUX27
] = SND_MIXER_SCHN_UNKNOWN
,
456 [PA_CHANNEL_POSITION_AUX28
] = SND_MIXER_SCHN_UNKNOWN
,
457 [PA_CHANNEL_POSITION_AUX29
] = SND_MIXER_SCHN_UNKNOWN
,
458 [PA_CHANNEL_POSITION_AUX30
] = SND_MIXER_SCHN_UNKNOWN
,
459 [PA_CHANNEL_POSITION_AUX31
] = SND_MIXER_SCHN_UNKNOWN
,
461 [PA_CHANNEL_POSITION_TOP_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
463 [PA_CHANNEL_POSITION_TOP_FRONT_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
464 [PA_CHANNEL_POSITION_TOP_FRONT_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
465 [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
,
467 [PA_CHANNEL_POSITION_TOP_REAR_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
468 [PA_CHANNEL_POSITION_TOP_REAR_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
469 [PA_CHANNEL_POSITION_TOP_REAR_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
472 static void setting_free(pa_alsa_setting
*s
) {
476 pa_idxset_free(s
->options
, NULL
, NULL
);
479 pa_xfree(s
->description
);
483 static void option_free(pa_alsa_option
*o
) {
486 pa_xfree(o
->alsa_name
);
488 pa_xfree(o
->description
);
492 static void decibel_fix_free(pa_alsa_decibel_fix
*db_fix
) {
495 pa_xfree(db_fix
->name
);
496 pa_xfree(db_fix
->db_values
);
501 static void element_free(pa_alsa_element
*e
) {
505 while ((o
= e
->options
)) {
506 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
511 decibel_fix_free(e
->db_fix
);
513 pa_xfree(e
->alsa_name
);
517 void pa_alsa_path_free(pa_alsa_path
*p
) {
523 while ((e
= p
->elements
)) {
524 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
528 while ((s
= p
->settings
)) {
529 PA_LLIST_REMOVE(pa_alsa_setting
, p
->settings
, s
);
534 pa_xfree(p
->description
);
538 void pa_alsa_path_set_free(pa_alsa_path_set
*ps
) {
542 while ((p
= ps
->paths
)) {
543 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
544 pa_alsa_path_free(p
);
550 static long to_alsa_dB(pa_volume_t v
) {
551 return (long) (pa_sw_volume_to_dB(v
) * 100.0);
554 static pa_volume_t
from_alsa_dB(long v
) {
555 return pa_sw_volume_from_dB((double) v
/ 100.0);
558 static long to_alsa_volume(pa_volume_t v
, long min
, long max
) {
561 w
= (long) round(((double) v
* (double) (max
- min
)) / PA_VOLUME_NORM
) + min
;
562 return PA_CLAMP_UNLIKELY(w
, min
, max
);
565 static pa_volume_t
from_alsa_volume(long v
, long min
, long max
) {
566 return (pa_volume_t
) round(((double) (v
- min
) * PA_VOLUME_NORM
) / (double) (max
- min
));
569 #define SELEM_INIT(sid, name) \
571 snd_mixer_selem_id_alloca(&(sid)); \
572 snd_mixer_selem_id_set_name((sid), (name)); \
573 snd_mixer_selem_id_set_index((sid), 0); \
576 static int element_get_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
577 snd_mixer_selem_id_t
*sid
;
578 snd_mixer_elem_t
*me
;
579 snd_mixer_selem_channel_id_t c
;
580 pa_channel_position_mask_t mask
= 0;
588 SELEM_INIT(sid
, e
->alsa_name
);
589 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
590 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
594 pa_cvolume_mute(v
, cm
->channels
);
596 /* We take the highest volume of all channels that match */
598 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
605 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
606 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
608 if ((r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
)) >= 0) {
609 /* If the channel volume is outside the limits set
610 * by the dB fix, we clamp the hw volume to be
611 * within the limits. */
612 if (value
< e
->db_fix
->min_step
) {
613 value
= e
->db_fix
->min_step
;
614 snd_mixer_selem_set_playback_volume(me
, c
, value
);
615 pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
616 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
617 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
618 } else if (value
> e
->db_fix
->max_step
) {
619 value
= e
->db_fix
->max_step
;
620 snd_mixer_selem_set_playback_volume(me
, c
, value
);
621 pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
622 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
623 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
626 /* Volume step -> dB value conversion. */
627 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
630 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
634 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
636 if ((r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
)) >= 0) {
637 /* If the channel volume is outside the limits set
638 * by the dB fix, we clamp the hw volume to be
639 * within the limits. */
640 if (value
< e
->db_fix
->min_step
) {
641 value
= e
->db_fix
->min_step
;
642 snd_mixer_selem_set_capture_volume(me
, c
, value
);
643 pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
644 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
645 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
646 } else if (value
> e
->db_fix
->max_step
) {
647 value
= e
->db_fix
->max_step
;
648 snd_mixer_selem_set_capture_volume(me
, c
, value
);
649 pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
650 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
651 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
654 /* Volume step -> dB value conversion. */
655 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
658 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
666 #ifdef HAVE_VALGRIND_MEMCHECK_H
667 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
670 f
= from_alsa_dB(value
);
675 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
676 if (snd_mixer_selem_has_playback_channel(me
, c
))
677 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
681 if (snd_mixer_selem_has_capture_channel(me
, c
))
682 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
690 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
693 for (k
= 0; k
< cm
->channels
; k
++)
694 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
695 if (v
->values
[k
] < f
)
698 mask
|= e
->masks
[c
][e
->n_channels
-1];
701 for (k
= 0; k
< cm
->channels
; k
++)
702 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
703 v
->values
[k
] = PA_VOLUME_NORM
;
708 int pa_alsa_path_get_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
719 pa_cvolume_reset(v
, cm
->channels
);
721 PA_LLIST_FOREACH(e
, p
->elements
) {
724 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
727 pa_assert(!p
->has_dB
|| e
->has_dB
);
729 if (element_get_volume(e
, m
, cm
, &ev
) < 0)
732 /* If we have no dB information all we can do is take the first element and leave */
738 pa_sw_cvolume_multiply(v
, v
, &ev
);
744 static int element_get_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t
*b
) {
745 snd_mixer_selem_id_t
*sid
;
746 snd_mixer_elem_t
*me
;
747 snd_mixer_selem_channel_id_t c
;
753 SELEM_INIT(sid
, e
->alsa_name
);
754 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
755 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
759 /* We return muted if at least one channel is muted */
761 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
765 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
766 if (snd_mixer_selem_has_playback_channel(me
, c
))
767 r
= snd_mixer_selem_get_playback_switch(me
, c
, &value
);
771 if (snd_mixer_selem_has_capture_channel(me
, c
))
772 r
= snd_mixer_selem_get_capture_switch(me
, c
, &value
);
790 int pa_alsa_path_get_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t
*muted
) {
800 PA_LLIST_FOREACH(e
, p
->elements
) {
803 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
806 if (element_get_switch(e
, m
, &b
) < 0)
819 /* Finds the closest item in db_fix->db_values and returns the corresponding
820 * step. *db_value is replaced with the value from the db_values table.
821 * Rounding is done based on the rounding parameter: -1 means rounding down and
822 * +1 means rounding up. */
823 static long decibel_fix_get_step(pa_alsa_decibel_fix
*db_fix
, long *db_value
, int rounding
) {
829 pa_assert(rounding
!= 0);
831 max_i
= db_fix
->max_step
- db_fix
->min_step
;
834 for (i
= 0; i
< max_i
; i
++) {
835 if (db_fix
->db_values
[i
] >= *db_value
)
839 for (i
= 0; i
< max_i
; i
++) {
840 if (db_fix
->db_values
[i
+ 1] > *db_value
)
845 *db_value
= db_fix
->db_values
[i
];
847 return i
+ db_fix
->min_step
;
850 /* Alsa lib documentation says for snd_mixer_selem_set_playback_dB() direction argument,
851 * that "-1 = accurate or first below, 0 = accurate, 1 = accurate or first above".
852 * But even with accurate nearest dB volume step is not selected, so that is why we need
853 * this function. Returns 0 and nearest selectable volume in *value_dB on success or
854 * negative error code if fails. */
855 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
) {
865 if (d
== PA_ALSA_DIRECTION_OUTPUT
) {
866 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, *value_dB
, +1, &alsa_val
)) >= 0)
867 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value_high
);
872 if (value_high
== *value_dB
)
875 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, *value_dB
, -1, &alsa_val
)) >= 0)
876 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value_low
);
878 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, *value_dB
, +1, &alsa_val
)) >= 0)
879 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value_high
);
884 if (value_high
== *value_dB
)
887 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, *value_dB
, -1, &alsa_val
)) >= 0)
888 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value_low
);
894 if (labs(value_high
- *value_dB
) < labs(value_low
- *value_dB
))
895 *value_dB
= value_high
;
897 *value_dB
= value_low
;
902 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
) {
904 snd_mixer_selem_id_t
*sid
;
906 snd_mixer_elem_t
*me
;
907 snd_mixer_selem_channel_id_t c
;
908 pa_channel_position_mask_t mask
= 0;
915 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
917 SELEM_INIT(sid
, e
->alsa_name
);
918 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
919 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
923 pa_cvolume_mute(&rv
, cm
->channels
);
925 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
927 pa_volume_t f
= PA_VOLUME_MUTED
;
928 pa_bool_t found
= FALSE
;
930 for (k
= 0; k
< cm
->channels
; k
++)
931 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
])) {
933 if (v
->values
[k
] > f
)
938 /* Hmm, so this channel does not exist in the volume
939 * struct, so let's bind it to the overall max of the
941 f
= pa_cvolume_max(v
);
945 long value
= to_alsa_dB(f
);
948 if (e
->volume_limit
>= 0 && value
> (e
->max_dB
* 100))
949 value
= e
->max_dB
* 100;
951 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
952 /* If we call set_playback_volume() without checking first
953 * if the channel is available, ALSA behaves very
954 * strangely and doesn't fail the call */
955 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
959 r
= snd_mixer_selem_set_playback_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
961 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
968 if ((r
= element_get_nearest_alsa_dB(me
, c
, PA_ALSA_DIRECTION_OUTPUT
, &value
)) >= 0)
969 r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, 0);
971 if ((r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, rounding
)) >= 0)
972 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
976 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
977 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value
);
983 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
987 r
= snd_mixer_selem_set_capture_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
989 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
996 if ((r
= element_get_nearest_alsa_dB(me
, c
, PA_ALSA_DIRECTION_INPUT
, &value
)) >= 0)
997 r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, 0);
999 if ((r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, rounding
)) >= 0)
1000 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
1004 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
1005 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value
);
1015 #ifdef HAVE_VALGRIND_MEMCHECK_H
1016 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
1019 f
= from_alsa_dB(value
);
1024 value
= to_alsa_volume(f
, e
->min_volume
, e
->max_volume
);
1026 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1027 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
1028 if ((r
= snd_mixer_selem_set_playback_volume(me
, c
, value
)) >= 0)
1029 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
1033 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
1034 if ((r
= snd_mixer_selem_set_capture_volume(me
, c
, value
)) >= 0)
1035 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
1043 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
1046 for (k
= 0; k
< cm
->channels
; k
++)
1047 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
1048 if (rv
.values
[k
] < f
)
1051 mask
|= e
->masks
[c
][e
->n_channels
-1];
1054 for (k
= 0; k
< cm
->channels
; k
++)
1055 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
1056 rv
.values
[k
] = PA_VOLUME_NORM
;
1062 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
) {
1071 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
1076 rv
= *v
; /* Remaining adjustment */
1077 pa_cvolume_reset(v
, cm
->channels
); /* Adjustment done */
1079 PA_LLIST_FOREACH(e
, p
->elements
) {
1082 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
1085 pa_assert(!p
->has_dB
|| e
->has_dB
);
1088 if (element_set_volume(e
, m
, cm
, &ev
, sync_volume
, write_to_hw
) < 0)
1096 pa_sw_cvolume_multiply(v
, v
, &ev
);
1097 pa_sw_cvolume_divide(&rv
, &rv
, &ev
);
1103 static int element_set_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t b
) {
1104 snd_mixer_elem_t
*me
;
1105 snd_mixer_selem_id_t
*sid
;
1111 SELEM_INIT(sid
, e
->alsa_name
);
1112 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1113 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1117 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1118 r
= snd_mixer_selem_set_playback_switch_all(me
, b
);
1120 r
= snd_mixer_selem_set_capture_switch_all(me
, b
);
1123 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1128 int pa_alsa_path_set_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t muted
) {
1137 PA_LLIST_FOREACH(e
, p
->elements
) {
1139 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
1142 if (element_set_switch(e
, m
, !muted
) < 0)
1149 /* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
1150 * function sets all channels of the volume element to e->min_volume, 0 dB or
1151 * e->constant_volume. */
1152 static int element_set_constant_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1153 snd_mixer_elem_t
*me
= NULL
;
1154 snd_mixer_selem_id_t
*sid
= NULL
;
1157 pa_bool_t volume_set
= FALSE
;
1162 SELEM_INIT(sid
, e
->alsa_name
);
1163 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1164 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1168 switch (e
->volume_use
) {
1169 case PA_ALSA_VOLUME_OFF
:
1170 volume
= e
->min_volume
;
1174 case PA_ALSA_VOLUME_ZERO
:
1178 volume
= decibel_fix_get_step(e
->db_fix
, &dB
, +1);
1183 case PA_ALSA_VOLUME_CONSTANT
:
1184 volume
= e
->constant_volume
;
1189 pa_assert_not_reached();
1193 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1194 r
= snd_mixer_selem_set_playback_volume_all(me
, volume
);
1196 r
= snd_mixer_selem_set_capture_volume_all(me
, volume
);
1198 pa_assert(e
->volume_use
== PA_ALSA_VOLUME_ZERO
);
1199 pa_assert(!e
->db_fix
);
1201 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1202 r
= snd_mixer_selem_set_playback_dB_all(me
, 0, +1);
1204 r
= snd_mixer_selem_set_capture_dB_all(me
, 0, +1);
1208 pa_log_warn("Failed to set volume of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1213 int pa_alsa_path_select(pa_alsa_path
*p
, snd_mixer_t
*m
) {
1220 pa_log_debug("Activating path %s", p
->name
);
1221 pa_alsa_path_dump(p
);
1223 PA_LLIST_FOREACH(e
, p
->elements
) {
1225 switch (e
->switch_use
) {
1226 case PA_ALSA_SWITCH_OFF
:
1227 r
= element_set_switch(e
, m
, FALSE
);
1230 case PA_ALSA_SWITCH_ON
:
1231 r
= element_set_switch(e
, m
, TRUE
);
1234 case PA_ALSA_SWITCH_MUTE
:
1235 case PA_ALSA_SWITCH_IGNORE
:
1236 case PA_ALSA_SWITCH_SELECT
:
1244 switch (e
->volume_use
) {
1245 case PA_ALSA_VOLUME_OFF
:
1246 case PA_ALSA_VOLUME_ZERO
:
1247 case PA_ALSA_VOLUME_CONSTANT
:
1248 r
= element_set_constant_volume(e
, m
);
1251 case PA_ALSA_VOLUME_MERGE
:
1252 case PA_ALSA_VOLUME_IGNORE
:
1264 static int check_required(pa_alsa_element
*e
, snd_mixer_elem_t
*me
) {
1265 pa_bool_t has_switch
;
1266 pa_bool_t has_enumeration
;
1267 pa_bool_t has_volume
;
1272 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1274 snd_mixer_selem_has_playback_switch(me
) ||
1275 (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
));
1278 snd_mixer_selem_has_capture_switch(me
) ||
1279 (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
));
1282 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1284 snd_mixer_selem_has_playback_volume(me
) ||
1285 (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
));
1288 snd_mixer_selem_has_capture_volume(me
) ||
1289 (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
));
1292 has_enumeration
= snd_mixer_selem_is_enumerated(me
);
1294 if ((e
->required
== PA_ALSA_REQUIRED_SWITCH
&& !has_switch
) ||
1295 (e
->required
== PA_ALSA_REQUIRED_VOLUME
&& !has_volume
) ||
1296 (e
->required
== PA_ALSA_REQUIRED_ENUMERATION
&& !has_enumeration
))
1299 if (e
->required
== PA_ALSA_REQUIRED_ANY
&& !(has_switch
|| has_volume
|| has_enumeration
))
1302 if ((e
->required_absent
== PA_ALSA_REQUIRED_SWITCH
&& has_switch
) ||
1303 (e
->required_absent
== PA_ALSA_REQUIRED_VOLUME
&& has_volume
) ||
1304 (e
->required_absent
== PA_ALSA_REQUIRED_ENUMERATION
&& has_enumeration
))
1307 if (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& (has_switch
|| has_volume
|| has_enumeration
))
1310 if (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) {
1311 switch (e
->required_any
) {
1312 case PA_ALSA_REQUIRED_VOLUME
:
1313 e
->path
->req_any_present
|= (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
);
1315 case PA_ALSA_REQUIRED_SWITCH
:
1316 e
->path
->req_any_present
|= (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
);
1318 case PA_ALSA_REQUIRED_ENUMERATION
:
1319 e
->path
->req_any_present
|= (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1321 case PA_ALSA_REQUIRED_ANY
:
1322 e
->path
->req_any_present
|=
1323 (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) ||
1324 (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) ||
1325 (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1328 pa_assert_not_reached();
1332 if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1334 PA_LLIST_FOREACH(o
, e
->options
) {
1335 e
->path
->req_any_present
|= (o
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) &&
1337 if (o
->required
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
< 0)
1339 if (o
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
>= 0)
1347 static int element_probe(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1348 snd_mixer_selem_id_t
*sid
;
1349 snd_mixer_elem_t
*me
;
1355 SELEM_INIT(sid
, e
->alsa_name
);
1357 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1359 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1362 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1363 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1364 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1369 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1370 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1372 if (!snd_mixer_selem_has_playback_switch(me
)) {
1373 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1374 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1376 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1381 if (!snd_mixer_selem_has_capture_switch(me
)) {
1382 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1383 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1385 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1389 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1390 e
->direction_try_other
= FALSE
;
1393 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1395 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1397 if (!snd_mixer_selem_has_playback_volume(me
)) {
1398 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1399 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1401 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1406 if (!snd_mixer_selem_has_capture_volume(me
)) {
1407 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1408 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1410 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1414 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1415 long min_dB
= 0, max_dB
= 0;
1418 e
->direction_try_other
= FALSE
;
1420 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1421 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1423 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1426 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1430 if (e
->min_volume
>= e
->max_volume
) {
1431 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
);
1432 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1434 } else if (e
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&&
1435 (e
->min_volume
> e
->constant_volume
|| e
->max_volume
< e
->constant_volume
)) {
1436 pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
1437 e
->constant_volume
, e
->alsa_name
, e
->min_volume
, e
->max_volume
);
1438 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1442 pa_channel_position_t p
;
1445 ((e
->min_volume
> e
->db_fix
->min_step
) ||
1446 (e
->max_volume
< e
->db_fix
->max_step
))) {
1447 pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1448 "real hardware range (%li-%li). Disabling the decibel fix.", e
->alsa_name
,
1449 e
->db_fix
->min_step
, e
->db_fix
->max_step
,
1450 e
->min_volume
, e
->max_volume
);
1452 decibel_fix_free(e
->db_fix
);
1458 e
->min_volume
= e
->db_fix
->min_step
;
1459 e
->max_volume
= e
->db_fix
->max_step
;
1460 min_dB
= e
->db_fix
->db_values
[0];
1461 max_dB
= e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
];
1462 } else if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1463 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1465 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1467 /* Check that the kernel driver returns consistent limits with
1468 * both _get_*_dB_range() and _ask_*_vol_dB(). */
1469 if (e
->has_dB
&& !e
->db_fix
) {
1470 long min_dB_checked
= 0;
1471 long max_dB_checked
= 0;
1473 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1474 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1476 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1479 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->min_volume
);
1483 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1484 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1486 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1489 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->max_volume
);
1493 if (min_dB
!= min_dB_checked
|| max_dB
!= max_dB_checked
) {
1494 pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
1495 "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
1496 "%0.2f dB at level %li.",
1498 min_dB
/ 100.0, max_dB
/ 100.0,
1499 min_dB_checked
/ 100.0, e
->min_volume
, max_dB_checked
/ 100.0, e
->max_volume
);
1505 #ifdef HAVE_VALGRIND_MEMCHECK_H
1506 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1507 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1510 e
->min_dB
= ((double) min_dB
) / 100.0;
1511 e
->max_dB
= ((double) max_dB
) / 100.0;
1513 if (min_dB
>= max_dB
) {
1514 pa_assert(!e
->db_fix
);
1515 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
);
1520 if (e
->volume_limit
>= 0) {
1521 if (e
->volume_limit
<= e
->min_volume
|| e
->volume_limit
> e
->max_volume
)
1522 pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1523 "%li-%li. The volume limit is ignored.",
1524 e
->alsa_name
, e
->path
->name
, e
->volume_limit
, e
->min_volume
+ 1, e
->max_volume
);
1527 e
->max_volume
= e
->volume_limit
;
1531 e
->db_fix
->max_step
= e
->max_volume
;
1532 e
->max_dB
= ((double) e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
]) / 100.0;
1535 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1536 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB
);
1538 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB
);
1541 pa_log_warn("Failed to get dB value of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1544 e
->max_dB
= ((double) max_dB
) / 100.0;
1550 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1551 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1553 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1558 if (!e
->override_map
) {
1559 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1560 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1563 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1566 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1569 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1572 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1574 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1577 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1578 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1580 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1583 if (e
->n_channels
<= 0) {
1584 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1588 if (e
->n_channels
> 2) {
1589 /* FIXME: In some places code like this is used:
1591 * e->masks[alsa_channel_ids[p]][e->n_channels-1]
1593 * The definition of e->masks is
1595 * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST][2];
1597 * Since the array size is fixed at 2, we obviously
1598 * don't support elements with more than two
1600 pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e
->alsa_name
, e
->n_channels
);
1604 if (!e
->override_map
) {
1605 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1606 pa_bool_t has_channel
;
1608 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1611 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1612 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1614 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1616 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1621 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1622 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1625 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1633 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1636 PA_LLIST_FOREACH(o
, e
->options
)
1637 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1638 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1642 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1643 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1647 PA_LLIST_FOREACH(o
, e
->options
) {
1650 for (i
= 0; i
< n
; i
++) {
1653 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1656 if (!pa_streq(buf
, o
->alsa_name
))
1664 if (check_required(e
, me
) < 0)
1670 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, pa_bool_t prefixed
) {
1677 if (!pa_startswith(section
, "Element "))
1683 /* This is not an element section, but an enum section? */
1684 if (strchr(section
, ':'))
1687 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1688 return p
->last_element
;
1690 PA_LLIST_FOREACH(e
, p
->elements
)
1691 if (pa_streq(e
->alsa_name
, section
))
1694 e
= pa_xnew0(pa_alsa_element
, 1);
1696 e
->alsa_name
= pa_xstrdup(section
);
1697 e
->direction
= p
->direction
;
1698 e
->volume_limit
= -1;
1700 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1703 p
->last_element
= e
;
1707 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1713 if (!pa_startswith(section
, "Option "))
1718 /* This is not an enum section, but an element section? */
1719 if (!(on
= strchr(section
, ':')))
1722 en
= pa_xstrndup(section
, on
- section
);
1725 if (p
->last_option
&&
1726 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1727 pa_streq(p
->last_option
->alsa_name
, on
)) {
1729 return p
->last_option
;
1732 pa_assert_se(e
= element_get(p
, en
, FALSE
));
1735 PA_LLIST_FOREACH(o
, e
->options
)
1736 if (pa_streq(o
->alsa_name
, on
))
1739 o
= pa_xnew0(pa_alsa_option
, 1);
1741 o
->alsa_name
= pa_xstrdup(on
);
1744 if (p
->last_option
&& p
->last_option
->element
== e
)
1745 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1747 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1754 static int element_parse_switch(
1755 const char *filename
,
1757 const char *section
,
1763 pa_alsa_path
*p
= userdata
;
1768 if (!(e
= element_get(p
, section
, TRUE
))) {
1769 pa_log("[%s:%u] Switch makes no sense in '%s'", filename
, line
, section
);
1773 if (pa_streq(rvalue
, "ignore"))
1774 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1775 else if (pa_streq(rvalue
, "mute"))
1776 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1777 else if (pa_streq(rvalue
, "off"))
1778 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1779 else if (pa_streq(rvalue
, "on"))
1780 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1781 else if (pa_streq(rvalue
, "select"))
1782 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1784 pa_log("[%s:%u] Switch invalid of '%s'", filename
, line
, section
);
1791 static int element_parse_volume(
1792 const char *filename
,
1794 const char *section
,
1800 pa_alsa_path
*p
= userdata
;
1805 if (!(e
= element_get(p
, section
, TRUE
))) {
1806 pa_log("[%s:%u] Volume makes no sense in '%s'", filename
, line
, section
);
1810 if (pa_streq(rvalue
, "ignore"))
1811 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1812 else if (pa_streq(rvalue
, "merge"))
1813 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1814 else if (pa_streq(rvalue
, "off"))
1815 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1816 else if (pa_streq(rvalue
, "zero"))
1817 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1821 if (pa_atou(rvalue
, &constant
) >= 0) {
1822 e
->volume_use
= PA_ALSA_VOLUME_CONSTANT
;
1823 e
->constant_volume
= constant
;
1825 pa_log("[%s:%u] Volume invalid of '%s'", filename
, line
, section
);
1833 static int element_parse_enumeration(
1834 const char *filename
,
1836 const char *section
,
1842 pa_alsa_path
*p
= userdata
;
1847 if (!(e
= element_get(p
, section
, TRUE
))) {
1848 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename
, line
, section
);
1852 if (pa_streq(rvalue
, "ignore"))
1853 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1854 else if (pa_streq(rvalue
, "select"))
1855 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1857 pa_log("[%s:%u] Enumeration invalid of '%s'", filename
, line
, section
);
1864 static int option_parse_priority(
1865 const char *filename
,
1867 const char *section
,
1873 pa_alsa_path
*p
= userdata
;
1879 if (!(o
= option_get(p
, section
))) {
1880 pa_log("[%s:%u] Priority makes no sense in '%s'", filename
, line
, section
);
1884 if (pa_atou(rvalue
, &prio
) < 0) {
1885 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
1893 static int option_parse_name(
1894 const char *filename
,
1896 const char *section
,
1902 pa_alsa_path
*p
= userdata
;
1907 if (!(o
= option_get(p
, section
))) {
1908 pa_log("[%s:%u] Name makes no sense in '%s'", filename
, line
, section
);
1913 o
->name
= pa_xstrdup(rvalue
);
1918 static int element_parse_required(
1919 const char *filename
,
1921 const char *section
,
1927 pa_alsa_path
*p
= userdata
;
1930 pa_alsa_required_t req
;
1934 e
= element_get(p
, section
, TRUE
);
1935 o
= option_get(p
, section
);
1937 pa_log("[%s:%u] Required makes no sense in '%s'", filename
, line
, section
);
1941 if (pa_streq(rvalue
, "ignore"))
1942 req
= PA_ALSA_REQUIRED_IGNORE
;
1943 else if (pa_streq(rvalue
, "switch") && e
)
1944 req
= PA_ALSA_REQUIRED_SWITCH
;
1945 else if (pa_streq(rvalue
, "volume") && e
)
1946 req
= PA_ALSA_REQUIRED_VOLUME
;
1947 else if (pa_streq(rvalue
, "enumeration"))
1948 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1949 else if (pa_streq(rvalue
, "any"))
1950 req
= PA_ALSA_REQUIRED_ANY
;
1952 pa_log("[%s:%u] Required invalid of '%s'", filename
, line
, section
);
1956 if (pa_streq(lvalue
, "required-absent")) {
1958 e
->required_absent
= req
;
1960 o
->required_absent
= req
;
1962 else if (pa_streq(lvalue
, "required-any")) {
1964 e
->required_any
= req
;
1965 e
->path
->has_req_any
= TRUE
;
1968 o
->required_any
= req
;
1969 o
->element
->path
->has_req_any
= TRUE
;
1982 static int element_parse_direction(
1983 const char *filename
,
1985 const char *section
,
1991 pa_alsa_path
*p
= userdata
;
1996 if (!(e
= element_get(p
, section
, TRUE
))) {
1997 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
2001 if (pa_streq(rvalue
, "playback"))
2002 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
2003 else if (pa_streq(rvalue
, "capture"))
2004 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
2006 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
2013 static int element_parse_direction_try_other(
2014 const char *filename
,
2016 const char *section
,
2022 pa_alsa_path
*p
= userdata
;
2026 if (!(e
= element_get(p
, section
, TRUE
))) {
2027 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
2031 if ((yes
= pa_parse_boolean(rvalue
)) < 0) {
2032 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
2036 e
->direction_try_other
= !!yes
;
2040 static int element_parse_volume_limit(
2041 const char *filename
,
2043 const char *section
,
2049 pa_alsa_path
*p
= userdata
;
2053 if (!(e
= element_get(p
, section
, TRUE
))) {
2054 pa_log("[%s:%u] volume-limit makes no sense in '%s'", filename
, line
, section
);
2058 if (pa_atol(rvalue
, &volume_limit
) < 0 || volume_limit
< 0) {
2059 pa_log("[%s:%u] Invalid value for volume-limit", filename
, line
);
2063 e
->volume_limit
= volume_limit
;
2067 static pa_channel_position_mask_t
parse_mask(const char *m
) {
2068 pa_channel_position_mask_t v
;
2070 if (pa_streq(m
, "all-left"))
2071 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
2072 else if (pa_streq(m
, "all-right"))
2073 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
2074 else if (pa_streq(m
, "all-center"))
2075 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
2076 else if (pa_streq(m
, "all-front"))
2077 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
2078 else if (pa_streq(m
, "all-rear"))
2079 v
= PA_CHANNEL_POSITION_MASK_REAR
;
2080 else if (pa_streq(m
, "all-side"))
2081 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
2082 else if (pa_streq(m
, "all-top"))
2083 v
= PA_CHANNEL_POSITION_MASK_TOP
;
2084 else if (pa_streq(m
, "all-no-lfe"))
2085 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
2086 else if (pa_streq(m
, "all"))
2087 v
= PA_CHANNEL_POSITION_MASK_ALL
;
2089 pa_channel_position_t p
;
2091 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
2094 v
= PA_CHANNEL_POSITION_MASK(p
);
2100 static int element_parse_override_map(
2101 const char *filename
,
2103 const char *section
,
2109 pa_alsa_path
*p
= userdata
;
2111 const char *state
= NULL
;
2115 if (!(e
= element_get(p
, section
, TRUE
))) {
2116 pa_log("[%s:%u] Override map makes no sense in '%s'", filename
, line
, section
);
2120 while ((n
= pa_split(rvalue
, ",", &state
))) {
2121 pa_channel_position_mask_t m
;
2126 if ((m
= parse_mask(n
)) == 0) {
2127 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename
, line
, n
, section
);
2133 if (pa_streq(lvalue
, "override-map.1"))
2134 e
->masks
[i
++][0] = m
;
2136 e
->masks
[i
++][1] = m
;
2138 /* Later on we might add override-map.3 and so on here ... */
2143 e
->override_map
= TRUE
;
2148 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
2149 snd_mixer_selem_id_t
*sid
;
2150 snd_mixer_elem_t
*me
;
2156 SELEM_INIT(sid
, e
->alsa_name
);
2157 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2158 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2162 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
2164 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
2165 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
2167 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
2170 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2173 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
2175 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
2176 pa_log_warn("Failed to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2182 int pa_alsa_setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
2189 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
2190 element_set_option(o
->element
, m
, o
->alsa_idx
);
2195 static int option_verify(pa_alsa_option
*o
) {
2196 static const struct description_map well_known_descriptions
[] = {
2197 { "input", N_("Input") },
2198 { "input-docking", N_("Docking Station Input") },
2199 { "input-docking-microphone", N_("Docking Station Microphone") },
2200 { "input-docking-linein", N_("Docking Station Line-In") },
2201 { "input-linein", N_("Line-In") },
2202 { "input-microphone", N_("Microphone") },
2203 { "input-microphone-front", N_("Front Microphone") },
2204 { "input-microphone-rear", N_("Rear Microphone") },
2205 { "input-microphone-external", N_("External Microphone") },
2206 { "input-microphone-internal", N_("Internal Microphone") },
2207 { "input-radio", N_("Radio") },
2208 { "input-video", N_("Video") },
2209 { "input-agc-on", N_("Automatic Gain Control") },
2210 { "input-agc-off", N_("No Automatic Gain Control") },
2211 { "input-boost-on", N_("Boost") },
2212 { "input-boost-off", N_("No Boost") },
2213 { "output-amplifier-on", N_("Amplifier") },
2214 { "output-amplifier-off", N_("No Amplifier") },
2215 { "output-bass-boost-on", N_("Bass Boost") },
2216 { "output-bass-boost-off", N_("No Bass Boost") },
2217 { "output-speaker", N_("Speaker") },
2218 { "output-headphones", N_("Headphones") }
2224 pa_log("No name set for option %s", o
->alsa_name
);
2228 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
2229 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
2230 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
2234 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
2235 !pa_streq(o
->alsa_name
, "on") &&
2236 !pa_streq(o
->alsa_name
, "off")) {
2237 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
2241 if (!o
->description
)
2242 o
->description
= pa_xstrdup(lookup_description(o
->name
,
2243 well_known_descriptions
,
2244 PA_ELEMENTSOF(well_known_descriptions
)));
2245 if (!o
->description
)
2246 o
->description
= pa_xstrdup(o
->name
);
2251 static int element_verify(pa_alsa_element
*e
) {
2256 // 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);
2257 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
2258 (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required_any
== e
->required_absent
) ||
2259 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) ||
2260 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
2261 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
2265 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
2266 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
2270 PA_LLIST_FOREACH(o
, e
->options
)
2271 if (option_verify(o
) < 0)
2277 static int path_verify(pa_alsa_path
*p
) {
2278 static const struct description_map well_known_descriptions
[] = {
2279 { "analog-input", N_("Analog Input") },
2280 { "analog-input-microphone", N_("Analog Microphone") },
2281 { "analog-input-microphone-front", N_("Front Microphone") },
2282 { "analog-input-microphone-rear", N_("Rear Microphone") },
2283 { "analog-input-microphone-dock", N_("Docking Station Microphone") },
2284 { "analog-input-microphone-internal", N_("Internal Microphone") },
2285 { "analog-input-linein", N_("Analog Line-In") },
2286 { "analog-input-radio", N_("Analog Radio") },
2287 { "analog-input-video", N_("Analog Video") },
2288 { "analog-output", N_("Analog Output") },
2289 { "analog-output-headphones", N_("Analog Headphones") },
2290 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
2291 { "analog-output-mono", N_("Analog Mono Output") },
2292 { "analog-output-speaker", N_("Analog Speakers") },
2293 { "iec958-stereo-output", N_("Digital Output (IEC958)") },
2294 { "iec958-passthrough-output", N_("Digital Passthrough (IEC958)") }
2301 PA_LLIST_FOREACH(e
, p
->elements
)
2302 if (element_verify(e
) < 0)
2305 if (!p
->description
)
2306 p
->description
= pa_xstrdup(lookup_description(p
->name
,
2307 well_known_descriptions
,
2308 PA_ELEMENTSOF(well_known_descriptions
)));
2310 if (!p
->description
)
2311 p
->description
= pa_xstrdup(p
->name
);
2316 pa_alsa_path
* pa_alsa_path_new(const char *fname
, pa_alsa_direction_t direction
) {
2322 pa_config_item items
[] = {
2324 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
2325 { "description", pa_config_parse_string
, NULL
, "General" },
2326 { "name", pa_config_parse_string
, NULL
, "General" },
2329 { "priority", option_parse_priority
, NULL
, NULL
},
2330 { "name", option_parse_name
, NULL
, NULL
},
2333 { "switch", element_parse_switch
, NULL
, NULL
},
2334 { "volume", element_parse_volume
, NULL
, NULL
},
2335 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
2336 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
2337 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
2338 /* ... later on we might add override-map.3 and so on here ... */
2339 { "required", element_parse_required
, NULL
, NULL
},
2340 { "required-any", element_parse_required
, NULL
, NULL
},
2341 { "required-absent", element_parse_required
, NULL
, NULL
},
2342 { "direction", element_parse_direction
, NULL
, NULL
},
2343 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2344 { "volume-limit", element_parse_volume_limit
, NULL
, NULL
},
2345 { NULL
, NULL
, NULL
, NULL
}
2350 p
= pa_xnew0(pa_alsa_path
, 1);
2351 n
= pa_path_get_filename(fname
);
2352 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2353 p
->direction
= direction
;
2355 items
[0].data
= &p
->priority
;
2356 items
[1].data
= &p
->description
;
2357 items
[2].data
= &p
->name
;
2359 fn
= pa_maybe_prefix_path(fname
,
2360 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/paths/" :
2363 r
= pa_config_parse(fn
, NULL
, items
, p
);
2369 if (path_verify(p
) < 0)
2375 pa_alsa_path_free(p
);
2379 pa_alsa_path
*pa_alsa_path_synthesize(const char *element
, pa_alsa_direction_t direction
) {
2385 p
= pa_xnew0(pa_alsa_path
, 1);
2386 p
->name
= pa_xstrdup(element
);
2387 p
->direction
= direction
;
2389 e
= pa_xnew0(pa_alsa_element
, 1);
2391 e
->alsa_name
= pa_xstrdup(element
);
2392 e
->direction
= direction
;
2393 e
->volume_limit
= -1;
2395 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2396 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2398 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2399 p
->last_element
= e
;
2403 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2404 pa_alsa_option
*o
, *n
;
2408 for (o
= e
->options
; o
; o
= n
) {
2411 if (o
->alsa_idx
< 0) {
2412 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2418 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2419 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2420 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2423 static void path_drop_unsupported(pa_alsa_path
*p
) {
2424 pa_alsa_element
*e
, *n
;
2428 for (e
= p
->elements
; e
; e
= n
) {
2431 if (!element_drop_unsupported(e
)) {
2432 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2438 static void path_make_options_unique(pa_alsa_path
*p
) {
2440 pa_alsa_option
*o
, *u
;
2442 PA_LLIST_FOREACH(e
, p
->elements
) {
2443 PA_LLIST_FOREACH(o
, e
->options
) {
2447 for (u
= o
->next
; u
; u
= u
->next
)
2448 if (pa_streq(u
->name
, o
->name
))
2454 m
= pa_xstrdup(o
->name
);
2456 /* OK, this name is not unique, hence let's rename */
2457 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2460 if (!pa_streq(u
->name
, m
))
2463 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2467 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2468 pa_xfree(u
->description
);
2469 u
->description
= nd
;
2479 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2482 for (; e
; e
= e
->next
)
2483 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2484 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2490 for (o
= e
->options
; o
; o
= o
->next
) {
2494 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2495 s
->options
= pa_idxset_copy(template->options
);
2496 s
->name
= pa_sprintf_malloc(_("%s+%s"), template->name
, o
->name
);
2498 (template->description
[0] && o
->description
[0])
2499 ? pa_sprintf_malloc(_("%s / %s"), template->description
, o
->description
)
2500 : (template->description
[0]
2501 ? pa_xstrdup(template->description
)
2502 : pa_xstrdup(o
->description
));
2504 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2506 s
= pa_xnew0(pa_alsa_setting
, 1);
2507 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2508 s
->name
= pa_xstrdup(o
->name
);
2509 s
->description
= pa_xstrdup(o
->description
);
2510 s
->priority
= o
->priority
;
2513 pa_idxset_put(s
->options
, o
, NULL
);
2515 if (element_create_settings(e
->next
, s
))
2516 /* This is not a leaf, so let's get rid of it */
2519 /* This is a leaf, so let's add it */
2520 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2522 e
->path
->last_setting
= s
;
2529 static void path_create_settings(pa_alsa_path
*p
) {
2532 element_create_settings(p
->elements
, NULL
);
2535 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2537 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2538 pa_channel_position_t t
;
2539 pa_channel_position_mask_t path_volume_channels
= 0;
2550 pa_log_debug("Probing path '%s'", p
->name
);
2552 PA_LLIST_FOREACH(e
, p
->elements
) {
2553 if (element_probe(e
, m
) < 0) {
2554 p
->supported
= FALSE
;
2555 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2558 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
);
2563 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2565 if (!p
->has_volume
) {
2566 p
->min_volume
= e
->min_volume
;
2567 p
->max_volume
= e
->max_volume
;
2571 if (!p
->has_volume
) {
2572 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2573 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2574 min_dB
[t
] = e
->min_dB
;
2575 max_dB
[t
] = e
->max_dB
;
2576 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2583 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2584 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2585 min_dB
[t
] += e
->min_dB
;
2586 max_dB
[t
] += e
->max_dB
;
2587 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2590 /* Hmm, there's another element before us
2591 * which cannot do dB volumes, so we we need
2592 * to 'neutralize' this slider */
2593 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2594 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2597 } else if (p
->has_volume
) {
2598 /* We can't use this volume, so let's ignore it */
2599 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2600 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2602 p
->has_volume
= TRUE
;
2605 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2609 if (p
->has_req_any
&& !p
->req_any_present
) {
2610 p
->supported
= FALSE
;
2611 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2615 path_drop_unsupported(p
);
2616 path_make_options_unique(p
);
2617 path_create_settings(p
);
2619 p
->supported
= TRUE
;
2622 p
->min_dB
= INFINITY
;
2623 p
->max_dB
= -INFINITY
;
2625 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2626 if (path_volume_channels
& PA_CHANNEL_POSITION_MASK(t
)) {
2627 if (p
->min_dB
> min_dB
[t
])
2628 p
->min_dB
= min_dB
[t
];
2630 if (p
->max_dB
< max_dB
[t
])
2631 p
->max_dB
= max_dB
[t
];
2638 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2641 pa_log_debug("Setting %s (%s) priority=%u",
2643 pa_strnull(s
->description
),
2647 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2650 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2652 pa_strnull(o
->name
),
2653 pa_strnull(o
->description
),
2658 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2662 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",
2672 (long long unsigned) e
->merged_mask
,
2674 pa_yes_no(e
->override_map
));
2676 PA_LLIST_FOREACH(o
, e
->options
)
2677 pa_alsa_option_dump(o
);
2680 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2685 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2686 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2688 pa_strnull(p
->description
),
2691 pa_yes_no(p
->probed
),
2692 pa_yes_no(p
->supported
),
2693 pa_yes_no(p
->has_mute
),
2694 pa_yes_no(p
->has_volume
),
2695 pa_yes_no(p
->has_dB
),
2696 p
->min_volume
, p
->max_volume
,
2697 p
->min_dB
, p
->max_dB
);
2699 PA_LLIST_FOREACH(e
, p
->elements
)
2700 pa_alsa_element_dump(e
);
2702 PA_LLIST_FOREACH(s
, p
->settings
)
2703 pa_alsa_setting_dump(s
);
2706 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2707 snd_mixer_selem_id_t
*sid
;
2708 snd_mixer_elem_t
*me
;
2714 SELEM_INIT(sid
, e
->alsa_name
);
2715 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2716 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2720 snd_mixer_elem_set_callback(me
, cb
);
2721 snd_mixer_elem_set_callback_private(me
, userdata
);
2724 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2731 PA_LLIST_FOREACH(e
, p
->elements
)
2732 element_set_callback(e
, m
, cb
, userdata
);
2735 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2742 PA_LLIST_FOREACH(p
, ps
->paths
)
2743 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2746 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
) {
2747 pa_alsa_path_set
*ps
;
2748 char **pn
= NULL
, **en
= NULL
, **ie
;
2749 pa_alsa_decibel_fix
*db_fix
;
2753 pa_assert(m
->profile_set
);
2754 pa_assert(m
->profile_set
->decibel_fixes
);
2755 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2757 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2760 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2761 ps
->direction
= direction
;
2763 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2764 pn
= m
->output_path_names
;
2765 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2766 pn
= m
->input_path_names
;
2771 for (in
= pn
; *in
; in
++) {
2773 pa_bool_t duplicate
= FALSE
;
2776 for (kn
= pn
; kn
< in
; kn
++)
2777 if (pa_streq(*kn
, *in
)) {
2785 fn
= pa_sprintf_malloc("%s.conf", *in
);
2787 if ((p
= pa_alsa_path_new(fn
, direction
))) {
2789 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2799 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2800 en
= m
->output_element
;
2801 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2802 en
= m
->input_element
;
2805 pa_alsa_path_set_free(ps
);
2809 for (ie
= en
; *ie
; ie
++) {
2813 p
= pa_alsa_path_synthesize(*ie
, direction
);
2816 /* Mark all other passed elements for require-absent */
2817 for (je
= en
; *je
; je
++) {
2823 e
= pa_xnew0(pa_alsa_element
, 1);
2825 e
->alsa_name
= pa_xstrdup(*je
);
2826 e
->direction
= direction
;
2827 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2828 e
->volume_limit
= -1;
2830 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2831 p
->last_element
= e
;
2834 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2839 /* Assign decibel fixes to elements. */
2840 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2843 PA_LLIST_FOREACH(p
, ps
->paths
) {
2846 PA_LLIST_FOREACH(e
, p
->elements
) {
2847 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2848 /* The profile set that contains the dB fix may be freed
2849 * before the element, so we have to copy the dB fix
2851 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2852 e
->db_fix
->profile_set
= NULL
;
2853 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2854 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
2863 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2867 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2870 pa_yes_no(ps
->probed
));
2872 PA_LLIST_FOREACH(p
, ps
->paths
)
2873 pa_alsa_path_dump(p
);
2877 static pa_bool_t
options_have_option(pa_alsa_option
*options
, const char *alsa_name
) {
2881 pa_assert(alsa_name
);
2883 PA_LLIST_FOREACH(o
, options
) {
2884 if (pa_streq(o
->alsa_name
, alsa_name
))
2890 static pa_bool_t
enumeration_is_subset(pa_alsa_option
*a_options
, pa_alsa_option
*b_options
) {
2891 pa_alsa_option
*oa
, *ob
;
2893 pa_assert(a_options
);
2894 pa_assert(b_options
);
2896 /* If there is an option A offers that B does not, then A is not a subset of B. */
2897 PA_LLIST_FOREACH(oa
, a_options
) {
2898 pa_bool_t found
= FALSE
;
2899 PA_LLIST_FOREACH(ob
, b_options
) {
2900 if (pa_streq(oa
->alsa_name
, ob
->alsa_name
)) {
2912 * Compares two elements to see if a is a subset of b
2914 static pa_bool_t
element_is_subset(pa_alsa_element
*a
, pa_alsa_element
*b
, snd_mixer_t
*m
) {
2920 * Every state is a subset of itself (with caveats for volume_limits and options)
2921 * IGNORE is a subset of every other state */
2923 /* Check the volume_use */
2924 if (a
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
2926 /* "Constant" is subset of "Constant" only when their constant values are equal */
2927 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& b
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& a
->constant_volume
!= b
->constant_volume
)
2930 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
2931 if (a
->volume_use
!= b
->volume_use
&& b
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
2934 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
2935 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
2936 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
2937 if (b
->volume_use
== PA_ALSA_VOLUME_MERGE
&& b
->volume_limit
>= 0) {
2940 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
)
2941 a_limit
= a
->constant_volume
;
2942 else if (a
->volume_use
== PA_ALSA_VOLUME_ZERO
) {
2946 int rounding
= (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
? +1 : -1);
2947 a_limit
= decibel_fix_get_step(a
->db_fix
, &dB
, rounding
);
2949 snd_mixer_selem_id_t
*sid
;
2950 snd_mixer_elem_t
*me
;
2952 SELEM_INIT(sid
, a
->alsa_name
);
2953 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2954 pa_log_warn("Element %s seems to have disappeared.", a
->alsa_name
);
2958 if (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
2959 if (snd_mixer_selem_ask_playback_dB_vol(me
, dB
, +1, &a_limit
) < 0)
2962 if (snd_mixer_selem_ask_capture_dB_vol(me
, dB
, -1, &a_limit
) < 0)
2966 } else if (a
->volume_use
== PA_ALSA_VOLUME_OFF
)
2967 a_limit
= a
->min_volume
;
2968 else if (a
->volume_use
== PA_ALSA_VOLUME_MERGE
)
2969 a_limit
= a
->volume_limit
;
2971 /* This should never be reached */
2974 if (a_limit
> b
->volume_limit
)
2979 if (a
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
2980 /* "On" is a subset of "Mute".
2981 * "Off" is a subset of "Mute".
2982 * "On" is a subset of "Select", if there is an "Option:On" in B.
2983 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
2984 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
2986 if (a
->switch_use
!= b
->switch_use
) {
2988 if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
|| a
->switch_use
== PA_ALSA_SWITCH_MUTE
2989 || b
->switch_use
== PA_ALSA_SWITCH_OFF
|| b
->switch_use
== PA_ALSA_SWITCH_ON
)
2992 if (b
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
2993 if (a
->switch_use
== PA_ALSA_SWITCH_ON
) {
2994 if (!options_have_option(b
->options
, "on"))
2996 } else if (a
->switch_use
== PA_ALSA_SWITCH_OFF
) {
2997 if (!options_have_option(b
->options
, "off"))
3001 } else if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3002 if (!enumeration_is_subset(a
->options
, b
->options
))
3007 if (a
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
) {
3008 if (!enumeration_is_subset(a
->options
, b
->options
))
3015 static void path_set_condense(pa_alsa_path_set
*ps
, snd_mixer_t
*m
) {
3016 pa_alsa_path
*p
, *np
;
3021 /* If we only have one path, then don't bother */
3022 if (!ps
->paths
|| !ps
->paths
->next
)
3025 for (p
= ps
->paths
; p
; p
= np
) {
3029 PA_LLIST_FOREACH(p2
, ps
->paths
) {
3030 pa_alsa_element
*ea
, *eb
;
3031 pa_bool_t is_subset
= TRUE
;
3036 /* Compare the elements of each set... */
3037 pa_assert_se(ea
= p
->elements
);
3038 pa_assert_se(eb
= p2
->elements
);
3041 if (pa_streq(ea
->alsa_name
, eb
->alsa_name
)) {
3042 if (element_is_subset(ea
, eb
, m
)) {
3045 if ((ea
&& !eb
) || (!ea
&& eb
))
3047 else if (!ea
&& !eb
)
3057 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p
->name
, p2
->name
);
3058 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
3059 pa_alsa_path_free(p
);
3066 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
3067 pa_alsa_path
*p
, *q
;
3069 PA_LLIST_FOREACH(p
, ps
->paths
) {
3073 for (q
= p
->next
; q
; q
= q
->next
)
3074 if (pa_streq(q
->name
, p
->name
))
3080 m
= pa_xstrdup(p
->name
);
3082 /* OK, this name is not unique, hence let's rename */
3083 for (i
= 1, q
= p
; q
; q
= q
->next
) {
3086 if (!pa_streq(q
->name
, m
))
3089 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
3093 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
3094 pa_xfree(q
->description
);
3095 q
->description
= nd
;
3104 void pa_alsa_path_set_probe(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
3105 pa_alsa_path
*p
, *n
;
3112 for (p
= ps
->paths
; p
; p
= n
) {
3115 if (pa_alsa_path_probe(p
, m
, ignore_dB
) < 0) {
3116 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
3117 pa_alsa_path_free(p
);
3121 pa_log_debug("Found mixer paths (before tidying):");
3122 pa_alsa_path_set_dump(ps
);
3124 path_set_condense(ps
, m
);
3125 path_set_make_paths_unique(ps
);
3128 pa_log_debug("Available mixer paths (after tidying):");
3129 pa_alsa_path_set_dump(ps
);
3132 static void mapping_free(pa_alsa_mapping
*m
) {
3136 pa_xfree(m
->description
);
3138 pa_xstrfreev(m
->device_strings
);
3139 pa_xstrfreev(m
->input_path_names
);
3140 pa_xstrfreev(m
->output_path_names
);
3141 pa_xstrfreev(m
->input_element
);
3142 pa_xstrfreev(m
->output_element
);
3144 pa_assert(!m
->input_pcm
);
3145 pa_assert(!m
->output_pcm
);
3150 static void profile_free(pa_alsa_profile
*p
) {
3154 pa_xfree(p
->description
);
3156 pa_xstrfreev(p
->input_mapping_names
);
3157 pa_xstrfreev(p
->output_mapping_names
);
3159 if (p
->input_mappings
)
3160 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
3162 if (p
->output_mappings
)
3163 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
3168 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
3174 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
3177 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
3183 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
3186 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
3189 if (ps
->decibel_fixes
) {
3190 pa_alsa_decibel_fix
*db_fix
;
3192 while ((db_fix
= pa_hashmap_steal_first(ps
->decibel_fixes
)))
3193 decibel_fix_free(db_fix
);
3195 pa_hashmap_free(ps
->decibel_fixes
, NULL
, NULL
);
3201 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
3204 if (!pa_startswith(name
, "Mapping "))
3209 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
3212 m
= pa_xnew0(pa_alsa_mapping
, 1);
3213 m
->profile_set
= ps
;
3214 m
->name
= pa_xstrdup(name
);
3215 pa_channel_map_init(&m
->channel_map
);
3217 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
3222 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
3225 if (!pa_startswith(name
, "Profile "))
3230 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
3233 p
= pa_xnew0(pa_alsa_profile
, 1);
3234 p
->profile_set
= ps
;
3235 p
->name
= pa_xstrdup(name
);
3237 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3242 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
3243 pa_alsa_decibel_fix
*db_fix
;
3245 if (!pa_startswith(name
, "DecibelFix "))
3250 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
3253 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
3254 db_fix
->profile_set
= ps
;
3255 db_fix
->name
= pa_xstrdup(name
);
3257 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
3262 static int mapping_parse_device_strings(
3263 const char *filename
,
3265 const char *section
,
3271 pa_alsa_profile_set
*ps
= userdata
;
3276 if (!(m
= mapping_get(ps
, section
))) {
3277 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3281 pa_xstrfreev(m
->device_strings
);
3282 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
3283 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
3290 static int mapping_parse_channel_map(
3291 const char *filename
,
3293 const char *section
,
3299 pa_alsa_profile_set
*ps
= userdata
;
3304 if (!(m
= mapping_get(ps
, section
))) {
3305 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3309 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
3310 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
3317 static int mapping_parse_paths(
3318 const char *filename
,
3320 const char *section
,
3326 pa_alsa_profile_set
*ps
= userdata
;
3331 if (!(m
= mapping_get(ps
, section
))) {
3332 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3336 if (pa_streq(lvalue
, "paths-input")) {
3337 pa_xstrfreev(m
->input_path_names
);
3338 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
3340 pa_xstrfreev(m
->output_path_names
);
3341 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
3347 static int mapping_parse_element(
3348 const char *filename
,
3350 const char *section
,
3356 pa_alsa_profile_set
*ps
= userdata
;
3361 if (!(m
= mapping_get(ps
, section
))) {
3362 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3366 if (pa_streq(lvalue
, "element-input")) {
3367 pa_xstrfreev(m
->input_element
);
3368 m
->input_element
= pa_split_spaces_strv(rvalue
);
3370 pa_xstrfreev(m
->output_element
);
3371 m
->output_element
= pa_split_spaces_strv(rvalue
);
3377 static int mapping_parse_direction(
3378 const char *filename
,
3380 const char *section
,
3386 pa_alsa_profile_set
*ps
= userdata
;
3391 if (!(m
= mapping_get(ps
, section
))) {
3392 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3396 if (pa_streq(rvalue
, "input"))
3397 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3398 else if (pa_streq(rvalue
, "output"))
3399 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3400 else if (pa_streq(rvalue
, "any"))
3401 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3403 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
3410 static int mapping_parse_description(
3411 const char *filename
,
3413 const char *section
,
3419 pa_alsa_profile_set
*ps
= userdata
;
3425 if ((m
= mapping_get(ps
, section
))) {
3426 pa_xfree(m
->description
);
3427 m
->description
= pa_xstrdup(rvalue
);
3428 } else if ((p
= profile_get(ps
, section
))) {
3429 pa_xfree(p
->description
);
3430 p
->description
= pa_xstrdup(rvalue
);
3432 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3439 static int mapping_parse_priority(
3440 const char *filename
,
3442 const char *section
,
3448 pa_alsa_profile_set
*ps
= userdata
;
3455 if (pa_atou(rvalue
, &prio
) < 0) {
3456 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
3460 if ((m
= mapping_get(ps
, section
)))
3462 else if ((p
= profile_get(ps
, section
)))
3465 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3472 static int profile_parse_mappings(
3473 const char *filename
,
3475 const char *section
,
3481 pa_alsa_profile_set
*ps
= userdata
;
3486 if (!(p
= profile_get(ps
, section
))) {
3487 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3491 if (pa_streq(lvalue
, "input-mappings")) {
3492 pa_xstrfreev(p
->input_mapping_names
);
3493 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
3495 pa_xstrfreev(p
->output_mapping_names
);
3496 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
3502 static int profile_parse_skip_probe(
3503 const char *filename
,
3505 const char *section
,
3511 pa_alsa_profile_set
*ps
= userdata
;
3517 if (!(p
= profile_get(ps
, section
))) {
3518 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3522 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
3523 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
3532 static int decibel_fix_parse_db_values(
3533 const char *filename
,
3535 const char *section
,
3541 pa_alsa_profile_set
*ps
= userdata
;
3542 pa_alsa_decibel_fix
*db_fix
;
3546 unsigned n
= 8; /* Current size of the db_values table. */
3547 unsigned min_step
= 0;
3548 unsigned max_step
= 0;
3549 unsigned i
= 0; /* Index to the items table. */
3550 unsigned prev_step
= 0;
3553 pa_assert(filename
);
3559 if (!(db_fix
= decibel_fix_get(ps
, section
))) {
3560 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3564 if (!(items
= pa_split_spaces_strv(rvalue
))) {
3565 pa_log("[%s:%u] Value missing", pa_strnull(filename
), line
);
3569 db_values
= pa_xnew(long, n
);
3571 while ((item
= items
[i
++])) {
3572 char *s
= item
; /* Step value string. */
3573 char *d
= item
; /* dB value string. */
3577 /* Move d forward until it points to a colon or to the end of the item. */
3578 for (; *d
&& *d
!= ':'; ++d
);
3581 /* item started with colon. */
3582 pa_log("[%s:%u] No step value found in %s", filename
, line
, item
);
3586 if (!*d
|| !*(d
+ 1)) {
3587 /* No colon found, or it was the last character in item. */
3588 pa_log("[%s:%u] No dB value found in %s", filename
, line
, item
);
3592 /* pa_atou() needs a null-terminating string. Let's replace the colon
3593 * with a zero byte. */
3596 if (pa_atou(s
, &step
) < 0) {
3597 pa_log("[%s:%u] Invalid step value: %s", filename
, line
, s
);
3601 if (pa_atod(d
, &db
) < 0) {
3602 pa_log("[%s:%u] Invalid dB value: %s", filename
, line
, d
);
3606 if (step
<= prev_step
&& i
!= 1) {
3607 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename
, line
, step
, prev_step
);
3611 if (db
< prev_db
&& i
!= 1) {
3612 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename
, line
, db
, prev_db
);
3618 db_values
[0] = (long) (db
* 100.0);
3622 /* Interpolate linearly. */
3623 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3625 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3627 /* Reallocate the db_values table if it's about to overflow. */
3628 if (prev_step
+ 1 - min_step
== n
) {
3630 db_values
= pa_xrenew(long, db_values
, n
);
3633 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3640 db_fix
->min_step
= min_step
;
3641 db_fix
->max_step
= max_step
;
3642 pa_xfree(db_fix
->db_values
);
3643 db_fix
->db_values
= db_values
;
3645 pa_xstrfreev(items
);
3650 pa_xstrfreev(items
);
3651 pa_xfree(db_values
);
3656 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3658 static const struct description_map well_known_descriptions
[] = {
3659 { "analog-mono", N_("Analog Mono") },
3660 { "analog-stereo", N_("Analog Stereo") },
3661 { "analog-surround-21", N_("Analog Surround 2.1") },
3662 { "analog-surround-30", N_("Analog Surround 3.0") },
3663 { "analog-surround-31", N_("Analog Surround 3.1") },
3664 { "analog-surround-40", N_("Analog Surround 4.0") },
3665 { "analog-surround-41", N_("Analog Surround 4.1") },
3666 { "analog-surround-50", N_("Analog Surround 5.0") },
3667 { "analog-surround-51", N_("Analog Surround 5.1") },
3668 { "analog-surround-61", N_("Analog Surround 6.0") },
3669 { "analog-surround-61", N_("Analog Surround 6.1") },
3670 { "analog-surround-70", N_("Analog Surround 7.0") },
3671 { "analog-surround-71", N_("Analog Surround 7.1") },
3672 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3673 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3674 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3675 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3676 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3681 if (!pa_channel_map_valid(&m
->channel_map
)) {
3682 pa_log("Mapping %s is missing channel map.", m
->name
);
3686 if (!m
->device_strings
) {
3687 pa_log("Mapping %s is missing device strings.", m
->name
);
3691 if ((m
->input_path_names
&& m
->input_element
) ||
3692 (m
->output_path_names
&& m
->output_element
)) {
3693 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3697 if (!m
->description
)
3698 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3699 well_known_descriptions
,
3700 PA_ELEMENTSOF(well_known_descriptions
)));
3702 if (!m
->description
)
3703 m
->description
= pa_xstrdup(m
->name
);
3706 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3708 else if (m
->channel_map
.channels
== bonus
->channels
)
3715 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3716 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3720 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3722 pa_strnull(m
->description
),
3724 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3725 pa_yes_no(m
->supported
),
3729 static void profile_set_add_auto_pair(
3730 pa_alsa_profile_set
*ps
,
3731 pa_alsa_mapping
*m
, /* output */
3732 pa_alsa_mapping
*n
/* input */) {
3740 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3743 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3747 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3749 name
= pa_sprintf_malloc("output:%s", m
->name
);
3751 name
= pa_sprintf_malloc("input:%s", n
->name
);
3753 if (pa_hashmap_get(ps
->profiles
, name
)) {
3758 p
= pa_xnew0(pa_alsa_profile
, 1);
3759 p
->profile_set
= ps
;
3763 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3764 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3765 p
->priority
+= m
->priority
* 100;
3769 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3770 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3771 p
->priority
+= n
->priority
;
3774 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3777 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3778 pa_alsa_mapping
*m
, *n
;
3779 void *m_state
, *n_state
;
3783 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3784 profile_set_add_auto_pair(ps
, m
, NULL
);
3786 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3787 profile_set_add_auto_pair(ps
, m
, n
);
3790 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3791 profile_set_add_auto_pair(ps
, NULL
, n
);
3794 static int profile_verify(pa_alsa_profile
*p
) {
3796 static const struct description_map well_known_descriptions
[] = {
3797 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3798 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3799 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3800 { "off", N_("Off") }
3805 /* Replace the output mapping names by the actual mappings */
3806 if (p
->output_mapping_names
) {
3809 pa_assert(!p
->output_mappings
);
3810 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3812 for (name
= p
->output_mapping_names
; *name
; name
++) {
3815 pa_bool_t duplicate
= FALSE
;
3817 for (in
= name
+ 1; *in
; in
++)
3818 if (pa_streq(*name
, *in
)) {
3826 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3827 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3831 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3837 pa_xstrfreev(p
->output_mapping_names
);
3838 p
->output_mapping_names
= NULL
;
3841 /* Replace the input mapping names by the actual mappings */
3842 if (p
->input_mapping_names
) {
3845 pa_assert(!p
->input_mappings
);
3846 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3848 for (name
= p
->input_mapping_names
; *name
; name
++) {
3851 pa_bool_t duplicate
= FALSE
;
3853 for (in
= name
+ 1; *in
; in
++)
3854 if (pa_streq(*name
, *in
)) {
3862 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3863 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3867 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3873 pa_xstrfreev(p
->input_mapping_names
);
3874 p
->input_mapping_names
= NULL
;
3877 if (!p
->input_mappings
&& !p
->output_mappings
) {
3878 pa_log("Profile '%s' lacks mappings.", p
->name
);
3882 if (!p
->description
)
3883 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3884 well_known_descriptions
,
3885 PA_ELEMENTSOF(well_known_descriptions
)));
3887 if (!p
->description
) {
3892 sb
= pa_strbuf_new();
3894 if (p
->output_mappings
)
3895 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3896 if (!pa_strbuf_isempty(sb
))
3897 pa_strbuf_puts(sb
, " + ");
3899 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
3902 if (p
->input_mappings
)
3903 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3904 if (!pa_strbuf_isempty(sb
))
3905 pa_strbuf_puts(sb
, " + ");
3907 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
3910 p
->description
= pa_strbuf_tostring_free(sb
);
3916 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
3921 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3923 pa_strnull(p
->description
),
3925 pa_yes_no(p
->supported
),
3926 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
3927 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
3929 if (p
->input_mappings
)
3930 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
3931 pa_log_debug("Input %s", m
->name
);
3933 if (p
->output_mappings
)
3934 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
3935 pa_log_debug("Output %s", m
->name
);
3938 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
3941 /* Check that the dB mapping has been configured. Since "db-values" is
3942 * currently the only option in the DecibelFix section, and decibel fix
3943 * objects don't get created if a DecibelFix section is empty, this is
3944 * actually a redundant check. Having this may prevent future bugs,
3946 if (!db_fix
->db_values
) {
3947 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
3954 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
3955 char *db_values
= NULL
;
3959 if (db_fix
->db_values
) {
3961 unsigned long i
, nsteps
;
3963 pa_assert(db_fix
->min_step
<= db_fix
->max_step
);
3964 nsteps
= db_fix
->max_step
- db_fix
->min_step
+ 1;
3966 buf
= pa_strbuf_new();
3967 for (i
= 0; i
< nsteps
; ++i
)
3968 pa_strbuf_printf(buf
, "[%li]:%0.2f ", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
3970 db_values
= pa_strbuf_tostring_free(buf
);
3973 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
3974 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
3976 pa_xfree(db_values
);
3979 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
3980 pa_alsa_profile_set
*ps
;
3983 pa_alsa_decibel_fix
*db_fix
;
3988 static pa_config_item items
[] = {
3990 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
3993 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
3994 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
3995 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
3996 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
3997 { "element-input", mapping_parse_element
, NULL
, NULL
},
3998 { "element-output", mapping_parse_element
, NULL
, NULL
},
3999 { "direction", mapping_parse_direction
, NULL
, NULL
},
4001 /* Shared by [Mapping ...] and [Profile ...] */
4002 { "description", mapping_parse_description
, NULL
, NULL
},
4003 { "priority", mapping_parse_priority
, NULL
, NULL
},
4006 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
4007 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
4008 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
4010 /* [DecibelFix ...] */
4011 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
4012 { NULL
, NULL
, NULL
, NULL
}
4015 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
4016 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4017 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4018 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4020 items
[0].data
= &ps
->auto_profiles
;
4023 fname
= "default.conf";
4025 fn
= pa_maybe_prefix_path(fname
,
4026 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
4027 PA_ALSA_PROFILE_SETS_DIR
);
4029 r
= pa_config_parse(fn
, NULL
, items
, ps
);
4035 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4036 if (mapping_verify(m
, bonus
) < 0)
4039 if (ps
->auto_profiles
)
4040 profile_set_add_auto(ps
);
4042 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4043 if (profile_verify(p
) < 0)
4046 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4047 if (decibel_fix_verify(db_fix
) < 0)
4053 pa_alsa_profile_set_free(ps
);
4057 void pa_alsa_profile_set_probe(
4058 pa_alsa_profile_set
*ps
,
4060 const pa_sample_spec
*ss
,
4061 unsigned default_n_fragments
,
4062 unsigned default_fragment_size_msec
) {
4065 pa_alsa_profile
*p
, *last
= NULL
;
4075 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
4076 pa_sample_spec try_ss
;
4077 pa_channel_map try_map
;
4078 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
4081 /* Is this already marked that it is supported? (i.e. from the config file) */
4085 pa_log_debug("Looking at profile %s", p
->name
);
4087 /* Close PCMs from the last iteration we don't need anymore */
4088 if (last
&& last
->output_mappings
)
4089 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
) {
4094 if (last
->supported
)
4097 if (!p
->output_mappings
|| !pa_idxset_get_by_data(p
->output_mappings
, m
, NULL
)) {
4098 snd_pcm_close(m
->output_pcm
);
4099 m
->output_pcm
= NULL
;
4103 if (last
&& last
->input_mappings
)
4104 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
) {
4109 if (last
->supported
)
4112 if (!p
->input_mappings
|| !pa_idxset_get_by_data(p
->input_mappings
, m
, NULL
)) {
4113 snd_pcm_close(m
->input_pcm
);
4114 m
->input_pcm
= NULL
;
4118 p
->supported
= TRUE
;
4120 /* Check if we can open all new ones */
4121 if (p
->output_mappings
)
4122 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4127 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
4128 try_map
= m
->channel_map
;
4130 try_ss
.channels
= try_map
.channels
;
4133 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
4134 pa_frame_size(&try_ss
);
4135 try_buffer_size
= default_n_fragments
* try_period_size
;
4137 if (!(m
->output_pcm
= pa_alsa_open_by_template(
4142 SND_PCM_STREAM_PLAYBACK
,
4143 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
4145 p
->supported
= FALSE
;
4150 if (p
->input_mappings
&& p
->supported
)
4151 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4156 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
4157 try_map
= m
->channel_map
;
4159 try_ss
.channels
= try_map
.channels
;
4162 pa_usec_to_bytes(default_fragment_size_msec
*PA_USEC_PER_MSEC
, &try_ss
) /
4163 pa_frame_size(&try_ss
);
4164 try_buffer_size
= default_n_fragments
* try_period_size
;
4166 if (!(m
->input_pcm
= pa_alsa_open_by_template(
4171 SND_PCM_STREAM_CAPTURE
,
4172 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
4174 p
->supported
= FALSE
;
4182 pa_log_debug("Profile %s supported.", p
->name
);
4189 if (last
->output_mappings
)
4190 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
)
4191 if (m
->output_pcm
) {
4193 if (last
->supported
)
4196 snd_pcm_close(m
->output_pcm
);
4197 m
->output_pcm
= NULL
;
4200 if (last
->input_mappings
)
4201 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
)
4204 if (last
->supported
)
4207 snd_pcm_close(m
->input_pcm
);
4208 m
->input_pcm
= NULL
;
4212 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4213 if (!p
->supported
) {
4214 pa_hashmap_remove(ps
->profiles
, p
->name
);
4218 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4219 if (m
->supported
<= 0) {
4220 pa_hashmap_remove(ps
->mappings
, m
->name
);
4227 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
4230 pa_alsa_decibel_fix
*db_fix
;
4235 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4238 pa_yes_no(ps
->auto_profiles
),
4239 pa_yes_no(ps
->probed
),
4240 pa_hashmap_size(ps
->mappings
),
4241 pa_hashmap_size(ps
->profiles
),
4242 pa_hashmap_size(ps
->decibel_fixes
));
4244 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4245 pa_alsa_mapping_dump(m
);
4247 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4248 pa_alsa_profile_dump(p
);
4250 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4251 pa_alsa_decibel_fix_dump(db_fix
);
4254 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
) {
4261 /* if there is no path, we don't want a port list */
4265 if (!ps
->paths
->next
){
4268 /* If there is only one path, but no or only one setting, then
4269 * we want a port list either */
4270 if (!ps
->paths
->settings
|| !ps
->paths
->settings
->next
)
4273 /* Ok, there is only one path, however with multiple settings,
4274 * so let's create a port for each setting */
4275 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4277 PA_LLIST_FOREACH(s
, ps
->paths
->settings
) {
4278 pa_device_port
*port
;
4279 pa_alsa_port_data
*data
;
4281 port
= pa_device_port_new(s
->name
, s
->description
, sizeof(pa_alsa_port_data
));
4282 port
->priority
= s
->priority
;
4284 data
= PA_DEVICE_PORT_DATA(port
);
4285 data
->path
= ps
->paths
;
4288 pa_hashmap_put(*p
, port
->name
, port
);
4293 /* We have multiple paths, so let's create a port for each
4294 * one, and each of each settings */
4295 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4297 PA_LLIST_FOREACH(path
, ps
->paths
) {
4299 if (!path
->settings
|| !path
->settings
->next
) {
4300 pa_device_port
*port
;
4301 pa_alsa_port_data
*data
;
4303 /* If there is no or just one setting we only need a
4306 port
= pa_device_port_new(path
->name
, path
->description
, sizeof(pa_alsa_port_data
));
4307 port
->priority
= path
->priority
* 100;
4310 data
= PA_DEVICE_PORT_DATA(port
);
4312 data
->setting
= path
->settings
;
4314 pa_hashmap_put(*p
, port
->name
, port
);
4318 PA_LLIST_FOREACH(s
, path
->settings
) {
4319 pa_device_port
*port
;
4320 pa_alsa_port_data
*data
;
4323 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4325 if (s
->description
[0])
4326 d
= pa_sprintf_malloc(_("%s / %s"), path
->description
, s
->description
);
4328 d
= pa_xstrdup(path
->description
);
4330 port
= pa_device_port_new(n
, d
, sizeof(pa_alsa_port_data
));
4331 port
->priority
= path
->priority
* 100 + s
->priority
;
4336 data
= PA_DEVICE_PORT_DATA(port
);
4340 pa_hashmap_put(*p
, port
->name
, port
);
4346 pa_log_debug("Added %u ports", pa_hashmap_size(*p
));