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, see <http://www.gnu.org/licenses/>.
25
#include <sys/types.h>
26
#include <asoundlib.h>
29
#ifdef HAVE_VALGRIND_MEMCHECK_H
30
#include <valgrind/memcheck.h>
33
#include <pulse/mainloop-api.h>
34
#include <pulse/sample.h>
35
#include <pulse/timeval.h>
36
#include <pulse/util.h>
37
#include <pulse/volume.h>
38
#include <pulse/xmalloc.h>
39
#include <pulse/utf8.h>
41
#include <pulsecore/i18n.h>
42
#include <pulsecore/log.h>
43
#include <pulsecore/macro.h>
44
#include <pulsecore/core-util.h>
45
#include <pulsecore/conf-parser.h>
46
#include <pulsecore/strbuf.h>
48
#include "alsa-mixer.h"
49
#include "alsa-util.h"
51
#ifdef HAVE_VALGRIND_MEMCHECK_H
52
/* These macros are workarounds for a bug in valgrind, which is not handling the
53
* ALSA TLV syscalls correctly. See
54
* http://valgrind.10908.n7.nabble.com/Missing-ioctl-for-SNDRV-CTL-IOCTL-TLV-READ-td42711.html */
56
static inline int vgfix_get_capture_dB(snd_mixer_elem_t *a, snd_mixer_selem_channel_id_t b, long *c) {
57
int r = snd_mixer_selem_get_capture_dB(a, b, c);
58
VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
62
static inline int vgfix_get_playback_dB(snd_mixer_elem_t *a, snd_mixer_selem_channel_id_t b, long *c) {
63
int r = snd_mixer_selem_get_playback_dB(a, b, c);
64
VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
68
static inline int vgfix_ask_capture_vol_dB(snd_mixer_elem_t *a, long b, long *c) {
69
int r = snd_mixer_selem_ask_capture_vol_dB(a, b, c);
70
VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
74
static inline int vgfix_ask_playback_vol_dB(snd_mixer_elem_t *a, long b, long *c) {
75
int r = snd_mixer_selem_ask_playback_vol_dB(a, b, c);
76
VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
80
static inline int vgfix_get_capture_dB_range(snd_mixer_elem_t *a, long *b, long *c) {
81
int r = snd_mixer_selem_get_capture_dB_range(a, b, c);
82
VALGRIND_MAKE_MEM_DEFINED(b, sizeof(*b));
83
VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
87
static inline int vgfix_get_playback_dB_range(snd_mixer_elem_t *a, long *b, long *c) {
88
int r = snd_mixer_selem_get_playback_dB_range(a, b, c);
89
VALGRIND_MAKE_MEM_DEFINED(b, sizeof(*b));
90
VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
94
#define snd_mixer_selem_get_capture_dB(a, b, c) vgfix_get_capture_dB(a, b, c)
95
#define snd_mixer_selem_get_playback_dB(a, b, c) vgfix_get_playback_dB(a, b, c)
96
#define snd_mixer_selem_ask_capture_vol_dB(a, b, c) vgfix_ask_capture_vol_dB(a, b, c)
97
#define snd_mixer_selem_ask_playback_vol_dB(a, b, c) vgfix_ask_playback_vol_dB(a, b, c)
98
#define snd_mixer_selem_get_capture_dB_range(a, b, c) vgfix_get_capture_dB_range(a, b, c)
99
#define snd_mixer_selem_get_playback_dB_range(a, b, c) vgfix_get_playback_dB_range(a, b, c)
103
static int setting_select(pa_alsa_setting *s, snd_mixer_t *m);
105
struct description_map {
107
const char *description;
110
static const char *lookup_description(const char *key, const struct description_map dm[], unsigned n) {
116
for (i = 0; i < n; i++)
117
if (pa_streq(dm[i].key, key))
118
return _(dm[i].description);
123
struct pa_alsa_fdlist {
126
/* This is a temporary buffer used to avoid lots of mallocs */
127
struct pollfd *work_fds;
133
pa_defer_event *defer;
138
void (*cb)(void *userdata);
142
static void io_cb(pa_mainloop_api *a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
144
struct pa_alsa_fdlist *fdl = userdata;
147
unsigned short revents;
151
pa_assert(fdl->mixer || fdl->hctl);
153
pa_assert(fdl->work_fds);
160
memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds);
162
for (i = 0; i < fdl->num_fds; i++) {
163
if (e == fdl->ios[i]) {
164
if (events & PA_IO_EVENT_INPUT)
165
fdl->work_fds[i].revents |= POLLIN;
166
if (events & PA_IO_EVENT_OUTPUT)
167
fdl->work_fds[i].revents |= POLLOUT;
168
if (events & PA_IO_EVENT_ERROR)
169
fdl->work_fds[i].revents |= POLLERR;
170
if (events & PA_IO_EVENT_HANGUP)
171
fdl->work_fds[i].revents |= POLLHUP;
176
pa_assert(i != fdl->num_fds);
179
err = snd_hctl_poll_descriptors_revents(fdl->hctl, fdl->work_fds, fdl->num_fds, &revents);
181
err = snd_mixer_poll_descriptors_revents(fdl->mixer, fdl->work_fds, fdl->num_fds, &revents);
184
pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
188
a->defer_enable(fdl->defer, 1);
192
snd_hctl_handle_events(fdl->hctl);
194
snd_mixer_handle_events(fdl->mixer);
198
static void defer_cb(pa_mainloop_api *a, pa_defer_event *e, void *userdata) {
199
struct pa_alsa_fdlist *fdl = userdata;
206
pa_assert(fdl->mixer || fdl->hctl);
208
a->defer_enable(fdl->defer, 0);
211
n = snd_hctl_poll_descriptors_count(fdl->hctl);
213
n = snd_mixer_poll_descriptors_count(fdl->mixer);
216
pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
219
pa_log("snd_mixer_poll_descriptors_count() equal 0");
222
num_fds = (unsigned) n;
224
if (num_fds != fdl->num_fds) {
228
pa_xfree(fdl->work_fds);
229
fdl->fds = pa_xnew0(struct pollfd, num_fds);
230
fdl->work_fds = pa_xnew(struct pollfd, num_fds);
233
memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds);
236
err = snd_hctl_poll_descriptors(fdl->hctl, fdl->work_fds, num_fds);
238
err = snd_mixer_poll_descriptors(fdl->mixer, fdl->work_fds, num_fds);
241
pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
247
if (memcmp(fdl->fds, fdl->work_fds, sizeof(struct pollfd) * num_fds) == 0)
251
for (i = 0; i < fdl->num_fds; i++)
252
a->io_free(fdl->ios[i]);
254
if (num_fds != fdl->num_fds) {
261
fdl->ios = pa_xnew(pa_io_event*, num_fds);
264
temp = fdl->work_fds;
265
fdl->work_fds = fdl->fds;
268
fdl->num_fds = num_fds;
270
for (i = 0;i < num_fds;i++)
271
fdl->ios[i] = a->io_new(a, fdl->fds[i].fd,
272
((fdl->fds[i].events & POLLIN) ? PA_IO_EVENT_INPUT : 0) |
273
((fdl->fds[i].events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0),
277
struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) {
278
struct pa_alsa_fdlist *fdl;
280
fdl = pa_xnew0(struct pa_alsa_fdlist, 1);
285
void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {
290
fdl->m->defer_free(fdl->defer);
296
for (i = 0; i < fdl->num_fds; i++)
297
fdl->m->io_free(fdl->ios[i]);
304
pa_xfree(fdl->work_fds);
309
/* We can listen to either a snd_hctl_t or a snd_mixer_t, but not both */
310
int pa_alsa_fdlist_set_handle(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, snd_hctl_t *hctl_handle, pa_mainloop_api *m) {
312
pa_assert(hctl_handle || mixer_handle);
313
pa_assert(!(hctl_handle && mixer_handle));
317
fdl->hctl = hctl_handle;
318
fdl->mixer = mixer_handle;
320
fdl->defer = m->defer_new(m, defer_cb, fdl);
325
struct pa_alsa_mixer_pdata {
327
pa_rtpoll_item *poll_item;
331
struct pa_alsa_mixer_pdata *pa_alsa_mixer_pdata_new(void) {
332
struct pa_alsa_mixer_pdata *pd;
334
pd = pa_xnew0(struct pa_alsa_mixer_pdata, 1);
339
void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata *pd) {
343
pa_rtpoll_item_free(pd->poll_item);
349
static int rtpoll_work_cb(pa_rtpoll_item *i) {
350
struct pa_alsa_mixer_pdata *pd;
353
unsigned short revents = 0;
356
pd = pa_rtpoll_item_get_userdata(i);
358
pa_assert_fp(i == pd->poll_item);
360
p = pa_rtpoll_item_get_pollfd(i, &n_fds);
362
if ((err = snd_mixer_poll_descriptors_revents(pd->mixer, p, n_fds, &revents)) < 0) {
363
pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
369
if (revents & (POLLNVAL | POLLERR)) {
370
pa_log_debug("Device disconnected, stopping poll on mixer");
372
} else if (revents & POLLERR) {
373
/* This shouldn't happen. */
374
pa_log_error("Got a POLLERR (revents = %04x), stopping poll on mixer", revents);
378
err = snd_mixer_handle_events(pd->mixer);
380
if (PA_LIKELY(err >= 0)) {
381
pa_rtpoll_item_free(i);
382
pa_alsa_set_mixer_rtpoll(pd, pd->mixer, pd->rtpoll);
384
pa_log_error("Error handling mixer event: %s", pa_alsa_strerror(err));
393
pa_rtpoll_item_free(i);
395
pd->poll_item = NULL;
402
int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata *pd, snd_mixer_t *mixer, pa_rtpoll *rtp) {
411
if ((n = snd_mixer_poll_descriptors_count(mixer)) < 0) {
412
pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
416
i = pa_rtpoll_item_new(rtp, PA_RTPOLL_LATE, (unsigned) n);
418
p = pa_rtpoll_item_get_pollfd(i, NULL);
420
memset(p, 0, sizeof(struct pollfd) * n);
422
if ((err = snd_mixer_poll_descriptors(mixer, p, (unsigned) n)) < 0) {
423
pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
424
pa_rtpoll_item_free(i);
432
pa_rtpoll_item_set_userdata(i, pd);
433
pa_rtpoll_item_set_work_callback(i, rtpoll_work_cb);
438
static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_MAX] = {
439
[PA_CHANNEL_POSITION_MONO] = SND_MIXER_SCHN_MONO, /* The ALSA name is just an alias! */
441
[PA_CHANNEL_POSITION_FRONT_CENTER] = SND_MIXER_SCHN_FRONT_CENTER,
442
[PA_CHANNEL_POSITION_FRONT_LEFT] = SND_MIXER_SCHN_FRONT_LEFT,
443
[PA_CHANNEL_POSITION_FRONT_RIGHT] = SND_MIXER_SCHN_FRONT_RIGHT,
445
[PA_CHANNEL_POSITION_REAR_CENTER] = SND_MIXER_SCHN_REAR_CENTER,
446
[PA_CHANNEL_POSITION_REAR_LEFT] = SND_MIXER_SCHN_REAR_LEFT,
447
[PA_CHANNEL_POSITION_REAR_RIGHT] = SND_MIXER_SCHN_REAR_RIGHT,
449
[PA_CHANNEL_POSITION_LFE] = SND_MIXER_SCHN_WOOFER,
451
[PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
452
[PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
454
[PA_CHANNEL_POSITION_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT,
455
[PA_CHANNEL_POSITION_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT,
457
[PA_CHANNEL_POSITION_AUX0] = SND_MIXER_SCHN_UNKNOWN,
458
[PA_CHANNEL_POSITION_AUX1] = SND_MIXER_SCHN_UNKNOWN,
459
[PA_CHANNEL_POSITION_AUX2] = SND_MIXER_SCHN_UNKNOWN,
460
[PA_CHANNEL_POSITION_AUX3] = SND_MIXER_SCHN_UNKNOWN,
461
[PA_CHANNEL_POSITION_AUX4] = SND_MIXER_SCHN_UNKNOWN,
462
[PA_CHANNEL_POSITION_AUX5] = SND_MIXER_SCHN_UNKNOWN,
463
[PA_CHANNEL_POSITION_AUX6] = SND_MIXER_SCHN_UNKNOWN,
464
[PA_CHANNEL_POSITION_AUX7] = SND_MIXER_SCHN_UNKNOWN,
465
[PA_CHANNEL_POSITION_AUX8] = SND_MIXER_SCHN_UNKNOWN,
466
[PA_CHANNEL_POSITION_AUX9] = SND_MIXER_SCHN_UNKNOWN,
467
[PA_CHANNEL_POSITION_AUX10] = SND_MIXER_SCHN_UNKNOWN,
468
[PA_CHANNEL_POSITION_AUX11] = SND_MIXER_SCHN_UNKNOWN,
469
[PA_CHANNEL_POSITION_AUX12] = SND_MIXER_SCHN_UNKNOWN,
470
[PA_CHANNEL_POSITION_AUX13] = SND_MIXER_SCHN_UNKNOWN,
471
[PA_CHANNEL_POSITION_AUX14] = SND_MIXER_SCHN_UNKNOWN,
472
[PA_CHANNEL_POSITION_AUX15] = SND_MIXER_SCHN_UNKNOWN,
473
[PA_CHANNEL_POSITION_AUX16] = SND_MIXER_SCHN_UNKNOWN,
474
[PA_CHANNEL_POSITION_AUX17] = SND_MIXER_SCHN_UNKNOWN,
475
[PA_CHANNEL_POSITION_AUX18] = SND_MIXER_SCHN_UNKNOWN,
476
[PA_CHANNEL_POSITION_AUX19] = SND_MIXER_SCHN_UNKNOWN,
477
[PA_CHANNEL_POSITION_AUX20] = SND_MIXER_SCHN_UNKNOWN,
478
[PA_CHANNEL_POSITION_AUX21] = SND_MIXER_SCHN_UNKNOWN,
479
[PA_CHANNEL_POSITION_AUX22] = SND_MIXER_SCHN_UNKNOWN,
480
[PA_CHANNEL_POSITION_AUX23] = SND_MIXER_SCHN_UNKNOWN,
481
[PA_CHANNEL_POSITION_AUX24] = SND_MIXER_SCHN_UNKNOWN,
482
[PA_CHANNEL_POSITION_AUX25] = SND_MIXER_SCHN_UNKNOWN,
483
[PA_CHANNEL_POSITION_AUX26] = SND_MIXER_SCHN_UNKNOWN,
484
[PA_CHANNEL_POSITION_AUX27] = SND_MIXER_SCHN_UNKNOWN,
485
[PA_CHANNEL_POSITION_AUX28] = SND_MIXER_SCHN_UNKNOWN,
486
[PA_CHANNEL_POSITION_AUX29] = SND_MIXER_SCHN_UNKNOWN,
487
[PA_CHANNEL_POSITION_AUX30] = SND_MIXER_SCHN_UNKNOWN,
488
[PA_CHANNEL_POSITION_AUX31] = SND_MIXER_SCHN_UNKNOWN,
490
[PA_CHANNEL_POSITION_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN,
492
[PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SND_MIXER_SCHN_UNKNOWN,
493
[PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SND_MIXER_SCHN_UNKNOWN,
494
[PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SND_MIXER_SCHN_UNKNOWN,
496
[PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SND_MIXER_SCHN_UNKNOWN,
497
[PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SND_MIXER_SCHN_UNKNOWN,
498
[PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SND_MIXER_SCHN_UNKNOWN
501
static void setting_free(pa_alsa_setting *s) {
505
pa_idxset_free(s->options, NULL);
508
pa_xfree(s->description);
512
static void option_free(pa_alsa_option *o) {
515
pa_xfree(o->alsa_name);
517
pa_xfree(o->description);
521
static void decibel_fix_free(pa_alsa_decibel_fix *db_fix) {
524
pa_xfree(db_fix->name);
525
pa_xfree(db_fix->db_values);
530
static void jack_free(pa_alsa_jack *j) {
533
pa_xfree(j->alsa_name);
538
static void element_free(pa_alsa_element *e) {
542
while ((o = e->options)) {
543
PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
548
decibel_fix_free(e->db_fix);
550
pa_xfree(e->alsa_name);
554
void pa_alsa_path_free(pa_alsa_path *p) {
561
while ((j = p->jacks)) {
562
PA_LLIST_REMOVE(pa_alsa_jack, p->jacks, j);
566
while ((e = p->elements)) {
567
PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
571
while ((s = p->settings)) {
572
PA_LLIST_REMOVE(pa_alsa_setting, p->settings, s);
576
pa_proplist_free(p->proplist);
578
pa_xfree(p->description);
579
pa_xfree(p->description_key);
583
void pa_alsa_path_set_free(pa_alsa_path_set *ps) {
587
pa_hashmap_free(ps->paths);
592
static long to_alsa_dB(pa_volume_t v) {
593
return (long) (pa_sw_volume_to_dB(v) * 100.0);
596
static pa_volume_t from_alsa_dB(long v) {
597
return pa_sw_volume_from_dB((double) v / 100.0);
600
static long to_alsa_volume(pa_volume_t v, long min, long max) {
603
w = (long) round(((double) v * (double) (max - min)) / PA_VOLUME_NORM) + min;
604
return PA_CLAMP_UNLIKELY(w, min, max);
607
static pa_volume_t from_alsa_volume(long v, long min, long max) {
608
return (pa_volume_t) round(((double) (v - min) * PA_VOLUME_NORM) / (double) (max - min));
611
#define SELEM_INIT(sid, name) \
613
snd_mixer_selem_id_alloca(&(sid)); \
614
snd_mixer_selem_id_set_name((sid), (name)); \
615
snd_mixer_selem_id_set_index((sid), 0); \
618
static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
619
snd_mixer_selem_id_t *sid;
620
snd_mixer_elem_t *me;
621
snd_mixer_selem_channel_id_t c;
622
pa_channel_position_mask_t mask = 0;
630
SELEM_INIT(sid, e->alsa_name);
631
if (!(me = snd_mixer_find_selem(m, sid))) {
632
pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
636
pa_cvolume_mute(v, cm->channels);
638
/* We take the highest volume of all channels that match */
640
for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
647
if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
648
if (snd_mixer_selem_has_playback_channel(me, c)) {
650
if ((r = snd_mixer_selem_get_playback_volume(me, c, &value)) >= 0) {
651
/* If the channel volume is outside the limits set
652
* by the dB fix, we clamp the hw volume to be
653
* within the limits. */
654
if (value < e->db_fix->min_step) {
655
value = e->db_fix->min_step;
656
snd_mixer_selem_set_playback_volume(me, c, value);
657
pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
658
"Volume reset to %0.2f dB.", e->alsa_name, c,
659
e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
660
} else if (value > e->db_fix->max_step) {
661
value = e->db_fix->max_step;
662
snd_mixer_selem_set_playback_volume(me, c, value);
663
pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
664
"Volume reset to %0.2f dB.", e->alsa_name, c,
665
e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
668
/* Volume step -> dB value conversion. */
669
value = e->db_fix->db_values[value - e->db_fix->min_step];
672
r = snd_mixer_selem_get_playback_dB(me, c, &value);
676
if (snd_mixer_selem_has_capture_channel(me, c)) {
678
if ((r = snd_mixer_selem_get_capture_volume(me, c, &value)) >= 0) {
679
/* If the channel volume is outside the limits set
680
* by the dB fix, we clamp the hw volume to be
681
* within the limits. */
682
if (value < e->db_fix->min_step) {
683
value = e->db_fix->min_step;
684
snd_mixer_selem_set_capture_volume(me, c, value);
685
pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
686
"Volume reset to %0.2f dB.", e->alsa_name, c,
687
e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
688
} else if (value > e->db_fix->max_step) {
689
value = e->db_fix->max_step;
690
snd_mixer_selem_set_capture_volume(me, c, value);
691
pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
692
"Volume reset to %0.2f dB.", e->alsa_name, c,
693
e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
696
/* Volume step -> dB value conversion. */
697
value = e->db_fix->db_values[value - e->db_fix->min_step];
700
r = snd_mixer_selem_get_capture_dB(me, c, &value);
708
#ifdef HAVE_VALGRIND_MEMCHECK_H
709
VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
712
f = from_alsa_dB(value);
717
if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
718
if (snd_mixer_selem_has_playback_channel(me, c))
719
r = snd_mixer_selem_get_playback_volume(me, c, &value);
723
if (snd_mixer_selem_has_capture_channel(me, c))
724
r = snd_mixer_selem_get_capture_volume(me, c, &value);
732
f = from_alsa_volume(value, e->min_volume, e->max_volume);
735
for (k = 0; k < cm->channels; k++)
736
if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
737
if (v->values[k] < f)
740
mask |= e->masks[c][e->n_channels-1];
743
for (k = 0; k < cm->channels; k++)
744
if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
745
v->values[k] = PA_VOLUME_NORM;
750
int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
761
pa_cvolume_reset(v, cm->channels);
763
PA_LLIST_FOREACH(e, p->elements) {
766
if (e->volume_use != PA_ALSA_VOLUME_MERGE)
769
pa_assert(!p->has_dB || e->has_dB);
771
if (element_get_volume(e, m, cm, &ev) < 0)
774
/* If we have no dB information all we can do is take the first element and leave */
780
pa_sw_cvolume_multiply(v, v, &ev);
786
static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, bool *b) {
787
snd_mixer_selem_id_t *sid;
788
snd_mixer_elem_t *me;
789
snd_mixer_selem_channel_id_t c;
795
SELEM_INIT(sid, e->alsa_name);
796
if (!(me = snd_mixer_find_selem(m, sid))) {
797
pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
801
/* We return muted if at least one channel is muted */
803
for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
807
if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
808
if (snd_mixer_selem_has_playback_channel(me, c))
809
r = snd_mixer_selem_get_playback_switch(me, c, &value);
813
if (snd_mixer_selem_has_capture_channel(me, c))
814
r = snd_mixer_selem_get_capture_switch(me, c, &value);
832
int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, bool *muted) {
842
PA_LLIST_FOREACH(e, p->elements) {
845
if (e->switch_use != PA_ALSA_SWITCH_MUTE)
848
if (element_get_switch(e, m, &b) < 0)
861
/* Finds the closest item in db_fix->db_values and returns the corresponding
862
* step. *db_value is replaced with the value from the db_values table.
863
* Rounding is done based on the rounding parameter: -1 means rounding down and
864
* +1 means rounding up. */
865
static long decibel_fix_get_step(pa_alsa_decibel_fix *db_fix, long *db_value, int rounding) {
871
pa_assert(rounding != 0);
873
max_i = db_fix->max_step - db_fix->min_step;
876
for (i = 0; i < max_i; i++) {
877
if (db_fix->db_values[i] >= *db_value)
881
for (i = 0; i < max_i; i++) {
882
if (db_fix->db_values[i + 1] > *db_value)
887
*db_value = db_fix->db_values[i];
889
return i + db_fix->min_step;
892
/* Alsa lib documentation says for snd_mixer_selem_set_playback_dB() direction argument,
893
* that "-1 = accurate or first below, 0 = accurate, 1 = accurate or first above".
894
* But even with accurate nearest dB volume step is not selected, so that is why we need
895
* this function. Returns 0 and nearest selectable volume in *value_dB on success or
896
* negative error code if fails. */
897
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) {
907
if (d == PA_ALSA_DIRECTION_OUTPUT) {
908
if ((r = snd_mixer_selem_ask_playback_dB_vol(me, *value_dB, +1, &alsa_val)) >= 0)
909
r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value_high);
914
if (value_high == *value_dB)
917
if ((r = snd_mixer_selem_ask_playback_dB_vol(me, *value_dB, -1, &alsa_val)) >= 0)
918
r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value_low);
920
if ((r = snd_mixer_selem_ask_capture_dB_vol(me, *value_dB, +1, &alsa_val)) >= 0)
921
r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value_high);
926
if (value_high == *value_dB)
929
if ((r = snd_mixer_selem_ask_capture_dB_vol(me, *value_dB, -1, &alsa_val)) >= 0)
930
r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value_low);
936
if (labs(value_high - *value_dB) < labs(value_low - *value_dB))
937
*value_dB = value_high;
939
*value_dB = value_low;
944
static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, bool deferred_volume, bool write_to_hw) {
946
snd_mixer_selem_id_t *sid;
948
snd_mixer_elem_t *me;
949
snd_mixer_selem_channel_id_t c;
950
pa_channel_position_mask_t mask = 0;
957
pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
959
SELEM_INIT(sid, e->alsa_name);
960
if (!(me = snd_mixer_find_selem(m, sid))) {
961
pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
965
pa_cvolume_mute(&rv, cm->channels);
967
for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
969
pa_volume_t f = PA_VOLUME_MUTED;
972
for (k = 0; k < cm->channels; k++)
973
if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) {
975
if (v->values[k] > f)
980
/* Hmm, so this channel does not exist in the volume
981
* struct, so let's bind it to the overall max of the
983
f = pa_cvolume_max(v);
987
long value = to_alsa_dB(f);
990
if (e->volume_limit >= 0 && value > (e->max_dB * 100))
991
value = e->max_dB * 100;
993
if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
994
/* If we call set_playback_volume() without checking first
995
* if the channel is available, ALSA behaves very
996
* strangely and doesn't fail the call */
997
if (snd_mixer_selem_has_playback_channel(me, c)) {
1001
r = snd_mixer_selem_set_playback_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding));
1003
decibel_fix_get_step(e->db_fix, &value, rounding);
1009
if (deferred_volume) {
1010
if ((r = element_get_nearest_alsa_dB(me, c, PA_ALSA_DIRECTION_OUTPUT, &value)) >= 0)
1011
r = snd_mixer_selem_set_playback_dB(me, c, value, 0);
1013
if ((r = snd_mixer_selem_set_playback_dB(me, c, value, rounding)) >= 0)
1014
r = snd_mixer_selem_get_playback_dB(me, c, &value);
1018
if ((r = snd_mixer_selem_ask_playback_dB_vol(me, value, rounding, &alsa_val)) >= 0)
1019
r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value);
1025
if (snd_mixer_selem_has_capture_channel(me, c)) {
1029
r = snd_mixer_selem_set_capture_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding));
1031
decibel_fix_get_step(e->db_fix, &value, rounding);
1037
if (deferred_volume) {
1038
if ((r = element_get_nearest_alsa_dB(me, c, PA_ALSA_DIRECTION_INPUT, &value)) >= 0)
1039
r = snd_mixer_selem_set_capture_dB(me, c, value, 0);
1041
if ((r = snd_mixer_selem_set_capture_dB(me, c, value, rounding)) >= 0)
1042
r = snd_mixer_selem_get_capture_dB(me, c, &value);
1046
if ((r = snd_mixer_selem_ask_capture_dB_vol(me, value, rounding, &alsa_val)) >= 0)
1047
r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value);
1057
f = from_alsa_dB(value);
1062
value = to_alsa_volume(f, e->min_volume, e->max_volume);
1064
if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1065
if (snd_mixer_selem_has_playback_channel(me, c)) {
1066
if ((r = snd_mixer_selem_set_playback_volume(me, c, value)) >= 0)
1067
r = snd_mixer_selem_get_playback_volume(me, c, &value);
1071
if (snd_mixer_selem_has_capture_channel(me, c)) {
1072
if ((r = snd_mixer_selem_set_capture_volume(me, c, value)) >= 0)
1073
r = snd_mixer_selem_get_capture_volume(me, c, &value);
1081
f = from_alsa_volume(value, e->min_volume, e->max_volume);
1084
for (k = 0; k < cm->channels; k++)
1085
if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
1086
if (rv.values[k] < f)
1089
mask |= e->masks[c][e->n_channels-1];
1092
for (k = 0; k < cm->channels; k++)
1093
if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
1094
rv.values[k] = PA_VOLUME_NORM;
1100
int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, bool deferred_volume, bool write_to_hw) {
1109
pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
1114
rv = *v; /* Remaining adjustment */
1115
pa_cvolume_reset(v, cm->channels); /* Adjustment done */
1117
PA_LLIST_FOREACH(e, p->elements) {
1120
if (e->volume_use != PA_ALSA_VOLUME_MERGE)
1123
pa_assert(!p->has_dB || e->has_dB);
1126
if (element_set_volume(e, m, cm, &ev, deferred_volume, write_to_hw) < 0)
1134
pa_sw_cvolume_multiply(v, v, &ev);
1135
pa_sw_cvolume_divide(&rv, &rv, &ev);
1141
static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, bool b) {
1142
snd_mixer_elem_t *me;
1143
snd_mixer_selem_id_t *sid;
1149
SELEM_INIT(sid, e->alsa_name);
1150
if (!(me = snd_mixer_find_selem(m, sid))) {
1151
pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1155
if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1156
r = snd_mixer_selem_set_playback_switch_all(me, b);
1158
r = snd_mixer_selem_set_capture_switch_all(me, b);
1161
pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1166
int pa_alsa_path_set_mute(pa_alsa_path *p, snd_mixer_t *m, bool muted) {
1175
PA_LLIST_FOREACH(e, p->elements) {
1177
if (e->switch_use != PA_ALSA_SWITCH_MUTE)
1180
if (element_set_switch(e, m, !muted) < 0)
1187
/* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
1188
* function sets all channels of the volume element to e->min_volume, 0 dB or
1189
* e->constant_volume. */
1190
static int element_set_constant_volume(pa_alsa_element *e, snd_mixer_t *m) {
1191
snd_mixer_elem_t *me = NULL;
1192
snd_mixer_selem_id_t *sid = NULL;
1195
bool volume_set = false;
1200
SELEM_INIT(sid, e->alsa_name);
1201
if (!(me = snd_mixer_find_selem(m, sid))) {
1202
pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1206
switch (e->volume_use) {
1207
case PA_ALSA_VOLUME_OFF:
1208
volume = e->min_volume;
1212
case PA_ALSA_VOLUME_ZERO:
1216
volume = decibel_fix_get_step(e->db_fix, &dB, (e->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1));
1221
case PA_ALSA_VOLUME_CONSTANT:
1222
volume = e->constant_volume;
1227
pa_assert_not_reached();
1231
if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1232
r = snd_mixer_selem_set_playback_volume_all(me, volume);
1234
r = snd_mixer_selem_set_capture_volume_all(me, volume);
1236
pa_assert(e->volume_use == PA_ALSA_VOLUME_ZERO);
1237
pa_assert(!e->db_fix);
1239
if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1240
r = snd_mixer_selem_set_playback_dB_all(me, 0, +1);
1242
r = snd_mixer_selem_set_capture_dB_all(me, 0, -1);
1246
pa_log_warn("Failed to set volume of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1251
int pa_alsa_path_select(pa_alsa_path *p, pa_alsa_setting *s, snd_mixer_t *m, bool device_is_muted) {
1258
pa_log_debug("Activating path %s", p->name);
1259
pa_alsa_path_dump(p);
1261
/* First turn on hw mute if available, to avoid noise
1262
* when setting the mixer controls. */
1263
if (p->mute_during_activation) {
1264
PA_LLIST_FOREACH(e, p->elements) {
1265
if (e->switch_use == PA_ALSA_SWITCH_MUTE)
1266
/* If the muting fails here, that's not a critical problem for
1267
* selecting a path, so we ignore the return value.
1268
* element_set_switch() will print a warning anyway, so this
1269
* won't be a silent failure either. */
1270
(void) element_set_switch(e, m, false);
1274
PA_LLIST_FOREACH(e, p->elements) {
1276
switch (e->switch_use) {
1277
case PA_ALSA_SWITCH_OFF:
1278
r = element_set_switch(e, m, false);
1281
case PA_ALSA_SWITCH_ON:
1282
r = element_set_switch(e, m, true);
1285
case PA_ALSA_SWITCH_MUTE:
1286
case PA_ALSA_SWITCH_IGNORE:
1287
case PA_ALSA_SWITCH_SELECT:
1295
switch (e->volume_use) {
1296
case PA_ALSA_VOLUME_OFF:
1297
case PA_ALSA_VOLUME_ZERO:
1298
case PA_ALSA_VOLUME_CONSTANT:
1299
r = element_set_constant_volume(e, m);
1302
case PA_ALSA_VOLUME_MERGE:
1303
case PA_ALSA_VOLUME_IGNORE:
1313
setting_select(s, m);
1315
/* Finally restore hw mute to the device mute status. */
1316
if (p->mute_during_activation) {
1317
PA_LLIST_FOREACH(e, p->elements) {
1318
if (e->switch_use == PA_ALSA_SWITCH_MUTE) {
1319
if (element_set_switch(e, m, !device_is_muted) < 0)
1328
static int check_required(pa_alsa_element *e, snd_mixer_elem_t *me) {
1330
bool has_enumeration;
1336
if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1338
snd_mixer_selem_has_playback_switch(me) ||
1339
(e->direction_try_other && snd_mixer_selem_has_capture_switch(me));
1342
snd_mixer_selem_has_capture_switch(me) ||
1343
(e->direction_try_other && snd_mixer_selem_has_playback_switch(me));
1346
if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1348
snd_mixer_selem_has_playback_volume(me) ||
1349
(e->direction_try_other && snd_mixer_selem_has_capture_volume(me));
1352
snd_mixer_selem_has_capture_volume(me) ||
1353
(e->direction_try_other && snd_mixer_selem_has_playback_volume(me));
1356
has_enumeration = snd_mixer_selem_is_enumerated(me);
1358
if ((e->required == PA_ALSA_REQUIRED_SWITCH && !has_switch) ||
1359
(e->required == PA_ALSA_REQUIRED_VOLUME && !has_volume) ||
1360
(e->required == PA_ALSA_REQUIRED_ENUMERATION && !has_enumeration))
1363
if (e->required == PA_ALSA_REQUIRED_ANY && !(has_switch || has_volume || has_enumeration))
1366
if ((e->required_absent == PA_ALSA_REQUIRED_SWITCH && has_switch) ||
1367
(e->required_absent == PA_ALSA_REQUIRED_VOLUME && has_volume) ||
1368
(e->required_absent == PA_ALSA_REQUIRED_ENUMERATION && has_enumeration))
1371
if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration))
1374
if (e->required_any != PA_ALSA_REQUIRED_IGNORE) {
1375
switch (e->required_any) {
1376
case PA_ALSA_REQUIRED_VOLUME:
1377
e->path->req_any_present |= (e->volume_use != PA_ALSA_VOLUME_IGNORE);
1379
case PA_ALSA_REQUIRED_SWITCH:
1380
e->path->req_any_present |= (e->switch_use != PA_ALSA_SWITCH_IGNORE);
1382
case PA_ALSA_REQUIRED_ENUMERATION:
1383
e->path->req_any_present |= (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
1385
case PA_ALSA_REQUIRED_ANY:
1386
e->path->req_any_present |=
1387
(e->volume_use != PA_ALSA_VOLUME_IGNORE) ||
1388
(e->switch_use != PA_ALSA_SWITCH_IGNORE) ||
1389
(e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
1392
pa_assert_not_reached();
1396
if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1398
PA_LLIST_FOREACH(o, e->options) {
1399
e->path->req_any_present |= (o->required_any != PA_ALSA_REQUIRED_IGNORE) &&
1401
if (o->required != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx < 0)
1403
if (o->required_absent != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx >= 0)
1411
static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
1412
snd_mixer_selem_id_t *sid;
1413
snd_mixer_elem_t *me;
1419
SELEM_INIT(sid, e->alsa_name);
1421
if (!(me = snd_mixer_find_selem(m, sid))) {
1423
if (e->required != PA_ALSA_REQUIRED_IGNORE)
1426
e->switch_use = PA_ALSA_SWITCH_IGNORE;
1427
e->volume_use = PA_ALSA_VOLUME_IGNORE;
1428
e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1433
if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
1434
if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1436
if (!snd_mixer_selem_has_playback_switch(me)) {
1437
if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
1438
e->direction = PA_ALSA_DIRECTION_INPUT;
1440
e->switch_use = PA_ALSA_SWITCH_IGNORE;
1445
if (!snd_mixer_selem_has_capture_switch(me)) {
1446
if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
1447
e->direction = PA_ALSA_DIRECTION_OUTPUT;
1449
e->switch_use = PA_ALSA_SWITCH_IGNORE;
1453
if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
1454
e->direction_try_other = false;
1457
if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1459
if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1461
if (!snd_mixer_selem_has_playback_volume(me)) {
1462
if (e->direction_try_other && snd_mixer_selem_has_capture_volume(me))
1463
e->direction = PA_ALSA_DIRECTION_INPUT;
1465
e->volume_use = PA_ALSA_VOLUME_IGNORE;
1470
if (!snd_mixer_selem_has_capture_volume(me)) {
1471
if (e->direction_try_other && snd_mixer_selem_has_playback_volume(me))
1472
e->direction = PA_ALSA_DIRECTION_OUTPUT;
1474
e->volume_use = PA_ALSA_VOLUME_IGNORE;
1478
if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1479
long min_dB = 0, max_dB = 0;
1482
e->direction_try_other = false;
1484
if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1485
r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume);
1487
r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
1490
pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1494
if (e->min_volume >= e->max_volume) {
1495
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);
1496
e->volume_use = PA_ALSA_VOLUME_IGNORE;
1498
} else if (e->volume_use == PA_ALSA_VOLUME_CONSTANT &&
1499
(e->min_volume > e->constant_volume || e->max_volume < e->constant_volume)) {
1500
pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
1501
e->constant_volume, e->alsa_name, e->min_volume, e->max_volume);
1502
e->volume_use = PA_ALSA_VOLUME_IGNORE;
1506
pa_channel_position_t p;
1509
((e->min_volume > e->db_fix->min_step) ||
1510
(e->max_volume < e->db_fix->max_step))) {
1511
pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1512
"real hardware range (%li-%li). Disabling the decibel fix.", e->alsa_name,
1513
e->db_fix->min_step, e->db_fix->max_step,
1514
e->min_volume, e->max_volume);
1516
decibel_fix_free(e->db_fix);
1522
e->min_volume = e->db_fix->min_step;
1523
e->max_volume = e->db_fix->max_step;
1524
min_dB = e->db_fix->db_values[0];
1525
max_dB = e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step];
1526
} else if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1527
e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0;
1529
e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0;
1531
/* Check that the kernel driver returns consistent limits with
1532
* both _get_*_dB_range() and _ask_*_vol_dB(). */
1533
if (e->has_dB && !e->db_fix) {
1534
long min_dB_checked = 0;
1535
long max_dB_checked = 0;
1537
if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1538
r = snd_mixer_selem_ask_playback_vol_dB(me, e->min_volume, &min_dB_checked);
1540
r = snd_mixer_selem_ask_capture_vol_dB(me, e->min_volume, &min_dB_checked);
1543
pa_log_warn("Failed to query the dB value for %s at volume level %li", e->alsa_name, e->min_volume);
1547
if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1548
r = snd_mixer_selem_ask_playback_vol_dB(me, e->max_volume, &max_dB_checked);
1550
r = snd_mixer_selem_ask_capture_vol_dB(me, e->max_volume, &max_dB_checked);
1553
pa_log_warn("Failed to query the dB value for %s at volume level %li", e->alsa_name, e->max_volume);
1557
if (min_dB != min_dB_checked || max_dB != max_dB_checked) {
1558
pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
1559
"doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
1560
"%0.2f dB at level %li.",
1562
min_dB / 100.0, max_dB / 100.0,
1563
min_dB_checked / 100.0, e->min_volume, max_dB_checked / 100.0, e->max_volume);
1569
e->min_dB = ((double) min_dB) / 100.0;
1570
e->max_dB = ((double) max_dB) / 100.0;
1572
if (min_dB >= max_dB) {
1573
pa_assert(!e->db_fix);
1574
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);
1579
if (e->volume_limit >= 0) {
1580
if (e->volume_limit <= e->min_volume || e->volume_limit > e->max_volume)
1581
pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1582
"%li-%li. The volume limit is ignored.",
1583
e->alsa_name, e->path->name, e->volume_limit, e->min_volume + 1, e->max_volume);
1586
e->max_volume = e->volume_limit;
1590
e->db_fix->max_step = e->max_volume;
1591
e->max_dB = ((double) e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step]) / 100.0;
1594
if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1595
r = snd_mixer_selem_ask_playback_vol_dB(me, e->max_volume, &max_dB);
1597
r = snd_mixer_selem_ask_capture_vol_dB(me, e->max_volume, &max_dB);
1600
pa_log_warn("Failed to get dB value of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1603
e->max_dB = ((double) max_dB) / 100.0;
1609
if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1610
is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
1612
is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
1617
if (!e->override_map) {
1618
for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1619
if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1622
e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
1625
e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
1628
e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
1631
for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1633
if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1636
if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1637
e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1639
e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1642
if (e->n_channels <= 0) {
1643
pa_log_warn("Volume element %s with no channels?", e->alsa_name);
1647
if (e->n_channels > 2) {
1648
/* FIXME: In some places code like this is used:
1650
* e->masks[alsa_channel_ids[p]][e->n_channels-1]
1652
* The definition of e->masks is
1654
* pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][2];
1656
* Since the array size is fixed at 2, we obviously
1657
* don't support elements with more than two
1659
pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e->alsa_name, e->n_channels);
1663
if (!e->override_map) {
1664
for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1667
if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1670
if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1671
has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1673
has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1675
e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
1680
for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1681
if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1684
e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
1692
if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1695
PA_LLIST_FOREACH(o, e->options)
1696
o->alsa_idx = pa_streq(o->alsa_name, "on") ? 1 : 0;
1697
} else if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1701
if ((n = snd_mixer_selem_get_enum_items(me)) < 0) {
1702
pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n));
1706
PA_LLIST_FOREACH(o, e->options) {
1709
for (i = 0; i < n; i++) {
1712
if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
1715
if (!pa_streq(buf, o->alsa_name))
1723
if (check_required(e, me) < 0)
1729
static int jack_probe(pa_alsa_jack *j, snd_mixer_t *m) {
1733
j->has_control = pa_alsa_mixer_find(m, j->alsa_name, 0) != NULL;
1735
if (j->has_control) {
1736
if (j->required_absent != PA_ALSA_REQUIRED_IGNORE)
1738
if (j->required_any != PA_ALSA_REQUIRED_IGNORE)
1739
j->path->req_any_present = true;
1741
if (j->required != PA_ALSA_REQUIRED_IGNORE)
1748
static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, bool prefixed) {
1755
if (!pa_startswith(section, "Element "))
1761
/* This is not an element section, but an enum section? */
1762
if (strchr(section, ':'))
1765
if (p->last_element && pa_streq(p->last_element->alsa_name, section))
1766
return p->last_element;
1768
PA_LLIST_FOREACH(e, p->elements)
1769
if (pa_streq(e->alsa_name, section))
1772
e = pa_xnew0(pa_alsa_element, 1);
1774
e->alsa_name = pa_xstrdup(section);
1775
e->direction = p->direction;
1776
e->volume_limit = -1;
1778
PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
1781
p->last_element = e;
1785
static pa_alsa_jack* jack_get(pa_alsa_path *p, const char *section) {
1788
if (!pa_startswith(section, "Jack "))
1792
if (p->last_jack && pa_streq(p->last_jack->name, section))
1793
return p->last_jack;
1795
PA_LLIST_FOREACH(j, p->jacks)
1796
if (pa_streq(j->name, section))
1799
j = pa_xnew0(pa_alsa_jack, 1);
1800
j->state_unplugged = PA_AVAILABLE_NO;
1801
j->state_plugged = PA_AVAILABLE_YES;
1803
j->name = pa_xstrdup(section);
1804
j->alsa_name = pa_sprintf_malloc("%s Jack", section);
1805
PA_LLIST_INSERT_AFTER(pa_alsa_jack, p->jacks, p->last_jack, j);
1812
static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
1818
if (!pa_startswith(section, "Option "))
1823
/* This is not an enum section, but an element section? */
1824
if (!(on = strchr(section, ':')))
1827
en = pa_xstrndup(section, on - section);
1830
if (p->last_option &&
1831
pa_streq(p->last_option->element->alsa_name, en) &&
1832
pa_streq(p->last_option->alsa_name, on)) {
1834
return p->last_option;
1837
pa_assert_se(e = element_get(p, en, false));
1840
PA_LLIST_FOREACH(o, e->options)
1841
if (pa_streq(o->alsa_name, on))
1844
o = pa_xnew0(pa_alsa_option, 1);
1846
o->alsa_name = pa_xstrdup(on);
1849
if (p->last_option && p->last_option->element == e)
1850
PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
1852
PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
1859
static int element_parse_switch(pa_config_parser_state *state) {
1865
p = state->userdata;
1867
if (!(e = element_get(p, state->section, true))) {
1868
pa_log("[%s:%u] Switch makes no sense in '%s'", state->filename, state->lineno, state->section);
1872
if (pa_streq(state->rvalue, "ignore"))
1873
e->switch_use = PA_ALSA_SWITCH_IGNORE;
1874
else if (pa_streq(state->rvalue, "mute"))
1875
e->switch_use = PA_ALSA_SWITCH_MUTE;
1876
else if (pa_streq(state->rvalue, "off"))
1877
e->switch_use = PA_ALSA_SWITCH_OFF;
1878
else if (pa_streq(state->rvalue, "on"))
1879
e->switch_use = PA_ALSA_SWITCH_ON;
1880
else if (pa_streq(state->rvalue, "select"))
1881
e->switch_use = PA_ALSA_SWITCH_SELECT;
1883
pa_log("[%s:%u] Switch invalid of '%s'", state->filename, state->lineno, state->section);
1890
static int element_parse_volume(pa_config_parser_state *state) {
1896
p = state->userdata;
1898
if (!(e = element_get(p, state->section, true))) {
1899
pa_log("[%s:%u] Volume makes no sense in '%s'", state->filename, state->lineno, state->section);
1903
if (pa_streq(state->rvalue, "ignore"))
1904
e->volume_use = PA_ALSA_VOLUME_IGNORE;
1905
else if (pa_streq(state->rvalue, "merge"))
1906
e->volume_use = PA_ALSA_VOLUME_MERGE;
1907
else if (pa_streq(state->rvalue, "off"))
1908
e->volume_use = PA_ALSA_VOLUME_OFF;
1909
else if (pa_streq(state->rvalue, "zero"))
1910
e->volume_use = PA_ALSA_VOLUME_ZERO;
1914
if (pa_atou(state->rvalue, &constant) >= 0) {
1915
e->volume_use = PA_ALSA_VOLUME_CONSTANT;
1916
e->constant_volume = constant;
1918
pa_log("[%s:%u] Volume invalid of '%s'", state->filename, state->lineno, state->section);
1926
static int element_parse_enumeration(pa_config_parser_state *state) {
1932
p = state->userdata;
1934
if (!(e = element_get(p, state->section, true))) {
1935
pa_log("[%s:%u] Enumeration makes no sense in '%s'", state->filename, state->lineno, state->section);
1939
if (pa_streq(state->rvalue, "ignore"))
1940
e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1941
else if (pa_streq(state->rvalue, "select"))
1942
e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
1944
pa_log("[%s:%u] Enumeration invalid of '%s'", state->filename, state->lineno, state->section);
1951
static int option_parse_priority(pa_config_parser_state *state) {
1958
p = state->userdata;
1960
if (!(o = option_get(p, state->section))) {
1961
pa_log("[%s:%u] Priority makes no sense in '%s'", state->filename, state->lineno, state->section);
1965
if (pa_atou(state->rvalue, &prio) < 0) {
1966
pa_log("[%s:%u] Priority invalid of '%s'", state->filename, state->lineno, state->section);
1974
static int option_parse_name(pa_config_parser_state *state) {
1980
p = state->userdata;
1982
if (!(o = option_get(p, state->section))) {
1983
pa_log("[%s:%u] Name makes no sense in '%s'", state->filename, state->lineno, state->section);
1988
o->name = pa_xstrdup(state->rvalue);
1993
static int element_parse_required(pa_config_parser_state *state) {
1998
pa_alsa_required_t req;
2002
p = state->userdata;
2004
e = element_get(p, state->section, true);
2005
o = option_get(p, state->section);
2006
j = jack_get(p, state->section);
2007
if (!e && !o && !j) {
2008
pa_log("[%s:%u] Required makes no sense in '%s'", state->filename, state->lineno, state->section);
2012
if (pa_streq(state->rvalue, "ignore"))
2013
req = PA_ALSA_REQUIRED_IGNORE;
2014
else if (pa_streq(state->rvalue, "switch") && e)
2015
req = PA_ALSA_REQUIRED_SWITCH;
2016
else if (pa_streq(state->rvalue, "volume") && e)
2017
req = PA_ALSA_REQUIRED_VOLUME;
2018
else if (pa_streq(state->rvalue, "enumeration"))
2019
req = PA_ALSA_REQUIRED_ENUMERATION;
2020
else if (pa_streq(state->rvalue, "any"))
2021
req = PA_ALSA_REQUIRED_ANY;
2023
pa_log("[%s:%u] Required invalid of '%s'", state->filename, state->lineno, state->section);
2027
if (pa_streq(state->lvalue, "required-absent")) {
2029
e->required_absent = req;
2031
o->required_absent = req;
2033
j->required_absent = req;
2035
else if (pa_streq(state->lvalue, "required-any")) {
2037
e->required_any = req;
2038
e->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
2041
o->required_any = req;
2042
o->element->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
2045
j->required_any = req;
2046
j->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
2062
static int element_parse_direction(pa_config_parser_state *state) {
2068
p = state->userdata;
2070
if (!(e = element_get(p, state->section, true))) {
2071
pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section);
2075
if (pa_streq(state->rvalue, "playback"))
2076
e->direction = PA_ALSA_DIRECTION_OUTPUT;
2077
else if (pa_streq(state->rvalue, "capture"))
2078
e->direction = PA_ALSA_DIRECTION_INPUT;
2080
pa_log("[%s:%u] Direction invalid of '%s'", state->filename, state->lineno, state->section);
2087
static int element_parse_direction_try_other(pa_config_parser_state *state) {
2094
p = state->userdata;
2096
if (!(e = element_get(p, state->section, true))) {
2097
pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section);
2101
if ((yes = pa_parse_boolean(state->rvalue)) < 0) {
2102
pa_log("[%s:%u] Direction invalid of '%s'", state->filename, state->lineno, state->section);
2106
e->direction_try_other = !!yes;
2110
static int element_parse_volume_limit(pa_config_parser_state *state) {
2117
p = state->userdata;
2119
if (!(e = element_get(p, state->section, true))) {
2120
pa_log("[%s:%u] volume-limit makes no sense in '%s'", state->filename, state->lineno, state->section);
2124
if (pa_atol(state->rvalue, &volume_limit) < 0 || volume_limit < 0) {
2125
pa_log("[%s:%u] Invalid value for volume-limit", state->filename, state->lineno);
2129
e->volume_limit = volume_limit;
2133
static pa_channel_position_mask_t parse_mask(const char *m) {
2134
pa_channel_position_mask_t v;
2136
if (pa_streq(m, "all-left"))
2137
v = PA_CHANNEL_POSITION_MASK_LEFT;
2138
else if (pa_streq(m, "all-right"))
2139
v = PA_CHANNEL_POSITION_MASK_RIGHT;
2140
else if (pa_streq(m, "all-center"))
2141
v = PA_CHANNEL_POSITION_MASK_CENTER;
2142
else if (pa_streq(m, "all-front"))
2143
v = PA_CHANNEL_POSITION_MASK_FRONT;
2144
else if (pa_streq(m, "all-rear"))
2145
v = PA_CHANNEL_POSITION_MASK_REAR;
2146
else if (pa_streq(m, "all-side"))
2147
v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER;
2148
else if (pa_streq(m, "all-top"))
2149
v = PA_CHANNEL_POSITION_MASK_TOP;
2150
else if (pa_streq(m, "all-no-lfe"))
2151
v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE);
2152
else if (pa_streq(m, "all"))
2153
v = PA_CHANNEL_POSITION_MASK_ALL;
2155
pa_channel_position_t p;
2157
if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
2160
v = PA_CHANNEL_POSITION_MASK(p);
2166
static int element_parse_override_map(pa_config_parser_state *state) {
2169
const char *split_state = NULL;
2175
p = state->userdata;
2177
if (!(e = element_get(p, state->section, true))) {
2178
pa_log("[%s:%u] Override map makes no sense in '%s'", state->filename, state->lineno, state->section);
2182
while ((n = pa_split(state->rvalue, ",", &split_state))) {
2183
pa_channel_position_mask_t m;
2188
if ((m = parse_mask(n)) == 0) {
2189
pa_log("[%s:%u] Override map '%s' invalid in '%s'", state->filename, state->lineno, n, state->section);
2195
if (pa_streq(state->lvalue, "override-map.1"))
2196
e->masks[i++][0] = m;
2198
e->masks[i++][1] = m;
2200
/* Later on we might add override-map.3 and so on here ... */
2205
e->override_map = true;
2210
static int jack_parse_state(pa_config_parser_state *state) {
2217
p = state->userdata;
2219
if (!(j = jack_get(p, state->section))) {
2220
pa_log("[%s:%u] state makes no sense in '%s'", state->filename, state->lineno, state->section);
2224
if (pa_streq(state->rvalue, "yes"))
2225
pa = PA_AVAILABLE_YES;
2226
else if (pa_streq(state->rvalue, "no"))
2227
pa = PA_AVAILABLE_NO;
2228
else if (pa_streq(state->rvalue, "unknown"))
2229
pa = PA_AVAILABLE_UNKNOWN;
2231
pa_log("[%s:%u] state must be 'yes', 'no' or 'unknown' in '%s'", state->filename, state->lineno, state->section);
2235
if (pa_streq(state->lvalue, "state.unplugged"))
2236
j->state_unplugged = pa;
2238
j->state_plugged = pa;
2239
pa_assert(pa_streq(state->lvalue, "state.plugged"));
2245
static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
2246
snd_mixer_selem_id_t *sid;
2247
snd_mixer_elem_t *me;
2253
SELEM_INIT(sid, e->alsa_name);
2254
if (!(me = snd_mixer_find_selem(m, sid))) {
2255
pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2259
if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
2261
if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
2262
r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
2264
r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
2267
pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
2270
pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
2272
if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0)
2273
pa_log_warn("Failed to set enumeration of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
2279
static int setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
2286
PA_IDXSET_FOREACH(o, s->options, idx)
2287
element_set_option(o->element, m, o->alsa_idx);
2292
static int option_verify(pa_alsa_option *o) {
2293
static const struct description_map well_known_descriptions[] = {
2294
{ "input", N_("Input") },
2295
{ "input-docking", N_("Docking Station Input") },
2296
{ "input-docking-microphone", N_("Docking Station Microphone") },
2297
{ "input-docking-linein", N_("Docking Station Line In") },
2298
{ "input-linein", N_("Line In") },
2299
{ "input-microphone", N_("Microphone") },
2300
{ "input-microphone-front", N_("Front Microphone") },
2301
{ "input-microphone-rear", N_("Rear Microphone") },
2302
{ "input-microphone-external", N_("External Microphone") },
2303
{ "input-microphone-internal", N_("Internal Microphone") },
2304
{ "input-radio", N_("Radio") },
2305
{ "input-video", N_("Video") },
2306
{ "input-agc-on", N_("Automatic Gain Control") },
2307
{ "input-agc-off", N_("No Automatic Gain Control") },
2308
{ "input-boost-on", N_("Boost") },
2309
{ "input-boost-off", N_("No Boost") },
2310
{ "output-amplifier-on", N_("Amplifier") },
2311
{ "output-amplifier-off", N_("No Amplifier") },
2312
{ "output-bass-boost-on", N_("Bass Boost") },
2313
{ "output-bass-boost-off", N_("No Bass Boost") },
2314
{ "output-speaker", N_("Speaker") },
2315
{ "output-headphones", N_("Headphones") }
2321
pa_log("No name set for option %s", o->alsa_name);
2325
if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
2326
o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
2327
pa_log("Element %s of option %s not set for select.", o->element->alsa_name, o->name);
2331
if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
2332
!pa_streq(o->alsa_name, "on") &&
2333
!pa_streq(o->alsa_name, "off")) {
2334
pa_log("Switch %s options need be named off or on ", o->element->alsa_name);
2338
if (!o->description)
2339
o->description = pa_xstrdup(lookup_description(o->name,
2340
well_known_descriptions,
2341
PA_ELEMENTSOF(well_known_descriptions)));
2342
if (!o->description)
2343
o->description = pa_xstrdup(o->name);
2348
static int element_verify(pa_alsa_element *e) {
2353
// 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);
2354
if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
2355
(e->required_any != PA_ALSA_REQUIRED_IGNORE && e->required_any == e->required_absent) ||
2356
(e->required_absent == PA_ALSA_REQUIRED_ANY && e->required_any != PA_ALSA_REQUIRED_IGNORE) ||
2357
(e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
2358
pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name);
2362
if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
2363
pa_log("Element %s cannot set select for both switch and enumeration.", e->alsa_name);
2367
PA_LLIST_FOREACH(o, e->options)
2368
if (option_verify(o) < 0)
2374
static int path_verify(pa_alsa_path *p) {
2375
static const struct description_map well_known_descriptions[] = {
2376
{ "analog-input", N_("Analog Input") },
2377
{ "analog-input-microphone", N_("Microphone") },
2378
{ "analog-input-microphone-front", N_("Front Microphone") },
2379
{ "analog-input-microphone-rear", N_("Rear Microphone") },
2380
{ "analog-input-microphone-dock", N_("Dock Microphone") },
2381
{ "analog-input-microphone-internal", N_("Internal Microphone") },
2382
{ "analog-input-microphone-headset", N_("Headset Microphone") },
2383
{ "analog-input-linein", N_("Line In") },
2384
{ "analog-input-radio", N_("Radio") },
2385
{ "analog-input-video", N_("Video") },
2386
{ "analog-output", N_("Analog Output") },
2387
{ "analog-output-headphones", N_("Headphones") },
2388
{ "analog-output-lfe-on-mono", N_("LFE on Separate Mono Output") },
2389
{ "analog-output-lineout", N_("Line Out") },
2390
{ "analog-output-mono", N_("Analog Mono Output") },
2391
{ "analog-output-speaker", N_("Speakers") },
2392
{ "hdmi-output", N_("HDMI / DisplayPort") },
2393
{ "iec958-stereo-output", N_("Digital Output (S/PDIF)") },
2394
{ "iec958-stereo-input", N_("Digital Input (S/PDIF)") },
2395
{ "iec958-passthrough-output", N_("Digital Passthrough (S/PDIF)") }
2402
PA_LLIST_FOREACH(e, p->elements)
2403
if (element_verify(e) < 0)
2406
if (!p->description)
2407
p->description = pa_xstrdup(lookup_description(p->description_key ? p->description_key : p->name,
2408
well_known_descriptions,
2409
PA_ELEMENTSOF(well_known_descriptions)));
2411
if (!p->description) {
2412
if (p->description_key)
2413
pa_log_warn("Path %s: Unrecognized description key: %s", p->name, p->description_key);
2415
p->description = pa_xstrdup(p->name);
2421
static const char *get_default_paths_dir(void) {
2422
if (pa_run_from_build_tree())
2423
return PA_SRCDIR "/modules/alsa/mixer/paths/";
2425
return PA_ALSA_PATHS_DIR;
2428
pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa_direction_t direction) {
2433
bool mute_during_activation = false;
2435
pa_config_item items[] = {
2437
{ "priority", pa_config_parse_unsigned, NULL, "General" },
2438
{ "description-key", pa_config_parse_string, NULL, "General" },
2439
{ "description", pa_config_parse_string, NULL, "General" },
2440
{ "mute-during-activation", pa_config_parse_bool, NULL, "General" },
2441
{ "eld-device", pa_config_parse_int, NULL, "General" },
2444
{ "priority", option_parse_priority, NULL, NULL },
2445
{ "name", option_parse_name, NULL, NULL },
2448
{ "state.plugged", jack_parse_state, NULL, NULL },
2449
{ "state.unplugged", jack_parse_state, NULL, NULL },
2452
{ "switch", element_parse_switch, NULL, NULL },
2453
{ "volume", element_parse_volume, NULL, NULL },
2454
{ "enumeration", element_parse_enumeration, NULL, NULL },
2455
{ "override-map.1", element_parse_override_map, NULL, NULL },
2456
{ "override-map.2", element_parse_override_map, NULL, NULL },
2457
/* ... later on we might add override-map.3 and so on here ... */
2458
{ "required", element_parse_required, NULL, NULL },
2459
{ "required-any", element_parse_required, NULL, NULL },
2460
{ "required-absent", element_parse_required, NULL, NULL },
2461
{ "direction", element_parse_direction, NULL, NULL },
2462
{ "direction-try-other", element_parse_direction_try_other, NULL, NULL },
2463
{ "volume-limit", element_parse_volume_limit, NULL, NULL },
2464
{ NULL, NULL, NULL, NULL }
2469
p = pa_xnew0(pa_alsa_path, 1);
2470
n = pa_path_get_filename(fname);
2471
p->name = pa_xstrndup(n, strcspn(n, "."));
2472
p->proplist = pa_proplist_new();
2473
p->direction = direction;
2476
items[0].data = &p->priority;
2477
items[1].data = &p->description_key;
2478
items[2].data = &p->description;
2479
items[3].data = &mute_during_activation;
2480
items[4].data = &p->eld_device;
2483
paths_dir = get_default_paths_dir();
2485
fn = pa_maybe_prefix_path(fname, paths_dir);
2487
r = pa_config_parse(fn, NULL, items, p->proplist, p);
2493
p->mute_during_activation = mute_during_activation;
2495
if (path_verify(p) < 0)
2501
pa_alsa_path_free(p);
2505
pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction) {
2511
p = pa_xnew0(pa_alsa_path, 1);
2512
p->name = pa_xstrdup(element);
2513
p->direction = direction;
2515
e = pa_xnew0(pa_alsa_element, 1);
2517
e->alsa_name = pa_xstrdup(element);
2518
e->direction = direction;
2519
e->volume_limit = -1;
2521
e->switch_use = PA_ALSA_SWITCH_MUTE;
2522
e->volume_use = PA_ALSA_VOLUME_MERGE;
2524
PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
2525
p->last_element = e;
2529
static bool element_drop_unsupported(pa_alsa_element *e) {
2530
pa_alsa_option *o, *n;
2534
for (o = e->options; o; o = n) {
2537
if (o->alsa_idx < 0) {
2538
PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
2544
e->switch_use != PA_ALSA_SWITCH_IGNORE ||
2545
e->volume_use != PA_ALSA_VOLUME_IGNORE ||
2546
e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
2549
static void path_drop_unsupported(pa_alsa_path *p) {
2550
pa_alsa_element *e, *n;
2554
for (e = p->elements; e; e = n) {
2557
if (!element_drop_unsupported(e)) {
2558
PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
2564
static void path_make_options_unique(pa_alsa_path *p) {
2566
pa_alsa_option *o, *u;
2568
PA_LLIST_FOREACH(e, p->elements) {
2569
PA_LLIST_FOREACH(o, e->options) {
2573
for (u = o->next; u; u = u->next)
2574
if (pa_streq(u->name, o->name))
2580
m = pa_xstrdup(o->name);
2582
/* OK, this name is not unique, hence let's rename */
2583
for (i = 1, u = o; u; u = u->next) {
2586
if (!pa_streq(u->name, m))
2589
nn = pa_sprintf_malloc("%s-%u", m, i);
2593
nd = pa_sprintf_malloc("%s %u", u->description, i);
2594
pa_xfree(u->description);
2595
u->description = nd;
2605
static bool element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
2608
for (; e; e = e->next)
2609
if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
2610
e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
2616
for (o = e->options; o; o = o->next) {
2620
s = pa_xnewdup(pa_alsa_setting, template, 1);
2621
s->options = pa_idxset_copy(template->options, NULL);
2622
s->name = pa_sprintf_malloc("%s+%s", template->name, o->name);
2624
(template->description[0] && o->description[0])
2625
? pa_sprintf_malloc("%s / %s", template->description, o->description)
2626
: (template->description[0]
2627
? pa_xstrdup(template->description)
2628
: pa_xstrdup(o->description));
2630
s->priority = PA_MAX(template->priority, o->priority);
2632
s = pa_xnew0(pa_alsa_setting, 1);
2633
s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2634
s->name = pa_xstrdup(o->name);
2635
s->description = pa_xstrdup(o->description);
2636
s->priority = o->priority;
2639
pa_idxset_put(s->options, o, NULL);
2641
if (element_create_settings(e->next, s))
2642
/* This is not a leaf, so let's get rid of it */
2645
/* This is a leaf, so let's add it */
2646
PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2648
e->path->last_setting = s;
2655
static void path_create_settings(pa_alsa_path *p) {
2658
element_create_settings(p->elements, NULL);
2661
int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, bool ignore_dB) {
2664
double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2665
pa_channel_position_t t;
2666
pa_channel_position_mask_t path_volume_channels = 0;
2672
return p->supported ? 0 : -1;
2678
pa_log_debug("Probing path '%s'", p->name);
2680
PA_LLIST_FOREACH(j, p->jacks) {
2681
if (jack_probe(j, m) < 0) {
2682
p->supported = false;
2683
pa_log_debug("Probe of jack '%s' failed.", j->alsa_name);
2686
pa_log_debug("Probe of jack '%s' succeeded (%s)", j->alsa_name, j->has_control ? "found!" : "not found");
2689
PA_LLIST_FOREACH(e, p->elements) {
2690
if (element_probe(e, m) < 0) {
2691
p->supported = false;
2692
pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2695
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);
2700
if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2702
if (!p->has_volume) {
2703
p->min_volume = e->min_volume;
2704
p->max_volume = e->max_volume;
2708
if (!p->has_volume) {
2709
for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2710
if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2711
min_dB[t] = e->min_dB;
2712
max_dB[t] = e->max_dB;
2713
path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2720
for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2721
if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2722
min_dB[t] += e->min_dB;
2723
max_dB[t] += e->max_dB;
2724
path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2727
/* Hmm, there's another element before us
2728
* which cannot do dB volumes, so we we need
2729
* to 'neutralize' this slider */
2730
e->volume_use = PA_ALSA_VOLUME_ZERO;
2731
pa_log_info("Zeroing volume of '%s' on path '%s'", e->alsa_name, p->name);
2734
} else if (p->has_volume) {
2735
/* We can't use this volume, so let's ignore it */
2736
e->volume_use = PA_ALSA_VOLUME_IGNORE;
2737
pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e->alsa_name, p->name);
2739
p->has_volume = true;
2742
if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2746
if (p->has_req_any && !p->req_any_present) {
2747
p->supported = false;
2748
pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name);
2752
path_drop_unsupported(p);
2753
path_make_options_unique(p);
2754
path_create_settings(p);
2756
p->supported = true;
2758
p->min_dB = INFINITY;
2759
p->max_dB = -INFINITY;
2761
for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2762
if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) {
2763
if (p->min_dB > min_dB[t])
2764
p->min_dB = min_dB[t];
2766
if (p->max_dB < max_dB[t])
2767
p->max_dB = max_dB[t];
2774
void pa_alsa_setting_dump(pa_alsa_setting *s) {
2777
pa_log_debug("Setting %s (%s) priority=%u",
2779
pa_strnull(s->description),
2783
void pa_alsa_jack_dump(pa_alsa_jack *j) {
2786
pa_log_debug("Jack %s, alsa_name='%s', detection %s", j->name, j->alsa_name, j->has_control ? "possible" : "unavailable");
2789
void pa_alsa_option_dump(pa_alsa_option *o) {
2792
pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2794
pa_strnull(o->name),
2795
pa_strnull(o->description),
2800
void pa_alsa_element_dump(pa_alsa_element *e) {
2804
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",
2814
(long long unsigned) e->merged_mask,
2816
pa_yes_no(e->override_map));
2818
PA_LLIST_FOREACH(o, e->options)
2819
pa_alsa_option_dump(o);
2822
void pa_alsa_path_dump(pa_alsa_path *p) {
2828
pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2829
"has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2831
pa_strnull(p->description),
2834
pa_yes_no(p->probed),
2835
pa_yes_no(p->supported),
2836
pa_yes_no(p->has_mute),
2837
pa_yes_no(p->has_volume),
2838
pa_yes_no(p->has_dB),
2839
p->min_volume, p->max_volume,
2840
p->min_dB, p->max_dB);
2842
PA_LLIST_FOREACH(e, p->elements)
2843
pa_alsa_element_dump(e);
2845
PA_LLIST_FOREACH(j, p->jacks)
2846
pa_alsa_jack_dump(j);
2848
PA_LLIST_FOREACH(s, p->settings)
2849
pa_alsa_setting_dump(s);
2852
static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2853
snd_mixer_selem_id_t *sid;
2854
snd_mixer_elem_t *me;
2860
SELEM_INIT(sid, e->alsa_name);
2861
if (!(me = snd_mixer_find_selem(m, sid))) {
2862
pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2866
snd_mixer_elem_set_callback(me, cb);
2867
snd_mixer_elem_set_callback_private(me, userdata);
2870
void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2877
PA_LLIST_FOREACH(e, p->elements)
2878
element_set_callback(e, m, cb, userdata);
2881
void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2889
PA_HASHMAP_FOREACH(p, ps->paths, state)
2890
pa_alsa_path_set_callback(p, m, cb, userdata);
2893
static pa_alsa_path *profile_set_get_path(pa_alsa_profile_set *ps, const char *path_name) {
2897
pa_assert(path_name);
2899
if ((path = pa_hashmap_get(ps->output_paths, path_name)))
2902
return pa_hashmap_get(ps->input_paths, path_name);
2905
static void profile_set_add_path(pa_alsa_profile_set *ps, pa_alsa_path *path) {
2909
switch (path->direction) {
2910
case PA_ALSA_DIRECTION_OUTPUT:
2911
pa_assert_se(pa_hashmap_put(ps->output_paths, path->name, path) >= 0);
2914
case PA_ALSA_DIRECTION_INPUT:
2915
pa_assert_se(pa_hashmap_put(ps->input_paths, path->name, path) >= 0);
2919
pa_assert_not_reached();
2923
pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction, const char *paths_dir) {
2924
pa_alsa_path_set *ps;
2925
char **pn = NULL, **en = NULL, **ie;
2926
pa_alsa_decibel_fix *db_fix;
2927
void *state, *state2;
2930
pa_assert(m->profile_set);
2931
pa_assert(m->profile_set->decibel_fixes);
2932
pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2934
if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2937
ps = pa_xnew0(pa_alsa_path_set, 1);
2938
ps->direction = direction;
2939
ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2941
if (direction == PA_ALSA_DIRECTION_OUTPUT)
2942
pn = m->output_path_names;
2944
pn = m->input_path_names;
2949
for (in = pn; *in; in++) {
2950
pa_alsa_path *p = NULL;
2951
bool duplicate = false;
2954
for (kn = pn; kn < in; kn++)
2955
if (pa_streq(*kn, *in)) {
2963
p = profile_set_get_path(m->profile_set, *in);
2965
if (p && p->direction != direction) {
2966
pa_log("Configuration error: Path %s is used both as an input and as an output path.", p->name);
2971
char *fn = pa_sprintf_malloc("%s.conf", *in);
2972
p = pa_alsa_path_new(paths_dir, fn, direction);
2975
profile_set_add_path(m->profile_set, p);
2979
pa_hashmap_put(ps->paths, p, p);
2986
if (direction == PA_ALSA_DIRECTION_OUTPUT)
2987
en = m->output_element;
2989
en = m->input_element;
2994
for (ie = en; *ie; ie++) {
2998
p = pa_alsa_path_synthesize(*ie, direction);
3000
/* Mark all other passed elements for require-absent */
3001
for (je = en; *je; je++) {
3007
e = pa_xnew0(pa_alsa_element, 1);
3009
e->alsa_name = pa_xstrdup(*je);
3010
e->direction = direction;
3011
e->required_absent = PA_ALSA_REQUIRED_ANY;
3012
e->volume_limit = -1;
3014
PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
3015
p->last_element = e;
3018
pa_hashmap_put(ps->paths, *ie, p);
3022
/* Assign decibel fixes to elements. */
3023
PA_HASHMAP_FOREACH(db_fix, m->profile_set->decibel_fixes, state) {
3026
PA_HASHMAP_FOREACH(p, ps->paths, state2) {
3029
PA_LLIST_FOREACH(e, p->elements) {
3030
if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_name)) {
3031
/* The profile set that contains the dB fix may be freed
3032
* before the element, so we have to copy the dB fix
3034
e->db_fix = pa_xnewdup(pa_alsa_decibel_fix, db_fix, 1);
3035
e->db_fix->profile_set = NULL;
3036
e->db_fix->name = pa_xstrdup(db_fix->name);
3037
e->db_fix->db_values = pa_xmemdup(db_fix->db_values, (db_fix->max_step - db_fix->min_step + 1) * sizeof(long));
3047
pa_alsa_path_set_free(ps);
3052
void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
3057
pa_log_debug("Path Set %p, direction=%i",
3061
PA_HASHMAP_FOREACH(p, ps->paths, state)
3062
pa_alsa_path_dump(p);
3065
static bool options_have_option(pa_alsa_option *options, const char *alsa_name) {
3069
pa_assert(alsa_name);
3071
PA_LLIST_FOREACH(o, options) {
3072
if (pa_streq(o->alsa_name, alsa_name))
3078
static bool enumeration_is_subset(pa_alsa_option *a_options, pa_alsa_option *b_options) {
3079
pa_alsa_option *oa, *ob;
3081
if (!a_options) return true;
3082
if (!b_options) return false;
3084
/* If there is an option A offers that B does not, then A is not a subset of B. */
3085
PA_LLIST_FOREACH(oa, a_options) {
3087
PA_LLIST_FOREACH(ob, b_options) {
3088
if (pa_streq(oa->alsa_name, ob->alsa_name)) {
3100
* Compares two elements to see if a is a subset of b
3102
static bool element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_t *m) {
3108
* Every state is a subset of itself (with caveats for volume_limits and options)
3109
* IGNORE is a subset of every other state */
3111
/* Check the volume_use */
3112
if (a->volume_use != PA_ALSA_VOLUME_IGNORE) {
3114
/* "Constant" is subset of "Constant" only when their constant values are equal */
3115
if (a->volume_use == PA_ALSA_VOLUME_CONSTANT && b->volume_use == PA_ALSA_VOLUME_CONSTANT && a->constant_volume != b->constant_volume)
3118
/* Different volume uses when b is not "Merge" means we are definitely not a subset */
3119
if (a->volume_use != b->volume_use && b->volume_use != PA_ALSA_VOLUME_MERGE)
3122
/* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
3123
* "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
3124
* "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
3125
if (b->volume_use == PA_ALSA_VOLUME_MERGE && b->volume_limit >= 0) {
3128
if (a->volume_use == PA_ALSA_VOLUME_CONSTANT)
3129
a_limit = a->constant_volume;
3130
else if (a->volume_use == PA_ALSA_VOLUME_ZERO) {
3134
int rounding = (a->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1);
3135
a_limit = decibel_fix_get_step(a->db_fix, &dB, rounding);
3137
snd_mixer_selem_id_t *sid;
3138
snd_mixer_elem_t *me;
3140
SELEM_INIT(sid, a->alsa_name);
3141
if (!(me = snd_mixer_find_selem(m, sid))) {
3142
pa_log_warn("Element %s seems to have disappeared.", a->alsa_name);
3146
if (a->direction == PA_ALSA_DIRECTION_OUTPUT) {
3147
if (snd_mixer_selem_ask_playback_dB_vol(me, dB, +1, &a_limit) < 0)
3150
if (snd_mixer_selem_ask_capture_dB_vol(me, dB, -1, &a_limit) < 0)
3154
} else if (a->volume_use == PA_ALSA_VOLUME_OFF)
3155
a_limit = a->min_volume;
3156
else if (a->volume_use == PA_ALSA_VOLUME_MERGE)
3157
a_limit = a->volume_limit;
3159
pa_assert_not_reached();
3161
if (a_limit > b->volume_limit)
3165
if (a->volume_use == PA_ALSA_VOLUME_MERGE) {
3167
/* If override-maps are different, they're not subsets */
3168
if (a->n_channels != b->n_channels)
3170
for (s = 0; s <= SND_MIXER_SCHN_LAST; s++)
3171
if (a->masks[s][a->n_channels-1] != b->masks[s][b->n_channels-1]) {
3172
pa_log_debug("Element %s is not a subset - mask a: 0x%" PRIx64 ", mask b: 0x%" PRIx64 ", at channel %d",
3173
a->alsa_name, a->masks[s][a->n_channels-1], b->masks[s][b->n_channels-1], s);
3179
if (a->switch_use != PA_ALSA_SWITCH_IGNORE) {
3180
/* "On" is a subset of "Mute".
3181
* "Off" is a subset of "Mute".
3182
* "On" is a subset of "Select", if there is an "Option:On" in B.
3183
* "Off" is a subset of "Select", if there is an "Option:Off" in B.
3184
* "Select" is a subset of "Select", if they have the same options (is this always true?). */
3186
if (a->switch_use != b->switch_use) {
3188
if (a->switch_use == PA_ALSA_SWITCH_SELECT || a->switch_use == PA_ALSA_SWITCH_MUTE
3189
|| b->switch_use == PA_ALSA_SWITCH_OFF || b->switch_use == PA_ALSA_SWITCH_ON)
3192
if (b->switch_use == PA_ALSA_SWITCH_SELECT) {
3193
if (a->switch_use == PA_ALSA_SWITCH_ON) {
3194
if (!options_have_option(b->options, "on"))
3196
} else if (a->switch_use == PA_ALSA_SWITCH_OFF) {
3197
if (!options_have_option(b->options, "off"))
3201
} else if (a->switch_use == PA_ALSA_SWITCH_SELECT) {
3202
if (!enumeration_is_subset(a->options, b->options))
3207
if (a->enumeration_use != PA_ALSA_ENUMERATION_IGNORE) {
3208
if (b->enumeration_use == PA_ALSA_ENUMERATION_IGNORE)
3210
if (!enumeration_is_subset(a->options, b->options))
3217
static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
3224
/* If we only have one path, then don't bother */
3225
if (pa_hashmap_size(ps->paths) < 2)
3228
PA_HASHMAP_FOREACH(p, ps->paths, state) {
3232
PA_HASHMAP_FOREACH(p2, ps->paths, state2) {
3233
pa_alsa_element *ea, *eb;
3234
pa_alsa_jack *ja, *jb;
3235
bool is_subset = true;
3240
/* If a has a jack that b does not have, a is not a subset */
3241
PA_LLIST_FOREACH(ja, p->jacks) {
3242
bool exists = false;
3244
if (!ja->has_control)
3247
PA_LLIST_FOREACH(jb, p2->jacks) {
3248
if (jb->has_control && pa_streq(jb->alsa_name, ja->alsa_name) &&
3249
(ja->state_plugged == jb->state_plugged) &&
3250
(ja->state_unplugged == jb->state_unplugged)) {
3262
/* Compare the elements of each set... */
3263
PA_LLIST_FOREACH(ea, p->elements) {
3264
bool found_matching_element = false;
3269
PA_LLIST_FOREACH(eb, p2->elements) {
3270
if (pa_streq(ea->alsa_name, eb->alsa_name)) {
3271
found_matching_element = true;
3272
is_subset = element_is_subset(ea, eb, m);
3277
if (!found_matching_element)
3282
pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p->name, p2->name);
3283
pa_hashmap_remove(ps->paths, p);
3290
static pa_alsa_path* path_set_find_path_by_description(pa_alsa_path_set *ps, const char* description, pa_alsa_path *ignore) {
3294
PA_HASHMAP_FOREACH(p, ps->paths, state)
3295
if (p != ignore && pa_streq(p->description, description))
3301
static void path_set_make_path_descriptions_unique(pa_alsa_path_set *ps) {
3302
pa_alsa_path *p, *q;
3303
void *state, *state2;
3305
PA_HASHMAP_FOREACH(p, ps->paths, state) {
3307
char *old_description;
3309
q = path_set_find_path_by_description(ps, p->description, p);
3314
old_description = pa_xstrdup(p->description);
3316
/* OK, this description is not unique, hence let's rename */
3318
PA_HASHMAP_FOREACH(q, ps->paths, state2) {
3319
char *new_description;
3321
if (!pa_streq(q->description, old_description))
3324
new_description = pa_sprintf_malloc("%s %u", q->description, i);
3325
pa_xfree(q->description);
3326
q->description = new_description;
3331
pa_xfree(old_description);
3335
static void mapping_free(pa_alsa_mapping *m) {
3339
pa_xfree(m->description);
3341
pa_proplist_free(m->proplist);
3343
pa_xstrfreev(m->device_strings);
3344
pa_xstrfreev(m->input_path_names);
3345
pa_xstrfreev(m->output_path_names);
3346
pa_xstrfreev(m->input_element);
3347
pa_xstrfreev(m->output_element);
3348
if (m->input_path_set)
3349
pa_alsa_path_set_free(m->input_path_set);
3350
if (m->output_path_set)
3351
pa_alsa_path_set_free(m->output_path_set);
3353
pa_assert(!m->input_pcm);
3354
pa_assert(!m->output_pcm);
3356
pa_alsa_ucm_mapping_context_free(&m->ucm_context);
3361
static void profile_free(pa_alsa_profile *p) {
3365
pa_xfree(p->description);
3367
pa_xstrfreev(p->input_mapping_names);
3368
pa_xstrfreev(p->output_mapping_names);
3370
if (p->input_mappings)
3371
pa_idxset_free(p->input_mappings, NULL);
3373
if (p->output_mappings)
3374
pa_idxset_free(p->output_mappings, NULL);
3379
void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
3382
if (ps->input_paths)
3383
pa_hashmap_free(ps->input_paths);
3385
if (ps->output_paths)
3386
pa_hashmap_free(ps->output_paths);
3389
pa_hashmap_free(ps->profiles);
3392
pa_hashmap_free(ps->mappings);
3394
if (ps->decibel_fixes)
3395
pa_hashmap_free(ps->decibel_fixes);
3400
pa_alsa_mapping *pa_alsa_mapping_get(pa_alsa_profile_set *ps, const char *name) {
3403
if (!pa_startswith(name, "Mapping "))
3408
if ((m = pa_hashmap_get(ps->mappings, name)))
3411
m = pa_xnew0(pa_alsa_mapping, 1);
3412
m->profile_set = ps;
3413
m->exact_channels = true;
3414
m->name = pa_xstrdup(name);
3415
pa_sample_spec_init(&m->sample_spec);
3416
pa_channel_map_init(&m->channel_map);
3417
m->proplist = pa_proplist_new();
3419
pa_hashmap_put(ps->mappings, m->name, m);
3424
static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
3427
if (!pa_startswith(name, "Profile "))
3432
if ((p = pa_hashmap_get(ps->profiles, name)))
3435
p = pa_xnew0(pa_alsa_profile, 1);
3436
p->profile_set = ps;
3437
p->name = pa_xstrdup(name);
3439
pa_hashmap_put(ps->profiles, p->name, p);
3444
static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) {
3445
pa_alsa_decibel_fix *db_fix;
3447
if (!pa_startswith(name, "DecibelFix "))
3452
if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name)))
3455
db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
3456
db_fix->profile_set = ps;
3457
db_fix->name = pa_xstrdup(name);
3459
pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix);
3464
static int mapping_parse_device_strings(pa_config_parser_state *state) {
3465
pa_alsa_profile_set *ps;
3470
ps = state->userdata;
3472
if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3473
pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3477
pa_xstrfreev(m->device_strings);
3478
if (!(m->device_strings = pa_split_spaces_strv(state->rvalue))) {
3479
pa_log("[%s:%u] Device string list empty of '%s'", state->filename, state->lineno, state->section);
3486
static int mapping_parse_channel_map(pa_config_parser_state *state) {
3487
pa_alsa_profile_set *ps;
3492
ps = state->userdata;
3494
if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3495
pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3499
if (!(pa_channel_map_parse(&m->channel_map, state->rvalue))) {
3500
pa_log("[%s:%u] Channel map invalid of '%s'", state->filename, state->lineno, state->section);
3507
static int mapping_parse_paths(pa_config_parser_state *state) {
3508
pa_alsa_profile_set *ps;
3513
ps = state->userdata;
3515
if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3516
pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3520
if (pa_streq(state->lvalue, "paths-input")) {
3521
pa_xstrfreev(m->input_path_names);
3522
m->input_path_names = pa_split_spaces_strv(state->rvalue);
3524
pa_xstrfreev(m->output_path_names);
3525
m->output_path_names = pa_split_spaces_strv(state->rvalue);
3531
static int mapping_parse_exact_channels(pa_config_parser_state *state) {
3532
pa_alsa_profile_set *ps;
3538
ps = state->userdata;
3540
if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3541
pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3545
if ((b = pa_parse_boolean(state->rvalue)) < 0) {
3546
pa_log("[%s:%u] %s has invalid value '%s'", state->filename, state->lineno, state->lvalue, state->section);
3550
m->exact_channels = b;
3555
static int mapping_parse_element(pa_config_parser_state *state) {
3556
pa_alsa_profile_set *ps;
3561
ps = state->userdata;
3563
if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3564
pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3568
if (pa_streq(state->lvalue, "element-input")) {
3569
pa_xstrfreev(m->input_element);
3570
m->input_element = pa_split_spaces_strv(state->rvalue);
3572
pa_xstrfreev(m->output_element);
3573
m->output_element = pa_split_spaces_strv(state->rvalue);
3579
static int mapping_parse_direction(pa_config_parser_state *state) {
3580
pa_alsa_profile_set *ps;
3585
ps = state->userdata;
3587
if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3588
pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
3592
if (pa_streq(state->rvalue, "input"))
3593
m->direction = PA_ALSA_DIRECTION_INPUT;
3594
else if (pa_streq(state->rvalue, "output"))
3595
m->direction = PA_ALSA_DIRECTION_OUTPUT;
3596
else if (pa_streq(state->rvalue, "any"))
3597
m->direction = PA_ALSA_DIRECTION_ANY;
3599
pa_log("[%s:%u] Direction %s invalid.", state->filename, state->lineno, state->rvalue);
3606
static int mapping_parse_description(pa_config_parser_state *state) {
3607
pa_alsa_profile_set *ps;
3613
ps = state->userdata;
3615
if ((m = pa_alsa_mapping_get(ps, state->section))) {
3616
pa_xfree(m->description);
3617
m->description = pa_xstrdup(state->rvalue);
3618
} else if ((p = profile_get(ps, state->section))) {
3619
pa_xfree(p->description);
3620
p->description = pa_xstrdup(state->rvalue);
3622
pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
3629
static int mapping_parse_priority(pa_config_parser_state *state) {
3630
pa_alsa_profile_set *ps;
3637
ps = state->userdata;
3639
if (pa_atou(state->rvalue, &prio) < 0) {
3640
pa_log("[%s:%u] Priority invalid of '%s'", state->filename, state->lineno, state->section);
3644
if ((m = pa_alsa_mapping_get(ps, state->section)))
3646
else if ((p = profile_get(ps, state->section)))
3649
pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
3656
static int mapping_parse_fallback(pa_config_parser_state *state) {
3657
pa_alsa_profile_set *ps;
3664
ps = state->userdata;
3666
if ((k = pa_parse_boolean(state->rvalue)) < 0) {
3667
pa_log("[%s:%u] Fallback invalid of '%s'", state->filename, state->lineno, state->section);
3671
if ((m = pa_alsa_mapping_get(ps, state->section)))
3673
else if ((p = profile_get(ps, state->section)))
3674
p->fallback_input = p->fallback_output = k;
3676
pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
3684
static int profile_parse_mappings(pa_config_parser_state *state) {
3685
pa_alsa_profile_set *ps;
3690
ps = state->userdata;
3692
if (!(p = profile_get(ps, state->section))) {
3693
pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3697
if (pa_streq(state->lvalue, "input-mappings")) {
3698
pa_xstrfreev(p->input_mapping_names);
3699
p->input_mapping_names = pa_split_spaces_strv(state->rvalue);
3701
pa_xstrfreev(p->output_mapping_names);
3702
p->output_mapping_names = pa_split_spaces_strv(state->rvalue);
3708
static int profile_parse_skip_probe(pa_config_parser_state *state) {
3709
pa_alsa_profile_set *ps;
3715
ps = state->userdata;
3717
if (!(p = profile_get(ps, state->section))) {
3718
pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3722
if ((b = pa_parse_boolean(state->rvalue)) < 0) {
3723
pa_log("[%s:%u] Skip probe invalid of '%s'", state->filename, state->lineno, state->section);
3732
static int decibel_fix_parse_db_values(pa_config_parser_state *state) {
3733
pa_alsa_profile_set *ps;
3734
pa_alsa_decibel_fix *db_fix;
3738
unsigned n = 8; /* Current size of the db_values table. */
3739
unsigned min_step = 0;
3740
unsigned max_step = 0;
3741
unsigned i = 0; /* Index to the items table. */
3742
unsigned prev_step = 0;
3747
ps = state->userdata;
3749
if (!(db_fix = decibel_fix_get(ps, state->section))) {
3750
pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3754
if (!(items = pa_split_spaces_strv(state->rvalue))) {
3755
pa_log("[%s:%u] Value missing", state->filename, state->lineno);
3759
db_values = pa_xnew(long, n);
3761
while ((item = items[i++])) {
3762
char *s = item; /* Step value string. */
3763
char *d = item; /* dB value string. */
3767
/* Move d forward until it points to a colon or to the end of the item. */
3768
for (; *d && *d != ':'; ++d);
3771
/* item started with colon. */
3772
pa_log("[%s:%u] No step value found in %s", state->filename, state->lineno, item);
3776
if (!*d || !*(d + 1)) {
3777
/* No colon found, or it was the last character in item. */
3778
pa_log("[%s:%u] No dB value found in %s", state->filename, state->lineno, item);
3782
/* pa_atou() needs a null-terminating string. Let's replace the colon
3783
* with a zero byte. */
3786
if (pa_atou(s, &step) < 0) {
3787
pa_log("[%s:%u] Invalid step value: %s", state->filename, state->lineno, s);
3791
if (pa_atod(d, &db) < 0) {
3792
pa_log("[%s:%u] Invalid dB value: %s", state->filename, state->lineno, d);
3796
if (step <= prev_step && i != 1) {
3797
pa_log("[%s:%u] Step value %u not greater than the previous value %u", state->filename, state->lineno, step, prev_step);
3801
if (db < prev_db && i != 1) {
3802
pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", state->filename, state->lineno, db, prev_db);
3808
db_values[0] = (long) (db * 100.0);
3812
/* Interpolate linearly. */
3813
double db_increment = (db - prev_db) / (step - prev_step);
3815
for (; prev_step < step; ++prev_step, prev_db += db_increment) {
3817
/* Reallocate the db_values table if it's about to overflow. */
3818
if (prev_step + 1 - min_step == n) {
3820
db_values = pa_xrenew(long, db_values, n);
3823
db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
3830
db_fix->min_step = min_step;
3831
db_fix->max_step = max_step;
3832
pa_xfree(db_fix->db_values);
3833
db_fix->db_values = db_values;
3835
pa_xstrfreev(items);
3840
pa_xstrfreev(items);
3841
pa_xfree(db_values);
3846
static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
3847
pa_alsa_direction_t direction, pa_hashmap *used_paths) {
3851
snd_pcm_t *pcm_handle;
3852
pa_alsa_path_set *ps;
3853
snd_mixer_t *mixer_handle;
3855
if (direction == PA_ALSA_DIRECTION_OUTPUT) {
3856
if (m->output_path_set)
3857
return; /* Already probed */
3858
m->output_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3859
pcm_handle = m->output_pcm;
3861
if (m->input_path_set)
3862
return; /* Already probed */
3863
m->input_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3864
pcm_handle = m->input_pcm;
3868
return; /* No paths */
3870
pa_assert(pcm_handle);
3872
mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL);
3873
if (!mixer_handle) {
3874
/* Cannot open mixer, remove all entries */
3875
pa_hashmap_remove_all(ps->paths);
3879
PA_HASHMAP_FOREACH(p, ps->paths, state) {
3880
if (pa_alsa_path_probe(p, mixer_handle, m->profile_set->ignore_dB) < 0) {
3881
pa_hashmap_remove(ps->paths, p);
3885
path_set_condense(ps, mixer_handle);
3886
path_set_make_path_descriptions_unique(ps);
3889
snd_mixer_close(mixer_handle);
3891
PA_HASHMAP_FOREACH(p, ps->paths, state)
3892
pa_hashmap_put(used_paths, p, p);
3894
pa_log_debug("Available mixer paths (after tidying):");
3895
pa_alsa_path_set_dump(ps);
3898
static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
3900
static const struct description_map well_known_descriptions[] = {
3901
{ "analog-mono", N_("Analog Mono") },
3902
{ "analog-stereo", N_("Analog Stereo") },
3903
{ "multichannel", N_("Multichannel") },
3904
{ "analog-surround-21", N_("Analog Surround 2.1") },
3905
{ "analog-surround-30", N_("Analog Surround 3.0") },
3906
{ "analog-surround-31", N_("Analog Surround 3.1") },
3907
{ "analog-surround-40", N_("Analog Surround 4.0") },
3908
{ "analog-surround-41", N_("Analog Surround 4.1") },
3909
{ "analog-surround-50", N_("Analog Surround 5.0") },
3910
{ "analog-surround-51", N_("Analog Surround 5.1") },
3911
{ "analog-surround-61", N_("Analog Surround 6.0") },
3912
{ "analog-surround-61", N_("Analog Surround 6.1") },
3913
{ "analog-surround-70", N_("Analog Surround 7.0") },
3914
{ "analog-surround-71", N_("Analog Surround 7.1") },
3915
{ "iec958-stereo", N_("Digital Stereo (IEC958)") },
3916
{ "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3917
{ "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3918
{ "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3919
{ "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
3920
{ "hdmi-stereo", N_("Digital Stereo (HDMI)") },
3921
{ "hdmi-surround-51", N_("Digital Surround 5.1 (HDMI)") }
3926
if (!pa_channel_map_valid(&m->channel_map)) {
3927
pa_log("Mapping %s is missing channel map.", m->name);
3931
if (!m->device_strings) {
3932
pa_log("Mapping %s is missing device strings.", m->name);
3936
if ((m->input_path_names && m->input_element) ||
3937
(m->output_path_names && m->output_element)) {
3938
pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
3942
if (!m->description)
3943
m->description = pa_xstrdup(lookup_description(m->name,
3944
well_known_descriptions,
3945
PA_ELEMENTSOF(well_known_descriptions)));
3947
if (!m->description)
3948
m->description = pa_xstrdup(m->name);
3951
if (pa_channel_map_equal(&m->channel_map, bonus))
3953
else if (m->channel_map.channels == bonus->channels)
3960
void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
3961
char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
3965
pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3967
pa_strnull(m->description),
3969
pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
3970
pa_yes_no(m->supported),
3974
static void profile_set_add_auto_pair(
3975
pa_alsa_profile_set *ps,
3976
pa_alsa_mapping *m, /* output */
3977
pa_alsa_mapping *n /* input */) {
3985
if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
3988
if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
3992
name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
3994
name = pa_sprintf_malloc("output:%s", m->name);
3996
name = pa_sprintf_malloc("input:%s", n->name);
3998
if (pa_hashmap_get(ps->profiles, name)) {
4003
p = pa_xnew0(pa_alsa_profile, 1);
4004
p->profile_set = ps;
4008
p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4009
pa_idxset_put(p->output_mappings, m, NULL);
4010
p->priority += m->priority * 100;
4011
p->fallback_output = m->fallback;
4015
p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4016
pa_idxset_put(p->input_mappings, n, NULL);
4017
p->priority += n->priority;
4018
p->fallback_input = n->fallback;
4021
pa_hashmap_put(ps->profiles, p->name, p);
4024
static void profile_set_add_auto(pa_alsa_profile_set *ps) {
4025
pa_alsa_mapping *m, *n;
4026
void *m_state, *n_state;
4030
/* The order is important here:
4031
1) try single inputs and outputs before trying their
4032
combination, because if the half-duplex test failed, we don't have
4034
2) try the output right before the input combinations with
4035
that output, because then the output_pcm is not closed between tests.
4037
PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
4038
profile_set_add_auto_pair(ps, NULL, n);
4040
PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
4041
profile_set_add_auto_pair(ps, m, NULL);
4043
PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
4044
profile_set_add_auto_pair(ps, m, n);
4049
static int profile_verify(pa_alsa_profile *p) {
4051
static const struct description_map well_known_descriptions[] = {
4052
{ "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
4053
{ "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
4054
{ "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
4055
{ "off", N_("Off") }
4060
/* Replace the output mapping names by the actual mappings */
4061
if (p->output_mapping_names) {
4064
pa_assert(!p->output_mappings);
4065
p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4067
for (name = p->output_mapping_names; *name; name++) {
4070
bool duplicate = false;
4072
for (in = name + 1; *in; in++)
4073
if (pa_streq(*name, *in)) {
4081
if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
4082
pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
4086
pa_idxset_put(p->output_mappings, m, NULL);
4092
pa_xstrfreev(p->output_mapping_names);
4093
p->output_mapping_names = NULL;
4096
/* Replace the input mapping names by the actual mappings */
4097
if (p->input_mapping_names) {
4100
pa_assert(!p->input_mappings);
4101
p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4103
for (name = p->input_mapping_names; *name; name++) {
4106
bool duplicate = false;
4108
for (in = name + 1; *in; in++)
4109
if (pa_streq(*name, *in)) {
4117
if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
4118
pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
4122
pa_idxset_put(p->input_mappings, m, NULL);
4128
pa_xstrfreev(p->input_mapping_names);
4129
p->input_mapping_names = NULL;
4132
if (!p->input_mappings && !p->output_mappings) {
4133
pa_log("Profile '%s' lacks mappings.", p->name);
4137
if (!p->description)
4138
p->description = pa_xstrdup(lookup_description(p->name,
4139
well_known_descriptions,
4140
PA_ELEMENTSOF(well_known_descriptions)));
4142
if (!p->description) {
4147
sb = pa_strbuf_new();
4149
if (p->output_mappings)
4150
PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4151
if (!pa_strbuf_isempty(sb))
4152
pa_strbuf_puts(sb, " + ");
4154
pa_strbuf_printf(sb, _("%s Output"), m->description);
4157
if (p->input_mappings)
4158
PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4159
if (!pa_strbuf_isempty(sb))
4160
pa_strbuf_puts(sb, " + ");
4162
pa_strbuf_printf(sb, _("%s Input"), m->description);
4165
p->description = pa_strbuf_tostring_free(sb);
4171
void pa_alsa_profile_dump(pa_alsa_profile *p) {
4176
pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4178
pa_strnull(p->description),
4180
pa_yes_no(p->supported),
4181
p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
4182
p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
4184
if (p->input_mappings)
4185
PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4186
pa_log_debug("Input %s", m->name);
4188
if (p->output_mappings)
4189
PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4190
pa_log_debug("Output %s", m->name);
4193
static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
4196
/* Check that the dB mapping has been configured. Since "db-values" is
4197
* currently the only option in the DecibelFix section, and decibel fix
4198
* objects don't get created if a DecibelFix section is empty, this is
4199
* actually a redundant check. Having this may prevent future bugs,
4201
if (!db_fix->db_values) {
4202
pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
4209
void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
4210
char *db_values = NULL;
4214
if (db_fix->db_values) {
4216
unsigned long i, nsteps;
4218
pa_assert(db_fix->min_step <= db_fix->max_step);
4219
nsteps = db_fix->max_step - db_fix->min_step + 1;
4221
buf = pa_strbuf_new();
4222
for (i = 0; i < nsteps; ++i)
4223
pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
4225
db_values = pa_strbuf_tostring_free(buf);
4228
pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4229
db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
4231
pa_xfree(db_values);
4234
pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
4235
pa_alsa_profile_set *ps;
4238
pa_alsa_decibel_fix *db_fix;
4243
static pa_config_item items[] = {
4245
{ "auto-profiles", pa_config_parse_bool, NULL, "General" },
4248
{ "device-strings", mapping_parse_device_strings, NULL, NULL },
4249
{ "channel-map", mapping_parse_channel_map, NULL, NULL },
4250
{ "paths-input", mapping_parse_paths, NULL, NULL },
4251
{ "paths-output", mapping_parse_paths, NULL, NULL },
4252
{ "element-input", mapping_parse_element, NULL, NULL },
4253
{ "element-output", mapping_parse_element, NULL, NULL },
4254
{ "direction", mapping_parse_direction, NULL, NULL },
4255
{ "exact-channels", mapping_parse_exact_channels, NULL, NULL },
4257
/* Shared by [Mapping ...] and [Profile ...] */
4258
{ "description", mapping_parse_description, NULL, NULL },
4259
{ "priority", mapping_parse_priority, NULL, NULL },
4260
{ "fallback", mapping_parse_fallback, NULL, NULL },
4263
{ "input-mappings", profile_parse_mappings, NULL, NULL },
4264
{ "output-mappings", profile_parse_mappings, NULL, NULL },
4265
{ "skip-probe", profile_parse_skip_probe, NULL, NULL },
4267
/* [DecibelFix ...] */
4268
{ "db-values", decibel_fix_parse_db_values, NULL, NULL },
4269
{ NULL, NULL, NULL, NULL }
4272
ps = pa_xnew0(pa_alsa_profile_set, 1);
4273
ps->mappings = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) mapping_free);
4274
ps->profiles = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) profile_free);
4275
ps->decibel_fixes = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) decibel_fix_free);
4276
ps->input_paths = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_alsa_path_free);
4277
ps->output_paths = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_alsa_path_free);
4279
items[0].data = &ps->auto_profiles;
4282
fname = "default.conf";
4284
fn = pa_maybe_prefix_path(fname,
4285
pa_run_from_build_tree() ? PA_SRCDIR "/modules/alsa/mixer/profile-sets/" :
4286
PA_ALSA_PROFILE_SETS_DIR);
4288
r = pa_config_parse(fn, NULL, items, NULL, ps);
4294
PA_HASHMAP_FOREACH(m, ps->mappings, state)
4295
if (mapping_verify(m, bonus) < 0)
4298
if (ps->auto_profiles)
4299
profile_set_add_auto(ps);
4301
PA_HASHMAP_FOREACH(p, ps->profiles, state)
4302
if (profile_verify(p) < 0)
4305
PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4306
if (decibel_fix_verify(db_fix) < 0)
4312
pa_alsa_profile_set_free(ps);
4316
static void profile_finalize_probing(pa_alsa_profile *to_be_finalized, pa_alsa_profile *next) {
4320
if (!to_be_finalized)
4323
if (to_be_finalized->output_mappings)
4324
PA_IDXSET_FOREACH(m, to_be_finalized->output_mappings, idx) {
4329
if (to_be_finalized->supported)
4332
/* If this mapping is also in the next profile, we won't close the
4333
* pcm handle here, because it would get immediately reopened
4335
if (next && next->output_mappings && pa_idxset_get_by_data(next->output_mappings, m, NULL))
4338
snd_pcm_close(m->output_pcm);
4339
m->output_pcm = NULL;
4342
if (to_be_finalized->input_mappings)
4343
PA_IDXSET_FOREACH(m, to_be_finalized->input_mappings, idx) {
4348
if (to_be_finalized->supported)
4351
/* If this mapping is also in the next profile, we won't close the
4352
* pcm handle here, because it would get immediately reopened
4354
if (next && next->input_mappings && pa_idxset_get_by_data(next->input_mappings, m, NULL))
4357
snd_pcm_close(m->input_pcm);
4358
m->input_pcm = NULL;
4362
static snd_pcm_t* mapping_open_pcm(pa_alsa_mapping *m,
4363
const pa_sample_spec *ss,
4365
bool exact_channels,
4367
unsigned default_n_fragments,
4368
unsigned default_fragment_size_msec) {
4371
pa_sample_spec try_ss = *ss;
4372
pa_channel_map try_map = m->channel_map;
4373
snd_pcm_uframes_t try_period_size, try_buffer_size;
4375
try_ss.channels = try_map.channels;
4378
pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
4379
pa_frame_size(&try_ss);
4380
try_buffer_size = default_n_fragments * try_period_size;
4382
handle = pa_alsa_open_by_template(
4383
m->device_strings, dev_id, NULL, &try_ss,
4384
&try_map, mode, &try_period_size,
4385
&try_buffer_size, 0, NULL, NULL, exact_channels);
4386
if (handle && !exact_channels && m->channel_map.channels != try_map.channels) {
4387
char buf[PA_CHANNEL_MAP_SNPRINT_MAX];
4388
pa_log_debug("Channel map for mapping '%s' permanently changed to '%s'", m->name,
4389
pa_channel_map_snprint(buf, sizeof(buf), &try_map));
4390
m->channel_map = try_map;
4395
static void paths_drop_unused(pa_hashmap* h, pa_hashmap *keep) {
4404
p = pa_hashmap_iterate(h, &state, &key);
4406
if (pa_hashmap_get(keep, p) == NULL)
4407
pa_hashmap_remove_and_free(h, key);
4408
p = pa_hashmap_iterate(h, &state, &key);
4412
static int add_profiles_to_probe(
4413
pa_alsa_profile **list,
4414
pa_hashmap *profiles,
4415
bool fallback_output,
4416
bool fallback_input) {
4421
PA_HASHMAP_FOREACH(p, profiles, state)
4422
if (p->fallback_input == fallback_input && p->fallback_output == fallback_output) {
4430
void pa_alsa_profile_set_probe(
4431
pa_alsa_profile_set *ps,
4433
const pa_sample_spec *ss,
4434
unsigned default_n_fragments,
4435
unsigned default_fragment_size_msec) {
4437
bool found_output = false, found_input = false;
4439
pa_alsa_profile *p, *last = NULL;
4440
pa_alsa_profile **pp, **probe_order;
4442
pa_hashmap *broken_inputs, *broken_outputs, *used_paths;
4451
broken_inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4452
broken_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4453
used_paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4454
pp = probe_order = pa_xnew0(pa_alsa_profile *, pa_hashmap_size(ps->profiles) + 1);
4456
pp += add_profiles_to_probe(pp, ps->profiles, false, false);
4457
pp += add_profiles_to_probe(pp, ps->profiles, false, true);
4458
pp += add_profiles_to_probe(pp, ps->profiles, true, false);
4459
pp += add_profiles_to_probe(pp, ps->profiles, true, true);
4461
for (pp = probe_order; *pp; pp++) {
4465
/* Skip if fallback and already found something */
4466
if (found_input && p->fallback_input)
4468
if (found_output && p->fallback_output)
4471
/* Skip if this is already marked that it is supported (i.e. from the config file) */
4472
if (!p->supported) {
4474
profile_finalize_probing(last, p);
4475
p->supported = true;
4477
if (p->output_mappings) {
4478
PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4479
if (pa_hashmap_get(broken_outputs, m) == m) {
4480
pa_log_debug("Skipping profile %s - will not be able to open output:%s", p->name, m->name);
4481
p->supported = false;
4487
if (p->input_mappings && p->supported) {
4488
PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4489
if (pa_hashmap_get(broken_inputs, m) == m) {
4490
pa_log_debug("Skipping profile %s - will not be able to open input:%s", p->name, m->name);
4491
p->supported = false;
4498
pa_log_debug("Looking at profile %s", p->name);
4500
/* Check if we can open all new ones */
4501
if (p->output_mappings && p->supported)
4502
PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4507
pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
4508
if (!(m->output_pcm = mapping_open_pcm(m, ss, dev_id, m->exact_channels,
4509
SND_PCM_STREAM_PLAYBACK,
4510
default_n_fragments,
4511
default_fragment_size_msec))) {
4512
p->supported = false;
4513
if (pa_idxset_size(p->output_mappings) == 1 &&
4514
((!p->input_mappings) || pa_idxset_size(p->input_mappings) == 0)) {
4515
pa_log_debug("Caching failure to open output:%s", m->name);
4516
pa_hashmap_put(broken_outputs, m, m);
4522
if (p->input_mappings && p->supported)
4523
PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4528
pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
4529
if (!(m->input_pcm = mapping_open_pcm(m, ss, dev_id, m->exact_channels,
4530
SND_PCM_STREAM_CAPTURE,
4531
default_n_fragments,
4532
default_fragment_size_msec))) {
4533
p->supported = false;
4534
if (pa_idxset_size(p->input_mappings) == 1 &&
4535
((!p->output_mappings) || pa_idxset_size(p->output_mappings) == 0)) {
4536
pa_log_debug("Caching failure to open input:%s", m->name);
4537
pa_hashmap_put(broken_inputs, m, m);
4549
pa_log_debug("Profile %s supported.", p->name);
4551
if (p->output_mappings)
4552
PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4553
if (m->output_pcm) {
4554
found_output |= !p->fallback_output;
4555
mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT, used_paths);
4558
if (p->input_mappings)
4559
PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4561
found_input |= !p->fallback_input;
4562
mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT, used_paths);
4567
profile_finalize_probing(last, NULL);
4569
pa_alsa_profile_set_drop_unsupported(ps);
4571
paths_drop_unused(ps->input_paths, used_paths);
4572
paths_drop_unused(ps->output_paths, used_paths);
4573
pa_hashmap_free(broken_inputs);
4574
pa_hashmap_free(broken_outputs);
4575
pa_hashmap_free(used_paths);
4576
pa_xfree(probe_order);
4581
void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
4584
pa_alsa_decibel_fix *db_fix;
4589
pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4592
pa_yes_no(ps->auto_profiles),
4593
pa_yes_no(ps->probed),
4594
pa_hashmap_size(ps->mappings),
4595
pa_hashmap_size(ps->profiles),
4596
pa_hashmap_size(ps->decibel_fixes));
4598
PA_HASHMAP_FOREACH(m, ps->mappings, state)
4599
pa_alsa_mapping_dump(m);
4601
PA_HASHMAP_FOREACH(p, ps->profiles, state)
4602
pa_alsa_profile_dump(p);
4604
PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4605
pa_alsa_decibel_fix_dump(db_fix);
4608
void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set *ps) {
4613
PA_HASHMAP_FOREACH(p, ps->profiles, state) {
4615
pa_hashmap_remove_and_free(ps->profiles, p->name);
4618
PA_HASHMAP_FOREACH(m, ps->mappings, state) {
4619
if (m->supported <= 0)
4620
pa_hashmap_remove_and_free(ps->mappings, m->name);
4624
static pa_device_port* device_port_alsa_init(pa_hashmap *ports, /* card ports */
4626
const char* description,
4628
pa_alsa_setting *setting,
4629
pa_card_profile *cp,
4630
pa_hashmap *extra, /* sink/source ports */
4637
p = pa_hashmap_get(ports, name);
4640
pa_alsa_port_data *data;
4641
pa_device_port_new_data port_data;
4643
pa_device_port_new_data_init(&port_data);
4644
pa_device_port_new_data_set_name(&port_data, name);
4645
pa_device_port_new_data_set_description(&port_data, description);
4646
pa_device_port_new_data_set_direction(&port_data, path->direction == PA_ALSA_DIRECTION_OUTPUT ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT);
4648
p = pa_device_port_new(core, &port_data, sizeof(pa_alsa_port_data));
4649
pa_device_port_new_data_done(&port_data);
4651
pa_hashmap_put(ports, p->name, p);
4652
pa_proplist_update(p->proplist, PA_UPDATE_REPLACE, path->proplist);
4654
data = PA_DEVICE_PORT_DATA(p);
4656
data->setting = setting;
4661
pa_hashmap_put(p->profiles, cp->name, cp);
4664
pa_hashmap_put(extra, p->name, p);
4665
pa_device_port_ref(p);
4671
void pa_alsa_path_set_add_ports(
4672
pa_alsa_path_set *ps,
4673
pa_card_profile *cp,
4674
pa_hashmap *ports, /* card ports */
4675
pa_hashmap *extra, /* sink/source ports */
4686
PA_HASHMAP_FOREACH(path, ps->paths, state) {
4687
if (!path->settings || !path->settings->next) {
4688
/* If there is no or just one setting we only need a
4690
pa_device_port *port = device_port_alsa_init(ports, path->name,
4691
path->description, path, path->settings, cp, extra, core);
4692
port->priority = path->priority * 100;
4696
PA_LLIST_FOREACH(s, path->settings) {
4697
pa_device_port *port;
4700
n = pa_sprintf_malloc("%s;%s", path->name, s->name);
4702
if (s->description[0])
4703
d = pa_sprintf_malloc("%s / %s", path->description, s->description);
4705
d = pa_xstrdup(path->description);
4707
port = device_port_alsa_init(ports, n, d, path, s, cp, extra, core);
4708
port->priority = path->priority * 100 + s->priority;
4717
void pa_alsa_add_ports(void *sink_or_source_new_data, pa_alsa_path_set *ps, pa_card *card) {
4720
pa_assert(sink_or_source_new_data);
4723
if (ps->direction == PA_ALSA_DIRECTION_OUTPUT)
4724
ports = ((pa_sink_new_data *) sink_or_source_new_data)->ports;
4726
ports = ((pa_source_new_data *) sink_or_source_new_data)->ports;
4728
if (ps->paths && pa_hashmap_size(ps->paths) > 0) {
4730
pa_alsa_path_set_add_ports(ps, NULL, card->ports, ports, card->core);
4733
pa_log_debug("Added %u ports", pa_hashmap_size(ports));