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_make_paths_unique(pa_alsa_path_set
*ps
) {
2877 pa_alsa_path
*p
, *q
;
2879 PA_LLIST_FOREACH(p
, ps
->paths
) {
2883 for (q
= p
->next
; q
; q
= q
->next
)
2884 if (pa_streq(q
->name
, p
->name
))
2890 m
= pa_xstrdup(p
->name
);
2892 /* OK, this name is not unique, hence let's rename */
2893 for (i
= 1, q
= p
; q
; q
= q
->next
) {
2896 if (!pa_streq(q
->name
, m
))
2899 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2903 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
2904 pa_xfree(q
->description
);
2905 q
->description
= nd
;
2914 void pa_alsa_path_set_probe(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2915 pa_alsa_path
*p
, *n
;
2922 for (p
= ps
->paths
; p
; p
= n
) {
2925 if (pa_alsa_path_probe(p
, m
, ignore_dB
) < 0) {
2926 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
2927 pa_alsa_path_free(p
);
2931 path_set_make_paths_unique(ps
);
2935 static void mapping_free(pa_alsa_mapping
*m
) {
2939 pa_xfree(m
->description
);
2941 pa_xstrfreev(m
->device_strings
);
2942 pa_xstrfreev(m
->input_path_names
);
2943 pa_xstrfreev(m
->output_path_names
);
2944 pa_xstrfreev(m
->input_element
);
2945 pa_xstrfreev(m
->output_element
);
2947 pa_assert(!m
->input_pcm
);
2948 pa_assert(!m
->output_pcm
);
2953 static void profile_free(pa_alsa_profile
*p
) {
2957 pa_xfree(p
->description
);
2959 pa_xstrfreev(p
->input_mapping_names
);
2960 pa_xstrfreev(p
->output_mapping_names
);
2962 if (p
->input_mappings
)
2963 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
2965 if (p
->output_mappings
)
2966 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
2971 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
2977 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
2980 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
2986 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
2989 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
2992 if (ps
->decibel_fixes
) {
2993 pa_alsa_decibel_fix
*db_fix
;
2995 while ((db_fix
= pa_hashmap_steal_first(ps
->decibel_fixes
)))
2996 decibel_fix_free(db_fix
);
2998 pa_hashmap_free(ps
->decibel_fixes
, NULL
, NULL
);
3004 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
3007 if (!pa_startswith(name
, "Mapping "))
3012 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
3015 m
= pa_xnew0(pa_alsa_mapping
, 1);
3016 m
->profile_set
= ps
;
3017 m
->name
= pa_xstrdup(name
);
3018 pa_channel_map_init(&m
->channel_map
);
3020 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
3025 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
3028 if (!pa_startswith(name
, "Profile "))
3033 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
3036 p
= pa_xnew0(pa_alsa_profile
, 1);
3037 p
->profile_set
= ps
;
3038 p
->name
= pa_xstrdup(name
);
3040 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3045 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
3046 pa_alsa_decibel_fix
*db_fix
;
3048 if (!pa_startswith(name
, "DecibelFix "))
3053 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
3056 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
3057 db_fix
->profile_set
= ps
;
3058 db_fix
->name
= pa_xstrdup(name
);
3060 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
3065 static int mapping_parse_device_strings(
3066 const char *filename
,
3068 const char *section
,
3074 pa_alsa_profile_set
*ps
= userdata
;
3079 if (!(m
= mapping_get(ps
, section
))) {
3080 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3084 pa_xstrfreev(m
->device_strings
);
3085 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
3086 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
3093 static int mapping_parse_channel_map(
3094 const char *filename
,
3096 const char *section
,
3102 pa_alsa_profile_set
*ps
= userdata
;
3107 if (!(m
= mapping_get(ps
, section
))) {
3108 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3112 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
3113 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
3120 static int mapping_parse_paths(
3121 const char *filename
,
3123 const char *section
,
3129 pa_alsa_profile_set
*ps
= userdata
;
3134 if (!(m
= mapping_get(ps
, section
))) {
3135 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3139 if (pa_streq(lvalue
, "paths-input")) {
3140 pa_xstrfreev(m
->input_path_names
);
3141 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
3143 pa_xstrfreev(m
->output_path_names
);
3144 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
3150 static int mapping_parse_element(
3151 const char *filename
,
3153 const char *section
,
3159 pa_alsa_profile_set
*ps
= userdata
;
3164 if (!(m
= mapping_get(ps
, section
))) {
3165 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3169 if (pa_streq(lvalue
, "element-input")) {
3170 pa_xstrfreev(m
->input_element
);
3171 m
->input_element
= pa_split_spaces_strv(rvalue
);
3173 pa_xstrfreev(m
->output_element
);
3174 m
->output_element
= pa_split_spaces_strv(rvalue
);
3180 static int mapping_parse_direction(
3181 const char *filename
,
3183 const char *section
,
3189 pa_alsa_profile_set
*ps
= userdata
;
3194 if (!(m
= mapping_get(ps
, section
))) {
3195 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3199 if (pa_streq(rvalue
, "input"))
3200 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3201 else if (pa_streq(rvalue
, "output"))
3202 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3203 else if (pa_streq(rvalue
, "any"))
3204 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3206 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
3213 static int mapping_parse_description(
3214 const char *filename
,
3216 const char *section
,
3222 pa_alsa_profile_set
*ps
= userdata
;
3228 if ((m
= mapping_get(ps
, section
))) {
3229 pa_xfree(m
->description
);
3230 m
->description
= pa_xstrdup(rvalue
);
3231 } else if ((p
= profile_get(ps
, section
))) {
3232 pa_xfree(p
->description
);
3233 p
->description
= pa_xstrdup(rvalue
);
3235 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3242 static int mapping_parse_priority(
3243 const char *filename
,
3245 const char *section
,
3251 pa_alsa_profile_set
*ps
= userdata
;
3258 if (pa_atou(rvalue
, &prio
) < 0) {
3259 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
3263 if ((m
= mapping_get(ps
, section
)))
3265 else if ((p
= profile_get(ps
, section
)))
3268 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3275 static int profile_parse_mappings(
3276 const char *filename
,
3278 const char *section
,
3284 pa_alsa_profile_set
*ps
= userdata
;
3289 if (!(p
= profile_get(ps
, section
))) {
3290 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3294 if (pa_streq(lvalue
, "input-mappings")) {
3295 pa_xstrfreev(p
->input_mapping_names
);
3296 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
3298 pa_xstrfreev(p
->output_mapping_names
);
3299 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
3305 static int profile_parse_skip_probe(
3306 const char *filename
,
3308 const char *section
,
3314 pa_alsa_profile_set
*ps
= userdata
;
3320 if (!(p
= profile_get(ps
, section
))) {
3321 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3325 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
3326 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
3335 static int decibel_fix_parse_db_values(
3336 const char *filename
,
3338 const char *section
,
3344 pa_alsa_profile_set
*ps
= userdata
;
3345 pa_alsa_decibel_fix
*db_fix
;
3349 unsigned n
= 8; /* Current size of the db_values table. */
3350 unsigned min_step
= 0;
3351 unsigned max_step
= 0;
3352 unsigned i
= 0; /* Index to the items table. */
3353 unsigned prev_step
= 0;
3356 pa_assert(filename
);
3362 if (!(db_fix
= decibel_fix_get(ps
, section
))) {
3363 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3367 if (!(items
= pa_split_spaces_strv(rvalue
))) {
3368 pa_log("[%s:%u] Value missing", pa_strnull(filename
), line
);
3372 db_values
= pa_xnew(long, n
);
3374 while ((item
= items
[i
++])) {
3375 char *s
= item
; /* Step value string. */
3376 char *d
= item
; /* dB value string. */
3380 /* Move d forward until it points to a colon or to the end of the item. */
3381 for (; *d
&& *d
!= ':'; ++d
);
3384 /* item started with colon. */
3385 pa_log("[%s:%u] No step value found in %s", filename
, line
, item
);
3389 if (!*d
|| !*(d
+ 1)) {
3390 /* No colon found, or it was the last character in item. */
3391 pa_log("[%s:%u] No dB value found in %s", filename
, line
, item
);
3395 /* pa_atou() needs a null-terminating string. Let's replace the colon
3396 * with a zero byte. */
3399 if (pa_atou(s
, &step
) < 0) {
3400 pa_log("[%s:%u] Invalid step value: %s", filename
, line
, s
);
3404 if (pa_atod(d
, &db
) < 0) {
3405 pa_log("[%s:%u] Invalid dB value: %s", filename
, line
, d
);
3409 if (step
<= prev_step
&& i
!= 1) {
3410 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename
, line
, step
, prev_step
);
3414 if (db
< prev_db
&& i
!= 1) {
3415 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename
, line
, db
, prev_db
);
3421 db_values
[0] = (long) (db
* 100.0);
3425 /* Interpolate linearly. */
3426 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3428 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3430 /* Reallocate the db_values table if it's about to overflow. */
3431 if (prev_step
+ 1 - min_step
== n
) {
3433 db_values
= pa_xrenew(long, db_values
, n
);
3436 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3443 db_fix
->min_step
= min_step
;
3444 db_fix
->max_step
= max_step
;
3445 pa_xfree(db_fix
->db_values
);
3446 db_fix
->db_values
= db_values
;
3448 pa_xstrfreev(items
);
3453 pa_xstrfreev(items
);
3454 pa_xfree(db_values
);
3459 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3461 static const struct description_map well_known_descriptions
[] = {
3462 { "analog-mono", N_("Analog Mono") },
3463 { "analog-stereo", N_("Analog Stereo") },
3464 { "analog-surround-21", N_("Analog Surround 2.1") },
3465 { "analog-surround-30", N_("Analog Surround 3.0") },
3466 { "analog-surround-31", N_("Analog Surround 3.1") },
3467 { "analog-surround-40", N_("Analog Surround 4.0") },
3468 { "analog-surround-41", N_("Analog Surround 4.1") },
3469 { "analog-surround-50", N_("Analog Surround 5.0") },
3470 { "analog-surround-51", N_("Analog Surround 5.1") },
3471 { "analog-surround-61", N_("Analog Surround 6.0") },
3472 { "analog-surround-61", N_("Analog Surround 6.1") },
3473 { "analog-surround-70", N_("Analog Surround 7.0") },
3474 { "analog-surround-71", N_("Analog Surround 7.1") },
3475 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3476 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3477 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3478 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3479 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3484 if (!pa_channel_map_valid(&m
->channel_map
)) {
3485 pa_log("Mapping %s is missing channel map.", m
->name
);
3489 if (!m
->device_strings
) {
3490 pa_log("Mapping %s is missing device strings.", m
->name
);
3494 if ((m
->input_path_names
&& m
->input_element
) ||
3495 (m
->output_path_names
&& m
->output_element
)) {
3496 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3500 if (!m
->description
)
3501 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3502 well_known_descriptions
,
3503 PA_ELEMENTSOF(well_known_descriptions
)));
3505 if (!m
->description
)
3506 m
->description
= pa_xstrdup(m
->name
);
3509 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3511 else if (m
->channel_map
.channels
== bonus
->channels
)
3518 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3519 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3523 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3525 pa_strnull(m
->description
),
3527 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3528 pa_yes_no(m
->supported
),
3532 static void profile_set_add_auto_pair(
3533 pa_alsa_profile_set
*ps
,
3534 pa_alsa_mapping
*m
, /* output */
3535 pa_alsa_mapping
*n
/* input */) {
3543 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3546 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3550 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3552 name
= pa_sprintf_malloc("output:%s", m
->name
);
3554 name
= pa_sprintf_malloc("input:%s", n
->name
);
3556 if (pa_hashmap_get(ps
->profiles
, name
)) {
3561 p
= pa_xnew0(pa_alsa_profile
, 1);
3562 p
->profile_set
= ps
;
3566 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3567 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3568 p
->priority
+= m
->priority
* 100;
3572 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3573 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3574 p
->priority
+= n
->priority
;
3577 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3580 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3581 pa_alsa_mapping
*m
, *n
;
3582 void *m_state
, *n_state
;
3586 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3587 profile_set_add_auto_pair(ps
, m
, NULL
);
3589 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3590 profile_set_add_auto_pair(ps
, m
, n
);
3593 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3594 profile_set_add_auto_pair(ps
, NULL
, n
);
3597 static int profile_verify(pa_alsa_profile
*p
) {
3599 static const struct description_map well_known_descriptions
[] = {
3600 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3601 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3602 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3603 { "off", N_("Off") }
3608 /* Replace the output mapping names by the actual mappings */
3609 if (p
->output_mapping_names
) {
3612 pa_assert(!p
->output_mappings
);
3613 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3615 for (name
= p
->output_mapping_names
; *name
; name
++) {
3618 pa_bool_t duplicate
= FALSE
;
3620 for (in
= name
+ 1; *in
; in
++)
3621 if (pa_streq(*name
, *in
)) {
3629 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3630 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3634 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3640 pa_xstrfreev(p
->output_mapping_names
);
3641 p
->output_mapping_names
= NULL
;
3644 /* Replace the input mapping names by the actual mappings */
3645 if (p
->input_mapping_names
) {
3648 pa_assert(!p
->input_mappings
);
3649 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3651 for (name
= p
->input_mapping_names
; *name
; name
++) {
3654 pa_bool_t duplicate
= FALSE
;
3656 for (in
= name
+ 1; *in
; in
++)
3657 if (pa_streq(*name
, *in
)) {
3665 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3666 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3670 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3676 pa_xstrfreev(p
->input_mapping_names
);
3677 p
->input_mapping_names
= NULL
;
3680 if (!p
->input_mappings
&& !p
->output_mappings
) {
3681 pa_log("Profile '%s' lacks mappings.", p
->name
);
3685 if (!p
->description
)
3686 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3687 well_known_descriptions
,
3688 PA_ELEMENTSOF(well_known_descriptions
)));
3690 if (!p
->description
) {
3695 sb
= pa_strbuf_new();
3697 if (p
->output_mappings
)
3698 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3699 if (!pa_strbuf_isempty(sb
))
3700 pa_strbuf_puts(sb
, " + ");
3702 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
3705 if (p
->input_mappings
)
3706 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3707 if (!pa_strbuf_isempty(sb
))
3708 pa_strbuf_puts(sb
, " + ");
3710 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
3713 p
->description
= pa_strbuf_tostring_free(sb
);
3719 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
3724 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3726 pa_strnull(p
->description
),
3728 pa_yes_no(p
->supported
),
3729 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
3730 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
3732 if (p
->input_mappings
)
3733 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
3734 pa_log_debug("Input %s", m
->name
);
3736 if (p
->output_mappings
)
3737 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
3738 pa_log_debug("Output %s", m
->name
);
3741 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
3744 /* Check that the dB mapping has been configured. Since "db-values" is
3745 * currently the only option in the DecibelFix section, and decibel fix
3746 * objects don't get created if a DecibelFix section is empty, this is
3747 * actually a redundant check. Having this may prevent future bugs,
3749 if (!db_fix
->db_values
) {
3750 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
3757 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
3758 char *db_values
= NULL
;
3762 if (db_fix
->db_values
) {
3764 unsigned long i
, nsteps
;
3766 pa_assert(db_fix
->min_step
<= db_fix
->max_step
);
3767 nsteps
= db_fix
->max_step
- db_fix
->min_step
+ 1;
3769 buf
= pa_strbuf_new();
3770 for (i
= 0; i
< nsteps
; ++i
)
3771 pa_strbuf_printf(buf
, "[%li]:%0.2f ", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
3773 db_values
= pa_strbuf_tostring_free(buf
);
3776 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
3777 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
3779 pa_xfree(db_values
);
3782 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
3783 pa_alsa_profile_set
*ps
;
3786 pa_alsa_decibel_fix
*db_fix
;
3791 static pa_config_item items
[] = {
3793 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
3796 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
3797 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
3798 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
3799 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
3800 { "element-input", mapping_parse_element
, NULL
, NULL
},
3801 { "element-output", mapping_parse_element
, NULL
, NULL
},
3802 { "direction", mapping_parse_direction
, NULL
, NULL
},
3804 /* Shared by [Mapping ...] and [Profile ...] */
3805 { "description", mapping_parse_description
, NULL
, NULL
},
3806 { "priority", mapping_parse_priority
, NULL
, NULL
},
3809 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
3810 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
3811 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
3813 /* [DecibelFix ...] */
3814 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
3815 { NULL
, NULL
, NULL
, NULL
}
3818 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
3819 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3820 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3821 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3823 items
[0].data
= &ps
->auto_profiles
;
3826 fname
= "default.conf";
3828 fn
= pa_maybe_prefix_path(fname
,
3829 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
3830 PA_ALSA_PROFILE_SETS_DIR
);
3832 r
= pa_config_parse(fn
, NULL
, items
, ps
);
3838 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3839 if (mapping_verify(m
, bonus
) < 0)
3842 if (ps
->auto_profiles
)
3843 profile_set_add_auto(ps
);
3845 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3846 if (profile_verify(p
) < 0)
3849 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
3850 if (decibel_fix_verify(db_fix
) < 0)
3856 pa_alsa_profile_set_free(ps
);
3860 void pa_alsa_profile_set_probe(
3861 pa_alsa_profile_set
*ps
,
3863 const pa_sample_spec
*ss
,
3864 unsigned default_n_fragments
,
3865 unsigned default_fragment_size_msec
) {
3868 pa_alsa_profile
*p
, *last
= NULL
;
3878 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
3879 pa_sample_spec try_ss
;
3880 pa_channel_map try_map
;
3881 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
3884 /* Is this already marked that it is supported? (i.e. from the config file) */
3888 pa_log_debug("Looking at profile %s", p
->name
);
3890 /* Close PCMs from the last iteration we don't need anymore */
3891 if (last
&& last
->output_mappings
)
3892 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
) {
3897 if (last
->supported
)
3900 if (!p
->output_mappings
|| !pa_idxset_get_by_data(p
->output_mappings
, m
, NULL
)) {
3901 snd_pcm_close(m
->output_pcm
);
3902 m
->output_pcm
= NULL
;
3906 if (last
&& last
->input_mappings
)
3907 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
) {
3912 if (last
->supported
)
3915 if (!p
->input_mappings
|| !pa_idxset_get_by_data(p
->input_mappings
, m
, NULL
)) {
3916 snd_pcm_close(m
->input_pcm
);
3917 m
->input_pcm
= NULL
;
3921 p
->supported
= TRUE
;
3923 /* Check if we can open all new ones */
3924 if (p
->output_mappings
)
3925 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3930 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
3931 try_map
= m
->channel_map
;
3933 try_ss
.channels
= try_map
.channels
;
3936 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
3937 pa_frame_size(&try_ss
);
3938 try_buffer_size
= default_n_fragments
* try_period_size
;
3940 if (!(m
->output_pcm
= pa_alsa_open_by_template(
3945 SND_PCM_STREAM_PLAYBACK
,
3946 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3948 p
->supported
= FALSE
;
3953 if (p
->input_mappings
&& p
->supported
)
3954 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3959 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
3960 try_map
= m
->channel_map
;
3962 try_ss
.channels
= try_map
.channels
;
3965 pa_usec_to_bytes(default_fragment_size_msec
*PA_USEC_PER_MSEC
, &try_ss
) /
3966 pa_frame_size(&try_ss
);
3967 try_buffer_size
= default_n_fragments
* try_period_size
;
3969 if (!(m
->input_pcm
= pa_alsa_open_by_template(
3974 SND_PCM_STREAM_CAPTURE
,
3975 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3977 p
->supported
= FALSE
;
3985 pa_log_debug("Profile %s supported.", p
->name
);
3992 if (last
->output_mappings
)
3993 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
)
3994 if (m
->output_pcm
) {
3996 if (last
->supported
)
3999 snd_pcm_close(m
->output_pcm
);
4000 m
->output_pcm
= NULL
;
4003 if (last
->input_mappings
)
4004 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
)
4007 if (last
->supported
)
4010 snd_pcm_close(m
->input_pcm
);
4011 m
->input_pcm
= NULL
;
4015 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4016 if (!p
->supported
) {
4017 pa_hashmap_remove(ps
->profiles
, p
->name
);
4021 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4022 if (m
->supported
<= 0) {
4023 pa_hashmap_remove(ps
->mappings
, m
->name
);
4030 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
4033 pa_alsa_decibel_fix
*db_fix
;
4038 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4041 pa_yes_no(ps
->auto_profiles
),
4042 pa_yes_no(ps
->probed
),
4043 pa_hashmap_size(ps
->mappings
),
4044 pa_hashmap_size(ps
->profiles
),
4045 pa_hashmap_size(ps
->decibel_fixes
));
4047 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4048 pa_alsa_mapping_dump(m
);
4050 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4051 pa_alsa_profile_dump(p
);
4053 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4054 pa_alsa_decibel_fix_dump(db_fix
);
4057 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
) {
4064 /* if there is no path, we don't want a port list */
4068 if (!ps
->paths
->next
){
4071 /* If there is only one path, but no or only one setting, then
4072 * we want a port list either */
4073 if (!ps
->paths
->settings
|| !ps
->paths
->settings
->next
)
4076 /* Ok, there is only one path, however with multiple settings,
4077 * so let's create a port for each setting */
4078 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4080 PA_LLIST_FOREACH(s
, ps
->paths
->settings
) {
4081 pa_device_port
*port
;
4082 pa_alsa_port_data
*data
;
4084 port
= pa_device_port_new(s
->name
, s
->description
, sizeof(pa_alsa_port_data
));
4085 port
->priority
= s
->priority
;
4087 data
= PA_DEVICE_PORT_DATA(port
);
4088 data
->path
= ps
->paths
;
4091 pa_hashmap_put(*p
, port
->name
, port
);
4096 /* We have multiple paths, so let's create a port for each
4097 * one, and each of each settings */
4098 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4100 PA_LLIST_FOREACH(path
, ps
->paths
) {
4102 if (!path
->settings
|| !path
->settings
->next
) {
4103 pa_device_port
*port
;
4104 pa_alsa_port_data
*data
;
4106 /* If there is no or just one setting we only need a
4109 port
= pa_device_port_new(path
->name
, path
->description
, sizeof(pa_alsa_port_data
));
4110 port
->priority
= path
->priority
* 100;
4113 data
= PA_DEVICE_PORT_DATA(port
);
4115 data
->setting
= path
->settings
;
4117 pa_hashmap_put(*p
, port
->name
, port
);
4121 PA_LLIST_FOREACH(s
, path
->settings
) {
4122 pa_device_port
*port
;
4123 pa_alsa_port_data
*data
;
4126 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4128 if (s
->description
[0])
4129 d
= pa_sprintf_malloc(_("%s / %s"), path
->description
, s
->description
);
4131 d
= pa_xstrdup(path
->description
);
4133 port
= pa_device_port_new(n
, d
, sizeof(pa_alsa_port_data
));
4134 port
->priority
= path
->priority
* 100 + s
->priority
;
4139 data
= PA_DEVICE_PORT_DATA(port
);
4143 pa_hashmap_put(*p
, port
->name
, port
);
4149 pa_log_debug("Added %u ports", pa_hashmap_size(*p
));