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/utf8.h>
43 #include <pulsecore/i18n.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
));
289 if (revents
& (POLLNVAL
| POLLERR
)) {
290 pa_log_debug("Device disconnected, stopping poll on mixer");
292 } else if (revents
& POLLERR
) {
293 /* This shouldn't happen. */
294 pa_log_error("Got a POLLERR (revents = %04x), stopping poll on mixer", revents
);
298 err
= snd_mixer_handle_events(pd
->mixer
);
300 if (PA_LIKELY(err
>= 0)) {
301 pa_rtpoll_item_free(i
);
302 pa_alsa_set_mixer_rtpoll(pd
, pd
->mixer
, pd
->rtpoll
);
304 pa_log_error("Error handling mixer event: %s", pa_alsa_strerror(err
));
313 pa_rtpoll_item_free(i
);
315 pd
->poll_item
= NULL
;
322 int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata
*pd
, snd_mixer_t
*mixer
, pa_rtpoll
*rtp
) {
331 if ((n
= snd_mixer_poll_descriptors_count(mixer
)) < 0) {
332 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n
));
336 i
= pa_rtpoll_item_new(rtp
, PA_RTPOLL_LATE
, (unsigned) n
);
338 p
= pa_rtpoll_item_get_pollfd(i
, NULL
);
340 memset(p
, 0, sizeof(struct pollfd
) * n
);
342 if ((err
= snd_mixer_poll_descriptors(mixer
, p
, (unsigned) n
)) < 0) {
343 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err
));
344 pa_rtpoll_item_free(i
);
352 pa_rtpoll_item_set_userdata(i
, pd
);
353 pa_rtpoll_item_set_work_callback(i
, rtpoll_work_cb
);
358 static int prepare_mixer(snd_mixer_t
*mixer
, const char *dev
) {
364 if ((err
= snd_mixer_attach(mixer
, dev
)) < 0) {
365 pa_log_info("Unable to attach to mixer %s: %s", dev
, pa_alsa_strerror(err
));
369 if ((err
= snd_mixer_selem_register(mixer
, NULL
, NULL
)) < 0) {
370 pa_log_warn("Unable to register mixer: %s", pa_alsa_strerror(err
));
374 if ((err
= snd_mixer_load(mixer
)) < 0) {
375 pa_log_warn("Unable to load mixer: %s", pa_alsa_strerror(err
));
379 pa_log_info("Successfully attached to mixer '%s'", dev
);
383 snd_mixer_t
*pa_alsa_open_mixer_for_pcm(snd_pcm_t
*pcm
, char **ctl_device
) {
387 snd_pcm_info_t
* info
;
388 snd_pcm_info_alloca(&info
);
392 if ((err
= snd_mixer_open(&m
, 0)) < 0) {
393 pa_log("Error opening mixer: %s", pa_alsa_strerror(err
));
397 /* First, try by name */
398 if ((dev
= snd_pcm_name(pcm
)))
399 if (prepare_mixer(m
, dev
) >= 0) {
401 *ctl_device
= pa_xstrdup(dev
);
406 /* Then, try by card index */
407 if (snd_pcm_info(pcm
, info
) >= 0) {
411 if ((card_idx
= snd_pcm_info_get_card(info
)) >= 0) {
413 md
= pa_sprintf_malloc("hw:%i", card_idx
);
415 if (!dev
|| !pa_streq(dev
, md
))
416 if (prepare_mixer(m
, md
) >= 0) {
434 static const snd_mixer_selem_channel_id_t alsa_channel_ids
[PA_CHANNEL_POSITION_MAX
] = {
435 [PA_CHANNEL_POSITION_MONO
] = SND_MIXER_SCHN_MONO
, /* The ALSA name is just an alias! */
437 [PA_CHANNEL_POSITION_FRONT_CENTER
] = SND_MIXER_SCHN_FRONT_CENTER
,
438 [PA_CHANNEL_POSITION_FRONT_LEFT
] = SND_MIXER_SCHN_FRONT_LEFT
,
439 [PA_CHANNEL_POSITION_FRONT_RIGHT
] = SND_MIXER_SCHN_FRONT_RIGHT
,
441 [PA_CHANNEL_POSITION_REAR_CENTER
] = SND_MIXER_SCHN_REAR_CENTER
,
442 [PA_CHANNEL_POSITION_REAR_LEFT
] = SND_MIXER_SCHN_REAR_LEFT
,
443 [PA_CHANNEL_POSITION_REAR_RIGHT
] = SND_MIXER_SCHN_REAR_RIGHT
,
445 [PA_CHANNEL_POSITION_LFE
] = SND_MIXER_SCHN_WOOFER
,
447 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
448 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
450 [PA_CHANNEL_POSITION_SIDE_LEFT
] = SND_MIXER_SCHN_SIDE_LEFT
,
451 [PA_CHANNEL_POSITION_SIDE_RIGHT
] = SND_MIXER_SCHN_SIDE_RIGHT
,
453 [PA_CHANNEL_POSITION_AUX0
] = SND_MIXER_SCHN_UNKNOWN
,
454 [PA_CHANNEL_POSITION_AUX1
] = SND_MIXER_SCHN_UNKNOWN
,
455 [PA_CHANNEL_POSITION_AUX2
] = SND_MIXER_SCHN_UNKNOWN
,
456 [PA_CHANNEL_POSITION_AUX3
] = SND_MIXER_SCHN_UNKNOWN
,
457 [PA_CHANNEL_POSITION_AUX4
] = SND_MIXER_SCHN_UNKNOWN
,
458 [PA_CHANNEL_POSITION_AUX5
] = SND_MIXER_SCHN_UNKNOWN
,
459 [PA_CHANNEL_POSITION_AUX6
] = SND_MIXER_SCHN_UNKNOWN
,
460 [PA_CHANNEL_POSITION_AUX7
] = SND_MIXER_SCHN_UNKNOWN
,
461 [PA_CHANNEL_POSITION_AUX8
] = SND_MIXER_SCHN_UNKNOWN
,
462 [PA_CHANNEL_POSITION_AUX9
] = SND_MIXER_SCHN_UNKNOWN
,
463 [PA_CHANNEL_POSITION_AUX10
] = SND_MIXER_SCHN_UNKNOWN
,
464 [PA_CHANNEL_POSITION_AUX11
] = SND_MIXER_SCHN_UNKNOWN
,
465 [PA_CHANNEL_POSITION_AUX12
] = SND_MIXER_SCHN_UNKNOWN
,
466 [PA_CHANNEL_POSITION_AUX13
] = SND_MIXER_SCHN_UNKNOWN
,
467 [PA_CHANNEL_POSITION_AUX14
] = SND_MIXER_SCHN_UNKNOWN
,
468 [PA_CHANNEL_POSITION_AUX15
] = SND_MIXER_SCHN_UNKNOWN
,
469 [PA_CHANNEL_POSITION_AUX16
] = SND_MIXER_SCHN_UNKNOWN
,
470 [PA_CHANNEL_POSITION_AUX17
] = SND_MIXER_SCHN_UNKNOWN
,
471 [PA_CHANNEL_POSITION_AUX18
] = SND_MIXER_SCHN_UNKNOWN
,
472 [PA_CHANNEL_POSITION_AUX19
] = SND_MIXER_SCHN_UNKNOWN
,
473 [PA_CHANNEL_POSITION_AUX20
] = SND_MIXER_SCHN_UNKNOWN
,
474 [PA_CHANNEL_POSITION_AUX21
] = SND_MIXER_SCHN_UNKNOWN
,
475 [PA_CHANNEL_POSITION_AUX22
] = SND_MIXER_SCHN_UNKNOWN
,
476 [PA_CHANNEL_POSITION_AUX23
] = SND_MIXER_SCHN_UNKNOWN
,
477 [PA_CHANNEL_POSITION_AUX24
] = SND_MIXER_SCHN_UNKNOWN
,
478 [PA_CHANNEL_POSITION_AUX25
] = SND_MIXER_SCHN_UNKNOWN
,
479 [PA_CHANNEL_POSITION_AUX26
] = SND_MIXER_SCHN_UNKNOWN
,
480 [PA_CHANNEL_POSITION_AUX27
] = SND_MIXER_SCHN_UNKNOWN
,
481 [PA_CHANNEL_POSITION_AUX28
] = SND_MIXER_SCHN_UNKNOWN
,
482 [PA_CHANNEL_POSITION_AUX29
] = SND_MIXER_SCHN_UNKNOWN
,
483 [PA_CHANNEL_POSITION_AUX30
] = SND_MIXER_SCHN_UNKNOWN
,
484 [PA_CHANNEL_POSITION_AUX31
] = SND_MIXER_SCHN_UNKNOWN
,
486 [PA_CHANNEL_POSITION_TOP_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
488 [PA_CHANNEL_POSITION_TOP_FRONT_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
489 [PA_CHANNEL_POSITION_TOP_FRONT_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
490 [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
,
492 [PA_CHANNEL_POSITION_TOP_REAR_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
493 [PA_CHANNEL_POSITION_TOP_REAR_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
494 [PA_CHANNEL_POSITION_TOP_REAR_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
497 static void setting_free(pa_alsa_setting
*s
) {
501 pa_idxset_free(s
->options
, NULL
, NULL
);
504 pa_xfree(s
->description
);
508 static void option_free(pa_alsa_option
*o
) {
511 pa_xfree(o
->alsa_name
);
513 pa_xfree(o
->description
);
517 static void decibel_fix_free(pa_alsa_decibel_fix
*db_fix
) {
520 pa_xfree(db_fix
->name
);
521 pa_xfree(db_fix
->db_values
);
526 static void element_free(pa_alsa_element
*e
) {
530 while ((o
= e
->options
)) {
531 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
536 decibel_fix_free(e
->db_fix
);
538 pa_xfree(e
->alsa_name
);
542 void pa_alsa_path_free(pa_alsa_path
*p
) {
548 while ((e
= p
->elements
)) {
549 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
553 while ((s
= p
->settings
)) {
554 PA_LLIST_REMOVE(pa_alsa_setting
, p
->settings
, s
);
559 pa_xfree(p
->description
);
563 void pa_alsa_path_set_free(pa_alsa_path_set
*ps
) {
567 while ((p
= ps
->paths
)) {
568 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
569 pa_alsa_path_free(p
);
575 static long to_alsa_dB(pa_volume_t v
) {
576 return (long) (pa_sw_volume_to_dB(v
) * 100.0);
579 static pa_volume_t
from_alsa_dB(long v
) {
580 return pa_sw_volume_from_dB((double) v
/ 100.0);
583 static long to_alsa_volume(pa_volume_t v
, long min
, long max
) {
586 w
= (long) round(((double) v
* (double) (max
- min
)) / PA_VOLUME_NORM
) + min
;
587 return PA_CLAMP_UNLIKELY(w
, min
, max
);
590 static pa_volume_t
from_alsa_volume(long v
, long min
, long max
) {
591 return (pa_volume_t
) round(((double) (v
- min
) * PA_VOLUME_NORM
) / (double) (max
- min
));
594 #define SELEM_INIT(sid, name) \
596 snd_mixer_selem_id_alloca(&(sid)); \
597 snd_mixer_selem_id_set_name((sid), (name)); \
598 snd_mixer_selem_id_set_index((sid), 0); \
601 static int element_get_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
602 snd_mixer_selem_id_t
*sid
;
603 snd_mixer_elem_t
*me
;
604 snd_mixer_selem_channel_id_t c
;
605 pa_channel_position_mask_t mask
= 0;
613 SELEM_INIT(sid
, e
->alsa_name
);
614 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
615 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
619 pa_cvolume_mute(v
, cm
->channels
);
621 /* We take the highest volume of all channels that match */
623 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
630 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
631 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
633 if ((r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
)) >= 0) {
634 /* If the channel volume is outside the limits set
635 * by the dB fix, we clamp the hw volume to be
636 * within the limits. */
637 if (value
< e
->db_fix
->min_step
) {
638 value
= e
->db_fix
->min_step
;
639 snd_mixer_selem_set_playback_volume(me
, c
, value
);
640 pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
641 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
642 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
643 } else if (value
> e
->db_fix
->max_step
) {
644 value
= e
->db_fix
->max_step
;
645 snd_mixer_selem_set_playback_volume(me
, c
, value
);
646 pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
647 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
648 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
651 /* Volume step -> dB value conversion. */
652 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
655 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
659 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
661 if ((r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
)) >= 0) {
662 /* If the channel volume is outside the limits set
663 * by the dB fix, we clamp the hw volume to be
664 * within the limits. */
665 if (value
< e
->db_fix
->min_step
) {
666 value
= e
->db_fix
->min_step
;
667 snd_mixer_selem_set_capture_volume(me
, c
, value
);
668 pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
669 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
670 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
671 } else if (value
> e
->db_fix
->max_step
) {
672 value
= e
->db_fix
->max_step
;
673 snd_mixer_selem_set_capture_volume(me
, c
, value
);
674 pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
675 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
676 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
679 /* Volume step -> dB value conversion. */
680 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
683 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
691 #ifdef HAVE_VALGRIND_MEMCHECK_H
692 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
695 f
= from_alsa_dB(value
);
700 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
701 if (snd_mixer_selem_has_playback_channel(me
, c
))
702 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
706 if (snd_mixer_selem_has_capture_channel(me
, c
))
707 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
715 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
718 for (k
= 0; k
< cm
->channels
; k
++)
719 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
720 if (v
->values
[k
] < f
)
723 mask
|= e
->masks
[c
][e
->n_channels
-1];
726 for (k
= 0; k
< cm
->channels
; k
++)
727 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
728 v
->values
[k
] = PA_VOLUME_NORM
;
733 int pa_alsa_path_get_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
744 pa_cvolume_reset(v
, cm
->channels
);
746 PA_LLIST_FOREACH(e
, p
->elements
) {
749 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
752 pa_assert(!p
->has_dB
|| e
->has_dB
);
754 if (element_get_volume(e
, m
, cm
, &ev
) < 0)
757 /* If we have no dB information all we can do is take the first element and leave */
763 pa_sw_cvolume_multiply(v
, v
, &ev
);
769 static int element_get_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t
*b
) {
770 snd_mixer_selem_id_t
*sid
;
771 snd_mixer_elem_t
*me
;
772 snd_mixer_selem_channel_id_t c
;
778 SELEM_INIT(sid
, e
->alsa_name
);
779 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
780 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
784 /* We return muted if at least one channel is muted */
786 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
790 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
791 if (snd_mixer_selem_has_playback_channel(me
, c
))
792 r
= snd_mixer_selem_get_playback_switch(me
, c
, &value
);
796 if (snd_mixer_selem_has_capture_channel(me
, c
))
797 r
= snd_mixer_selem_get_capture_switch(me
, c
, &value
);
815 int pa_alsa_path_get_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t
*muted
) {
825 PA_LLIST_FOREACH(e
, p
->elements
) {
828 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
831 if (element_get_switch(e
, m
, &b
) < 0)
844 /* Finds the closest item in db_fix->db_values and returns the corresponding
845 * step. *db_value is replaced with the value from the db_values table.
846 * Rounding is done based on the rounding parameter: -1 means rounding down and
847 * +1 means rounding up. */
848 static long decibel_fix_get_step(pa_alsa_decibel_fix
*db_fix
, long *db_value
, int rounding
) {
854 pa_assert(rounding
!= 0);
856 max_i
= db_fix
->max_step
- db_fix
->min_step
;
859 for (i
= 0; i
< max_i
; i
++) {
860 if (db_fix
->db_values
[i
] >= *db_value
)
864 for (i
= 0; i
< max_i
; i
++) {
865 if (db_fix
->db_values
[i
+ 1] > *db_value
)
870 *db_value
= db_fix
->db_values
[i
];
872 return i
+ db_fix
->min_step
;
875 /* Alsa lib documentation says for snd_mixer_selem_set_playback_dB() direction argument,
876 * that "-1 = accurate or first below, 0 = accurate, 1 = accurate or first above".
877 * But even with accurate nearest dB volume step is not selected, so that is why we need
878 * this function. Returns 0 and nearest selectable volume in *value_dB on success or
879 * negative error code if fails. */
880 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
) {
890 if (d
== PA_ALSA_DIRECTION_OUTPUT
) {
891 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, *value_dB
, +1, &alsa_val
)) >= 0)
892 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value_high
);
897 if (value_high
== *value_dB
)
900 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, *value_dB
, -1, &alsa_val
)) >= 0)
901 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value_low
);
903 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, *value_dB
, +1, &alsa_val
)) >= 0)
904 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value_high
);
909 if (value_high
== *value_dB
)
912 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, *value_dB
, -1, &alsa_val
)) >= 0)
913 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value_low
);
919 if (labs(value_high
- *value_dB
) < labs(value_low
- *value_dB
))
920 *value_dB
= value_high
;
922 *value_dB
= value_low
;
927 static int element_set_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
, pa_bool_t deferred_volume
, pa_bool_t write_to_hw
) {
929 snd_mixer_selem_id_t
*sid
;
931 snd_mixer_elem_t
*me
;
932 snd_mixer_selem_channel_id_t c
;
933 pa_channel_position_mask_t mask
= 0;
940 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
942 SELEM_INIT(sid
, e
->alsa_name
);
943 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
944 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
948 pa_cvolume_mute(&rv
, cm
->channels
);
950 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
952 pa_volume_t f
= PA_VOLUME_MUTED
;
953 pa_bool_t found
= FALSE
;
955 for (k
= 0; k
< cm
->channels
; k
++)
956 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
])) {
958 if (v
->values
[k
] > f
)
963 /* Hmm, so this channel does not exist in the volume
964 * struct, so let's bind it to the overall max of the
966 f
= pa_cvolume_max(v
);
970 long value
= to_alsa_dB(f
);
973 if (e
->volume_limit
>= 0 && value
> (e
->max_dB
* 100))
974 value
= e
->max_dB
* 100;
976 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
977 /* If we call set_playback_volume() without checking first
978 * if the channel is available, ALSA behaves very
979 * strangely and doesn't fail the call */
980 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
984 r
= snd_mixer_selem_set_playback_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
986 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
992 if (deferred_volume
) {
993 if ((r
= element_get_nearest_alsa_dB(me
, c
, PA_ALSA_DIRECTION_OUTPUT
, &value
)) >= 0)
994 r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, 0);
996 if ((r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, rounding
)) >= 0)
997 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
1001 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
1002 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value
);
1008 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
1012 r
= snd_mixer_selem_set_capture_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
1014 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
1020 if (deferred_volume
) {
1021 if ((r
= element_get_nearest_alsa_dB(me
, c
, PA_ALSA_DIRECTION_INPUT
, &value
)) >= 0)
1022 r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, 0);
1024 if ((r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, rounding
)) >= 0)
1025 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
1029 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
1030 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value
);
1040 #ifdef HAVE_VALGRIND_MEMCHECK_H
1041 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
1044 f
= from_alsa_dB(value
);
1049 value
= to_alsa_volume(f
, e
->min_volume
, e
->max_volume
);
1051 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1052 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
1053 if ((r
= snd_mixer_selem_set_playback_volume(me
, c
, value
)) >= 0)
1054 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
1058 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
1059 if ((r
= snd_mixer_selem_set_capture_volume(me
, c
, value
)) >= 0)
1060 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
1068 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
1071 for (k
= 0; k
< cm
->channels
; k
++)
1072 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
1073 if (rv
.values
[k
] < f
)
1076 mask
|= e
->masks
[c
][e
->n_channels
-1];
1079 for (k
= 0; k
< cm
->channels
; k
++)
1080 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
1081 rv
.values
[k
] = PA_VOLUME_NORM
;
1087 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 deferred_volume
, pa_bool_t write_to_hw
) {
1096 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
1101 rv
= *v
; /* Remaining adjustment */
1102 pa_cvolume_reset(v
, cm
->channels
); /* Adjustment done */
1104 PA_LLIST_FOREACH(e
, p
->elements
) {
1107 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
1110 pa_assert(!p
->has_dB
|| e
->has_dB
);
1113 if (element_set_volume(e
, m
, cm
, &ev
, deferred_volume
, write_to_hw
) < 0)
1121 pa_sw_cvolume_multiply(v
, v
, &ev
);
1122 pa_sw_cvolume_divide(&rv
, &rv
, &ev
);
1128 static int element_set_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t b
) {
1129 snd_mixer_elem_t
*me
;
1130 snd_mixer_selem_id_t
*sid
;
1136 SELEM_INIT(sid
, e
->alsa_name
);
1137 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1138 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1142 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1143 r
= snd_mixer_selem_set_playback_switch_all(me
, b
);
1145 r
= snd_mixer_selem_set_capture_switch_all(me
, b
);
1148 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1153 int pa_alsa_path_set_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t muted
) {
1162 PA_LLIST_FOREACH(e
, p
->elements
) {
1164 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
1167 if (element_set_switch(e
, m
, !muted
) < 0)
1174 /* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
1175 * function sets all channels of the volume element to e->min_volume, 0 dB or
1176 * e->constant_volume. */
1177 static int element_set_constant_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1178 snd_mixer_elem_t
*me
= NULL
;
1179 snd_mixer_selem_id_t
*sid
= NULL
;
1182 pa_bool_t volume_set
= FALSE
;
1187 SELEM_INIT(sid
, e
->alsa_name
);
1188 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1189 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1193 switch (e
->volume_use
) {
1194 case PA_ALSA_VOLUME_OFF
:
1195 volume
= e
->min_volume
;
1199 case PA_ALSA_VOLUME_ZERO
:
1203 volume
= decibel_fix_get_step(e
->db_fix
, &dB
, (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
? +1 : -1));
1208 case PA_ALSA_VOLUME_CONSTANT
:
1209 volume
= e
->constant_volume
;
1214 pa_assert_not_reached();
1218 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1219 r
= snd_mixer_selem_set_playback_volume_all(me
, volume
);
1221 r
= snd_mixer_selem_set_capture_volume_all(me
, volume
);
1223 pa_assert(e
->volume_use
== PA_ALSA_VOLUME_ZERO
);
1224 pa_assert(!e
->db_fix
);
1226 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1227 r
= snd_mixer_selem_set_playback_dB_all(me
, 0, +1);
1229 r
= snd_mixer_selem_set_capture_dB_all(me
, 0, -1);
1233 pa_log_warn("Failed to set volume of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1238 int pa_alsa_path_select(pa_alsa_path
*p
, snd_mixer_t
*m
) {
1245 pa_log_debug("Activating path %s", p
->name
);
1246 pa_alsa_path_dump(p
);
1248 PA_LLIST_FOREACH(e
, p
->elements
) {
1250 switch (e
->switch_use
) {
1251 case PA_ALSA_SWITCH_OFF
:
1252 r
= element_set_switch(e
, m
, FALSE
);
1255 case PA_ALSA_SWITCH_ON
:
1256 r
= element_set_switch(e
, m
, TRUE
);
1259 case PA_ALSA_SWITCH_MUTE
:
1260 case PA_ALSA_SWITCH_IGNORE
:
1261 case PA_ALSA_SWITCH_SELECT
:
1269 switch (e
->volume_use
) {
1270 case PA_ALSA_VOLUME_OFF
:
1271 case PA_ALSA_VOLUME_ZERO
:
1272 case PA_ALSA_VOLUME_CONSTANT
:
1273 r
= element_set_constant_volume(e
, m
);
1276 case PA_ALSA_VOLUME_MERGE
:
1277 case PA_ALSA_VOLUME_IGNORE
:
1289 static int check_required(pa_alsa_element
*e
, snd_mixer_elem_t
*me
) {
1290 pa_bool_t has_switch
;
1291 pa_bool_t has_enumeration
;
1292 pa_bool_t has_volume
;
1297 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1299 snd_mixer_selem_has_playback_switch(me
) ||
1300 (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
));
1303 snd_mixer_selem_has_capture_switch(me
) ||
1304 (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
));
1307 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1309 snd_mixer_selem_has_playback_volume(me
) ||
1310 (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
));
1313 snd_mixer_selem_has_capture_volume(me
) ||
1314 (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
));
1317 has_enumeration
= snd_mixer_selem_is_enumerated(me
);
1319 if ((e
->required
== PA_ALSA_REQUIRED_SWITCH
&& !has_switch
) ||
1320 (e
->required
== PA_ALSA_REQUIRED_VOLUME
&& !has_volume
) ||
1321 (e
->required
== PA_ALSA_REQUIRED_ENUMERATION
&& !has_enumeration
))
1324 if (e
->required
== PA_ALSA_REQUIRED_ANY
&& !(has_switch
|| has_volume
|| has_enumeration
))
1327 if ((e
->required_absent
== PA_ALSA_REQUIRED_SWITCH
&& has_switch
) ||
1328 (e
->required_absent
== PA_ALSA_REQUIRED_VOLUME
&& has_volume
) ||
1329 (e
->required_absent
== PA_ALSA_REQUIRED_ENUMERATION
&& has_enumeration
))
1332 if (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& (has_switch
|| has_volume
|| has_enumeration
))
1335 if (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) {
1336 switch (e
->required_any
) {
1337 case PA_ALSA_REQUIRED_VOLUME
:
1338 e
->path
->req_any_present
|= (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
);
1340 case PA_ALSA_REQUIRED_SWITCH
:
1341 e
->path
->req_any_present
|= (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
);
1343 case PA_ALSA_REQUIRED_ENUMERATION
:
1344 e
->path
->req_any_present
|= (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1346 case PA_ALSA_REQUIRED_ANY
:
1347 e
->path
->req_any_present
|=
1348 (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) ||
1349 (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) ||
1350 (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1353 pa_assert_not_reached();
1357 if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1359 PA_LLIST_FOREACH(o
, e
->options
) {
1360 e
->path
->req_any_present
|= (o
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) &&
1362 if (o
->required
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
< 0)
1364 if (o
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
>= 0)
1372 static int element_probe(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1373 snd_mixer_selem_id_t
*sid
;
1374 snd_mixer_elem_t
*me
;
1380 SELEM_INIT(sid
, e
->alsa_name
);
1382 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1384 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1387 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1388 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1389 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1394 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1395 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1397 if (!snd_mixer_selem_has_playback_switch(me
)) {
1398 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1399 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1401 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1406 if (!snd_mixer_selem_has_capture_switch(me
)) {
1407 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1408 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1410 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1414 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1415 e
->direction_try_other
= FALSE
;
1418 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1420 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1422 if (!snd_mixer_selem_has_playback_volume(me
)) {
1423 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1424 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1426 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1431 if (!snd_mixer_selem_has_capture_volume(me
)) {
1432 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1433 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1435 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1439 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1440 long min_dB
= 0, max_dB
= 0;
1443 e
->direction_try_other
= FALSE
;
1445 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1446 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1448 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1451 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1455 if (e
->min_volume
>= e
->max_volume
) {
1456 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
);
1457 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1459 } else if (e
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&&
1460 (e
->min_volume
> e
->constant_volume
|| e
->max_volume
< e
->constant_volume
)) {
1461 pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
1462 e
->constant_volume
, e
->alsa_name
, e
->min_volume
, e
->max_volume
);
1463 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1467 pa_channel_position_t p
;
1470 ((e
->min_volume
> e
->db_fix
->min_step
) ||
1471 (e
->max_volume
< e
->db_fix
->max_step
))) {
1472 pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1473 "real hardware range (%li-%li). Disabling the decibel fix.", e
->alsa_name
,
1474 e
->db_fix
->min_step
, e
->db_fix
->max_step
,
1475 e
->min_volume
, e
->max_volume
);
1477 decibel_fix_free(e
->db_fix
);
1483 e
->min_volume
= e
->db_fix
->min_step
;
1484 e
->max_volume
= e
->db_fix
->max_step
;
1485 min_dB
= e
->db_fix
->db_values
[0];
1486 max_dB
= e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
];
1487 } else if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1488 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1490 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1492 /* Check that the kernel driver returns consistent limits with
1493 * both _get_*_dB_range() and _ask_*_vol_dB(). */
1494 if (e
->has_dB
&& !e
->db_fix
) {
1495 long min_dB_checked
= 0;
1496 long max_dB_checked
= 0;
1498 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1499 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1501 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1504 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->min_volume
);
1508 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1509 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1511 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1514 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->max_volume
);
1518 if (min_dB
!= min_dB_checked
|| max_dB
!= max_dB_checked
) {
1519 pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
1520 "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
1521 "%0.2f dB at level %li.",
1523 min_dB
/ 100.0, max_dB
/ 100.0,
1524 min_dB_checked
/ 100.0, e
->min_volume
, max_dB_checked
/ 100.0, e
->max_volume
);
1530 #ifdef HAVE_VALGRIND_MEMCHECK_H
1531 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1532 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1535 e
->min_dB
= ((double) min_dB
) / 100.0;
1536 e
->max_dB
= ((double) max_dB
) / 100.0;
1538 if (min_dB
>= max_dB
) {
1539 pa_assert(!e
->db_fix
);
1540 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
);
1545 if (e
->volume_limit
>= 0) {
1546 if (e
->volume_limit
<= e
->min_volume
|| e
->volume_limit
> e
->max_volume
)
1547 pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1548 "%li-%li. The volume limit is ignored.",
1549 e
->alsa_name
, e
->path
->name
, e
->volume_limit
, e
->min_volume
+ 1, e
->max_volume
);
1552 e
->max_volume
= e
->volume_limit
;
1556 e
->db_fix
->max_step
= e
->max_volume
;
1557 e
->max_dB
= ((double) e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
]) / 100.0;
1560 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1561 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB
);
1563 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB
);
1566 pa_log_warn("Failed to get dB value of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1569 e
->max_dB
= ((double) max_dB
) / 100.0;
1575 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1576 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1578 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1583 if (!e
->override_map
) {
1584 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1585 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1588 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1591 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1594 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1597 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1599 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1602 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1603 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1605 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1608 if (e
->n_channels
<= 0) {
1609 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1613 if (e
->n_channels
> 2) {
1614 /* FIXME: In some places code like this is used:
1616 * e->masks[alsa_channel_ids[p]][e->n_channels-1]
1618 * The definition of e->masks is
1620 * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST][2];
1622 * Since the array size is fixed at 2, we obviously
1623 * don't support elements with more than two
1625 pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e
->alsa_name
, e
->n_channels
);
1629 if (!e
->override_map
) {
1630 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1631 pa_bool_t has_channel
;
1633 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1636 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1637 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1639 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1641 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1646 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1647 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1650 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1658 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1661 PA_LLIST_FOREACH(o
, e
->options
)
1662 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1663 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1667 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1668 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1672 PA_LLIST_FOREACH(o
, e
->options
) {
1675 for (i
= 0; i
< n
; i
++) {
1678 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1681 if (!pa_streq(buf
, o
->alsa_name
))
1689 if (check_required(e
, me
) < 0)
1695 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, pa_bool_t prefixed
) {
1702 if (!pa_startswith(section
, "Element "))
1708 /* This is not an element section, but an enum section? */
1709 if (strchr(section
, ':'))
1712 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1713 return p
->last_element
;
1715 PA_LLIST_FOREACH(e
, p
->elements
)
1716 if (pa_streq(e
->alsa_name
, section
))
1719 e
= pa_xnew0(pa_alsa_element
, 1);
1721 e
->alsa_name
= pa_xstrdup(section
);
1722 e
->direction
= p
->direction
;
1723 e
->volume_limit
= -1;
1725 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1728 p
->last_element
= e
;
1732 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1738 if (!pa_startswith(section
, "Option "))
1743 /* This is not an enum section, but an element section? */
1744 if (!(on
= strchr(section
, ':')))
1747 en
= pa_xstrndup(section
, on
- section
);
1750 if (p
->last_option
&&
1751 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1752 pa_streq(p
->last_option
->alsa_name
, on
)) {
1754 return p
->last_option
;
1757 pa_assert_se(e
= element_get(p
, en
, FALSE
));
1760 PA_LLIST_FOREACH(o
, e
->options
)
1761 if (pa_streq(o
->alsa_name
, on
))
1764 o
= pa_xnew0(pa_alsa_option
, 1);
1766 o
->alsa_name
= pa_xstrdup(on
);
1769 if (p
->last_option
&& p
->last_option
->element
== e
)
1770 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1772 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1779 static int element_parse_switch(
1780 const char *filename
,
1782 const char *section
,
1788 pa_alsa_path
*p
= userdata
;
1793 if (!(e
= element_get(p
, section
, TRUE
))) {
1794 pa_log("[%s:%u] Switch makes no sense in '%s'", filename
, line
, section
);
1798 if (pa_streq(rvalue
, "ignore"))
1799 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1800 else if (pa_streq(rvalue
, "mute"))
1801 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1802 else if (pa_streq(rvalue
, "off"))
1803 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1804 else if (pa_streq(rvalue
, "on"))
1805 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1806 else if (pa_streq(rvalue
, "select"))
1807 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1809 pa_log("[%s:%u] Switch invalid of '%s'", filename
, line
, section
);
1816 static int element_parse_volume(
1817 const char *filename
,
1819 const char *section
,
1825 pa_alsa_path
*p
= userdata
;
1830 if (!(e
= element_get(p
, section
, TRUE
))) {
1831 pa_log("[%s:%u] Volume makes no sense in '%s'", filename
, line
, section
);
1835 if (pa_streq(rvalue
, "ignore"))
1836 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1837 else if (pa_streq(rvalue
, "merge"))
1838 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1839 else if (pa_streq(rvalue
, "off"))
1840 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1841 else if (pa_streq(rvalue
, "zero"))
1842 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1846 if (pa_atou(rvalue
, &constant
) >= 0) {
1847 e
->volume_use
= PA_ALSA_VOLUME_CONSTANT
;
1848 e
->constant_volume
= constant
;
1850 pa_log("[%s:%u] Volume invalid of '%s'", filename
, line
, section
);
1858 static int element_parse_enumeration(
1859 const char *filename
,
1861 const char *section
,
1867 pa_alsa_path
*p
= userdata
;
1872 if (!(e
= element_get(p
, section
, TRUE
))) {
1873 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename
, line
, section
);
1877 if (pa_streq(rvalue
, "ignore"))
1878 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1879 else if (pa_streq(rvalue
, "select"))
1880 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1882 pa_log("[%s:%u] Enumeration invalid of '%s'", filename
, line
, section
);
1889 static int option_parse_priority(
1890 const char *filename
,
1892 const char *section
,
1898 pa_alsa_path
*p
= userdata
;
1904 if (!(o
= option_get(p
, section
))) {
1905 pa_log("[%s:%u] Priority makes no sense in '%s'", filename
, line
, section
);
1909 if (pa_atou(rvalue
, &prio
) < 0) {
1910 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
1918 static int option_parse_name(
1919 const char *filename
,
1921 const char *section
,
1927 pa_alsa_path
*p
= userdata
;
1932 if (!(o
= option_get(p
, section
))) {
1933 pa_log("[%s:%u] Name makes no sense in '%s'", filename
, line
, section
);
1938 o
->name
= pa_xstrdup(rvalue
);
1943 static int element_parse_required(
1944 const char *filename
,
1946 const char *section
,
1952 pa_alsa_path
*p
= userdata
;
1955 pa_alsa_required_t req
;
1959 e
= element_get(p
, section
, TRUE
);
1960 o
= option_get(p
, section
);
1962 pa_log("[%s:%u] Required makes no sense in '%s'", filename
, line
, section
);
1966 if (pa_streq(rvalue
, "ignore"))
1967 req
= PA_ALSA_REQUIRED_IGNORE
;
1968 else if (pa_streq(rvalue
, "switch") && e
)
1969 req
= PA_ALSA_REQUIRED_SWITCH
;
1970 else if (pa_streq(rvalue
, "volume") && e
)
1971 req
= PA_ALSA_REQUIRED_VOLUME
;
1972 else if (pa_streq(rvalue
, "enumeration"))
1973 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1974 else if (pa_streq(rvalue
, "any"))
1975 req
= PA_ALSA_REQUIRED_ANY
;
1977 pa_log("[%s:%u] Required invalid of '%s'", filename
, line
, section
);
1981 if (pa_streq(lvalue
, "required-absent")) {
1983 e
->required_absent
= req
;
1985 o
->required_absent
= req
;
1987 else if (pa_streq(lvalue
, "required-any")) {
1989 e
->required_any
= req
;
1990 e
->path
->has_req_any
= TRUE
;
1993 o
->required_any
= req
;
1994 o
->element
->path
->has_req_any
= TRUE
;
2007 static int element_parse_direction(
2008 const char *filename
,
2010 const char *section
,
2016 pa_alsa_path
*p
= userdata
;
2021 if (!(e
= element_get(p
, section
, TRUE
))) {
2022 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
2026 if (pa_streq(rvalue
, "playback"))
2027 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
2028 else if (pa_streq(rvalue
, "capture"))
2029 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
2031 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
2038 static int element_parse_direction_try_other(
2039 const char *filename
,
2041 const char *section
,
2047 pa_alsa_path
*p
= userdata
;
2051 if (!(e
= element_get(p
, section
, TRUE
))) {
2052 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
2056 if ((yes
= pa_parse_boolean(rvalue
)) < 0) {
2057 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
2061 e
->direction_try_other
= !!yes
;
2065 static int element_parse_volume_limit(
2066 const char *filename
,
2068 const char *section
,
2074 pa_alsa_path
*p
= userdata
;
2078 if (!(e
= element_get(p
, section
, TRUE
))) {
2079 pa_log("[%s:%u] volume-limit makes no sense in '%s'", filename
, line
, section
);
2083 if (pa_atol(rvalue
, &volume_limit
) < 0 || volume_limit
< 0) {
2084 pa_log("[%s:%u] Invalid value for volume-limit", filename
, line
);
2088 e
->volume_limit
= volume_limit
;
2092 static pa_channel_position_mask_t
parse_mask(const char *m
) {
2093 pa_channel_position_mask_t v
;
2095 if (pa_streq(m
, "all-left"))
2096 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
2097 else if (pa_streq(m
, "all-right"))
2098 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
2099 else if (pa_streq(m
, "all-center"))
2100 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
2101 else if (pa_streq(m
, "all-front"))
2102 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
2103 else if (pa_streq(m
, "all-rear"))
2104 v
= PA_CHANNEL_POSITION_MASK_REAR
;
2105 else if (pa_streq(m
, "all-side"))
2106 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
2107 else if (pa_streq(m
, "all-top"))
2108 v
= PA_CHANNEL_POSITION_MASK_TOP
;
2109 else if (pa_streq(m
, "all-no-lfe"))
2110 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
2111 else if (pa_streq(m
, "all"))
2112 v
= PA_CHANNEL_POSITION_MASK_ALL
;
2114 pa_channel_position_t p
;
2116 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
2119 v
= PA_CHANNEL_POSITION_MASK(p
);
2125 static int element_parse_override_map(
2126 const char *filename
,
2128 const char *section
,
2134 pa_alsa_path
*p
= userdata
;
2136 const char *state
= NULL
;
2140 if (!(e
= element_get(p
, section
, TRUE
))) {
2141 pa_log("[%s:%u] Override map makes no sense in '%s'", filename
, line
, section
);
2145 while ((n
= pa_split(rvalue
, ",", &state
))) {
2146 pa_channel_position_mask_t m
;
2151 if ((m
= parse_mask(n
)) == 0) {
2152 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename
, line
, n
, section
);
2158 if (pa_streq(lvalue
, "override-map.1"))
2159 e
->masks
[i
++][0] = m
;
2161 e
->masks
[i
++][1] = m
;
2163 /* Later on we might add override-map.3 and so on here ... */
2168 e
->override_map
= TRUE
;
2173 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
2174 snd_mixer_selem_id_t
*sid
;
2175 snd_mixer_elem_t
*me
;
2181 SELEM_INIT(sid
, e
->alsa_name
);
2182 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2183 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2187 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
2189 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
2190 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
2192 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
2195 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2198 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
2200 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
2201 pa_log_warn("Failed to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2207 int pa_alsa_setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
2214 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
2215 element_set_option(o
->element
, m
, o
->alsa_idx
);
2220 static int option_verify(pa_alsa_option
*o
) {
2221 static const struct description_map well_known_descriptions
[] = {
2222 { "input", N_("Input") },
2223 { "input-docking", N_("Docking Station Input") },
2224 { "input-docking-microphone", N_("Docking Station Microphone") },
2225 { "input-docking-linein", N_("Docking Station Line-In") },
2226 { "input-linein", N_("Line-In") },
2227 { "input-microphone", N_("Microphone") },
2228 { "input-microphone-front", N_("Front Microphone") },
2229 { "input-microphone-rear", N_("Rear Microphone") },
2230 { "input-microphone-external", N_("External Microphone") },
2231 { "input-microphone-internal", N_("Internal Microphone") },
2232 { "input-radio", N_("Radio") },
2233 { "input-video", N_("Video") },
2234 { "input-agc-on", N_("Automatic Gain Control") },
2235 { "input-agc-off", N_("No Automatic Gain Control") },
2236 { "input-boost-on", N_("Boost") },
2237 { "input-boost-off", N_("No Boost") },
2238 { "output-amplifier-on", N_("Amplifier") },
2239 { "output-amplifier-off", N_("No Amplifier") },
2240 { "output-bass-boost-on", N_("Bass Boost") },
2241 { "output-bass-boost-off", N_("No Bass Boost") },
2242 { "output-speaker", N_("Speaker") },
2243 { "output-headphones", N_("Headphones") }
2249 pa_log("No name set for option %s", o
->alsa_name
);
2253 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
2254 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
2255 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
2259 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
2260 !pa_streq(o
->alsa_name
, "on") &&
2261 !pa_streq(o
->alsa_name
, "off")) {
2262 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
2266 if (!o
->description
)
2267 o
->description
= pa_xstrdup(lookup_description(o
->name
,
2268 well_known_descriptions
,
2269 PA_ELEMENTSOF(well_known_descriptions
)));
2270 if (!o
->description
)
2271 o
->description
= pa_xstrdup(o
->name
);
2276 static int element_verify(pa_alsa_element
*e
) {
2281 // 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);
2282 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
2283 (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required_any
== e
->required_absent
) ||
2284 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) ||
2285 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
2286 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
2290 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
2291 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
2295 PA_LLIST_FOREACH(o
, e
->options
)
2296 if (option_verify(o
) < 0)
2302 static int path_verify(pa_alsa_path
*p
) {
2303 static const struct description_map well_known_descriptions
[] = {
2304 { "analog-input", N_("Analog Input") },
2305 { "analog-input-microphone", N_("Analog Microphone") },
2306 { "analog-input-microphone-front", N_("Front Microphone") },
2307 { "analog-input-microphone-rear", N_("Rear Microphone") },
2308 { "analog-input-microphone-dock", N_("Docking Station Microphone") },
2309 { "analog-input-microphone-internal", N_("Internal Microphone") },
2310 { "analog-input-linein", N_("Analog Line-In") },
2311 { "analog-input-radio", N_("Analog Radio") },
2312 { "analog-input-video", N_("Analog Video") },
2313 { "analog-output", N_("Analog Output") },
2314 { "analog-output-headphones", N_("Analog Headphones") },
2315 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
2316 { "analog-output-mono", N_("Analog Mono Output") },
2317 { "analog-output-speaker", N_("Analog Speakers") },
2318 { "iec958-stereo-output", N_("Digital Output (IEC958)") },
2319 { "iec958-passthrough-output", N_("Digital Passthrough (IEC958)") }
2326 PA_LLIST_FOREACH(e
, p
->elements
)
2327 if (element_verify(e
) < 0)
2330 if (!p
->description
)
2331 p
->description
= pa_xstrdup(lookup_description(p
->name
,
2332 well_known_descriptions
,
2333 PA_ELEMENTSOF(well_known_descriptions
)));
2335 if (!p
->description
)
2336 p
->description
= pa_xstrdup(p
->name
);
2341 static const char *get_default_paths_dir(void) {
2342 if (pa_run_from_build_tree())
2343 return PA_BUILDDIR
"/modules/alsa/mixer/paths/";
2345 return PA_ALSA_PATHS_DIR
;
2348 pa_alsa_path
* pa_alsa_path_new(const char *paths_dir
, const char *fname
, pa_alsa_direction_t direction
) {
2354 pa_config_item items
[] = {
2356 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
2357 { "description", pa_config_parse_string
, NULL
, "General" },
2358 { "name", pa_config_parse_string
, NULL
, "General" },
2361 { "priority", option_parse_priority
, NULL
, NULL
},
2362 { "name", option_parse_name
, NULL
, NULL
},
2365 { "switch", element_parse_switch
, NULL
, NULL
},
2366 { "volume", element_parse_volume
, NULL
, NULL
},
2367 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
2368 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
2369 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
2370 /* ... later on we might add override-map.3 and so on here ... */
2371 { "required", element_parse_required
, NULL
, NULL
},
2372 { "required-any", element_parse_required
, NULL
, NULL
},
2373 { "required-absent", element_parse_required
, NULL
, NULL
},
2374 { "direction", element_parse_direction
, NULL
, NULL
},
2375 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2376 { "volume-limit", element_parse_volume_limit
, NULL
, NULL
},
2377 { NULL
, NULL
, NULL
, NULL
}
2382 p
= pa_xnew0(pa_alsa_path
, 1);
2383 n
= pa_path_get_filename(fname
);
2384 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2385 p
->direction
= direction
;
2387 items
[0].data
= &p
->priority
;
2388 items
[1].data
= &p
->description
;
2389 items
[2].data
= &p
->name
;
2392 paths_dir
= get_default_paths_dir();
2394 fn
= pa_maybe_prefix_path(fname
, paths_dir
);
2396 r
= pa_config_parse(fn
, NULL
, items
, p
);
2402 if (path_verify(p
) < 0)
2408 pa_alsa_path_free(p
);
2412 pa_alsa_path
*pa_alsa_path_synthesize(const char *element
, pa_alsa_direction_t direction
) {
2418 p
= pa_xnew0(pa_alsa_path
, 1);
2419 p
->name
= pa_xstrdup(element
);
2420 p
->direction
= direction
;
2422 e
= pa_xnew0(pa_alsa_element
, 1);
2424 e
->alsa_name
= pa_xstrdup(element
);
2425 e
->direction
= direction
;
2426 e
->volume_limit
= -1;
2428 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2429 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2431 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2432 p
->last_element
= e
;
2436 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2437 pa_alsa_option
*o
, *n
;
2441 for (o
= e
->options
; o
; o
= n
) {
2444 if (o
->alsa_idx
< 0) {
2445 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2451 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2452 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2453 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2456 static void path_drop_unsupported(pa_alsa_path
*p
) {
2457 pa_alsa_element
*e
, *n
;
2461 for (e
= p
->elements
; e
; e
= n
) {
2464 if (!element_drop_unsupported(e
)) {
2465 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2471 static void path_make_options_unique(pa_alsa_path
*p
) {
2473 pa_alsa_option
*o
, *u
;
2475 PA_LLIST_FOREACH(e
, p
->elements
) {
2476 PA_LLIST_FOREACH(o
, e
->options
) {
2480 for (u
= o
->next
; u
; u
= u
->next
)
2481 if (pa_streq(u
->name
, o
->name
))
2487 m
= pa_xstrdup(o
->name
);
2489 /* OK, this name is not unique, hence let's rename */
2490 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2493 if (!pa_streq(u
->name
, m
))
2496 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2500 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2501 pa_xfree(u
->description
);
2502 u
->description
= nd
;
2512 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2515 for (; e
; e
= e
->next
)
2516 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2517 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2523 for (o
= e
->options
; o
; o
= o
->next
) {
2527 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2528 s
->options
= pa_idxset_copy(template->options
);
2529 s
->name
= pa_sprintf_malloc(_("%s+%s"), template->name
, o
->name
);
2531 (template->description
[0] && o
->description
[0])
2532 ? pa_sprintf_malloc(_("%s / %s"), template->description
, o
->description
)
2533 : (template->description
[0]
2534 ? pa_xstrdup(template->description
)
2535 : pa_xstrdup(o
->description
));
2537 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2539 s
= pa_xnew0(pa_alsa_setting
, 1);
2540 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2541 s
->name
= pa_xstrdup(o
->name
);
2542 s
->description
= pa_xstrdup(o
->description
);
2543 s
->priority
= o
->priority
;
2546 pa_idxset_put(s
->options
, o
, NULL
);
2548 if (element_create_settings(e
->next
, s
))
2549 /* This is not a leaf, so let's get rid of it */
2552 /* This is a leaf, so let's add it */
2553 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2555 e
->path
->last_setting
= s
;
2562 static void path_create_settings(pa_alsa_path
*p
) {
2565 element_create_settings(p
->elements
, NULL
);
2568 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2570 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2571 pa_channel_position_t t
;
2572 pa_channel_position_mask_t path_volume_channels
= 0;
2583 pa_log_debug("Probing path '%s'", p
->name
);
2585 PA_LLIST_FOREACH(e
, p
->elements
) {
2586 if (element_probe(e
, m
) < 0) {
2587 p
->supported
= FALSE
;
2588 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2591 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
);
2596 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2598 if (!p
->has_volume
) {
2599 p
->min_volume
= e
->min_volume
;
2600 p
->max_volume
= e
->max_volume
;
2604 if (!p
->has_volume
) {
2605 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2606 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2607 min_dB
[t
] = e
->min_dB
;
2608 max_dB
[t
] = e
->max_dB
;
2609 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2616 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2617 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2618 min_dB
[t
] += e
->min_dB
;
2619 max_dB
[t
] += e
->max_dB
;
2620 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2623 /* Hmm, there's another element before us
2624 * which cannot do dB volumes, so we we need
2625 * to 'neutralize' this slider */
2626 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2627 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2630 } else if (p
->has_volume
) {
2631 /* We can't use this volume, so let's ignore it */
2632 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2633 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2635 p
->has_volume
= TRUE
;
2638 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2642 if (p
->has_req_any
&& !p
->req_any_present
) {
2643 p
->supported
= FALSE
;
2644 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2648 path_drop_unsupported(p
);
2649 path_make_options_unique(p
);
2650 path_create_settings(p
);
2652 p
->supported
= TRUE
;
2655 p
->min_dB
= INFINITY
;
2656 p
->max_dB
= -INFINITY
;
2658 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2659 if (path_volume_channels
& PA_CHANNEL_POSITION_MASK(t
)) {
2660 if (p
->min_dB
> min_dB
[t
])
2661 p
->min_dB
= min_dB
[t
];
2663 if (p
->max_dB
< max_dB
[t
])
2664 p
->max_dB
= max_dB
[t
];
2671 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2674 pa_log_debug("Setting %s (%s) priority=%u",
2676 pa_strnull(s
->description
),
2680 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2683 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2685 pa_strnull(o
->name
),
2686 pa_strnull(o
->description
),
2691 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2695 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",
2705 (long long unsigned) e
->merged_mask
,
2707 pa_yes_no(e
->override_map
));
2709 PA_LLIST_FOREACH(o
, e
->options
)
2710 pa_alsa_option_dump(o
);
2713 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2718 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2719 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2721 pa_strnull(p
->description
),
2724 pa_yes_no(p
->probed
),
2725 pa_yes_no(p
->supported
),
2726 pa_yes_no(p
->has_mute
),
2727 pa_yes_no(p
->has_volume
),
2728 pa_yes_no(p
->has_dB
),
2729 p
->min_volume
, p
->max_volume
,
2730 p
->min_dB
, p
->max_dB
);
2732 PA_LLIST_FOREACH(e
, p
->elements
)
2733 pa_alsa_element_dump(e
);
2735 PA_LLIST_FOREACH(s
, p
->settings
)
2736 pa_alsa_setting_dump(s
);
2739 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2740 snd_mixer_selem_id_t
*sid
;
2741 snd_mixer_elem_t
*me
;
2747 SELEM_INIT(sid
, e
->alsa_name
);
2748 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2749 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2753 snd_mixer_elem_set_callback(me
, cb
);
2754 snd_mixer_elem_set_callback_private(me
, userdata
);
2757 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2764 PA_LLIST_FOREACH(e
, p
->elements
)
2765 element_set_callback(e
, m
, cb
, userdata
);
2768 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2775 PA_LLIST_FOREACH(p
, ps
->paths
)
2776 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2779 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
, const char *paths_dir
) {
2780 pa_alsa_path_set
*ps
;
2781 char **pn
= NULL
, **en
= NULL
, **ie
;
2782 pa_alsa_decibel_fix
*db_fix
;
2786 pa_assert(m
->profile_set
);
2787 pa_assert(m
->profile_set
->decibel_fixes
);
2788 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2790 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2793 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2794 ps
->direction
= direction
;
2796 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2797 pn
= m
->output_path_names
;
2798 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2799 pn
= m
->input_path_names
;
2804 for (in
= pn
; *in
; in
++) {
2806 pa_bool_t duplicate
= FALSE
;
2809 for (kn
= pn
; kn
< in
; kn
++)
2810 if (pa_streq(*kn
, *in
)) {
2818 fn
= pa_sprintf_malloc("%s.conf", *in
);
2820 if ((p
= pa_alsa_path_new(paths_dir
, fn
, direction
))) {
2822 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2832 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2833 en
= m
->output_element
;
2834 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2835 en
= m
->input_element
;
2838 pa_alsa_path_set_free(ps
);
2842 for (ie
= en
; *ie
; ie
++) {
2846 p
= pa_alsa_path_synthesize(*ie
, direction
);
2849 /* Mark all other passed elements for require-absent */
2850 for (je
= en
; *je
; je
++) {
2856 e
= pa_xnew0(pa_alsa_element
, 1);
2858 e
->alsa_name
= pa_xstrdup(*je
);
2859 e
->direction
= direction
;
2860 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2861 e
->volume_limit
= -1;
2863 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2864 p
->last_element
= e
;
2867 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2872 /* Assign decibel fixes to elements. */
2873 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2876 PA_LLIST_FOREACH(p
, ps
->paths
) {
2879 PA_LLIST_FOREACH(e
, p
->elements
) {
2880 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2881 /* The profile set that contains the dB fix may be freed
2882 * before the element, so we have to copy the dB fix
2884 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2885 e
->db_fix
->profile_set
= NULL
;
2886 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2887 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
2896 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2900 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2903 pa_yes_no(ps
->probed
));
2905 PA_LLIST_FOREACH(p
, ps
->paths
)
2906 pa_alsa_path_dump(p
);
2910 static pa_bool_t
options_have_option(pa_alsa_option
*options
, const char *alsa_name
) {
2914 pa_assert(alsa_name
);
2916 PA_LLIST_FOREACH(o
, options
) {
2917 if (pa_streq(o
->alsa_name
, alsa_name
))
2923 static pa_bool_t
enumeration_is_subset(pa_alsa_option
*a_options
, pa_alsa_option
*b_options
) {
2924 pa_alsa_option
*oa
, *ob
;
2926 if (!a_options
) return TRUE
;
2927 if (!b_options
) return FALSE
;
2929 /* If there is an option A offers that B does not, then A is not a subset of B. */
2930 PA_LLIST_FOREACH(oa
, a_options
) {
2931 pa_bool_t found
= FALSE
;
2932 PA_LLIST_FOREACH(ob
, b_options
) {
2933 if (pa_streq(oa
->alsa_name
, ob
->alsa_name
)) {
2945 * Compares two elements to see if a is a subset of b
2947 static pa_bool_t
element_is_subset(pa_alsa_element
*a
, pa_alsa_element
*b
, snd_mixer_t
*m
) {
2953 * Every state is a subset of itself (with caveats for volume_limits and options)
2954 * IGNORE is a subset of every other state */
2956 /* Check the volume_use */
2957 if (a
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
2959 /* "Constant" is subset of "Constant" only when their constant values are equal */
2960 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& b
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&& a
->constant_volume
!= b
->constant_volume
)
2963 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
2964 if (a
->volume_use
!= b
->volume_use
&& b
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
2967 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
2968 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
2969 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
2970 if (b
->volume_use
== PA_ALSA_VOLUME_MERGE
&& b
->volume_limit
>= 0) {
2973 if (a
->volume_use
== PA_ALSA_VOLUME_CONSTANT
)
2974 a_limit
= a
->constant_volume
;
2975 else if (a
->volume_use
== PA_ALSA_VOLUME_ZERO
) {
2979 int rounding
= (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
? +1 : -1);
2980 a_limit
= decibel_fix_get_step(a
->db_fix
, &dB
, rounding
);
2982 snd_mixer_selem_id_t
*sid
;
2983 snd_mixer_elem_t
*me
;
2985 SELEM_INIT(sid
, a
->alsa_name
);
2986 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2987 pa_log_warn("Element %s seems to have disappeared.", a
->alsa_name
);
2991 if (a
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
2992 if (snd_mixer_selem_ask_playback_dB_vol(me
, dB
, +1, &a_limit
) < 0)
2995 if (snd_mixer_selem_ask_capture_dB_vol(me
, dB
, -1, &a_limit
) < 0)
2999 } else if (a
->volume_use
== PA_ALSA_VOLUME_OFF
)
3000 a_limit
= a
->min_volume
;
3001 else if (a
->volume_use
== PA_ALSA_VOLUME_MERGE
)
3002 a_limit
= a
->volume_limit
;
3004 /* This should never be reached */
3007 if (a_limit
> b
->volume_limit
)
3012 if (a
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
3013 /* "On" is a subset of "Mute".
3014 * "Off" is a subset of "Mute".
3015 * "On" is a subset of "Select", if there is an "Option:On" in B.
3016 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3017 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3019 if (a
->switch_use
!= b
->switch_use
) {
3021 if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
|| a
->switch_use
== PA_ALSA_SWITCH_MUTE
3022 || b
->switch_use
== PA_ALSA_SWITCH_OFF
|| b
->switch_use
== PA_ALSA_SWITCH_ON
)
3025 if (b
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3026 if (a
->switch_use
== PA_ALSA_SWITCH_ON
) {
3027 if (!options_have_option(b
->options
, "on"))
3029 } else if (a
->switch_use
== PA_ALSA_SWITCH_OFF
) {
3030 if (!options_have_option(b
->options
, "off"))
3034 } else if (a
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
3035 if (!enumeration_is_subset(a
->options
, b
->options
))
3040 if (a
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
) {
3041 if (b
->enumeration_use
== PA_ALSA_ENUMERATION_IGNORE
)
3043 if (!enumeration_is_subset(a
->options
, b
->options
))
3050 static void path_set_condense(pa_alsa_path_set
*ps
, snd_mixer_t
*m
) {
3051 pa_alsa_path
*p
, *np
;
3056 /* If we only have one path, then don't bother */
3057 if (!ps
->paths
|| !ps
->paths
->next
)
3060 for (p
= ps
->paths
; p
; p
= np
) {
3064 PA_LLIST_FOREACH(p2
, ps
->paths
) {
3065 pa_alsa_element
*ea
, *eb
;
3066 pa_bool_t is_subset
= TRUE
;
3071 /* Compare the elements of each set... */
3072 pa_assert_se(ea
= p
->elements
);
3073 pa_assert_se(eb
= p2
->elements
);
3076 if (pa_streq(ea
->alsa_name
, eb
->alsa_name
)) {
3077 if (element_is_subset(ea
, eb
, m
)) {
3080 if ((ea
&& !eb
) || (!ea
&& eb
))
3082 else if (!ea
&& !eb
)
3092 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p
->name
, p2
->name
);
3093 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
3094 pa_alsa_path_free(p
);
3101 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
3102 pa_alsa_path
*p
, *q
;
3104 PA_LLIST_FOREACH(p
, ps
->paths
) {
3108 for (q
= p
->next
; q
; q
= q
->next
)
3109 if (pa_streq(q
->name
, p
->name
))
3115 m
= pa_xstrdup(p
->name
);
3117 /* OK, this name is not unique, hence let's rename */
3118 for (i
= 1, q
= p
; q
; q
= q
->next
) {
3121 if (!pa_streq(q
->name
, m
))
3124 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
3128 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
3129 pa_xfree(q
->description
);
3130 q
->description
= nd
;
3139 void pa_alsa_path_set_probe(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
3140 pa_alsa_path
*p
, *n
;
3147 for (p
= ps
->paths
; p
; p
= n
) {
3150 if (pa_alsa_path_probe(p
, m
, ignore_dB
) < 0) {
3151 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
3152 pa_alsa_path_free(p
);
3156 pa_log_debug("Found mixer paths (before tidying):");
3157 pa_alsa_path_set_dump(ps
);
3159 path_set_condense(ps
, m
);
3160 path_set_make_paths_unique(ps
);
3163 pa_log_debug("Available mixer paths (after tidying):");
3164 pa_alsa_path_set_dump(ps
);
3167 static void mapping_free(pa_alsa_mapping
*m
) {
3171 pa_xfree(m
->description
);
3173 pa_xstrfreev(m
->device_strings
);
3174 pa_xstrfreev(m
->input_path_names
);
3175 pa_xstrfreev(m
->output_path_names
);
3176 pa_xstrfreev(m
->input_element
);
3177 pa_xstrfreev(m
->output_element
);
3179 pa_assert(!m
->input_pcm
);
3180 pa_assert(!m
->output_pcm
);
3185 static void profile_free(pa_alsa_profile
*p
) {
3189 pa_xfree(p
->description
);
3191 pa_xstrfreev(p
->input_mapping_names
);
3192 pa_xstrfreev(p
->output_mapping_names
);
3194 if (p
->input_mappings
)
3195 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
3197 if (p
->output_mappings
)
3198 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
3203 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
3209 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
3212 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
3218 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
3221 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
3224 if (ps
->decibel_fixes
) {
3225 pa_alsa_decibel_fix
*db_fix
;
3227 while ((db_fix
= pa_hashmap_steal_first(ps
->decibel_fixes
)))
3228 decibel_fix_free(db_fix
);
3230 pa_hashmap_free(ps
->decibel_fixes
, NULL
, NULL
);
3236 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
3239 if (!pa_startswith(name
, "Mapping "))
3244 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
3247 m
= pa_xnew0(pa_alsa_mapping
, 1);
3248 m
->profile_set
= ps
;
3249 m
->name
= pa_xstrdup(name
);
3250 pa_channel_map_init(&m
->channel_map
);
3252 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
3257 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
3260 if (!pa_startswith(name
, "Profile "))
3265 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
3268 p
= pa_xnew0(pa_alsa_profile
, 1);
3269 p
->profile_set
= ps
;
3270 p
->name
= pa_xstrdup(name
);
3272 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3277 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
3278 pa_alsa_decibel_fix
*db_fix
;
3280 if (!pa_startswith(name
, "DecibelFix "))
3285 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
3288 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
3289 db_fix
->profile_set
= ps
;
3290 db_fix
->name
= pa_xstrdup(name
);
3292 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
3297 static int mapping_parse_device_strings(
3298 const char *filename
,
3300 const char *section
,
3306 pa_alsa_profile_set
*ps
= userdata
;
3311 if (!(m
= mapping_get(ps
, section
))) {
3312 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3316 pa_xstrfreev(m
->device_strings
);
3317 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
3318 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
3325 static int mapping_parse_channel_map(
3326 const char *filename
,
3328 const char *section
,
3334 pa_alsa_profile_set
*ps
= userdata
;
3339 if (!(m
= mapping_get(ps
, section
))) {
3340 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3344 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
3345 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
3352 static int mapping_parse_paths(
3353 const char *filename
,
3355 const char *section
,
3361 pa_alsa_profile_set
*ps
= userdata
;
3366 if (!(m
= mapping_get(ps
, section
))) {
3367 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3371 if (pa_streq(lvalue
, "paths-input")) {
3372 pa_xstrfreev(m
->input_path_names
);
3373 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
3375 pa_xstrfreev(m
->output_path_names
);
3376 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
3382 static int mapping_parse_element(
3383 const char *filename
,
3385 const char *section
,
3391 pa_alsa_profile_set
*ps
= userdata
;
3396 if (!(m
= mapping_get(ps
, section
))) {
3397 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3401 if (pa_streq(lvalue
, "element-input")) {
3402 pa_xstrfreev(m
->input_element
);
3403 m
->input_element
= pa_split_spaces_strv(rvalue
);
3405 pa_xstrfreev(m
->output_element
);
3406 m
->output_element
= pa_split_spaces_strv(rvalue
);
3412 static int mapping_parse_direction(
3413 const char *filename
,
3415 const char *section
,
3421 pa_alsa_profile_set
*ps
= userdata
;
3426 if (!(m
= mapping_get(ps
, section
))) {
3427 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3431 if (pa_streq(rvalue
, "input"))
3432 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3433 else if (pa_streq(rvalue
, "output"))
3434 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3435 else if (pa_streq(rvalue
, "any"))
3436 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3438 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
3445 static int mapping_parse_description(
3446 const char *filename
,
3448 const char *section
,
3454 pa_alsa_profile_set
*ps
= userdata
;
3460 if ((m
= mapping_get(ps
, section
))) {
3461 pa_xfree(m
->description
);
3462 m
->description
= pa_xstrdup(rvalue
);
3463 } else if ((p
= profile_get(ps
, section
))) {
3464 pa_xfree(p
->description
);
3465 p
->description
= pa_xstrdup(rvalue
);
3467 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3474 static int mapping_parse_priority(
3475 const char *filename
,
3477 const char *section
,
3483 pa_alsa_profile_set
*ps
= userdata
;
3490 if (pa_atou(rvalue
, &prio
) < 0) {
3491 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
3495 if ((m
= mapping_get(ps
, section
)))
3497 else if ((p
= profile_get(ps
, section
)))
3500 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3507 static int profile_parse_mappings(
3508 const char *filename
,
3510 const char *section
,
3516 pa_alsa_profile_set
*ps
= userdata
;
3521 if (!(p
= profile_get(ps
, section
))) {
3522 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3526 if (pa_streq(lvalue
, "input-mappings")) {
3527 pa_xstrfreev(p
->input_mapping_names
);
3528 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
3530 pa_xstrfreev(p
->output_mapping_names
);
3531 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
3537 static int profile_parse_skip_probe(
3538 const char *filename
,
3540 const char *section
,
3546 pa_alsa_profile_set
*ps
= userdata
;
3552 if (!(p
= profile_get(ps
, section
))) {
3553 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3557 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
3558 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
3567 static int decibel_fix_parse_db_values(
3568 const char *filename
,
3570 const char *section
,
3576 pa_alsa_profile_set
*ps
= userdata
;
3577 pa_alsa_decibel_fix
*db_fix
;
3581 unsigned n
= 8; /* Current size of the db_values table. */
3582 unsigned min_step
= 0;
3583 unsigned max_step
= 0;
3584 unsigned i
= 0; /* Index to the items table. */
3585 unsigned prev_step
= 0;
3588 pa_assert(filename
);
3594 if (!(db_fix
= decibel_fix_get(ps
, section
))) {
3595 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3599 if (!(items
= pa_split_spaces_strv(rvalue
))) {
3600 pa_log("[%s:%u] Value missing", pa_strnull(filename
), line
);
3604 db_values
= pa_xnew(long, n
);
3606 while ((item
= items
[i
++])) {
3607 char *s
= item
; /* Step value string. */
3608 char *d
= item
; /* dB value string. */
3612 /* Move d forward until it points to a colon or to the end of the item. */
3613 for (; *d
&& *d
!= ':'; ++d
);
3616 /* item started with colon. */
3617 pa_log("[%s:%u] No step value found in %s", filename
, line
, item
);
3621 if (!*d
|| !*(d
+ 1)) {
3622 /* No colon found, or it was the last character in item. */
3623 pa_log("[%s:%u] No dB value found in %s", filename
, line
, item
);
3627 /* pa_atou() needs a null-terminating string. Let's replace the colon
3628 * with a zero byte. */
3631 if (pa_atou(s
, &step
) < 0) {
3632 pa_log("[%s:%u] Invalid step value: %s", filename
, line
, s
);
3636 if (pa_atod(d
, &db
) < 0) {
3637 pa_log("[%s:%u] Invalid dB value: %s", filename
, line
, d
);
3641 if (step
<= prev_step
&& i
!= 1) {
3642 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename
, line
, step
, prev_step
);
3646 if (db
< prev_db
&& i
!= 1) {
3647 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename
, line
, db
, prev_db
);
3653 db_values
[0] = (long) (db
* 100.0);
3657 /* Interpolate linearly. */
3658 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3660 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3662 /* Reallocate the db_values table if it's about to overflow. */
3663 if (prev_step
+ 1 - min_step
== n
) {
3665 db_values
= pa_xrenew(long, db_values
, n
);
3668 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3675 db_fix
->min_step
= min_step
;
3676 db_fix
->max_step
= max_step
;
3677 pa_xfree(db_fix
->db_values
);
3678 db_fix
->db_values
= db_values
;
3680 pa_xstrfreev(items
);
3685 pa_xstrfreev(items
);
3686 pa_xfree(db_values
);
3691 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3693 static const struct description_map well_known_descriptions
[] = {
3694 { "analog-mono", N_("Analog Mono") },
3695 { "analog-stereo", N_("Analog Stereo") },
3696 { "analog-surround-21", N_("Analog Surround 2.1") },
3697 { "analog-surround-30", N_("Analog Surround 3.0") },
3698 { "analog-surround-31", N_("Analog Surround 3.1") },
3699 { "analog-surround-40", N_("Analog Surround 4.0") },
3700 { "analog-surround-41", N_("Analog Surround 4.1") },
3701 { "analog-surround-50", N_("Analog Surround 5.0") },
3702 { "analog-surround-51", N_("Analog Surround 5.1") },
3703 { "analog-surround-61", N_("Analog Surround 6.0") },
3704 { "analog-surround-61", N_("Analog Surround 6.1") },
3705 { "analog-surround-70", N_("Analog Surround 7.0") },
3706 { "analog-surround-71", N_("Analog Surround 7.1") },
3707 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3708 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3709 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3710 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3711 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3716 if (!pa_channel_map_valid(&m
->channel_map
)) {
3717 pa_log("Mapping %s is missing channel map.", m
->name
);
3721 if (!m
->device_strings
) {
3722 pa_log("Mapping %s is missing device strings.", m
->name
);
3726 if ((m
->input_path_names
&& m
->input_element
) ||
3727 (m
->output_path_names
&& m
->output_element
)) {
3728 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3732 if (!m
->description
)
3733 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3734 well_known_descriptions
,
3735 PA_ELEMENTSOF(well_known_descriptions
)));
3737 if (!m
->description
)
3738 m
->description
= pa_xstrdup(m
->name
);
3741 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3743 else if (m
->channel_map
.channels
== bonus
->channels
)
3750 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3751 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3755 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3757 pa_strnull(m
->description
),
3759 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3760 pa_yes_no(m
->supported
),
3764 static void profile_set_add_auto_pair(
3765 pa_alsa_profile_set
*ps
,
3766 pa_alsa_mapping
*m
, /* output */
3767 pa_alsa_mapping
*n
/* input */) {
3775 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3778 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3782 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3784 name
= pa_sprintf_malloc("output:%s", m
->name
);
3786 name
= pa_sprintf_malloc("input:%s", n
->name
);
3788 if (pa_hashmap_get(ps
->profiles
, name
)) {
3793 p
= pa_xnew0(pa_alsa_profile
, 1);
3794 p
->profile_set
= ps
;
3798 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3799 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3800 p
->priority
+= m
->priority
* 100;
3804 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3805 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3806 p
->priority
+= n
->priority
;
3809 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3812 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3813 pa_alsa_mapping
*m
, *n
;
3814 void *m_state
, *n_state
;
3818 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3819 profile_set_add_auto_pair(ps
, m
, NULL
);
3821 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3822 profile_set_add_auto_pair(ps
, m
, n
);
3825 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3826 profile_set_add_auto_pair(ps
, NULL
, n
);
3829 static int profile_verify(pa_alsa_profile
*p
) {
3831 static const struct description_map well_known_descriptions
[] = {
3832 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3833 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3834 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3835 { "off", N_("Off") }
3840 /* Replace the output mapping names by the actual mappings */
3841 if (p
->output_mapping_names
) {
3844 pa_assert(!p
->output_mappings
);
3845 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3847 for (name
= p
->output_mapping_names
; *name
; name
++) {
3850 pa_bool_t duplicate
= FALSE
;
3852 for (in
= name
+ 1; *in
; in
++)
3853 if (pa_streq(*name
, *in
)) {
3861 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3862 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p
->name
, *name
);
3866 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3872 pa_xstrfreev(p
->output_mapping_names
);
3873 p
->output_mapping_names
= NULL
;
3876 /* Replace the input mapping names by the actual mappings */
3877 if (p
->input_mapping_names
) {
3880 pa_assert(!p
->input_mappings
);
3881 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3883 for (name
= p
->input_mapping_names
; *name
; name
++) {
3886 pa_bool_t duplicate
= FALSE
;
3888 for (in
= name
+ 1; *in
; in
++)
3889 if (pa_streq(*name
, *in
)) {
3897 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3898 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p
->name
, *name
);
3902 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3908 pa_xstrfreev(p
->input_mapping_names
);
3909 p
->input_mapping_names
= NULL
;
3912 if (!p
->input_mappings
&& !p
->output_mappings
) {
3913 pa_log("Profile '%s' lacks mappings.", p
->name
);
3917 if (!p
->description
)
3918 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3919 well_known_descriptions
,
3920 PA_ELEMENTSOF(well_known_descriptions
)));
3922 if (!p
->description
) {
3927 sb
= pa_strbuf_new();
3929 if (p
->output_mappings
)
3930 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3931 if (!pa_strbuf_isempty(sb
))
3932 pa_strbuf_puts(sb
, " + ");
3934 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
3937 if (p
->input_mappings
)
3938 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3939 if (!pa_strbuf_isempty(sb
))
3940 pa_strbuf_puts(sb
, " + ");
3942 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
3945 p
->description
= pa_strbuf_tostring_free(sb
);
3951 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
3956 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3958 pa_strnull(p
->description
),
3960 pa_yes_no(p
->supported
),
3961 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
3962 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
3964 if (p
->input_mappings
)
3965 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
3966 pa_log_debug("Input %s", m
->name
);
3968 if (p
->output_mappings
)
3969 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
3970 pa_log_debug("Output %s", m
->name
);
3973 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
3976 /* Check that the dB mapping has been configured. Since "db-values" is
3977 * currently the only option in the DecibelFix section, and decibel fix
3978 * objects don't get created if a DecibelFix section is empty, this is
3979 * actually a redundant check. Having this may prevent future bugs,
3981 if (!db_fix
->db_values
) {
3982 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
3989 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
3990 char *db_values
= NULL
;
3994 if (db_fix
->db_values
) {
3996 unsigned long i
, nsteps
;
3998 pa_assert(db_fix
->min_step
<= db_fix
->max_step
);
3999 nsteps
= db_fix
->max_step
- db_fix
->min_step
+ 1;
4001 buf
= pa_strbuf_new();
4002 for (i
= 0; i
< nsteps
; ++i
)
4003 pa_strbuf_printf(buf
, "[%li]:%0.2f ", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
4005 db_values
= pa_strbuf_tostring_free(buf
);
4008 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4009 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
4011 pa_xfree(db_values
);
4014 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
4015 pa_alsa_profile_set
*ps
;
4018 pa_alsa_decibel_fix
*db_fix
;
4023 static pa_config_item items
[] = {
4025 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
4028 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
4029 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
4030 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
4031 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
4032 { "element-input", mapping_parse_element
, NULL
, NULL
},
4033 { "element-output", mapping_parse_element
, NULL
, NULL
},
4034 { "direction", mapping_parse_direction
, NULL
, NULL
},
4036 /* Shared by [Mapping ...] and [Profile ...] */
4037 { "description", mapping_parse_description
, NULL
, NULL
},
4038 { "priority", mapping_parse_priority
, NULL
, NULL
},
4041 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
4042 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
4043 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
4045 /* [DecibelFix ...] */
4046 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
4047 { NULL
, NULL
, NULL
, NULL
}
4050 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
4051 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4052 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4053 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4055 items
[0].data
= &ps
->auto_profiles
;
4058 fname
= "default.conf";
4060 fn
= pa_maybe_prefix_path(fname
,
4061 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
4062 PA_ALSA_PROFILE_SETS_DIR
);
4064 r
= pa_config_parse(fn
, NULL
, items
, ps
);
4070 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4071 if (mapping_verify(m
, bonus
) < 0)
4074 if (ps
->auto_profiles
)
4075 profile_set_add_auto(ps
);
4077 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4078 if (profile_verify(p
) < 0)
4081 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4082 if (decibel_fix_verify(db_fix
) < 0)
4088 pa_alsa_profile_set_free(ps
);
4092 void pa_alsa_profile_set_probe(
4093 pa_alsa_profile_set
*ps
,
4095 const pa_sample_spec
*ss
,
4096 unsigned default_n_fragments
,
4097 unsigned default_fragment_size_msec
) {
4100 pa_alsa_profile
*p
, *last
= NULL
;
4110 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
4111 pa_sample_spec try_ss
;
4112 pa_channel_map try_map
;
4113 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
4116 /* Is this already marked that it is supported? (i.e. from the config file) */
4120 pa_log_debug("Looking at profile %s", p
->name
);
4122 /* Close PCMs from the last iteration we don't need anymore */
4123 if (last
&& last
->output_mappings
)
4124 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
) {
4129 if (last
->supported
)
4132 if (!p
->output_mappings
|| !pa_idxset_get_by_data(p
->output_mappings
, m
, NULL
)) {
4133 snd_pcm_close(m
->output_pcm
);
4134 m
->output_pcm
= NULL
;
4138 if (last
&& last
->input_mappings
)
4139 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
) {
4144 if (last
->supported
)
4147 if (!p
->input_mappings
|| !pa_idxset_get_by_data(p
->input_mappings
, m
, NULL
)) {
4148 snd_pcm_close(m
->input_pcm
);
4149 m
->input_pcm
= NULL
;
4153 p
->supported
= TRUE
;
4155 /* Check if we can open all new ones */
4156 if (p
->output_mappings
)
4157 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
4162 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
4163 try_map
= m
->channel_map
;
4165 try_ss
.channels
= try_map
.channels
;
4168 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
4169 pa_frame_size(&try_ss
);
4170 try_buffer_size
= default_n_fragments
* try_period_size
;
4172 if (!(m
->output_pcm
= pa_alsa_open_by_template(
4177 SND_PCM_STREAM_PLAYBACK
,
4178 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
4180 p
->supported
= FALSE
;
4185 if (p
->input_mappings
&& p
->supported
)
4186 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
4191 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
4192 try_map
= m
->channel_map
;
4194 try_ss
.channels
= try_map
.channels
;
4197 pa_usec_to_bytes(default_fragment_size_msec
*PA_USEC_PER_MSEC
, &try_ss
) /
4198 pa_frame_size(&try_ss
);
4199 try_buffer_size
= default_n_fragments
* try_period_size
;
4201 if (!(m
->input_pcm
= pa_alsa_open_by_template(
4206 SND_PCM_STREAM_CAPTURE
,
4207 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
4209 p
->supported
= FALSE
;
4217 pa_log_debug("Profile %s supported.", p
->name
);
4224 if (last
->output_mappings
)
4225 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
)
4226 if (m
->output_pcm
) {
4228 if (last
->supported
)
4231 snd_pcm_close(m
->output_pcm
);
4232 m
->output_pcm
= NULL
;
4235 if (last
->input_mappings
)
4236 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
)
4239 if (last
->supported
)
4242 snd_pcm_close(m
->input_pcm
);
4243 m
->input_pcm
= NULL
;
4247 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4248 if (!p
->supported
) {
4249 pa_hashmap_remove(ps
->profiles
, p
->name
);
4253 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4254 if (m
->supported
<= 0) {
4255 pa_hashmap_remove(ps
->mappings
, m
->name
);
4262 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
4265 pa_alsa_decibel_fix
*db_fix
;
4270 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4273 pa_yes_no(ps
->auto_profiles
),
4274 pa_yes_no(ps
->probed
),
4275 pa_hashmap_size(ps
->mappings
),
4276 pa_hashmap_size(ps
->profiles
),
4277 pa_hashmap_size(ps
->decibel_fixes
));
4279 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4280 pa_alsa_mapping_dump(m
);
4282 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4283 pa_alsa_profile_dump(p
);
4285 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4286 pa_alsa_decibel_fix_dump(db_fix
);
4289 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
) {
4296 /* if there is no path, we don't want a port list */
4300 if (!ps
->paths
->next
){
4303 /* If there is only one path, but no or only one setting, then
4304 * we want a port list either */
4305 if (!ps
->paths
->settings
|| !ps
->paths
->settings
->next
)
4308 /* Ok, there is only one path, however with multiple settings,
4309 * so let's create a port for each setting */
4310 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4312 PA_LLIST_FOREACH(s
, ps
->paths
->settings
) {
4313 pa_device_port
*port
;
4314 pa_alsa_port_data
*data
;
4316 port
= pa_device_port_new(s
->name
, s
->description
, sizeof(pa_alsa_port_data
));
4317 port
->priority
= s
->priority
;
4319 data
= PA_DEVICE_PORT_DATA(port
);
4320 data
->path
= ps
->paths
;
4323 pa_hashmap_put(*p
, port
->name
, port
);
4328 /* We have multiple paths, so let's create a port for each
4329 * one, and each of each settings */
4330 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4332 PA_LLIST_FOREACH(path
, ps
->paths
) {
4334 if (!path
->settings
|| !path
->settings
->next
) {
4335 pa_device_port
*port
;
4336 pa_alsa_port_data
*data
;
4338 /* If there is no or just one setting we only need a
4341 port
= pa_device_port_new(path
->name
, path
->description
, sizeof(pa_alsa_port_data
));
4342 port
->priority
= path
->priority
* 100;
4345 data
= PA_DEVICE_PORT_DATA(port
);
4347 data
->setting
= path
->settings
;
4349 pa_hashmap_put(*p
, port
->name
, port
);
4353 PA_LLIST_FOREACH(s
, path
->settings
) {
4354 pa_device_port
*port
;
4355 pa_alsa_port_data
*data
;
4358 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4360 if (s
->description
[0])
4361 d
= pa_sprintf_malloc(_("%s / %s"), path
->description
, s
->description
);
4363 d
= pa_xstrdup(path
->description
);
4365 port
= pa_device_port_new(n
, d
, sizeof(pa_alsa_port_data
));
4366 port
->priority
= path
->priority
* 100 + s
->priority
;
4371 data
= PA_DEVICE_PORT_DATA(port
);
4375 pa_hashmap_put(*p
, port
->name
, port
);
4381 pa_log_debug("Added %u ports", pa_hashmap_size(*p
));