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
, (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
? +1 : -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 if (!a_options
) return TRUE
;
2894 if (!b_options
) return FALSE
;
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 (b
->enumeration_use
== PA_ALSA_ENUMERATION_IGNORE
)
3010 if (!enumeration_is_subset(a
->options
, b
->options
))
3017 static void path_set_condense(pa_alsa_path_set
*ps
, snd_mixer_t
*m
) {
3018 pa_alsa_path
*p
, *np
;
3023 /* If we only have one path, then don't bother */
3024 if (!ps
->paths
|| !ps
->paths
->next
)
3027 for (p
= ps
->paths
; p
; p
= np
) {
3031 PA_LLIST_FOREACH(p2
, ps
->paths
) {
3032 pa_alsa_element
*ea
, *eb
;
3033 pa_bool_t is_subset
= TRUE
;
3038 /* Compare the elements of each set... */
3039 pa_assert_se(ea
= p
->elements
);
3040 pa_assert_se(eb
= p2
->elements
);
3043 if (pa_streq(ea
->alsa_name
, eb
->alsa_name
)) {
3044 if (element_is_subset(ea
, eb
, m
)) {
3047 if ((ea
&& !eb
) || (!ea
&& eb
))
3049 else if (!ea
&& !eb
)
3059 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p
->name
, p2
->name
);
3060 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
3061 pa_alsa_path_free(p
);
3068 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
3069 pa_alsa_path
*p
, *q
;
3071 PA_LLIST_FOREACH(p
, ps
->paths
) {
3075 for (q
= p
->next
; q
; q
= q
->next
)
3076 if (pa_streq(q
->name
, p
->name
))
3082 m
= pa_xstrdup(p
->name
);
3084 /* OK, this name is not unique, hence let's rename */
3085 for (i
= 1, q
= p
; q
; q
= q
->next
) {
3088 if (!pa_streq(q
->name
, m
))
3091 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
3095 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
3096 pa_xfree(q
->description
);
3097 q
->description
= nd
;
3106 void pa_alsa_path_set_probe(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
3107 pa_alsa_path
*p
, *n
;
3114 for (p
= ps
->paths
; p
; p
= n
) {
3117 if (pa_alsa_path_probe(p
, m
, ignore_dB
) < 0) {
3118 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
3119 pa_alsa_path_free(p
);
3123 pa_log_debug("Found mixer paths (before tidying):");
3124 pa_alsa_path_set_dump(ps
);
3126 path_set_condense(ps
, m
);
3127 path_set_make_paths_unique(ps
);
3130 pa_log_debug("Available mixer paths (after tidying):");
3131 pa_alsa_path_set_dump(ps
);
3134 static void mapping_free(pa_alsa_mapping
*m
) {
3138 pa_xfree(m
->description
);
3140 pa_xstrfreev(m
->device_strings
);
3141 pa_xstrfreev(m
->input_path_names
);
3142 pa_xstrfreev(m
->output_path_names
);
3143 pa_xstrfreev(m
->input_element
);
3144 pa_xstrfreev(m
->output_element
);
3146 pa_assert(!m
->input_pcm
);
3147 pa_assert(!m
->output_pcm
);
3152 static void profile_free(pa_alsa_profile
*p
) {
3156 pa_xfree(p
->description
);
3158 pa_xstrfreev(p
->input_mapping_names
);
3159 pa_xstrfreev(p
->output_mapping_names
);
3161 if (p
->input_mappings
)
3162 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
3164 if (p
->output_mappings
)
3165 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
3170 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
3176 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
3179 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
3185 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
3188 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
3191 if (ps
->decibel_fixes
) {
3192 pa_alsa_decibel_fix
*db_fix
;
3194 while ((db_fix
= pa_hashmap_steal_first(ps
->decibel_fixes
)))
3195 decibel_fix_free(db_fix
);
3197 pa_hashmap_free(ps
->decibel_fixes
, NULL
, NULL
);
3203 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
3206 if (!pa_startswith(name
, "Mapping "))
3211 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
3214 m
= pa_xnew0(pa_alsa_mapping
, 1);
3215 m
->profile_set
= ps
;
3216 m
->name
= pa_xstrdup(name
);
3217 pa_channel_map_init(&m
->channel_map
);
3219 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
3224 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
3227 if (!pa_startswith(name
, "Profile "))
3232 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
3235 p
= pa_xnew0(pa_alsa_profile
, 1);
3236 p
->profile_set
= ps
;
3237 p
->name
= pa_xstrdup(name
);
3239 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3244 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
3245 pa_alsa_decibel_fix
*db_fix
;
3247 if (!pa_startswith(name
, "DecibelFix "))
3252 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
3255 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
3256 db_fix
->profile_set
= ps
;
3257 db_fix
->name
= pa_xstrdup(name
);
3259 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
3264 static int mapping_parse_device_strings(
3265 const char *filename
,
3267 const char *section
,
3273 pa_alsa_profile_set
*ps
= userdata
;
3278 if (!(m
= mapping_get(ps
, section
))) {
3279 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3283 pa_xstrfreev(m
->device_strings
);
3284 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
3285 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
3292 static int mapping_parse_channel_map(
3293 const char *filename
,
3295 const char *section
,
3301 pa_alsa_profile_set
*ps
= userdata
;
3306 if (!(m
= mapping_get(ps
, section
))) {
3307 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3311 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
3312 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
3319 static int mapping_parse_paths(
3320 const char *filename
,
3322 const char *section
,
3328 pa_alsa_profile_set
*ps
= userdata
;
3333 if (!(m
= mapping_get(ps
, section
))) {
3334 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3338 if (pa_streq(lvalue
, "paths-input")) {
3339 pa_xstrfreev(m
->input_path_names
);
3340 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
3342 pa_xstrfreev(m
->output_path_names
);
3343 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
3349 static int mapping_parse_element(
3350 const char *filename
,
3352 const char *section
,
3358 pa_alsa_profile_set
*ps
= userdata
;
3363 if (!(m
= mapping_get(ps
, section
))) {
3364 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3368 if (pa_streq(lvalue
, "element-input")) {
3369 pa_xstrfreev(m
->input_element
);
3370 m
->input_element
= pa_split_spaces_strv(rvalue
);
3372 pa_xstrfreev(m
->output_element
);
3373 m
->output_element
= pa_split_spaces_strv(rvalue
);
3379 static int mapping_parse_direction(
3380 const char *filename
,
3382 const char *section
,
3388 pa_alsa_profile_set
*ps
= userdata
;
3393 if (!(m
= mapping_get(ps
, section
))) {
3394 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3398 if (pa_streq(rvalue
, "input"))
3399 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3400 else if (pa_streq(rvalue
, "output"))
3401 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3402 else if (pa_streq(rvalue
, "any"))
3403 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3405 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
3412 static int mapping_parse_description(
3413 const char *filename
,
3415 const char *section
,
3421 pa_alsa_profile_set
*ps
= userdata
;
3427 if ((m
= mapping_get(ps
, section
))) {
3428 pa_xfree(m
->description
);
3429 m
->description
= pa_xstrdup(rvalue
);
3430 } else if ((p
= profile_get(ps
, section
))) {
3431 pa_xfree(p
->description
);
3432 p
->description
= pa_xstrdup(rvalue
);
3434 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3441 static int mapping_parse_priority(
3442 const char *filename
,
3444 const char *section
,
3450 pa_alsa_profile_set
*ps
= userdata
;
3457 if (pa_atou(rvalue
, &prio
) < 0) {
3458 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
3462 if ((m
= mapping_get(ps
, section
)))
3464 else if ((p
= profile_get(ps
, section
)))
3467 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3474 static int profile_parse_mappings(
3475 const char *filename
,
3477 const char *section
,
3483 pa_alsa_profile_set
*ps
= userdata
;
3488 if (!(p
= profile_get(ps
, section
))) {
3489 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3493 if (pa_streq(lvalue
, "input-mappings")) {
3494 pa_xstrfreev(p
->input_mapping_names
);
3495 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
3497 pa_xstrfreev(p
->output_mapping_names
);
3498 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
3504 static int profile_parse_skip_probe(
3505 const char *filename
,
3507 const char *section
,
3513 pa_alsa_profile_set
*ps
= userdata
;
3519 if (!(p
= profile_get(ps
, section
))) {
3520 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3524 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
3525 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
3534 static int decibel_fix_parse_db_values(
3535 const char *filename
,
3537 const char *section
,
3543 pa_alsa_profile_set
*ps
= userdata
;
3544 pa_alsa_decibel_fix
*db_fix
;
3548 unsigned n
= 8; /* Current size of the db_values table. */
3549 unsigned min_step
= 0;
3550 unsigned max_step
= 0;
3551 unsigned i
= 0; /* Index to the items table. */
3552 unsigned prev_step
= 0;
3555 pa_assert(filename
);
3561 if (!(db_fix
= decibel_fix_get(ps
, section
))) {
3562 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3566 if (!(items
= pa_split_spaces_strv(rvalue
))) {
3567 pa_log("[%s:%u] Value missing", pa_strnull(filename
), line
);
3571 db_values
= pa_xnew(long, n
);
3573 while ((item
= items
[i
++])) {
3574 char *s
= item
; /* Step value string. */
3575 char *d
= item
; /* dB value string. */
3579 /* Move d forward until it points to a colon or to the end of the item. */
3580 for (; *d
&& *d
!= ':'; ++d
);
3583 /* item started with colon. */
3584 pa_log("[%s:%u] No step value found in %s", filename
, line
, item
);
3588 if (!*d
|| !*(d
+ 1)) {
3589 /* No colon found, or it was the last character in item. */
3590 pa_log("[%s:%u] No dB value found in %s", filename
, line
, item
);
3594 /* pa_atou() needs a null-terminating string. Let's replace the colon
3595 * with a zero byte. */
3598 if (pa_atou(s
, &step
) < 0) {
3599 pa_log("[%s:%u] Invalid step value: %s", filename
, line
, s
);
3603 if (pa_atod(d
, &db
) < 0) {
3604 pa_log("[%s:%u] Invalid dB value: %s", filename
, line
, d
);
3608 if (step
<= prev_step
&& i
!= 1) {
3609 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename
, line
, step
, prev_step
);
3613 if (db
< prev_db
&& i
!= 1) {
3614 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename
, line
, db
, prev_db
);
3620 db_values
[0] = (long) (db
* 100.0);
3624 /* Interpolate linearly. */
3625 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3627 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3629 /* Reallocate the db_values table if it's about to overflow. */
3630 if (prev_step
+ 1 - min_step
== n
) {
3632 db_values
= pa_xrenew(long, db_values
, n
);
3635 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3642 db_fix
->min_step
= min_step
;
3643 db_fix
->max_step
= max_step
;
3644 pa_xfree(db_fix
->db_values
);
3645 db_fix
->db_values
= db_values
;
3647 pa_xstrfreev(items
);
3652 pa_xstrfreev(items
);
3653 pa_xfree(db_values
);
3658 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3660 static const struct description_map well_known_descriptions
[] = {
3661 { "analog-mono", N_("Analog Mono") },
3662 { "analog-stereo", N_("Analog Stereo") },
3663 { "analog-surround-21", N_("Analog Surround 2.1") },
3664 { "analog-surround-30", N_("Analog Surround 3.0") },
3665 { "analog-surround-31", N_("Analog Surround 3.1") },
3666 { "analog-surround-40", N_("Analog Surround 4.0") },
3667 { "analog-surround-41", N_("Analog Surround 4.1") },
3668 { "analog-surround-50", N_("Analog Surround 5.0") },
3669 { "analog-surround-51", N_("Analog Surround 5.1") },
3670 { "analog-surround-61", N_("Analog Surround 6.0") },
3671 { "analog-surround-61", N_("Analog Surround 6.1") },
3672 { "analog-surround-70", N_("Analog Surround 7.0") },
3673 { "analog-surround-71", N_("Analog Surround 7.1") },
3674 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3675 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3676 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3677 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3678 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3683 if (!pa_channel_map_valid(&m
->channel_map
)) {
3684 pa_log("Mapping %s is missing channel map.", m
->name
);
3688 if (!m
->device_strings
) {
3689 pa_log("Mapping %s is missing device strings.", m
->name
);
3693 if ((m
->input_path_names
&& m
->input_element
) ||
3694 (m
->output_path_names
&& m
->output_element
)) {
3695 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3699 if (!m
->description
)
3700 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3701 well_known_descriptions
,
3702 PA_ELEMENTSOF(well_known_descriptions
)));
3704 if (!m
->description
)
3705 m
->description
= pa_xstrdup(m
->name
);
3708 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3710 else if (m
->channel_map
.channels
== bonus
->channels
)
3717 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3718 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3722 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3724 pa_strnull(m
->description
),
3726 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3727 pa_yes_no(m
->supported
),
3731 static void profile_set_add_auto_pair(
3732 pa_alsa_profile_set
*ps
,
3733 pa_alsa_mapping
*m
, /* output */
3734 pa_alsa_mapping
*n
/* input */) {
3742 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3745 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3749 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3751 name
= pa_sprintf_malloc("output:%s", m
->name
);
3753 name
= pa_sprintf_malloc("input:%s", n
->name
);
3755 if (pa_hashmap_get(ps
->profiles
, name
)) {
3760 p
= pa_xnew0(pa_alsa_profile
, 1);
3761 p
->profile_set
= ps
;
3765 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3766 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3767 p
->priority
+= m
->priority
* 100;
3771 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3772 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3773 p
->priority
+= n
->priority
;
3776 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3779 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3780 pa_alsa_mapping
*m
, *n
;
3781 void *m_state
, *n_state
;
3785 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3786 profile_set_add_auto_pair(ps
, m
, NULL
);
3788 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3789 profile_set_add_auto_pair(ps
, m
, n
);
3792 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3793 profile_set_add_auto_pair(ps
, NULL
, n
);
3796 static int profile_verify(pa_alsa_profile
*p
) {
3798 static const struct description_map well_known_descriptions
[] = {
3799 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3800 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3801 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3802 { "off", N_("Off") }
3807 /* Replace the output mapping names by the actual mappings */
3808 if (p
->output_mapping_names
) {
3811 pa_assert(!p
->output_mappings
);
3812 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3814 for (name
= p
->output_mapping_names
; *name
; name
++) {
3817 pa_bool_t duplicate
= FALSE
;
3819 for (in
= name
+ 1; *in
; in
++)
3820 if (pa_streq(*name
, *in
)) {
3828 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3829 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3833 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3839 pa_xstrfreev(p
->output_mapping_names
);
3840 p
->output_mapping_names
= NULL
;
3843 /* Replace the input mapping names by the actual mappings */
3844 if (p
->input_mapping_names
) {
3847 pa_assert(!p
->input_mappings
);
3848 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3850 for (name
= p
->input_mapping_names
; *name
; name
++) {
3853 pa_bool_t duplicate
= FALSE
;
3855 for (in
= name
+ 1; *in
; in
++)
3856 if (pa_streq(*name
, *in
)) {
3864 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3865 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3869 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3875 pa_xstrfreev(p
->input_mapping_names
);
3876 p
->input_mapping_names
= NULL
;
3879 if (!p
->input_mappings
&& !p
->output_mappings
) {
3880 pa_log("Profile '%s' lacks mappings.", p
->name
);
3884 if (!p
->description
)
3885 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3886 well_known_descriptions
,
3887 PA_ELEMENTSOF(well_known_descriptions
)));
3889 if (!p
->description
) {
3894 sb
= pa_strbuf_new();
3896 if (p
->output_mappings
)
3897 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3898 if (!pa_strbuf_isempty(sb
))
3899 pa_strbuf_puts(sb
, " + ");
3901 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
3904 if (p
->input_mappings
)
3905 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3906 if (!pa_strbuf_isempty(sb
))
3907 pa_strbuf_puts(sb
, " + ");
3909 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
3912 p
->description
= pa_strbuf_tostring_free(sb
);
3918 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
3923 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3925 pa_strnull(p
->description
),
3927 pa_yes_no(p
->supported
),
3928 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
3929 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
3931 if (p
->input_mappings
)
3932 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
3933 pa_log_debug("Input %s", m
->name
);
3935 if (p
->output_mappings
)
3936 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
3937 pa_log_debug("Output %s", m
->name
);
3940 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
3943 /* Check that the dB mapping has been configured. Since "db-values" is
3944 * currently the only option in the DecibelFix section, and decibel fix
3945 * objects don't get created if a DecibelFix section is empty, this is
3946 * actually a redundant check. Having this may prevent future bugs,
3948 if (!db_fix
->db_values
) {
3949 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
3956 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
3957 char *db_values
= NULL
;
3961 if (db_fix
->db_values
) {
3963 unsigned long i
, nsteps
;
3965 pa_assert(db_fix
->min_step
<= db_fix
->max_step
);
3966 nsteps
= db_fix
->max_step
- db_fix
->min_step
+ 1;
3968 buf
= pa_strbuf_new();
3969 for (i
= 0; i
< nsteps
; ++i
)
3970 pa_strbuf_printf(buf
, "[%li]:%0.2f ", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
3972 db_values
= pa_strbuf_tostring_free(buf
);
3975 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
3976 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
3978 pa_xfree(db_values
);
3981 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
3982 pa_alsa_profile_set
*ps
;
3985 pa_alsa_decibel_fix
*db_fix
;
3990 static pa_config_item items
[] = {
3992 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
3995 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
3996 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
3997 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
3998 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
3999 { "element-input", mapping_parse_element
, NULL
, NULL
},
4000 { "element-output", mapping_parse_element
, NULL
, NULL
},
4001 { "direction", mapping_parse_direction
, NULL
, NULL
},
4003 /* Shared by [Mapping ...] and [Profile ...] */
4004 { "description", mapping_parse_description
, NULL
, NULL
},
4005 { "priority", mapping_parse_priority
, NULL
, NULL
},
4008 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
4009 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
4010 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
4012 /* [DecibelFix ...] */
4013 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
4014 { NULL
, NULL
, NULL
, NULL
}
4017 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
4018 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4019 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4020 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4022 items
[0].data
= &ps
->auto_profiles
;
4025 fname
= "default.conf";
4027 fn
= pa_maybe_prefix_path(fname
,
4028 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
4029 PA_ALSA_PROFILE_SETS_DIR
);
4031 r
= pa_config_parse(fn
, NULL
, items
, ps
);
4037 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4038 if (mapping_verify(m
, bonus
) < 0)
4041 if (ps
->auto_profiles
)
4042 profile_set_add_auto(ps
);
4044 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4045 if (profile_verify(p
) < 0)
4048 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4049 if (decibel_fix_verify(db_fix
) < 0)
4055 pa_alsa_profile_set_free(ps
);
4059 void pa_alsa_profile_set_probe(
4060 pa_alsa_profile_set
*ps
,
4062 const pa_sample_spec
*ss
,
4063 unsigned default_n_fragments
,
4064 unsigned default_fragment_size_msec
) {
4067 pa_alsa_profile
*p
, *last
= NULL
;
4077 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
4078 pa_sample_spec try_ss
;
4079 pa_channel_map try_map
;
4080 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
4083 /* Is this already marked that it is supported? (i.e. from the config file) */
4087 pa_log_debug("Looking at profile %s", p
->name
);
4089 /* Close PCMs from the last iteration we don't need anymore */
4090 if (last
&& last
->output_mappings
)
4091 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
) {
4096 if (last
->supported
)
4099 if (!p
->output_mappings
|| !pa_idxset_get_by_data(p
->output_mappings
, m
, NULL
)) {
4100 snd_pcm_close(m
->output_pcm
);
4101 m
->output_pcm
= NULL
;
4105 if (last
&& last
->input_mappings
)
4106 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
) {
4111 if (last
->supported
)
4114 if (!p
->input_mappings
|| !pa_idxset_get_by_data(p
->input_mappings
, m
, NULL
)) {
4115 snd_pcm_close(m
->input_pcm
);
4116 m
->input_pcm
= NULL
;
4120 p
->supported
= TRUE
;
4122 /* Check if we can open all new ones */
4123 if (p
->output_mappings
)
4124 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4129 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
4130 try_map
= m
->channel_map
;
4132 try_ss
.channels
= try_map
.channels
;
4135 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
4136 pa_frame_size(&try_ss
);
4137 try_buffer_size
= default_n_fragments
* try_period_size
;
4139 if (!(m
->output_pcm
= pa_alsa_open_by_template(
4144 SND_PCM_STREAM_PLAYBACK
,
4145 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
4147 p
->supported
= FALSE
;
4152 if (p
->input_mappings
&& p
->supported
)
4153 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4158 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
4159 try_map
= m
->channel_map
;
4161 try_ss
.channels
= try_map
.channels
;
4164 pa_usec_to_bytes(default_fragment_size_msec
*PA_USEC_PER_MSEC
, &try_ss
) /
4165 pa_frame_size(&try_ss
);
4166 try_buffer_size
= default_n_fragments
* try_period_size
;
4168 if (!(m
->input_pcm
= pa_alsa_open_by_template(
4173 SND_PCM_STREAM_CAPTURE
,
4174 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
4176 p
->supported
= FALSE
;
4184 pa_log_debug("Profile %s supported.", p
->name
);
4191 if (last
->output_mappings
)
4192 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
)
4193 if (m
->output_pcm
) {
4195 if (last
->supported
)
4198 snd_pcm_close(m
->output_pcm
);
4199 m
->output_pcm
= NULL
;
4202 if (last
->input_mappings
)
4203 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
)
4206 if (last
->supported
)
4209 snd_pcm_close(m
->input_pcm
);
4210 m
->input_pcm
= NULL
;
4214 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4215 if (!p
->supported
) {
4216 pa_hashmap_remove(ps
->profiles
, p
->name
);
4220 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4221 if (m
->supported
<= 0) {
4222 pa_hashmap_remove(ps
->mappings
, m
->name
);
4229 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
4232 pa_alsa_decibel_fix
*db_fix
;
4237 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4240 pa_yes_no(ps
->auto_profiles
),
4241 pa_yes_no(ps
->probed
),
4242 pa_hashmap_size(ps
->mappings
),
4243 pa_hashmap_size(ps
->profiles
),
4244 pa_hashmap_size(ps
->decibel_fixes
));
4246 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4247 pa_alsa_mapping_dump(m
);
4249 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4250 pa_alsa_profile_dump(p
);
4252 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4253 pa_alsa_decibel_fix_dump(db_fix
);
4256 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
) {
4263 /* if there is no path, we don't want a port list */
4267 if (!ps
->paths
->next
){
4270 /* If there is only one path, but no or only one setting, then
4271 * we want a port list either */
4272 if (!ps
->paths
->settings
|| !ps
->paths
->settings
->next
)
4275 /* Ok, there is only one path, however with multiple settings,
4276 * so let's create a port for each setting */
4277 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4279 PA_LLIST_FOREACH(s
, ps
->paths
->settings
) {
4280 pa_device_port
*port
;
4281 pa_alsa_port_data
*data
;
4283 port
= pa_device_port_new(s
->name
, s
->description
, sizeof(pa_alsa_port_data
));
4284 port
->priority
= s
->priority
;
4286 data
= PA_DEVICE_PORT_DATA(port
);
4287 data
->path
= ps
->paths
;
4290 pa_hashmap_put(*p
, port
->name
, port
);
4295 /* We have multiple paths, so let's create a port for each
4296 * one, and each of each settings */
4297 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4299 PA_LLIST_FOREACH(path
, ps
->paths
) {
4301 if (!path
->settings
|| !path
->settings
->next
) {
4302 pa_device_port
*port
;
4303 pa_alsa_port_data
*data
;
4305 /* If there is no or just one setting we only need a
4308 port
= pa_device_port_new(path
->name
, path
->description
, sizeof(pa_alsa_port_data
));
4309 port
->priority
= path
->priority
* 100;
4312 data
= PA_DEVICE_PORT_DATA(port
);
4314 data
->setting
= path
->settings
;
4316 pa_hashmap_put(*p
, port
->name
, port
);
4320 PA_LLIST_FOREACH(s
, path
->settings
) {
4321 pa_device_port
*port
;
4322 pa_alsa_port_data
*data
;
4325 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4327 if (s
->description
[0])
4328 d
= pa_sprintf_malloc(_("%s / %s"), path
->description
, s
->description
);
4330 d
= pa_xstrdup(path
->description
);
4332 port
= pa_device_port_new(n
, d
, sizeof(pa_alsa_port_data
));
4333 port
->priority
= path
->priority
* 100 + s
->priority
;
4338 data
= PA_DEVICE_PORT_DATA(port
);
4342 pa_hashmap_put(*p
, port
->name
, port
);
4348 pa_log_debug("Added %u ports", pa_hashmap_size(*p
));