2 This file is part of PulseAudio.
4 Copyright 2004-2009 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 #include <sys/types.h>
28 #include <asoundlib.h>
31 #ifdef HAVE_VALGRIND_MEMCHECK_H
32 #include <valgrind/memcheck.h>
35 #include <pulse/mainloop-api.h>
36 #include <pulse/sample.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/volume.h>
40 #include <pulse/xmalloc.h>
41 #include <pulse/i18n.h>
42 #include <pulse/utf8.h>
44 #include <pulsecore/log.h>
45 #include <pulsecore/macro.h>
46 #include <pulsecore/core-util.h>
47 #include <pulsecore/conf-parser.h>
48 #include <pulsecore/strbuf.h>
50 #include "alsa-mixer.h"
51 #include "alsa-util.h"
53 struct description_map
{
55 const char *description
;
58 static const char *lookup_description(const char *name
, const struct description_map dm
[], unsigned n
) {
61 for (i
= 0; i
< n
; i
++)
62 if (pa_streq(dm
[i
].name
, name
))
63 return _(dm
[i
].description
);
68 struct pa_alsa_fdlist
{
71 /* This is a temporary buffer used to avoid lots of mallocs */
72 struct pollfd
*work_fds
;
77 pa_defer_event
*defer
;
82 void (*cb
)(void *userdata
);
86 static void io_cb(pa_mainloop_api
*a
, pa_io_event
*e
, int fd
, pa_io_event_flags_t events
, void *userdata
) {
88 struct pa_alsa_fdlist
*fdl
= userdata
;
91 unsigned short revents
;
95 pa_assert(fdl
->mixer
);
97 pa_assert(fdl
->work_fds
);
104 memcpy(fdl
->work_fds
, fdl
->fds
, sizeof(struct pollfd
) * fdl
->num_fds
);
106 for (i
= 0; i
< fdl
->num_fds
; i
++) {
107 if (e
== fdl
->ios
[i
]) {
108 if (events
& PA_IO_EVENT_INPUT
)
109 fdl
->work_fds
[i
].revents
|= POLLIN
;
110 if (events
& PA_IO_EVENT_OUTPUT
)
111 fdl
->work_fds
[i
].revents
|= POLLOUT
;
112 if (events
& PA_IO_EVENT_ERROR
)
113 fdl
->work_fds
[i
].revents
|= POLLERR
;
114 if (events
& PA_IO_EVENT_HANGUP
)
115 fdl
->work_fds
[i
].revents
|= POLLHUP
;
120 pa_assert(i
!= fdl
->num_fds
);
122 if ((err
= snd_mixer_poll_descriptors_revents(fdl
->mixer
, fdl
->work_fds
, fdl
->num_fds
, &revents
)) < 0) {
123 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err
));
127 a
->defer_enable(fdl
->defer
, 1);
130 snd_mixer_handle_events(fdl
->mixer
);
133 static void defer_cb(pa_mainloop_api
*a
, pa_defer_event
*e
, void *userdata
) {
134 struct pa_alsa_fdlist
*fdl
= userdata
;
141 pa_assert(fdl
->mixer
);
143 a
->defer_enable(fdl
->defer
, 0);
145 if ((n
= snd_mixer_poll_descriptors_count(fdl
->mixer
)) < 0) {
146 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n
));
149 num_fds
= (unsigned) n
;
151 if (num_fds
!= fdl
->num_fds
) {
155 pa_xfree(fdl
->work_fds
);
156 fdl
->fds
= pa_xnew0(struct pollfd
, num_fds
);
157 fdl
->work_fds
= pa_xnew(struct pollfd
, num_fds
);
160 memset(fdl
->work_fds
, 0, sizeof(struct pollfd
) * num_fds
);
162 if ((err
= snd_mixer_poll_descriptors(fdl
->mixer
, fdl
->work_fds
, num_fds
)) < 0) {
163 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err
));
169 if (memcmp(fdl
->fds
, fdl
->work_fds
, sizeof(struct pollfd
) * num_fds
) == 0)
173 for (i
= 0; i
< fdl
->num_fds
; i
++)
174 a
->io_free(fdl
->ios
[i
]);
176 if (num_fds
!= fdl
->num_fds
) {
183 fdl
->ios
= pa_xnew(pa_io_event
*, num_fds
);
186 temp
= fdl
->work_fds
;
187 fdl
->work_fds
= fdl
->fds
;
190 fdl
->num_fds
= num_fds
;
192 for (i
= 0;i
< num_fds
;i
++)
193 fdl
->ios
[i
] = a
->io_new(a
, fdl
->fds
[i
].fd
,
194 ((fdl
->fds
[i
].events
& POLLIN
) ? PA_IO_EVENT_INPUT
: 0) |
195 ((fdl
->fds
[i
].events
& POLLOUT
) ? PA_IO_EVENT_OUTPUT
: 0),
199 struct pa_alsa_fdlist
*pa_alsa_fdlist_new(void) {
200 struct pa_alsa_fdlist
*fdl
;
202 fdl
= pa_xnew0(struct pa_alsa_fdlist
, 1);
207 void pa_alsa_fdlist_free(struct pa_alsa_fdlist
*fdl
) {
212 fdl
->m
->defer_free(fdl
->defer
);
218 for (i
= 0; i
< fdl
->num_fds
; i
++)
219 fdl
->m
->io_free(fdl
->ios
[i
]);
226 pa_xfree(fdl
->work_fds
);
231 int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist
*fdl
, snd_mixer_t
*mixer_handle
, pa_mainloop_api
*m
) {
233 pa_assert(mixer_handle
);
237 fdl
->mixer
= mixer_handle
;
239 fdl
->defer
= m
->defer_new(m
, defer_cb
, fdl
);
244 struct pa_alsa_mixer_pdata
{
246 pa_rtpoll_item
*poll_item
;
251 struct pa_alsa_mixer_pdata
*pa_alsa_mixer_pdata_new(void) {
252 struct pa_alsa_mixer_pdata
*pd
;
254 pd
= pa_xnew0(struct pa_alsa_mixer_pdata
, 1);
259 void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata
*pd
) {
263 pa_rtpoll_item_free(pd
->poll_item
);
269 static int rtpoll_work_cb(pa_rtpoll_item
*i
) {
270 struct pa_alsa_mixer_pdata
*pd
;
273 unsigned short revents
= 0;
276 pd
= pa_rtpoll_item_get_userdata(i
);
278 pa_assert_fp(i
== pd
->poll_item
);
280 p
= pa_rtpoll_item_get_pollfd(i
, &n_fds
);
282 if ((err
= snd_mixer_poll_descriptors_revents(pd
->mixer
, p
, n_fds
, &revents
)) < 0) {
283 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err
));
284 pa_rtpoll_item_free(i
);
289 snd_mixer_handle_events(pd
->mixer
);
290 pa_rtpoll_item_free(i
);
291 pa_alsa_set_mixer_rtpoll(pd
, pd
->mixer
, pd
->rtpoll
);
297 int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata
*pd
, snd_mixer_t
*mixer
, pa_rtpoll
*rtp
) {
306 if ((n
= snd_mixer_poll_descriptors_count(mixer
)) < 0) {
307 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n
));
311 i
= pa_rtpoll_item_new(rtp
, PA_RTPOLL_LATE
, (unsigned) n
);
313 p
= pa_rtpoll_item_get_pollfd(i
, NULL
);
315 memset(p
, 0, sizeof(struct pollfd
) * n
);
317 if ((err
= snd_mixer_poll_descriptors(mixer
, p
, (unsigned) n
)) < 0) {
318 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err
));
319 pa_rtpoll_item_free(i
);
327 pa_rtpoll_item_set_userdata(i
, pd
);
328 pa_rtpoll_item_set_work_callback(i
, rtpoll_work_cb
);
333 static int prepare_mixer(snd_mixer_t
*mixer
, const char *dev
) {
339 if ((err
= snd_mixer_attach(mixer
, dev
)) < 0) {
340 pa_log_info("Unable to attach to mixer %s: %s", dev
, pa_alsa_strerror(err
));
344 if ((err
= snd_mixer_selem_register(mixer
, NULL
, NULL
)) < 0) {
345 pa_log_warn("Unable to register mixer: %s", pa_alsa_strerror(err
));
349 if ((err
= snd_mixer_load(mixer
)) < 0) {
350 pa_log_warn("Unable to load mixer: %s", pa_alsa_strerror(err
));
354 pa_log_info("Successfully attached to mixer '%s'", dev
);
358 snd_mixer_t
*pa_alsa_open_mixer_for_pcm(snd_pcm_t
*pcm
, char **ctl_device
) {
362 snd_pcm_info_t
* info
;
363 snd_pcm_info_alloca(&info
);
367 if ((err
= snd_mixer_open(&m
, 0)) < 0) {
368 pa_log("Error opening mixer: %s", pa_alsa_strerror(err
));
372 /* First, try by name */
373 if ((dev
= snd_pcm_name(pcm
)))
374 if (prepare_mixer(m
, dev
) >= 0) {
376 *ctl_device
= pa_xstrdup(dev
);
381 /* Then, try by card index */
382 if (snd_pcm_info(pcm
, info
) >= 0) {
386 if ((card_idx
= snd_pcm_info_get_card(info
)) >= 0) {
388 md
= pa_sprintf_malloc("hw:%i", card_idx
);
390 if (!dev
|| !pa_streq(dev
, md
))
391 if (prepare_mixer(m
, md
) >= 0) {
409 static const snd_mixer_selem_channel_id_t alsa_channel_ids
[PA_CHANNEL_POSITION_MAX
] = {
410 [PA_CHANNEL_POSITION_MONO
] = SND_MIXER_SCHN_MONO
, /* The ALSA name is just an alias! */
412 [PA_CHANNEL_POSITION_FRONT_CENTER
] = SND_MIXER_SCHN_FRONT_CENTER
,
413 [PA_CHANNEL_POSITION_FRONT_LEFT
] = SND_MIXER_SCHN_FRONT_LEFT
,
414 [PA_CHANNEL_POSITION_FRONT_RIGHT
] = SND_MIXER_SCHN_FRONT_RIGHT
,
416 [PA_CHANNEL_POSITION_REAR_CENTER
] = SND_MIXER_SCHN_REAR_CENTER
,
417 [PA_CHANNEL_POSITION_REAR_LEFT
] = SND_MIXER_SCHN_REAR_LEFT
,
418 [PA_CHANNEL_POSITION_REAR_RIGHT
] = SND_MIXER_SCHN_REAR_RIGHT
,
420 [PA_CHANNEL_POSITION_LFE
] = SND_MIXER_SCHN_WOOFER
,
422 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
423 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
425 [PA_CHANNEL_POSITION_SIDE_LEFT
] = SND_MIXER_SCHN_SIDE_LEFT
,
426 [PA_CHANNEL_POSITION_SIDE_RIGHT
] = SND_MIXER_SCHN_SIDE_RIGHT
,
428 [PA_CHANNEL_POSITION_AUX0
] = SND_MIXER_SCHN_UNKNOWN
,
429 [PA_CHANNEL_POSITION_AUX1
] = SND_MIXER_SCHN_UNKNOWN
,
430 [PA_CHANNEL_POSITION_AUX2
] = SND_MIXER_SCHN_UNKNOWN
,
431 [PA_CHANNEL_POSITION_AUX3
] = SND_MIXER_SCHN_UNKNOWN
,
432 [PA_CHANNEL_POSITION_AUX4
] = SND_MIXER_SCHN_UNKNOWN
,
433 [PA_CHANNEL_POSITION_AUX5
] = SND_MIXER_SCHN_UNKNOWN
,
434 [PA_CHANNEL_POSITION_AUX6
] = SND_MIXER_SCHN_UNKNOWN
,
435 [PA_CHANNEL_POSITION_AUX7
] = SND_MIXER_SCHN_UNKNOWN
,
436 [PA_CHANNEL_POSITION_AUX8
] = SND_MIXER_SCHN_UNKNOWN
,
437 [PA_CHANNEL_POSITION_AUX9
] = SND_MIXER_SCHN_UNKNOWN
,
438 [PA_CHANNEL_POSITION_AUX10
] = SND_MIXER_SCHN_UNKNOWN
,
439 [PA_CHANNEL_POSITION_AUX11
] = SND_MIXER_SCHN_UNKNOWN
,
440 [PA_CHANNEL_POSITION_AUX12
] = SND_MIXER_SCHN_UNKNOWN
,
441 [PA_CHANNEL_POSITION_AUX13
] = SND_MIXER_SCHN_UNKNOWN
,
442 [PA_CHANNEL_POSITION_AUX14
] = SND_MIXER_SCHN_UNKNOWN
,
443 [PA_CHANNEL_POSITION_AUX15
] = SND_MIXER_SCHN_UNKNOWN
,
444 [PA_CHANNEL_POSITION_AUX16
] = SND_MIXER_SCHN_UNKNOWN
,
445 [PA_CHANNEL_POSITION_AUX17
] = SND_MIXER_SCHN_UNKNOWN
,
446 [PA_CHANNEL_POSITION_AUX18
] = SND_MIXER_SCHN_UNKNOWN
,
447 [PA_CHANNEL_POSITION_AUX19
] = SND_MIXER_SCHN_UNKNOWN
,
448 [PA_CHANNEL_POSITION_AUX20
] = SND_MIXER_SCHN_UNKNOWN
,
449 [PA_CHANNEL_POSITION_AUX21
] = SND_MIXER_SCHN_UNKNOWN
,
450 [PA_CHANNEL_POSITION_AUX22
] = SND_MIXER_SCHN_UNKNOWN
,
451 [PA_CHANNEL_POSITION_AUX23
] = SND_MIXER_SCHN_UNKNOWN
,
452 [PA_CHANNEL_POSITION_AUX24
] = SND_MIXER_SCHN_UNKNOWN
,
453 [PA_CHANNEL_POSITION_AUX25
] = SND_MIXER_SCHN_UNKNOWN
,
454 [PA_CHANNEL_POSITION_AUX26
] = SND_MIXER_SCHN_UNKNOWN
,
455 [PA_CHANNEL_POSITION_AUX27
] = SND_MIXER_SCHN_UNKNOWN
,
456 [PA_CHANNEL_POSITION_AUX28
] = SND_MIXER_SCHN_UNKNOWN
,
457 [PA_CHANNEL_POSITION_AUX29
] = SND_MIXER_SCHN_UNKNOWN
,
458 [PA_CHANNEL_POSITION_AUX30
] = SND_MIXER_SCHN_UNKNOWN
,
459 [PA_CHANNEL_POSITION_AUX31
] = SND_MIXER_SCHN_UNKNOWN
,
461 [PA_CHANNEL_POSITION_TOP_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
463 [PA_CHANNEL_POSITION_TOP_FRONT_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
464 [PA_CHANNEL_POSITION_TOP_FRONT_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
465 [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
,
467 [PA_CHANNEL_POSITION_TOP_REAR_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
468 [PA_CHANNEL_POSITION_TOP_REAR_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
469 [PA_CHANNEL_POSITION_TOP_REAR_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
472 static void setting_free(pa_alsa_setting
*s
) {
476 pa_idxset_free(s
->options
, NULL
, NULL
);
479 pa_xfree(s
->description
);
483 static void option_free(pa_alsa_option
*o
) {
486 pa_xfree(o
->alsa_name
);
488 pa_xfree(o
->description
);
492 static void decibel_fix_free(pa_alsa_decibel_fix
*db_fix
) {
495 pa_xfree(db_fix
->name
);
496 pa_xfree(db_fix
->db_values
);
501 static void element_free(pa_alsa_element
*e
) {
505 while ((o
= e
->options
)) {
506 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
511 decibel_fix_free(e
->db_fix
);
513 pa_xfree(e
->alsa_name
);
517 void pa_alsa_path_free(pa_alsa_path
*p
) {
523 while ((e
= p
->elements
)) {
524 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
528 while ((s
= p
->settings
)) {
529 PA_LLIST_REMOVE(pa_alsa_setting
, p
->settings
, s
);
534 pa_xfree(p
->description
);
538 void pa_alsa_path_set_free(pa_alsa_path_set
*ps
) {
542 while ((p
= ps
->paths
)) {
543 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
544 pa_alsa_path_free(p
);
550 static long to_alsa_dB(pa_volume_t v
) {
551 return (long) (pa_sw_volume_to_dB(v
) * 100.0);
554 static pa_volume_t
from_alsa_dB(long v
) {
555 return pa_sw_volume_from_dB((double) v
/ 100.0);
558 static long to_alsa_volume(pa_volume_t v
, long min
, long max
) {
561 w
= (long) round(((double) v
* (double) (max
- min
)) / PA_VOLUME_NORM
) + min
;
562 return PA_CLAMP_UNLIKELY(w
, min
, max
);
565 static pa_volume_t
from_alsa_volume(long v
, long min
, long max
) {
566 return (pa_volume_t
) round(((double) (v
- min
) * PA_VOLUME_NORM
) / (double) (max
- min
));
569 #define SELEM_INIT(sid, name) \
571 snd_mixer_selem_id_alloca(&(sid)); \
572 snd_mixer_selem_id_set_name((sid), (name)); \
573 snd_mixer_selem_id_set_index((sid), 0); \
576 static int element_get_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
577 snd_mixer_selem_id_t
*sid
;
578 snd_mixer_elem_t
*me
;
579 snd_mixer_selem_channel_id_t c
;
580 pa_channel_position_mask_t mask
= 0;
588 SELEM_INIT(sid
, e
->alsa_name
);
589 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
590 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
594 pa_cvolume_mute(v
, cm
->channels
);
596 /* We take the highest volume of all channels that match */
598 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
605 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
606 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
608 if ((r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
)) >= 0) {
609 /* If the channel volume is outside the limits set
610 * by the dB fix, we clamp the hw volume to be
611 * within the limits. */
612 if (value
< e
->db_fix
->min_step
) {
613 value
= e
->db_fix
->min_step
;
614 snd_mixer_selem_set_playback_volume(me
, c
, value
);
615 pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
616 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
617 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
618 } else if (value
> e
->db_fix
->max_step
) {
619 value
= e
->db_fix
->max_step
;
620 snd_mixer_selem_set_playback_volume(me
, c
, value
);
621 pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
622 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
623 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
626 /* Volume step -> dB value conversion. */
627 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
630 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
634 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
636 if ((r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
)) >= 0) {
637 /* If the channel volume is outside the limits set
638 * by the dB fix, we clamp the hw volume to be
639 * within the limits. */
640 if (value
< e
->db_fix
->min_step
) {
641 value
= e
->db_fix
->min_step
;
642 snd_mixer_selem_set_capture_volume(me
, c
, value
);
643 pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
644 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
645 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
646 } else if (value
> e
->db_fix
->max_step
) {
647 value
= e
->db_fix
->max_step
;
648 snd_mixer_selem_set_capture_volume(me
, c
, value
);
649 pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
650 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
651 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
654 /* Volume step -> dB value conversion. */
655 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
658 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
666 #ifdef HAVE_VALGRIND_MEMCHECK_H
667 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
670 f
= from_alsa_dB(value
);
675 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
676 if (snd_mixer_selem_has_playback_channel(me
, c
))
677 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
681 if (snd_mixer_selem_has_capture_channel(me
, c
))
682 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
690 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
693 for (k
= 0; k
< cm
->channels
; k
++)
694 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
695 if (v
->values
[k
] < f
)
698 mask
|= e
->masks
[c
][e
->n_channels
-1];
701 for (k
= 0; k
< cm
->channels
; k
++)
702 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
703 v
->values
[k
] = PA_VOLUME_NORM
;
708 int pa_alsa_path_get_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
719 pa_cvolume_reset(v
, cm
->channels
);
721 PA_LLIST_FOREACH(e
, p
->elements
) {
724 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
727 pa_assert(!p
->has_dB
|| e
->has_dB
);
729 if (element_get_volume(e
, m
, cm
, &ev
) < 0)
732 /* If we have no dB information all we can do is take the first element and leave */
738 pa_sw_cvolume_multiply(v
, v
, &ev
);
744 static int element_get_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t
*b
) {
745 snd_mixer_selem_id_t
*sid
;
746 snd_mixer_elem_t
*me
;
747 snd_mixer_selem_channel_id_t c
;
753 SELEM_INIT(sid
, e
->alsa_name
);
754 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
755 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
759 /* We return muted if at least one channel is muted */
761 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
765 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
766 if (snd_mixer_selem_has_playback_channel(me
, c
))
767 r
= snd_mixer_selem_get_playback_switch(me
, c
, &value
);
771 if (snd_mixer_selem_has_capture_channel(me
, c
))
772 r
= snd_mixer_selem_get_capture_switch(me
, c
, &value
);
790 int pa_alsa_path_get_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t
*muted
) {
800 PA_LLIST_FOREACH(e
, p
->elements
) {
803 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
806 if (element_get_switch(e
, m
, &b
) < 0)
819 /* Finds the closest item in db_fix->db_values and returns the corresponding
820 * step. *db_value is replaced with the value from the db_values table.
821 * Rounding is done based on the rounding parameter: -1 means rounding down and
822 * +1 means rounding up. */
823 static long decibel_fix_get_step(pa_alsa_decibel_fix
*db_fix
, long *db_value
, int rounding
) {
829 pa_assert(rounding
!= 0);
831 max_i
= db_fix
->max_step
- db_fix
->min_step
;
834 for (i
= 0; i
< max_i
; i
++) {
835 if (db_fix
->db_values
[i
] >= *db_value
)
839 for (i
= 0; i
< max_i
; i
++) {
840 if (db_fix
->db_values
[i
+ 1] > *db_value
)
845 *db_value
= db_fix
->db_values
[i
];
847 return i
+ db_fix
->min_step
;
850 /* Alsa lib documentation says for snd_mixer_selem_set_playback_dB() direction argument,
851 * that "-1 = accurate or first below, 0 = accurate, 1 = accurate or first above".
852 * But even with accurate nearest dB volume step is not selected, so that is why we need
853 * this function. Returns 0 and nearest selectable volume in *value_dB on success or
854 * negative error code if fails. */
855 static int element_get_nearest_alsa_dB(snd_mixer_elem_t
*me
, snd_mixer_selem_channel_id_t c
, pa_alsa_direction_t d
, long *value_dB
) {
865 if (d
== PA_ALSA_DIRECTION_OUTPUT
) {
866 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, *value_dB
, +1, &alsa_val
)) >= 0)
867 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value_high
);
872 if (value_high
== *value_dB
)
875 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, *value_dB
, -1, &alsa_val
)) >= 0)
876 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value_low
);
878 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, *value_dB
, +1, &alsa_val
)) >= 0)
879 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value_high
);
884 if (value_high
== *value_dB
)
887 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, *value_dB
, -1, &alsa_val
)) >= 0)
888 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value_low
);
894 if (labs(value_high
- *value_dB
) < labs(value_low
- *value_dB
))
895 *value_dB
= value_high
;
897 *value_dB
= value_low
;
902 static int element_set_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
, pa_bool_t sync_volume
, pa_bool_t write_to_hw
) {
904 snd_mixer_selem_id_t
*sid
;
906 snd_mixer_elem_t
*me
;
907 snd_mixer_selem_channel_id_t c
;
908 pa_channel_position_mask_t mask
= 0;
915 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
917 SELEM_INIT(sid
, e
->alsa_name
);
918 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
919 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
923 pa_cvolume_mute(&rv
, cm
->channels
);
925 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
927 pa_volume_t f
= PA_VOLUME_MUTED
;
928 pa_bool_t found
= FALSE
;
930 for (k
= 0; k
< cm
->channels
; k
++)
931 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
])) {
933 if (v
->values
[k
] > f
)
938 /* Hmm, so this channel does not exist in the volume
939 * struct, so let's bind it to the overall max of the
941 f
= pa_cvolume_max(v
);
945 long value
= to_alsa_dB(f
);
948 if (e
->volume_limit
>= 0 && value
> (e
->max_dB
* 100))
949 value
= e
->max_dB
* 100;
951 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
952 /* If we call set_playback_volume() without checking first
953 * if the channel is available, ALSA behaves very
954 * strangely and doesn't fail the call */
955 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
959 r
= snd_mixer_selem_set_playback_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
961 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
968 if ((r
= element_get_nearest_alsa_dB(me
, c
, PA_ALSA_DIRECTION_OUTPUT
, &value
)) >= 0)
969 r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, 0);
971 if ((r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, rounding
)) >= 0)
972 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
976 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
977 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value
);
983 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
987 r
= snd_mixer_selem_set_capture_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
989 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
996 if ((r
= element_get_nearest_alsa_dB(me
, c
, PA_ALSA_DIRECTION_INPUT
, &value
)) >= 0)
997 r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, 0);
999 if ((r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, rounding
)) >= 0)
1000 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
1004 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
1005 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value
);
1015 #ifdef HAVE_VALGRIND_MEMCHECK_H
1016 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
1019 f
= from_alsa_dB(value
);
1024 value
= to_alsa_volume(f
, e
->min_volume
, e
->max_volume
);
1026 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1027 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
1028 if ((r
= snd_mixer_selem_set_playback_volume(me
, c
, value
)) >= 0)
1029 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
1033 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
1034 if ((r
= snd_mixer_selem_set_capture_volume(me
, c
, value
)) >= 0)
1035 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
1043 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
1046 for (k
= 0; k
< cm
->channels
; k
++)
1047 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
1048 if (rv
.values
[k
] < f
)
1051 mask
|= e
->masks
[c
][e
->n_channels
-1];
1054 for (k
= 0; k
< cm
->channels
; k
++)
1055 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
1056 rv
.values
[k
] = PA_VOLUME_NORM
;
1062 int pa_alsa_path_set_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
, pa_bool_t sync_volume
, pa_bool_t write_to_hw
) {
1071 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
1076 rv
= *v
; /* Remaining adjustment */
1077 pa_cvolume_reset(v
, cm
->channels
); /* Adjustment done */
1079 PA_LLIST_FOREACH(e
, p
->elements
) {
1082 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
1085 pa_assert(!p
->has_dB
|| e
->has_dB
);
1088 if (element_set_volume(e
, m
, cm
, &ev
, sync_volume
, write_to_hw
) < 0)
1096 pa_sw_cvolume_multiply(v
, v
, &ev
);
1097 pa_sw_cvolume_divide(&rv
, &rv
, &ev
);
1103 static int element_set_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t b
) {
1104 snd_mixer_elem_t
*me
;
1105 snd_mixer_selem_id_t
*sid
;
1111 SELEM_INIT(sid
, e
->alsa_name
);
1112 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1113 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1117 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1118 r
= snd_mixer_selem_set_playback_switch_all(me
, b
);
1120 r
= snd_mixer_selem_set_capture_switch_all(me
, b
);
1123 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1128 int pa_alsa_path_set_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t muted
) {
1137 PA_LLIST_FOREACH(e
, p
->elements
) {
1139 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
1142 if (element_set_switch(e
, m
, !muted
) < 0)
1149 /* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
1150 * function sets all channels of the volume element to e->min_volume, 0 dB or
1151 * e->constant_volume. */
1152 static int element_set_constant_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1153 snd_mixer_elem_t
*me
= NULL
;
1154 snd_mixer_selem_id_t
*sid
= NULL
;
1157 pa_bool_t volume_set
= FALSE
;
1162 SELEM_INIT(sid
, e
->alsa_name
);
1163 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1164 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1168 switch (e
->volume_use
) {
1169 case PA_ALSA_VOLUME_OFF
:
1170 volume
= e
->min_volume
;
1174 case PA_ALSA_VOLUME_ZERO
:
1178 volume
= decibel_fix_get_step(e
->db_fix
, &dB
, +1);
1183 case PA_ALSA_VOLUME_CONSTANT
:
1184 volume
= e
->constant_volume
;
1189 pa_assert_not_reached();
1193 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1194 r
= snd_mixer_selem_set_playback_volume_all(me
, volume
);
1196 r
= snd_mixer_selem_set_capture_volume_all(me
, volume
);
1198 pa_assert(e
->volume_use
== PA_ALSA_VOLUME_ZERO
);
1199 pa_assert(!e
->db_fix
);
1201 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1202 r
= snd_mixer_selem_set_playback_dB_all(me
, 0, +1);
1204 r
= snd_mixer_selem_set_capture_dB_all(me
, 0, +1);
1208 pa_log_warn("Failed to set volume of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1213 int pa_alsa_path_select(pa_alsa_path
*p
, snd_mixer_t
*m
) {
1220 pa_log_debug("Activating path %s", p
->name
);
1221 pa_alsa_path_dump(p
);
1223 PA_LLIST_FOREACH(e
, p
->elements
) {
1225 switch (e
->switch_use
) {
1226 case PA_ALSA_SWITCH_OFF
:
1227 r
= element_set_switch(e
, m
, FALSE
);
1230 case PA_ALSA_SWITCH_ON
:
1231 r
= element_set_switch(e
, m
, TRUE
);
1234 case PA_ALSA_SWITCH_MUTE
:
1235 case PA_ALSA_SWITCH_IGNORE
:
1236 case PA_ALSA_SWITCH_SELECT
:
1244 switch (e
->volume_use
) {
1245 case PA_ALSA_VOLUME_OFF
:
1246 case PA_ALSA_VOLUME_ZERO
:
1247 case PA_ALSA_VOLUME_CONSTANT
:
1248 r
= element_set_constant_volume(e
, m
);
1251 case PA_ALSA_VOLUME_MERGE
:
1252 case PA_ALSA_VOLUME_IGNORE
:
1264 static int check_required(pa_alsa_element
*e
, snd_mixer_elem_t
*me
) {
1265 pa_bool_t has_switch
;
1266 pa_bool_t has_enumeration
;
1267 pa_bool_t has_volume
;
1272 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1274 snd_mixer_selem_has_playback_switch(me
) ||
1275 (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
));
1278 snd_mixer_selem_has_capture_switch(me
) ||
1279 (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
));
1282 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1284 snd_mixer_selem_has_playback_volume(me
) ||
1285 (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
));
1288 snd_mixer_selem_has_capture_volume(me
) ||
1289 (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
));
1292 has_enumeration
= snd_mixer_selem_is_enumerated(me
);
1294 if ((e
->required
== PA_ALSA_REQUIRED_SWITCH
&& !has_switch
) ||
1295 (e
->required
== PA_ALSA_REQUIRED_VOLUME
&& !has_volume
) ||
1296 (e
->required
== PA_ALSA_REQUIRED_ENUMERATION
&& !has_enumeration
))
1299 if (e
->required
== PA_ALSA_REQUIRED_ANY
&& !(has_switch
|| has_volume
|| has_enumeration
))
1302 if ((e
->required_absent
== PA_ALSA_REQUIRED_SWITCH
&& has_switch
) ||
1303 (e
->required_absent
== PA_ALSA_REQUIRED_VOLUME
&& has_volume
) ||
1304 (e
->required_absent
== PA_ALSA_REQUIRED_ENUMERATION
&& has_enumeration
))
1307 if (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& (has_switch
|| has_volume
|| has_enumeration
))
1310 if (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) {
1311 switch (e
->required_any
) {
1312 case PA_ALSA_REQUIRED_VOLUME
:
1313 e
->path
->req_any_present
|= (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
);
1315 case PA_ALSA_REQUIRED_SWITCH
:
1316 e
->path
->req_any_present
|= (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
);
1318 case PA_ALSA_REQUIRED_ENUMERATION
:
1319 e
->path
->req_any_present
|= (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1321 case PA_ALSA_REQUIRED_ANY
:
1322 e
->path
->req_any_present
|=
1323 (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) ||
1324 (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) ||
1325 (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1328 pa_assert_not_reached();
1332 if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1334 PA_LLIST_FOREACH(o
, e
->options
) {
1335 e
->path
->req_any_present
|= (o
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) &&
1337 if (o
->required
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
< 0)
1339 if (o
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
>= 0)
1347 static int element_probe(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1348 snd_mixer_selem_id_t
*sid
;
1349 snd_mixer_elem_t
*me
;
1355 SELEM_INIT(sid
, e
->alsa_name
);
1357 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1359 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1362 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1363 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1364 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1369 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1370 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1372 if (!snd_mixer_selem_has_playback_switch(me
)) {
1373 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1374 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1376 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1381 if (!snd_mixer_selem_has_capture_switch(me
)) {
1382 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1383 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1385 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1389 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1390 e
->direction_try_other
= FALSE
;
1393 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1395 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1397 if (!snd_mixer_selem_has_playback_volume(me
)) {
1398 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1399 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1401 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1406 if (!snd_mixer_selem_has_capture_volume(me
)) {
1407 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1408 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1410 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1414 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1415 long min_dB
= 0, max_dB
= 0;
1418 e
->direction_try_other
= FALSE
;
1420 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1421 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1423 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1426 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1430 if (e
->min_volume
>= e
->max_volume
) {
1431 pa_log_warn("Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense.", e
->min_volume
, e
->max_volume
);
1432 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1434 } else if (e
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&&
1435 (e
->min_volume
> e
->constant_volume
|| e
->max_volume
< e
->constant_volume
)) {
1436 pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
1437 e
->constant_volume
, e
->alsa_name
, e
->min_volume
, e
->max_volume
);
1438 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1442 pa_channel_position_t p
;
1445 ((e
->min_volume
> e
->db_fix
->min_step
) ||
1446 (e
->max_volume
< e
->db_fix
->max_step
))) {
1447 pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1448 "real hardware range (%li-%li). Disabling the decibel fix.", e
->alsa_name
,
1449 e
->db_fix
->min_step
, e
->db_fix
->max_step
,
1450 e
->min_volume
, e
->max_volume
);
1452 decibel_fix_free(e
->db_fix
);
1458 e
->min_volume
= e
->db_fix
->min_step
;
1459 e
->max_volume
= e
->db_fix
->max_step
;
1460 min_dB
= e
->db_fix
->db_values
[0];
1461 max_dB
= e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
];
1462 } else if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1463 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1465 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1467 /* Check that the kernel driver returns consistent limits with
1468 * both _get_*_dB_range() and _ask_*_vol_dB(). */
1469 if (e
->has_dB
&& !e
->db_fix
) {
1470 long min_dB_checked
= 0;
1471 long max_dB_checked
= 0;
1473 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1474 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1476 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1479 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->min_volume
);
1483 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1484 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1486 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1489 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->max_volume
);
1493 if (min_dB
!= min_dB_checked
|| max_dB
!= max_dB_checked
) {
1494 pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
1495 "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
1496 "%0.2f dB at level %li.",
1498 min_dB
/ 100.0, max_dB
/ 100.0,
1499 min_dB_checked
/ 100.0, e
->min_volume
, max_dB_checked
/ 100.0, e
->max_volume
);
1505 #ifdef HAVE_VALGRIND_MEMCHECK_H
1506 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1507 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1510 e
->min_dB
= ((double) min_dB
) / 100.0;
1511 e
->max_dB
= ((double) max_dB
) / 100.0;
1513 if (min_dB
>= max_dB
) {
1514 pa_assert(!e
->db_fix
);
1515 pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.", e
->min_dB
, e
->max_dB
);
1520 if (e
->volume_limit
>= 0) {
1521 if (e
->volume_limit
<= e
->min_volume
|| e
->volume_limit
> e
->max_volume
)
1522 pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1523 "%li-%li. The volume limit is ignored.",
1524 e
->alsa_name
, e
->path
->name
, e
->volume_limit
, e
->min_volume
+ 1, e
->max_volume
);
1527 e
->max_volume
= e
->volume_limit
;
1531 e
->db_fix
->max_step
= e
->max_volume
;
1532 e
->max_dB
= ((double) e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
]) / 100.0;
1535 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1536 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB
);
1538 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB
);
1541 pa_log_warn("Failed to get dB value of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1544 e
->max_dB
= ((double) max_dB
) / 100.0;
1550 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1551 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1553 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1558 if (!e
->override_map
) {
1559 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1560 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1563 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1566 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1569 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1572 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1574 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1577 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1578 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1580 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1583 if (e
->n_channels
<= 0) {
1584 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1588 if (e
->n_channels
> 2) {
1589 /* FIXME: In some places code like this is used:
1591 * e->masks[alsa_channel_ids[p]][e->n_channels-1]
1593 * The definition of e->masks is
1595 * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST][2];
1597 * Since the array size is fixed at 2, we obviously
1598 * don't support elements with more than two
1600 pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e
->alsa_name
, e
->n_channels
);
1604 if (!e
->override_map
) {
1605 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1606 pa_bool_t has_channel
;
1608 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1611 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1612 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1614 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1616 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1621 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1622 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1625 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1633 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1636 PA_LLIST_FOREACH(o
, e
->options
)
1637 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1638 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1642 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1643 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1647 PA_LLIST_FOREACH(o
, e
->options
) {
1650 for (i
= 0; i
< n
; i
++) {
1653 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1656 if (!pa_streq(buf
, o
->alsa_name
))
1664 if (check_required(e
, me
) < 0)
1670 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, pa_bool_t prefixed
) {
1677 if (!pa_startswith(section
, "Element "))
1683 /* This is not an element section, but an enum section? */
1684 if (strchr(section
, ':'))
1687 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1688 return p
->last_element
;
1690 PA_LLIST_FOREACH(e
, p
->elements
)
1691 if (pa_streq(e
->alsa_name
, section
))
1694 e
= pa_xnew0(pa_alsa_element
, 1);
1696 e
->alsa_name
= pa_xstrdup(section
);
1697 e
->direction
= p
->direction
;
1698 e
->volume_limit
= -1;
1700 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1703 p
->last_element
= e
;
1707 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1713 if (!pa_startswith(section
, "Option "))
1718 /* This is not an enum section, but an element section? */
1719 if (!(on
= strchr(section
, ':')))
1722 en
= pa_xstrndup(section
, on
- section
);
1725 if (p
->last_option
&&
1726 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1727 pa_streq(p
->last_option
->alsa_name
, on
)) {
1729 return p
->last_option
;
1732 pa_assert_se(e
= element_get(p
, en
, FALSE
));
1735 PA_LLIST_FOREACH(o
, e
->options
)
1736 if (pa_streq(o
->alsa_name
, on
))
1739 o
= pa_xnew0(pa_alsa_option
, 1);
1741 o
->alsa_name
= pa_xstrdup(on
);
1744 if (p
->last_option
&& p
->last_option
->element
== e
)
1745 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1747 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1754 static int element_parse_switch(
1755 const char *filename
,
1757 const char *section
,
1763 pa_alsa_path
*p
= userdata
;
1768 if (!(e
= element_get(p
, section
, TRUE
))) {
1769 pa_log("[%s:%u] Switch makes no sense in '%s'", filename
, line
, section
);
1773 if (pa_streq(rvalue
, "ignore"))
1774 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1775 else if (pa_streq(rvalue
, "mute"))
1776 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1777 else if (pa_streq(rvalue
, "off"))
1778 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1779 else if (pa_streq(rvalue
, "on"))
1780 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1781 else if (pa_streq(rvalue
, "select"))
1782 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1784 pa_log("[%s:%u] Switch invalid of '%s'", filename
, line
, section
);
1791 static int element_parse_volume(
1792 const char *filename
,
1794 const char *section
,
1800 pa_alsa_path
*p
= userdata
;
1805 if (!(e
= element_get(p
, section
, TRUE
))) {
1806 pa_log("[%s:%u] Volume makes no sense in '%s'", filename
, line
, section
);
1810 if (pa_streq(rvalue
, "ignore"))
1811 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1812 else if (pa_streq(rvalue
, "merge"))
1813 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1814 else if (pa_streq(rvalue
, "off"))
1815 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1816 else if (pa_streq(rvalue
, "zero"))
1817 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1821 if (pa_atou(rvalue
, &constant
) >= 0) {
1822 e
->volume_use
= PA_ALSA_VOLUME_CONSTANT
;
1823 e
->constant_volume
= constant
;
1825 pa_log("[%s:%u] Volume invalid of '%s'", filename
, line
, section
);
1833 static int element_parse_enumeration(
1834 const char *filename
,
1836 const char *section
,
1842 pa_alsa_path
*p
= userdata
;
1847 if (!(e
= element_get(p
, section
, TRUE
))) {
1848 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename
, line
, section
);
1852 if (pa_streq(rvalue
, "ignore"))
1853 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1854 else if (pa_streq(rvalue
, "select"))
1855 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1857 pa_log("[%s:%u] Enumeration invalid of '%s'", filename
, line
, section
);
1864 static int option_parse_priority(
1865 const char *filename
,
1867 const char *section
,
1873 pa_alsa_path
*p
= userdata
;
1879 if (!(o
= option_get(p
, section
))) {
1880 pa_log("[%s:%u] Priority makes no sense in '%s'", filename
, line
, section
);
1884 if (pa_atou(rvalue
, &prio
) < 0) {
1885 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
1893 static int option_parse_name(
1894 const char *filename
,
1896 const char *section
,
1902 pa_alsa_path
*p
= userdata
;
1907 if (!(o
= option_get(p
, section
))) {
1908 pa_log("[%s:%u] Name makes no sense in '%s'", filename
, line
, section
);
1913 o
->name
= pa_xstrdup(rvalue
);
1918 static int element_parse_required(
1919 const char *filename
,
1921 const char *section
,
1927 pa_alsa_path
*p
= userdata
;
1930 pa_alsa_required_t req
;
1934 e
= element_get(p
, section
, TRUE
);
1935 o
= option_get(p
, section
);
1937 pa_log("[%s:%u] Required makes no sense in '%s'", filename
, line
, section
);
1941 if (pa_streq(rvalue
, "ignore"))
1942 req
= PA_ALSA_REQUIRED_IGNORE
;
1943 else if (pa_streq(rvalue
, "switch") && e
)
1944 req
= PA_ALSA_REQUIRED_SWITCH
;
1945 else if (pa_streq(rvalue
, "volume") && e
)
1946 req
= PA_ALSA_REQUIRED_VOLUME
;
1947 else if (pa_streq(rvalue
, "enumeration"))
1948 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1949 else if (pa_streq(rvalue
, "any"))
1950 req
= PA_ALSA_REQUIRED_ANY
;
1952 pa_log("[%s:%u] Required invalid of '%s'", filename
, line
, section
);
1956 if (pa_streq(lvalue
, "required-absent")) {
1958 e
->required_absent
= req
;
1960 o
->required_absent
= req
;
1962 else if (pa_streq(lvalue
, "required-any")) {
1964 e
->required_any
= req
;
1965 e
->path
->has_req_any
= TRUE
;
1968 o
->required_any
= req
;
1969 o
->element
->path
->has_req_any
= TRUE
;
1982 static int element_parse_direction(
1983 const char *filename
,
1985 const char *section
,
1991 pa_alsa_path
*p
= userdata
;
1996 if (!(e
= element_get(p
, section
, TRUE
))) {
1997 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
2001 if (pa_streq(rvalue
, "playback"))
2002 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
2003 else if (pa_streq(rvalue
, "capture"))
2004 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
2006 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
2013 static int element_parse_direction_try_other(
2014 const char *filename
,
2016 const char *section
,
2022 pa_alsa_path
*p
= userdata
;
2026 if (!(e
= element_get(p
, section
, TRUE
))) {
2027 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
2031 if ((yes
= pa_parse_boolean(rvalue
)) < 0) {
2032 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
2036 e
->direction_try_other
= !!yes
;
2040 static int element_parse_volume_limit(
2041 const char *filename
,
2043 const char *section
,
2049 pa_alsa_path
*p
= userdata
;
2053 if (!(e
= element_get(p
, section
, TRUE
))) {
2054 pa_log("[%s:%u] volume-limit makes no sense in '%s'", filename
, line
, section
);
2058 if (pa_atol(rvalue
, &volume_limit
) < 0 || volume_limit
< 0) {
2059 pa_log("[%s:%u] Invalid value for volume-limit", filename
, line
);
2063 e
->volume_limit
= volume_limit
;
2067 static pa_channel_position_mask_t
parse_mask(const char *m
) {
2068 pa_channel_position_mask_t v
;
2070 if (pa_streq(m
, "all-left"))
2071 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
2072 else if (pa_streq(m
, "all-right"))
2073 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
2074 else if (pa_streq(m
, "all-center"))
2075 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
2076 else if (pa_streq(m
, "all-front"))
2077 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
2078 else if (pa_streq(m
, "all-rear"))
2079 v
= PA_CHANNEL_POSITION_MASK_REAR
;
2080 else if (pa_streq(m
, "all-side"))
2081 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
2082 else if (pa_streq(m
, "all-top"))
2083 v
= PA_CHANNEL_POSITION_MASK_TOP
;
2084 else if (pa_streq(m
, "all-no-lfe"))
2085 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
2086 else if (pa_streq(m
, "all"))
2087 v
= PA_CHANNEL_POSITION_MASK_ALL
;
2089 pa_channel_position_t p
;
2091 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
2094 v
= PA_CHANNEL_POSITION_MASK(p
);
2100 static int element_parse_override_map(
2101 const char *filename
,
2103 const char *section
,
2109 pa_alsa_path
*p
= userdata
;
2111 const char *state
= NULL
;
2115 if (!(e
= element_get(p
, section
, TRUE
))) {
2116 pa_log("[%s:%u] Override map makes no sense in '%s'", filename
, line
, section
);
2120 while ((n
= pa_split(rvalue
, ",", &state
))) {
2121 pa_channel_position_mask_t m
;
2126 if ((m
= parse_mask(n
)) == 0) {
2127 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename
, line
, n
, section
);
2133 if (pa_streq(lvalue
, "override-map.1"))
2134 e
->masks
[i
++][0] = m
;
2136 e
->masks
[i
++][1] = m
;
2138 /* Later on we might add override-map.3 and so on here ... */
2143 e
->override_map
= TRUE
;
2148 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
2149 snd_mixer_selem_id_t
*sid
;
2150 snd_mixer_elem_t
*me
;
2156 SELEM_INIT(sid
, e
->alsa_name
);
2157 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2158 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2162 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
2164 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
2165 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
2167 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
2170 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2173 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
2175 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
2176 pa_log_warn("Failed to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2182 int pa_alsa_setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
2189 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
2190 element_set_option(o
->element
, m
, o
->alsa_idx
);
2195 static int option_verify(pa_alsa_option
*o
) {
2196 static const struct description_map well_known_descriptions
[] = {
2197 { "input", N_("Input") },
2198 { "input-docking", N_("Docking Station Input") },
2199 { "input-docking-microphone", N_("Docking Station Microphone") },
2200 { "input-docking-linein", N_("Docking Station Line-In") },
2201 { "input-linein", N_("Line-In") },
2202 { "input-microphone", N_("Microphone") },
2203 { "input-microphone-front", N_("Front Microphone") },
2204 { "input-microphone-rear", N_("Rear Microphone") },
2205 { "input-microphone-external", N_("External Microphone") },
2206 { "input-microphone-internal", N_("Internal Microphone") },
2207 { "input-radio", N_("Radio") },
2208 { "input-video", N_("Video") },
2209 { "input-agc-on", N_("Automatic Gain Control") },
2210 { "input-agc-off", N_("No Automatic Gain Control") },
2211 { "input-boost-on", N_("Boost") },
2212 { "input-boost-off", N_("No Boost") },
2213 { "output-amplifier-on", N_("Amplifier") },
2214 { "output-amplifier-off", N_("No Amplifier") },
2215 { "output-bass-boost-on", N_("Bass Boost") },
2216 { "output-bass-boost-off", N_("No Bass Boost") },
2217 { "output-speaker", N_("Speaker") },
2218 { "output-headphones", N_("Headphones") }
2224 pa_log("No name set for option %s", o
->alsa_name
);
2228 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
2229 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
2230 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
2234 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
2235 !pa_streq(o
->alsa_name
, "on") &&
2236 !pa_streq(o
->alsa_name
, "off")) {
2237 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
2241 if (!o
->description
)
2242 o
->description
= pa_xstrdup(lookup_description(o
->name
,
2243 well_known_descriptions
,
2244 PA_ELEMENTSOF(well_known_descriptions
)));
2245 if (!o
->description
)
2246 o
->description
= pa_xstrdup(o
->name
);
2251 static int element_verify(pa_alsa_element
*e
) {
2256 // pa_log_debug("Element %s, path %s: r=%d, r-any=%d, r-abs=%d", e->alsa_name, e->path->name, e->required, e->required_any, e->required_absent);
2257 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
2258 (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required_any
== e
->required_absent
) ||
2259 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) ||
2260 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
2261 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
2265 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
2266 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
2270 PA_LLIST_FOREACH(o
, e
->options
)
2271 if (option_verify(o
) < 0)
2277 static int path_verify(pa_alsa_path
*p
) {
2278 static const struct description_map well_known_descriptions
[] = {
2279 { "analog-input", N_("Analog Input") },
2280 { "analog-input-microphone", N_("Analog Microphone") },
2281 { "analog-input-microphone-front", N_("Front Microphone") },
2282 { "analog-input-microphone-rear", N_("Rear Microphone") },
2283 { "analog-input-microphone-dock", N_("Docking Station Microphone") },
2284 { "analog-input-microphone-internal", N_("Internal Microphone") },
2285 { "analog-input-linein", N_("Analog Line-In") },
2286 { "analog-input-radio", N_("Analog Radio") },
2287 { "analog-input-video", N_("Analog Video") },
2288 { "analog-output", N_("Analog Output") },
2289 { "analog-output-headphones", N_("Analog Headphones") },
2290 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
2291 { "analog-output-mono", N_("Analog Mono Output") },
2292 { "analog-output-speaker", N_("Analog Speakers") },
2293 { "iec958-stereo-output", N_("Digital Output (IEC958)") },
2294 { "iec958-passthrough-output", N_("Digital Passthrough (IEC958)") }
2301 PA_LLIST_FOREACH(e
, p
->elements
)
2302 if (element_verify(e
) < 0)
2305 if (!p
->description
)
2306 p
->description
= pa_xstrdup(lookup_description(p
->name
,
2307 well_known_descriptions
,
2308 PA_ELEMENTSOF(well_known_descriptions
)));
2310 if (!p
->description
)
2311 p
->description
= pa_xstrdup(p
->name
);
2316 pa_alsa_path
* pa_alsa_path_new(const char *fname
, pa_alsa_direction_t direction
) {
2322 pa_config_item items
[] = {
2324 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
2325 { "description", pa_config_parse_string
, NULL
, "General" },
2326 { "name", pa_config_parse_string
, NULL
, "General" },
2329 { "priority", option_parse_priority
, NULL
, NULL
},
2330 { "name", option_parse_name
, NULL
, NULL
},
2333 { "switch", element_parse_switch
, NULL
, NULL
},
2334 { "volume", element_parse_volume
, NULL
, NULL
},
2335 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
2336 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
2337 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
2338 /* ... later on we might add override-map.3 and so on here ... */
2339 { "required", element_parse_required
, NULL
, NULL
},
2340 { "required-any", element_parse_required
, NULL
, NULL
},
2341 { "required-absent", element_parse_required
, NULL
, NULL
},
2342 { "direction", element_parse_direction
, NULL
, NULL
},
2343 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2344 { "volume-limit", element_parse_volume_limit
, NULL
, NULL
},
2345 { NULL
, NULL
, NULL
, NULL
}
2350 p
= pa_xnew0(pa_alsa_path
, 1);
2351 n
= pa_path_get_filename(fname
);
2352 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2353 p
->direction
= direction
;
2355 items
[0].data
= &p
->priority
;
2356 items
[1].data
= &p
->description
;
2357 items
[2].data
= &p
->name
;
2359 fn
= pa_maybe_prefix_path(fname
,
2360 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/paths/" :
2363 r
= pa_config_parse(fn
, NULL
, items
, p
);
2369 if (path_verify(p
) < 0)
2375 pa_alsa_path_free(p
);
2379 pa_alsa_path
*pa_alsa_path_synthesize(const char *element
, pa_alsa_direction_t direction
) {
2385 p
= pa_xnew0(pa_alsa_path
, 1);
2386 p
->name
= pa_xstrdup(element
);
2387 p
->direction
= direction
;
2389 e
= pa_xnew0(pa_alsa_element
, 1);
2391 e
->alsa_name
= pa_xstrdup(element
);
2392 e
->direction
= direction
;
2393 e
->volume_limit
= -1;
2395 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2396 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2398 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2399 p
->last_element
= e
;
2403 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2404 pa_alsa_option
*o
, *n
;
2408 for (o
= e
->options
; o
; o
= n
) {
2411 if (o
->alsa_idx
< 0) {
2412 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2418 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2419 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2420 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2423 static void path_drop_unsupported(pa_alsa_path
*p
) {
2424 pa_alsa_element
*e
, *n
;
2428 for (e
= p
->elements
; e
; e
= n
) {
2431 if (!element_drop_unsupported(e
)) {
2432 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2438 static void path_make_options_unique(pa_alsa_path
*p
) {
2440 pa_alsa_option
*o
, *u
;
2442 PA_LLIST_FOREACH(e
, p
->elements
) {
2443 PA_LLIST_FOREACH(o
, e
->options
) {
2447 for (u
= o
->next
; u
; u
= u
->next
)
2448 if (pa_streq(u
->name
, o
->name
))
2454 m
= pa_xstrdup(o
->name
);
2456 /* OK, this name is not unique, hence let's rename */
2457 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2460 if (!pa_streq(u
->name
, m
))
2463 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2467 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2468 pa_xfree(u
->description
);
2469 u
->description
= nd
;
2479 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2482 for (; e
; e
= e
->next
)
2483 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2484 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2490 for (o
= e
->options
; o
; o
= o
->next
) {
2494 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2495 s
->options
= pa_idxset_copy(template->options
);
2496 s
->name
= pa_sprintf_malloc(_("%s+%s"), template->name
, o
->name
);
2498 (template->description
[0] && o
->description
[0])
2499 ? pa_sprintf_malloc(_("%s / %s"), template->description
, o
->description
)
2500 : (template->description
[0]
2501 ? pa_xstrdup(template->description
)
2502 : pa_xstrdup(o
->description
));
2504 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2506 s
= pa_xnew0(pa_alsa_setting
, 1);
2507 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2508 s
->name
= pa_xstrdup(o
->name
);
2509 s
->description
= pa_xstrdup(o
->description
);
2510 s
->priority
= o
->priority
;
2513 pa_idxset_put(s
->options
, o
, NULL
);
2515 if (element_create_settings(e
->next
, s
))
2516 /* This is not a leaf, so let's get rid of it */
2519 /* This is a leaf, so let's add it */
2520 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2522 e
->path
->last_setting
= s
;
2529 static void path_create_settings(pa_alsa_path
*p
) {
2532 element_create_settings(p
->elements
, NULL
);
2535 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2537 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2538 pa_channel_position_t t
;
2539 pa_channel_position_mask_t path_volume_channels
= 0;
2550 pa_log_debug("Probing path '%s'", p
->name
);
2552 PA_LLIST_FOREACH(e
, p
->elements
) {
2553 if (element_probe(e
, m
) < 0) {
2554 p
->supported
= FALSE
;
2555 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2558 pa_log_debug("Probe of element '%s' succeeded (volume=%d, switch=%d, enumeration=%d).", e
->alsa_name
, e
->volume_use
, e
->switch_use
, e
->enumeration_use
);
2563 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2565 if (!p
->has_volume
) {
2566 p
->min_volume
= e
->min_volume
;
2567 p
->max_volume
= e
->max_volume
;
2571 if (!p
->has_volume
) {
2572 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2573 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2574 min_dB
[t
] = e
->min_dB
;
2575 max_dB
[t
] = e
->max_dB
;
2576 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2583 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2584 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2585 min_dB
[t
] += e
->min_dB
;
2586 max_dB
[t
] += e
->max_dB
;
2587 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2590 /* Hmm, there's another element before us
2591 * which cannot do dB volumes, so we we need
2592 * to 'neutralize' this slider */
2593 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2594 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2597 } else if (p
->has_volume
) {
2598 /* We can't use this volume, so let's ignore it */
2599 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2600 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2602 p
->has_volume
= TRUE
;
2605 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2609 if (p
->has_req_any
&& !p
->req_any_present
) {
2610 p
->supported
= FALSE
;
2611 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2615 path_drop_unsupported(p
);
2616 path_make_options_unique(p
);
2617 path_create_settings(p
);
2619 p
->supported
= TRUE
;
2622 p
->min_dB
= INFINITY
;
2623 p
->max_dB
= -INFINITY
;
2625 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2626 if (path_volume_channels
& PA_CHANNEL_POSITION_MASK(t
)) {
2627 if (p
->min_dB
> min_dB
[t
])
2628 p
->min_dB
= min_dB
[t
];
2630 if (p
->max_dB
< max_dB
[t
])
2631 p
->max_dB
= max_dB
[t
];
2638 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2641 pa_log_debug("Setting %s (%s) priority=%u",
2643 pa_strnull(s
->description
),
2647 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2650 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2652 pa_strnull(o
->name
),
2653 pa_strnull(o
->description
),
2658 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2662 pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, volume_limit=%li, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s",
2672 (long long unsigned) e
->merged_mask
,
2674 pa_yes_no(e
->override_map
));
2676 PA_LLIST_FOREACH(o
, e
->options
)
2677 pa_alsa_option_dump(o
);
2680 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2685 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2686 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2688 pa_strnull(p
->description
),
2691 pa_yes_no(p
->probed
),
2692 pa_yes_no(p
->supported
),
2693 pa_yes_no(p
->has_mute
),
2694 pa_yes_no(p
->has_volume
),
2695 pa_yes_no(p
->has_dB
),
2696 p
->min_volume
, p
->max_volume
,
2697 p
->min_dB
, p
->max_dB
);
2699 PA_LLIST_FOREACH(e
, p
->elements
)
2700 pa_alsa_element_dump(e
);
2702 PA_LLIST_FOREACH(s
, p
->settings
)
2703 pa_alsa_setting_dump(s
);
2706 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2707 snd_mixer_selem_id_t
*sid
;
2708 snd_mixer_elem_t
*me
;
2714 SELEM_INIT(sid
, e
->alsa_name
);
2715 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2716 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2720 snd_mixer_elem_set_callback(me
, cb
);
2721 snd_mixer_elem_set_callback_private(me
, userdata
);
2724 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2731 PA_LLIST_FOREACH(e
, p
->elements
)
2732 element_set_callback(e
, m
, cb
, userdata
);
2735 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2742 PA_LLIST_FOREACH(p
, ps
->paths
)
2743 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2746 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
) {
2747 pa_alsa_path_set
*ps
;
2748 char **pn
= NULL
, **en
= NULL
, **ie
;
2749 pa_alsa_decibel_fix
*db_fix
;
2753 pa_assert(m
->profile_set
);
2754 pa_assert(m
->profile_set
->decibel_fixes
);
2755 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2757 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2760 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2761 ps
->direction
= direction
;
2763 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2764 pn
= m
->output_path_names
;
2765 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2766 pn
= m
->input_path_names
;
2771 for (in
= pn
; *in
; in
++) {
2773 pa_bool_t duplicate
= FALSE
;
2776 for (kn
= pn
; kn
< in
; kn
++)
2777 if (pa_streq(*kn
, *in
)) {
2785 fn
= pa_sprintf_malloc("%s.conf", *in
);
2787 if ((p
= pa_alsa_path_new(fn
, direction
))) {
2789 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2799 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2800 en
= m
->output_element
;
2801 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2802 en
= m
->input_element
;
2805 pa_alsa_path_set_free(ps
);
2809 for (ie
= en
; *ie
; ie
++) {
2813 p
= pa_alsa_path_synthesize(*ie
, direction
);
2816 /* Mark all other passed elements for require-absent */
2817 for (je
= en
; *je
; je
++) {
2823 e
= pa_xnew0(pa_alsa_element
, 1);
2825 e
->alsa_name
= pa_xstrdup(*je
);
2826 e
->direction
= direction
;
2827 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2828 e
->volume_limit
= -1;
2830 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2831 p
->last_element
= e
;
2834 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2839 /* Assign decibel fixes to elements. */
2840 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2843 PA_LLIST_FOREACH(p
, ps
->paths
) {
2846 PA_LLIST_FOREACH(e
, p
->elements
) {
2847 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2848 /* The profile set that contains the dB fix may be freed
2849 * before the element, so we have to copy the dB fix
2851 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2852 e
->db_fix
->profile_set
= NULL
;
2853 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2854 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
2863 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2867 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2870 pa_yes_no(ps
->probed
));
2872 PA_LLIST_FOREACH(p
, ps
->paths
)
2873 pa_alsa_path_dump(p
);
2876 static void path_set_unify(pa_alsa_path_set
*ps
) {
2878 pa_bool_t has_dB
= TRUE
, has_volume
= TRUE
, has_mute
= TRUE
;
2881 /* We have issues dealing with paths that vary too wildly. That
2882 * means for now we have to have all paths support volume/mute/dB
2885 PA_LLIST_FOREACH(p
, ps
->paths
) {
2886 pa_assert(p
->probed
);
2890 else if (!p
->has_dB
)
2897 if (!has_volume
|| !has_dB
|| !has_mute
) {
2900 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2902 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2905 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2907 PA_LLIST_FOREACH(p
, ps
->paths
) {
2909 p
->has_volume
= FALSE
;
2914 p
->has_mute
= FALSE
;
2919 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
2920 pa_alsa_path
*p
, *q
;
2922 PA_LLIST_FOREACH(p
, ps
->paths
) {
2926 for (q
= p
->next
; q
; q
= q
->next
)
2927 if (pa_streq(q
->name
, p
->name
))
2933 m
= pa_xstrdup(p
->name
);
2935 /* OK, this name is not unique, hence let's rename */
2936 for (i
= 1, q
= p
; q
; q
= q
->next
) {
2939 if (!pa_streq(q
->name
, m
))
2942 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2946 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
2947 pa_xfree(q
->description
);
2948 q
->description
= nd
;
2957 void pa_alsa_path_set_probe(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2958 pa_alsa_path
*p
, *n
;
2965 for (p
= ps
->paths
; p
; p
= n
) {
2968 if (pa_alsa_path_probe(p
, m
, ignore_dB
) < 0) {
2969 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
2970 pa_alsa_path_free(p
);
2975 path_set_make_paths_unique(ps
);
2979 static void mapping_free(pa_alsa_mapping
*m
) {
2983 pa_xfree(m
->description
);
2985 pa_xstrfreev(m
->device_strings
);
2986 pa_xstrfreev(m
->input_path_names
);
2987 pa_xstrfreev(m
->output_path_names
);
2988 pa_xstrfreev(m
->input_element
);
2989 pa_xstrfreev(m
->output_element
);
2991 pa_assert(!m
->input_pcm
);
2992 pa_assert(!m
->output_pcm
);
2997 static void profile_free(pa_alsa_profile
*p
) {
3001 pa_xfree(p
->description
);
3003 pa_xstrfreev(p
->input_mapping_names
);
3004 pa_xstrfreev(p
->output_mapping_names
);
3006 if (p
->input_mappings
)
3007 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
3009 if (p
->output_mappings
)
3010 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
3015 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
3021 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
3024 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
3030 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
3033 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
3036 if (ps
->decibel_fixes
) {
3037 pa_alsa_decibel_fix
*db_fix
;
3039 while ((db_fix
= pa_hashmap_steal_first(ps
->decibel_fixes
)))
3040 decibel_fix_free(db_fix
);
3042 pa_hashmap_free(ps
->decibel_fixes
, NULL
, NULL
);
3048 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
3051 if (!pa_startswith(name
, "Mapping "))
3056 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
3059 m
= pa_xnew0(pa_alsa_mapping
, 1);
3060 m
->profile_set
= ps
;
3061 m
->name
= pa_xstrdup(name
);
3062 pa_channel_map_init(&m
->channel_map
);
3064 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
3069 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
3072 if (!pa_startswith(name
, "Profile "))
3077 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
3080 p
= pa_xnew0(pa_alsa_profile
, 1);
3081 p
->profile_set
= ps
;
3082 p
->name
= pa_xstrdup(name
);
3084 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3089 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
3090 pa_alsa_decibel_fix
*db_fix
;
3092 if (!pa_startswith(name
, "DecibelFix "))
3097 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
3100 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
3101 db_fix
->profile_set
= ps
;
3102 db_fix
->name
= pa_xstrdup(name
);
3104 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
3109 static int mapping_parse_device_strings(
3110 const char *filename
,
3112 const char *section
,
3118 pa_alsa_profile_set
*ps
= userdata
;
3123 if (!(m
= mapping_get(ps
, section
))) {
3124 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3128 pa_xstrfreev(m
->device_strings
);
3129 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
3130 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
3137 static int mapping_parse_channel_map(
3138 const char *filename
,
3140 const char *section
,
3146 pa_alsa_profile_set
*ps
= userdata
;
3151 if (!(m
= mapping_get(ps
, section
))) {
3152 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3156 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
3157 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
3164 static int mapping_parse_paths(
3165 const char *filename
,
3167 const char *section
,
3173 pa_alsa_profile_set
*ps
= userdata
;
3178 if (!(m
= mapping_get(ps
, section
))) {
3179 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3183 if (pa_streq(lvalue
, "paths-input")) {
3184 pa_xstrfreev(m
->input_path_names
);
3185 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
3187 pa_xstrfreev(m
->output_path_names
);
3188 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
3194 static int mapping_parse_element(
3195 const char *filename
,
3197 const char *section
,
3203 pa_alsa_profile_set
*ps
= userdata
;
3208 if (!(m
= mapping_get(ps
, section
))) {
3209 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3213 if (pa_streq(lvalue
, "element-input")) {
3214 pa_xstrfreev(m
->input_element
);
3215 m
->input_element
= pa_split_spaces_strv(rvalue
);
3217 pa_xstrfreev(m
->output_element
);
3218 m
->output_element
= pa_split_spaces_strv(rvalue
);
3224 static int mapping_parse_direction(
3225 const char *filename
,
3227 const char *section
,
3233 pa_alsa_profile_set
*ps
= userdata
;
3238 if (!(m
= mapping_get(ps
, section
))) {
3239 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3243 if (pa_streq(rvalue
, "input"))
3244 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3245 else if (pa_streq(rvalue
, "output"))
3246 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3247 else if (pa_streq(rvalue
, "any"))
3248 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3250 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
3257 static int mapping_parse_description(
3258 const char *filename
,
3260 const char *section
,
3266 pa_alsa_profile_set
*ps
= userdata
;
3272 if ((m
= mapping_get(ps
, section
))) {
3273 pa_xfree(m
->description
);
3274 m
->description
= pa_xstrdup(rvalue
);
3275 } else if ((p
= profile_get(ps
, section
))) {
3276 pa_xfree(p
->description
);
3277 p
->description
= pa_xstrdup(rvalue
);
3279 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3286 static int mapping_parse_priority(
3287 const char *filename
,
3289 const char *section
,
3295 pa_alsa_profile_set
*ps
= userdata
;
3302 if (pa_atou(rvalue
, &prio
) < 0) {
3303 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
3307 if ((m
= mapping_get(ps
, section
)))
3309 else if ((p
= profile_get(ps
, section
)))
3312 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3319 static int profile_parse_mappings(
3320 const char *filename
,
3322 const char *section
,
3328 pa_alsa_profile_set
*ps
= userdata
;
3333 if (!(p
= profile_get(ps
, section
))) {
3334 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3338 if (pa_streq(lvalue
, "input-mappings")) {
3339 pa_xstrfreev(p
->input_mapping_names
);
3340 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
3342 pa_xstrfreev(p
->output_mapping_names
);
3343 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
3349 static int profile_parse_skip_probe(
3350 const char *filename
,
3352 const char *section
,
3358 pa_alsa_profile_set
*ps
= userdata
;
3364 if (!(p
= profile_get(ps
, section
))) {
3365 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3369 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
3370 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
3379 static int decibel_fix_parse_db_values(
3380 const char *filename
,
3382 const char *section
,
3388 pa_alsa_profile_set
*ps
= userdata
;
3389 pa_alsa_decibel_fix
*db_fix
;
3393 unsigned n
= 8; /* Current size of the db_values table. */
3394 unsigned min_step
= 0;
3395 unsigned max_step
= 0;
3396 unsigned i
= 0; /* Index to the items table. */
3397 unsigned prev_step
= 0;
3400 pa_assert(filename
);
3406 if (!(db_fix
= decibel_fix_get(ps
, section
))) {
3407 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3411 if (!(items
= pa_split_spaces_strv(rvalue
))) {
3412 pa_log("[%s:%u] Value missing", pa_strnull(filename
), line
);
3416 db_values
= pa_xnew(long, n
);
3418 while ((item
= items
[i
++])) {
3419 char *s
= item
; /* Step value string. */
3420 char *d
= item
; /* dB value string. */
3424 /* Move d forward until it points to a colon or to the end of the item. */
3425 for (; *d
&& *d
!= ':'; ++d
);
3428 /* item started with colon. */
3429 pa_log("[%s:%u] No step value found in %s", filename
, line
, item
);
3433 if (!*d
|| !*(d
+ 1)) {
3434 /* No colon found, or it was the last character in item. */
3435 pa_log("[%s:%u] No dB value found in %s", filename
, line
, item
);
3439 /* pa_atou() needs a null-terminating string. Let's replace the colon
3440 * with a zero byte. */
3443 if (pa_atou(s
, &step
) < 0) {
3444 pa_log("[%s:%u] Invalid step value: %s", filename
, line
, s
);
3448 if (pa_atod(d
, &db
) < 0) {
3449 pa_log("[%s:%u] Invalid dB value: %s", filename
, line
, d
);
3453 if (step
<= prev_step
&& i
!= 1) {
3454 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename
, line
, step
, prev_step
);
3458 if (db
< prev_db
&& i
!= 1) {
3459 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename
, line
, db
, prev_db
);
3465 db_values
[0] = (long) (db
* 100.0);
3469 /* Interpolate linearly. */
3470 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3472 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3474 /* Reallocate the db_values table if it's about to overflow. */
3475 if (prev_step
+ 1 - min_step
== n
) {
3477 db_values
= pa_xrenew(long, db_values
, n
);
3480 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3487 db_fix
->min_step
= min_step
;
3488 db_fix
->max_step
= max_step
;
3489 pa_xfree(db_fix
->db_values
);
3490 db_fix
->db_values
= db_values
;
3492 pa_xstrfreev(items
);
3497 pa_xstrfreev(items
);
3498 pa_xfree(db_values
);
3503 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3505 static const struct description_map well_known_descriptions
[] = {
3506 { "analog-mono", N_("Analog Mono") },
3507 { "analog-stereo", N_("Analog Stereo") },
3508 { "analog-surround-21", N_("Analog Surround 2.1") },
3509 { "analog-surround-30", N_("Analog Surround 3.0") },
3510 { "analog-surround-31", N_("Analog Surround 3.1") },
3511 { "analog-surround-40", N_("Analog Surround 4.0") },
3512 { "analog-surround-41", N_("Analog Surround 4.1") },
3513 { "analog-surround-50", N_("Analog Surround 5.0") },
3514 { "analog-surround-51", N_("Analog Surround 5.1") },
3515 { "analog-surround-61", N_("Analog Surround 6.0") },
3516 { "analog-surround-61", N_("Analog Surround 6.1") },
3517 { "analog-surround-70", N_("Analog Surround 7.0") },
3518 { "analog-surround-71", N_("Analog Surround 7.1") },
3519 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3520 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3521 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3522 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3523 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3528 if (!pa_channel_map_valid(&m
->channel_map
)) {
3529 pa_log("Mapping %s is missing channel map.", m
->name
);
3533 if (!m
->device_strings
) {
3534 pa_log("Mapping %s is missing device strings.", m
->name
);
3538 if ((m
->input_path_names
&& m
->input_element
) ||
3539 (m
->output_path_names
&& m
->output_element
)) {
3540 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3544 if (!m
->description
)
3545 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3546 well_known_descriptions
,
3547 PA_ELEMENTSOF(well_known_descriptions
)));
3549 if (!m
->description
)
3550 m
->description
= pa_xstrdup(m
->name
);
3553 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3555 else if (m
->channel_map
.channels
== bonus
->channels
)
3562 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3563 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3567 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3569 pa_strnull(m
->description
),
3571 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3572 pa_yes_no(m
->supported
),
3576 static void profile_set_add_auto_pair(
3577 pa_alsa_profile_set
*ps
,
3578 pa_alsa_mapping
*m
, /* output */
3579 pa_alsa_mapping
*n
/* input */) {
3587 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3590 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3594 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3596 name
= pa_sprintf_malloc("output:%s", m
->name
);
3598 name
= pa_sprintf_malloc("input:%s", n
->name
);
3600 if (pa_hashmap_get(ps
->profiles
, name
)) {
3605 p
= pa_xnew0(pa_alsa_profile
, 1);
3606 p
->profile_set
= ps
;
3610 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3611 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3612 p
->priority
+= m
->priority
* 100;
3616 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3617 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3618 p
->priority
+= n
->priority
;
3621 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3624 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3625 pa_alsa_mapping
*m
, *n
;
3626 void *m_state
, *n_state
;
3630 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3631 profile_set_add_auto_pair(ps
, m
, NULL
);
3633 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3634 profile_set_add_auto_pair(ps
, m
, n
);
3637 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3638 profile_set_add_auto_pair(ps
, NULL
, n
);
3641 static int profile_verify(pa_alsa_profile
*p
) {
3643 static const struct description_map well_known_descriptions
[] = {
3644 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3645 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3646 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3647 { "off", N_("Off") }
3652 /* Replace the output mapping names by the actual mappings */
3653 if (p
->output_mapping_names
) {
3656 pa_assert(!p
->output_mappings
);
3657 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3659 for (name
= p
->output_mapping_names
; *name
; name
++) {
3662 pa_bool_t duplicate
= FALSE
;
3664 for (in
= name
+ 1; *in
; in
++)
3665 if (pa_streq(*name
, *in
)) {
3673 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3674 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3678 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3684 pa_xstrfreev(p
->output_mapping_names
);
3685 p
->output_mapping_names
= NULL
;
3688 /* Replace the input mapping names by the actual mappings */
3689 if (p
->input_mapping_names
) {
3692 pa_assert(!p
->input_mappings
);
3693 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3695 for (name
= p
->input_mapping_names
; *name
; name
++) {
3698 pa_bool_t duplicate
= FALSE
;
3700 for (in
= name
+ 1; *in
; in
++)
3701 if (pa_streq(*name
, *in
)) {
3709 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3710 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3714 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3720 pa_xstrfreev(p
->input_mapping_names
);
3721 p
->input_mapping_names
= NULL
;
3724 if (!p
->input_mappings
&& !p
->output_mappings
) {
3725 pa_log("Profile '%s' lacks mappings.", p
->name
);
3729 if (!p
->description
)
3730 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3731 well_known_descriptions
,
3732 PA_ELEMENTSOF(well_known_descriptions
)));
3734 if (!p
->description
) {
3739 sb
= pa_strbuf_new();
3741 if (p
->output_mappings
)
3742 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3743 if (!pa_strbuf_isempty(sb
))
3744 pa_strbuf_puts(sb
, " + ");
3746 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
3749 if (p
->input_mappings
)
3750 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3751 if (!pa_strbuf_isempty(sb
))
3752 pa_strbuf_puts(sb
, " + ");
3754 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
3757 p
->description
= pa_strbuf_tostring_free(sb
);
3763 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
3768 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3770 pa_strnull(p
->description
),
3772 pa_yes_no(p
->supported
),
3773 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
3774 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
3776 if (p
->input_mappings
)
3777 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
3778 pa_log_debug("Input %s", m
->name
);
3780 if (p
->output_mappings
)
3781 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
3782 pa_log_debug("Output %s", m
->name
);
3785 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
3788 /* Check that the dB mapping has been configured. Since "db-values" is
3789 * currently the only option in the DecibelFix section, and decibel fix
3790 * objects don't get created if a DecibelFix section is empty, this is
3791 * actually a redundant check. Having this may prevent future bugs,
3793 if (!db_fix
->db_values
) {
3794 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
3801 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
3802 char *db_values
= NULL
;
3806 if (db_fix
->db_values
) {
3808 unsigned long i
, nsteps
;
3810 pa_assert(db_fix
->min_step
<= db_fix
->max_step
);
3811 nsteps
= db_fix
->max_step
- db_fix
->min_step
+ 1;
3813 buf
= pa_strbuf_new();
3814 for (i
= 0; i
< nsteps
; ++i
)
3815 pa_strbuf_printf(buf
, "[%li]:%0.2f ", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
3817 db_values
= pa_strbuf_tostring_free(buf
);
3820 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
3821 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
3823 pa_xfree(db_values
);
3826 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
3827 pa_alsa_profile_set
*ps
;
3830 pa_alsa_decibel_fix
*db_fix
;
3835 static pa_config_item items
[] = {
3837 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
3840 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
3841 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
3842 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
3843 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
3844 { "element-input", mapping_parse_element
, NULL
, NULL
},
3845 { "element-output", mapping_parse_element
, NULL
, NULL
},
3846 { "direction", mapping_parse_direction
, NULL
, NULL
},
3848 /* Shared by [Mapping ...] and [Profile ...] */
3849 { "description", mapping_parse_description
, NULL
, NULL
},
3850 { "priority", mapping_parse_priority
, NULL
, NULL
},
3853 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
3854 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
3855 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
3857 /* [DecibelFix ...] */
3858 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
3859 { NULL
, NULL
, NULL
, NULL
}
3862 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
3863 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3864 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3865 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3867 items
[0].data
= &ps
->auto_profiles
;
3870 fname
= "default.conf";
3872 fn
= pa_maybe_prefix_path(fname
,
3873 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
3874 PA_ALSA_PROFILE_SETS_DIR
);
3876 r
= pa_config_parse(fn
, NULL
, items
, ps
);
3882 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3883 if (mapping_verify(m
, bonus
) < 0)
3886 if (ps
->auto_profiles
)
3887 profile_set_add_auto(ps
);
3889 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3890 if (profile_verify(p
) < 0)
3893 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
3894 if (decibel_fix_verify(db_fix
) < 0)
3900 pa_alsa_profile_set_free(ps
);
3904 void pa_alsa_profile_set_probe(
3905 pa_alsa_profile_set
*ps
,
3907 const pa_sample_spec
*ss
,
3908 unsigned default_n_fragments
,
3909 unsigned default_fragment_size_msec
) {
3912 pa_alsa_profile
*p
, *last
= NULL
;
3922 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
3923 pa_sample_spec try_ss
;
3924 pa_channel_map try_map
;
3925 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
3928 /* Is this already marked that it is supported? (i.e. from the config file) */
3932 pa_log_debug("Looking at profile %s", p
->name
);
3934 /* Close PCMs from the last iteration we don't need anymore */
3935 if (last
&& last
->output_mappings
)
3936 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
) {
3941 if (last
->supported
)
3944 if (!p
->output_mappings
|| !pa_idxset_get_by_data(p
->output_mappings
, m
, NULL
)) {
3945 snd_pcm_close(m
->output_pcm
);
3946 m
->output_pcm
= NULL
;
3950 if (last
&& last
->input_mappings
)
3951 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
) {
3956 if (last
->supported
)
3959 if (!p
->input_mappings
|| !pa_idxset_get_by_data(p
->input_mappings
, m
, NULL
)) {
3960 snd_pcm_close(m
->input_pcm
);
3961 m
->input_pcm
= NULL
;
3965 p
->supported
= TRUE
;
3967 /* Check if we can open all new ones */
3968 if (p
->output_mappings
)
3969 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3974 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
3975 try_map
= m
->channel_map
;
3977 try_ss
.channels
= try_map
.channels
;
3980 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
3981 pa_frame_size(&try_ss
);
3982 try_buffer_size
= default_n_fragments
* try_period_size
;
3984 if (!(m
->output_pcm
= pa_alsa_open_by_template(
3989 SND_PCM_STREAM_PLAYBACK
,
3990 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3992 p
->supported
= FALSE
;
3997 if (p
->input_mappings
&& p
->supported
)
3998 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4003 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
4004 try_map
= m
->channel_map
;
4006 try_ss
.channels
= try_map
.channels
;
4009 pa_usec_to_bytes(default_fragment_size_msec
*PA_USEC_PER_MSEC
, &try_ss
) /
4010 pa_frame_size(&try_ss
);
4011 try_buffer_size
= default_n_fragments
* try_period_size
;
4013 if (!(m
->input_pcm
= pa_alsa_open_by_template(
4018 SND_PCM_STREAM_CAPTURE
,
4019 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
4021 p
->supported
= FALSE
;
4029 pa_log_debug("Profile %s supported.", p
->name
);
4036 if (last
->output_mappings
)
4037 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
)
4038 if (m
->output_pcm
) {
4040 if (last
->supported
)
4043 snd_pcm_close(m
->output_pcm
);
4044 m
->output_pcm
= NULL
;
4047 if (last
->input_mappings
)
4048 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
)
4051 if (last
->supported
)
4054 snd_pcm_close(m
->input_pcm
);
4055 m
->input_pcm
= NULL
;
4059 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4060 if (!p
->supported
) {
4061 pa_hashmap_remove(ps
->profiles
, p
->name
);
4065 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4066 if (m
->supported
<= 0) {
4067 pa_hashmap_remove(ps
->mappings
, m
->name
);
4074 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
4077 pa_alsa_decibel_fix
*db_fix
;
4082 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4085 pa_yes_no(ps
->auto_profiles
),
4086 pa_yes_no(ps
->probed
),
4087 pa_hashmap_size(ps
->mappings
),
4088 pa_hashmap_size(ps
->profiles
),
4089 pa_hashmap_size(ps
->decibel_fixes
));
4091 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4092 pa_alsa_mapping_dump(m
);
4094 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4095 pa_alsa_profile_dump(p
);
4097 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4098 pa_alsa_decibel_fix_dump(db_fix
);
4101 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
) {
4108 /* if there is no path, we don't want a port list */
4112 if (!ps
->paths
->next
){
4115 /* If there is only one path, but no or only one setting, then
4116 * we want a port list either */
4117 if (!ps
->paths
->settings
|| !ps
->paths
->settings
->next
)
4120 /* Ok, there is only one path, however with multiple settings,
4121 * so let's create a port for each setting */
4122 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4124 PA_LLIST_FOREACH(s
, ps
->paths
->settings
) {
4125 pa_device_port
*port
;
4126 pa_alsa_port_data
*data
;
4128 port
= pa_device_port_new(s
->name
, s
->description
, sizeof(pa_alsa_port_data
));
4129 port
->priority
= s
->priority
;
4131 data
= PA_DEVICE_PORT_DATA(port
);
4132 data
->path
= ps
->paths
;
4135 pa_hashmap_put(*p
, port
->name
, port
);
4140 /* We have multiple paths, so let's create a port for each
4141 * one, and each of each settings */
4142 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4144 PA_LLIST_FOREACH(path
, ps
->paths
) {
4146 if (!path
->settings
|| !path
->settings
->next
) {
4147 pa_device_port
*port
;
4148 pa_alsa_port_data
*data
;
4150 /* If there is no or just one setting we only need a
4153 port
= pa_device_port_new(path
->name
, path
->description
, sizeof(pa_alsa_port_data
));
4154 port
->priority
= path
->priority
* 100;
4157 data
= PA_DEVICE_PORT_DATA(port
);
4159 data
->setting
= path
->settings
;
4161 pa_hashmap_put(*p
, port
->name
, port
);
4165 PA_LLIST_FOREACH(s
, path
->settings
) {
4166 pa_device_port
*port
;
4167 pa_alsa_port_data
*data
;
4170 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4172 if (s
->description
[0])
4173 d
= pa_sprintf_malloc(_("%s / %s"), path
->description
, s
->description
);
4175 d
= pa_xstrdup(path
->description
);
4177 port
= pa_device_port_new(n
, d
, sizeof(pa_alsa_port_data
));
4178 port
->priority
= path
->priority
* 100 + s
->priority
;
4183 data
= PA_DEVICE_PORT_DATA(port
);
4187 pa_hashmap_put(*p
, port
->name
, port
);
4193 pa_log_debug("Added %u ports", pa_hashmap_size(*p
));