alsa: adjust priority bonus of mappings that match the configured default channel map
[pulseaudio-mirror.git] / src / modules / alsa / alsa-mixer.c
bloba4c2ee0f7adb7b68280069b5291bae5ed06fb0aa
1 /***
2 This file is part of PulseAudio.
4 Copyright 2004-2009 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include <sys/types.h>
28 #include <limits.h>
29 #include <asoundlib.h>
31 #ifdef HAVE_VALGRIND_MEMCHECK_H
32 #include <valgrind/memcheck.h>
33 #endif
35 #include <pulse/sample.h>
36 #include <pulse/xmalloc.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/i18n.h>
40 #include <pulse/utf8.h>
42 #include <pulsecore/log.h>
43 #include <pulsecore/macro.h>
44 #include <pulsecore/core-util.h>
45 #include <pulsecore/atomic.h>
46 #include <pulsecore/core-error.h>
47 #include <pulsecore/once.h>
48 #include <pulsecore/thread.h>
49 #include <pulsecore/conf-parser.h>
50 #include <pulsecore/strbuf.h>
52 #include "alsa-mixer.h"
53 #include "alsa-util.h"
55 struct description_map {
56 const char *name;
57 const char *description;
60 static const char *lookup_description(const char *name, const struct description_map dm[], unsigned n) {
61 unsigned i;
63 for (i = 0; i < n; i++)
64 if (pa_streq(dm[i].name, name))
65 return dm[i].description;
67 return NULL;
70 struct pa_alsa_fdlist {
71 unsigned num_fds;
72 struct pollfd *fds;
73 /* This is a temporary buffer used to avoid lots of mallocs */
74 struct pollfd *work_fds;
76 snd_mixer_t *mixer;
78 pa_mainloop_api *m;
79 pa_defer_event *defer;
80 pa_io_event **ios;
82 pa_bool_t polled;
84 void (*cb)(void *userdata);
85 void *userdata;
88 static void io_cb(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
90 struct pa_alsa_fdlist *fdl = userdata;
91 int err;
92 unsigned i;
93 unsigned short revents;
95 pa_assert(a);
96 pa_assert(fdl);
97 pa_assert(fdl->mixer);
98 pa_assert(fdl->fds);
99 pa_assert(fdl->work_fds);
101 if (fdl->polled)
102 return;
104 fdl->polled = TRUE;
106 memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds);
108 for (i = 0; i < fdl->num_fds; i++) {
109 if (e == fdl->ios[i]) {
110 if (events & PA_IO_EVENT_INPUT)
111 fdl->work_fds[i].revents |= POLLIN;
112 if (events & PA_IO_EVENT_OUTPUT)
113 fdl->work_fds[i].revents |= POLLOUT;
114 if (events & PA_IO_EVENT_ERROR)
115 fdl->work_fds[i].revents |= POLLERR;
116 if (events & PA_IO_EVENT_HANGUP)
117 fdl->work_fds[i].revents |= POLLHUP;
118 break;
122 pa_assert(i != fdl->num_fds);
124 if ((err = snd_mixer_poll_descriptors_revents(fdl->mixer, fdl->work_fds, fdl->num_fds, &revents)) < 0) {
125 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
126 return;
129 a->defer_enable(fdl->defer, 1);
131 if (revents)
132 snd_mixer_handle_events(fdl->mixer);
135 static void defer_cb(pa_mainloop_api*a, pa_defer_event* e, void *userdata) {
136 struct pa_alsa_fdlist *fdl = userdata;
137 unsigned num_fds, i;
138 int err, n;
139 struct pollfd *temp;
141 pa_assert(a);
142 pa_assert(fdl);
143 pa_assert(fdl->mixer);
145 a->defer_enable(fdl->defer, 0);
147 if ((n = snd_mixer_poll_descriptors_count(fdl->mixer)) < 0) {
148 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
149 return;
151 num_fds = (unsigned) n;
153 if (num_fds != fdl->num_fds) {
154 if (fdl->fds)
155 pa_xfree(fdl->fds);
156 if (fdl->work_fds)
157 pa_xfree(fdl->work_fds);
158 fdl->fds = pa_xnew0(struct pollfd, num_fds);
159 fdl->work_fds = pa_xnew(struct pollfd, num_fds);
162 memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds);
164 if ((err = snd_mixer_poll_descriptors(fdl->mixer, fdl->work_fds, num_fds)) < 0) {
165 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
166 return;
169 fdl->polled = FALSE;
171 if (memcmp(fdl->fds, fdl->work_fds, sizeof(struct pollfd) * num_fds) == 0)
172 return;
174 if (fdl->ios) {
175 for (i = 0; i < fdl->num_fds; i++)
176 a->io_free(fdl->ios[i]);
178 if (num_fds != fdl->num_fds) {
179 pa_xfree(fdl->ios);
180 fdl->ios = NULL;
184 if (!fdl->ios)
185 fdl->ios = pa_xnew(pa_io_event*, num_fds);
187 /* Swap pointers */
188 temp = fdl->work_fds;
189 fdl->work_fds = fdl->fds;
190 fdl->fds = temp;
192 fdl->num_fds = num_fds;
194 for (i = 0;i < num_fds;i++)
195 fdl->ios[i] = a->io_new(a, fdl->fds[i].fd,
196 ((fdl->fds[i].events & POLLIN) ? PA_IO_EVENT_INPUT : 0) |
197 ((fdl->fds[i].events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0),
198 io_cb, fdl);
201 struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) {
202 struct pa_alsa_fdlist *fdl;
204 fdl = pa_xnew0(struct pa_alsa_fdlist, 1);
206 return fdl;
209 void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {
210 pa_assert(fdl);
212 if (fdl->defer) {
213 pa_assert(fdl->m);
214 fdl->m->defer_free(fdl->defer);
217 if (fdl->ios) {
218 unsigned i;
219 pa_assert(fdl->m);
220 for (i = 0; i < fdl->num_fds; i++)
221 fdl->m->io_free(fdl->ios[i]);
222 pa_xfree(fdl->ios);
225 if (fdl->fds)
226 pa_xfree(fdl->fds);
227 if (fdl->work_fds)
228 pa_xfree(fdl->work_fds);
230 pa_xfree(fdl);
233 int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m) {
234 pa_assert(fdl);
235 pa_assert(mixer_handle);
236 pa_assert(m);
237 pa_assert(!fdl->m);
239 fdl->mixer = mixer_handle;
240 fdl->m = m;
241 fdl->defer = m->defer_new(m, defer_cb, fdl);
243 return 0;
246 static int prepare_mixer(snd_mixer_t *mixer, const char *dev) {
247 int err;
249 pa_assert(mixer);
250 pa_assert(dev);
252 if ((err = snd_mixer_attach(mixer, dev)) < 0) {
253 pa_log_info("Unable to attach to mixer %s: %s", dev, pa_alsa_strerror(err));
254 return -1;
257 if ((err = snd_mixer_selem_register(mixer, NULL, NULL)) < 0) {
258 pa_log_warn("Unable to register mixer: %s", pa_alsa_strerror(err));
259 return -1;
262 if ((err = snd_mixer_load(mixer)) < 0) {
263 pa_log_warn("Unable to load mixer: %s", pa_alsa_strerror(err));
264 return -1;
267 pa_log_info("Successfully attached to mixer '%s'", dev);
268 return 0;
271 snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device) {
272 int err;
273 snd_mixer_t *m;
274 const char *dev;
275 snd_pcm_info_t* info;
276 snd_pcm_info_alloca(&info);
278 pa_assert(pcm);
280 if ((err = snd_mixer_open(&m, 0)) < 0) {
281 pa_log("Error opening mixer: %s", pa_alsa_strerror(err));
282 return NULL;
285 /* First, try by name */
286 if ((dev = snd_pcm_name(pcm)))
287 if (prepare_mixer(m, dev) >= 0) {
288 if (ctl_device)
289 *ctl_device = pa_xstrdup(dev);
291 return m;
294 /* Then, try by card index */
295 if (snd_pcm_info(pcm, info) >= 0) {
296 char *md;
297 int card_idx;
299 if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
301 md = pa_sprintf_malloc("hw:%i", card_idx);
303 if (!dev || !pa_streq(dev, md))
304 if (prepare_mixer(m, md) >= 0) {
306 if (ctl_device)
307 *ctl_device = md;
308 else
309 pa_xfree(md);
311 return m;
314 pa_xfree(md);
318 snd_mixer_close(m);
319 return NULL;
322 static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_MAX] = {
323 [PA_CHANNEL_POSITION_MONO] = SND_MIXER_SCHN_MONO, /* The ALSA name is just an alias! */
325 [PA_CHANNEL_POSITION_FRONT_CENTER] = SND_MIXER_SCHN_FRONT_CENTER,
326 [PA_CHANNEL_POSITION_FRONT_LEFT] = SND_MIXER_SCHN_FRONT_LEFT,
327 [PA_CHANNEL_POSITION_FRONT_RIGHT] = SND_MIXER_SCHN_FRONT_RIGHT,
329 [PA_CHANNEL_POSITION_REAR_CENTER] = SND_MIXER_SCHN_REAR_CENTER,
330 [PA_CHANNEL_POSITION_REAR_LEFT] = SND_MIXER_SCHN_REAR_LEFT,
331 [PA_CHANNEL_POSITION_REAR_RIGHT] = SND_MIXER_SCHN_REAR_RIGHT,
333 [PA_CHANNEL_POSITION_LFE] = SND_MIXER_SCHN_WOOFER,
335 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
336 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
338 [PA_CHANNEL_POSITION_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT,
339 [PA_CHANNEL_POSITION_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT,
341 [PA_CHANNEL_POSITION_AUX0] = SND_MIXER_SCHN_UNKNOWN,
342 [PA_CHANNEL_POSITION_AUX1] = SND_MIXER_SCHN_UNKNOWN,
343 [PA_CHANNEL_POSITION_AUX2] = SND_MIXER_SCHN_UNKNOWN,
344 [PA_CHANNEL_POSITION_AUX3] = SND_MIXER_SCHN_UNKNOWN,
345 [PA_CHANNEL_POSITION_AUX4] = SND_MIXER_SCHN_UNKNOWN,
346 [PA_CHANNEL_POSITION_AUX5] = SND_MIXER_SCHN_UNKNOWN,
347 [PA_CHANNEL_POSITION_AUX6] = SND_MIXER_SCHN_UNKNOWN,
348 [PA_CHANNEL_POSITION_AUX7] = SND_MIXER_SCHN_UNKNOWN,
349 [PA_CHANNEL_POSITION_AUX8] = SND_MIXER_SCHN_UNKNOWN,
350 [PA_CHANNEL_POSITION_AUX9] = SND_MIXER_SCHN_UNKNOWN,
351 [PA_CHANNEL_POSITION_AUX10] = SND_MIXER_SCHN_UNKNOWN,
352 [PA_CHANNEL_POSITION_AUX11] = SND_MIXER_SCHN_UNKNOWN,
353 [PA_CHANNEL_POSITION_AUX12] = SND_MIXER_SCHN_UNKNOWN,
354 [PA_CHANNEL_POSITION_AUX13] = SND_MIXER_SCHN_UNKNOWN,
355 [PA_CHANNEL_POSITION_AUX14] = SND_MIXER_SCHN_UNKNOWN,
356 [PA_CHANNEL_POSITION_AUX15] = SND_MIXER_SCHN_UNKNOWN,
357 [PA_CHANNEL_POSITION_AUX16] = SND_MIXER_SCHN_UNKNOWN,
358 [PA_CHANNEL_POSITION_AUX17] = SND_MIXER_SCHN_UNKNOWN,
359 [PA_CHANNEL_POSITION_AUX18] = SND_MIXER_SCHN_UNKNOWN,
360 [PA_CHANNEL_POSITION_AUX19] = SND_MIXER_SCHN_UNKNOWN,
361 [PA_CHANNEL_POSITION_AUX20] = SND_MIXER_SCHN_UNKNOWN,
362 [PA_CHANNEL_POSITION_AUX21] = SND_MIXER_SCHN_UNKNOWN,
363 [PA_CHANNEL_POSITION_AUX22] = SND_MIXER_SCHN_UNKNOWN,
364 [PA_CHANNEL_POSITION_AUX23] = SND_MIXER_SCHN_UNKNOWN,
365 [PA_CHANNEL_POSITION_AUX24] = SND_MIXER_SCHN_UNKNOWN,
366 [PA_CHANNEL_POSITION_AUX25] = SND_MIXER_SCHN_UNKNOWN,
367 [PA_CHANNEL_POSITION_AUX26] = SND_MIXER_SCHN_UNKNOWN,
368 [PA_CHANNEL_POSITION_AUX27] = SND_MIXER_SCHN_UNKNOWN,
369 [PA_CHANNEL_POSITION_AUX28] = SND_MIXER_SCHN_UNKNOWN,
370 [PA_CHANNEL_POSITION_AUX29] = SND_MIXER_SCHN_UNKNOWN,
371 [PA_CHANNEL_POSITION_AUX30] = SND_MIXER_SCHN_UNKNOWN,
372 [PA_CHANNEL_POSITION_AUX31] = SND_MIXER_SCHN_UNKNOWN,
374 [PA_CHANNEL_POSITION_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN,
376 [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SND_MIXER_SCHN_UNKNOWN,
377 [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SND_MIXER_SCHN_UNKNOWN,
378 [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SND_MIXER_SCHN_UNKNOWN,
380 [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SND_MIXER_SCHN_UNKNOWN,
381 [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SND_MIXER_SCHN_UNKNOWN,
382 [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SND_MIXER_SCHN_UNKNOWN
385 static void setting_free(pa_alsa_setting *s) {
386 pa_assert(s);
388 if (s->options)
389 pa_idxset_free(s->options, NULL, NULL);
391 pa_xfree(s->name);
392 pa_xfree(s->description);
393 pa_xfree(s);
396 static void option_free(pa_alsa_option *o) {
397 pa_assert(o);
399 pa_xfree(o->alsa_name);
400 pa_xfree(o->name);
401 pa_xfree(o->description);
402 pa_xfree(o);
405 static void element_free(pa_alsa_element *e) {
406 pa_alsa_option *o;
407 pa_assert(e);
409 while ((o = e->options)) {
410 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
411 option_free(o);
414 pa_xfree(e->alsa_name);
415 pa_xfree(e);
418 void pa_alsa_path_free(pa_alsa_path *p) {
419 pa_alsa_element *e;
420 pa_alsa_setting *s;
422 pa_assert(p);
424 while ((e = p->elements)) {
425 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
426 element_free(e);
429 while ((s = p->settings)) {
430 PA_LLIST_REMOVE(pa_alsa_setting, p->settings, s);
431 setting_free(s);
434 pa_xfree(p->name);
435 pa_xfree(p->description);
436 pa_xfree(p);
439 void pa_alsa_path_set_free(pa_alsa_path_set *ps) {
440 pa_alsa_path *p;
441 pa_assert(ps);
443 while ((p = ps->paths)) {
444 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
445 pa_alsa_path_free(p);
448 pa_xfree(ps);
451 static long to_alsa_dB(pa_volume_t v) {
452 return (long) (pa_sw_volume_to_dB(v) * 100.0);
455 static pa_volume_t from_alsa_dB(long v) {
456 return pa_sw_volume_from_dB((double) v / 100.0);
459 static long to_alsa_volume(pa_volume_t v, long min, long max) {
460 long w;
462 w = (long) round(((double) v * (double) (max - min)) / PA_VOLUME_NORM) + min;
463 return PA_CLAMP_UNLIKELY(w, min, max);
466 static pa_volume_t from_alsa_volume(long v, long min, long max) {
467 return (pa_volume_t) round(((double) (v - min) * PA_VOLUME_NORM) / (double) (max - min));
470 #define SELEM_INIT(sid, name) \
471 do { \
472 snd_mixer_selem_id_alloca(&(sid)); \
473 snd_mixer_selem_id_set_name((sid), (name)); \
474 snd_mixer_selem_id_set_index((sid), 0); \
475 } while(FALSE)
477 static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
478 snd_mixer_selem_id_t *sid;
479 snd_mixer_elem_t *me;
480 snd_mixer_selem_channel_id_t c;
481 pa_channel_position_mask_t mask = 0;
482 pa_volume_t max_channel_volume = PA_VOLUME_MUTED;
483 unsigned k;
485 pa_assert(m);
486 pa_assert(e);
487 pa_assert(cm);
488 pa_assert(v);
490 SELEM_INIT(sid, e->alsa_name);
491 if (!(me = snd_mixer_find_selem(m, sid))) {
492 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
493 return -1;
496 pa_cvolume_mute(v, cm->channels);
498 /* We take the highest volume of all channels that match */
500 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
501 int r;
502 pa_volume_t f;
504 if (e->has_dB) {
505 long value = 0;
507 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
508 if (snd_mixer_selem_has_playback_channel(me, c))
509 r = snd_mixer_selem_get_playback_dB(me, c, &value);
510 else
511 r = -1;
512 } else {
513 if (snd_mixer_selem_has_capture_channel(me, c))
514 r = snd_mixer_selem_get_capture_dB(me, c, &value);
515 else
516 r = -1;
519 if (r < 0)
520 continue;
522 #ifdef HAVE_VALGRIND_MEMCHECK_H
523 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
524 #endif
526 f = from_alsa_dB(value);
528 } else {
529 long value = 0;
531 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
532 if (snd_mixer_selem_has_playback_channel(me, c))
533 r = snd_mixer_selem_get_playback_volume(me, c, &value);
534 else
535 r = -1;
536 } else {
537 if (snd_mixer_selem_has_capture_channel(me, c))
538 r = snd_mixer_selem_get_capture_volume(me, c, &value);
539 else
540 r = -1;
543 if (r < 0)
544 continue;
546 f = from_alsa_volume(value, e->min_volume, e->max_volume);
549 if (f > max_channel_volume)
550 max_channel_volume = f;
552 for (k = 0; k < cm->channels; k++)
553 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
554 if (v->values[k] < f)
555 v->values[k] = f;
557 mask |= e->masks[c][e->n_channels-1];
560 for (k = 0; k < cm->channels; k++)
561 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
562 v->values[k] = max_channel_volume;
564 return 0;
567 int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
568 pa_alsa_element *e;
570 pa_assert(m);
571 pa_assert(p);
572 pa_assert(cm);
573 pa_assert(v);
575 if (!p->has_volume)
576 return -1;
578 pa_cvolume_reset(v, cm->channels);
580 PA_LLIST_FOREACH(e, p->elements) {
581 pa_cvolume ev;
583 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
584 continue;
586 pa_assert(!p->has_dB || e->has_dB);
588 if (element_get_volume(e, m, cm, &ev) < 0)
589 return -1;
591 /* If we have no dB information all we can do is take the first element and leave */
592 if (!p->has_dB) {
593 *v = ev;
594 return 0;
597 pa_sw_cvolume_multiply(v, v, &ev);
600 return 0;
603 static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t *b) {
604 snd_mixer_selem_id_t *sid;
605 snd_mixer_elem_t *me;
606 snd_mixer_selem_channel_id_t c;
608 pa_assert(m);
609 pa_assert(e);
610 pa_assert(b);
612 SELEM_INIT(sid, e->alsa_name);
613 if (!(me = snd_mixer_find_selem(m, sid))) {
614 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
615 return -1;
618 /* We return muted if at least one channel is muted */
620 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
621 int r;
622 int value = 0;
624 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
625 if (snd_mixer_selem_has_playback_channel(me, c))
626 r = snd_mixer_selem_get_playback_switch(me, c, &value);
627 else
628 r = -1;
629 } else {
630 if (snd_mixer_selem_has_capture_channel(me, c))
631 r = snd_mixer_selem_get_capture_switch(me, c, &value);
632 else
633 r = -1;
636 if (r < 0)
637 continue;
639 if (!value) {
640 *b = FALSE;
641 return 0;
645 *b = TRUE;
646 return 0;
649 int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t *muted) {
650 pa_alsa_element *e;
652 pa_assert(m);
653 pa_assert(p);
654 pa_assert(muted);
656 if (!p->has_mute)
657 return -1;
659 PA_LLIST_FOREACH(e, p->elements) {
660 pa_bool_t b;
662 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
663 continue;
665 if (element_get_switch(e, m, &b) < 0)
666 return -1;
668 if (!b) {
669 *muted = TRUE;
670 return 0;
674 *muted = FALSE;
675 return 0;
678 static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
679 snd_mixer_selem_id_t *sid;
680 pa_cvolume rv;
681 snd_mixer_elem_t *me;
682 snd_mixer_selem_channel_id_t c;
683 pa_channel_position_mask_t mask = 0;
684 pa_volume_t max_channel_volume = PA_VOLUME_MUTED;
685 unsigned k;
687 pa_assert(m);
688 pa_assert(e);
689 pa_assert(cm);
690 pa_assert(v);
691 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
693 SELEM_INIT(sid, e->alsa_name);
694 if (!(me = snd_mixer_find_selem(m, sid))) {
695 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
696 return -1;
699 pa_cvolume_mute(&rv, cm->channels);
701 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
702 int r;
703 pa_volume_t f = PA_VOLUME_MUTED;
704 pa_bool_t found = FALSE;
706 for (k = 0; k < cm->channels; k++)
707 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) {
708 found = TRUE;
709 if (v->values[k] > f)
710 f = v->values[k];
713 if (!found) {
714 /* Hmm, so this channel does not exist in the volume
715 * struct, so let's bind it to the overall max of the
716 * volume. */
717 f = pa_cvolume_max(v);
720 if (e->has_dB) {
721 long value = to_alsa_dB(f);
723 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
724 /* If we call set_play_volume() without checking first
725 * if the channel is available, ALSA behaves ver
726 * strangely and doesn't fail the call */
727 if (snd_mixer_selem_has_playback_channel(me, c)) {
728 if ((r = snd_mixer_selem_set_playback_dB(me, c, value, +1)) >= 0)
729 r = snd_mixer_selem_get_playback_dB(me, c, &value);
730 } else
731 r = -1;
732 } else {
733 if (snd_mixer_selem_has_capture_channel(me, c)) {
734 if ((r = snd_mixer_selem_set_capture_dB(me, c, value, +1)) >= 0)
735 r = snd_mixer_selem_get_capture_dB(me, c, &value);
736 } else
737 r = -1;
740 if (r < 0)
741 continue;
743 #ifdef HAVE_VALGRIND_MEMCHECK_H
744 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
745 #endif
747 f = from_alsa_dB(value);
749 } else {
750 long value;
752 value = to_alsa_volume(f, e->min_volume, e->max_volume);
754 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
755 if (snd_mixer_selem_has_playback_channel(me, c)) {
756 if ((r = snd_mixer_selem_set_playback_volume(me, c, value)) >= 0)
757 r = snd_mixer_selem_get_playback_volume(me, c, &value);
758 } else
759 r = -1;
760 } else {
761 if (snd_mixer_selem_has_capture_channel(me, c)) {
762 if ((r = snd_mixer_selem_set_capture_volume(me, c, value)) >= 0)
763 r = snd_mixer_selem_get_capture_volume(me, c, &value);
764 } else
765 r = -1;
768 if (r < 0)
769 continue;
771 f = from_alsa_volume(value, e->min_volume, e->max_volume);
774 if (f > max_channel_volume)
775 max_channel_volume = f;
777 for (k = 0; k < cm->channels; k++)
778 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
779 if (rv.values[k] < f)
780 rv.values[k] = f;
782 mask |= e->masks[c][e->n_channels-1];
785 for (k = 0; k < cm->channels; k++)
786 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
787 rv.values[k] = max_channel_volume;
789 *v = rv;
790 return 0;
793 int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
794 pa_alsa_element *e;
795 pa_cvolume rv;
797 pa_assert(m);
798 pa_assert(p);
799 pa_assert(cm);
800 pa_assert(v);
801 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
803 if (!p->has_volume)
804 return -1;
806 rv = *v; /* Remaining adjustment */
807 pa_cvolume_reset(v, cm->channels); /* Adjustment done */
809 PA_LLIST_FOREACH(e, p->elements) {
810 pa_cvolume ev;
812 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
813 continue;
815 pa_assert(!p->has_dB || e->has_dB);
817 ev = rv;
818 if (element_set_volume(e, m, cm, &ev) < 0)
819 return -1;
821 if (!p->has_dB) {
822 *v = ev;
823 return 0;
826 pa_sw_cvolume_multiply(v, v, &ev);
827 pa_sw_cvolume_divide(&rv, &rv, &ev);
830 return 0;
833 static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t b) {
834 snd_mixer_elem_t *me;
835 snd_mixer_selem_id_t *sid;
836 int r;
838 pa_assert(m);
839 pa_assert(e);
841 SELEM_INIT(sid, e->alsa_name);
842 if (!(me = snd_mixer_find_selem(m, sid))) {
843 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
844 return -1;
847 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
848 r = snd_mixer_selem_set_playback_switch_all(me, b);
849 else
850 r = snd_mixer_selem_set_capture_switch_all(me, b);
852 if (r < 0)
853 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
855 return r;
858 int pa_alsa_path_set_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t muted) {
859 pa_alsa_element *e;
861 pa_assert(m);
862 pa_assert(p);
864 if (!p->has_mute)
865 return -1;
867 PA_LLIST_FOREACH(e, p->elements) {
869 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
870 continue;
872 if (element_set_switch(e, m, !muted) < 0)
873 return -1;
876 return 0;
879 static int element_mute_volume(pa_alsa_element *e, snd_mixer_t *m) {
880 snd_mixer_elem_t *me;
881 snd_mixer_selem_id_t *sid;
882 int r;
884 pa_assert(m);
885 pa_assert(e);
887 SELEM_INIT(sid, e->alsa_name);
888 if (!(me = snd_mixer_find_selem(m, sid))) {
889 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
890 return -1;
893 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
894 r = snd_mixer_selem_set_playback_volume_all(me, e->min_volume);
895 else
896 r = snd_mixer_selem_set_capture_volume_all(me, e->min_volume);
898 if (r < 0)
899 pa_log_warn("Faile to set volume to muted of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
901 return r;
904 /* The volume to 0dB */
905 static int element_zero_volume(pa_alsa_element *e, snd_mixer_t *m) {
906 snd_mixer_elem_t *me;
907 snd_mixer_selem_id_t *sid;
908 int r;
910 pa_assert(m);
911 pa_assert(e);
913 SELEM_INIT(sid, e->alsa_name);
914 if (!(me = snd_mixer_find_selem(m, sid))) {
915 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
916 return -1;
919 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
920 r = snd_mixer_selem_set_playback_dB_all(me, 0, +1);
921 else
922 r = snd_mixer_selem_set_capture_dB_all(me, 0, +1);
924 if (r < 0)
925 pa_log_warn("Faile to set volume to 0dB of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
927 return r;
930 int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
931 pa_alsa_element *e;
932 int r;
934 pa_assert(m);
935 pa_assert(p);
937 pa_log_debug("Activating path %s", p->name);
938 pa_alsa_path_dump(p);
940 PA_LLIST_FOREACH(e, p->elements) {
942 switch (e->switch_use) {
943 case PA_ALSA_SWITCH_OFF:
944 r = element_set_switch(e, m, FALSE);
945 break;
947 case PA_ALSA_SWITCH_ON:
948 r = element_set_switch(e, m, TRUE);
949 break;
951 case PA_ALSA_SWITCH_MUTE:
952 case PA_ALSA_SWITCH_IGNORE:
953 case PA_ALSA_SWITCH_SELECT:
954 r = 0;
955 break;
958 if (r < 0)
959 return -1;
961 switch (e->volume_use) {
962 case PA_ALSA_VOLUME_OFF:
963 r = element_mute_volume(e, m);
964 break;
966 case PA_ALSA_VOLUME_ZERO:
967 r = element_zero_volume(e, m);
968 break;
970 case PA_ALSA_VOLUME_MERGE:
971 case PA_ALSA_VOLUME_IGNORE:
972 r = 0;
973 break;
976 if (r < 0)
977 return -1;
980 return 0;
983 static int check_required(pa_alsa_element *e, snd_mixer_elem_t *me) {
984 pa_bool_t has_switch;
985 pa_bool_t has_enumeration;
986 pa_bool_t has_volume;
988 pa_assert(e);
989 pa_assert(me);
991 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
992 has_switch =
993 snd_mixer_selem_has_playback_switch(me) ||
994 (e->direction_try_other && snd_mixer_selem_has_capture_switch(me));
995 } else {
996 has_switch =
997 snd_mixer_selem_has_capture_switch(me) ||
998 (e->direction_try_other && snd_mixer_selem_has_playback_switch(me));
1001 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1002 has_volume =
1003 snd_mixer_selem_has_playback_volume(me) ||
1004 (e->direction_try_other && snd_mixer_selem_has_capture_volume(me));
1005 } else {
1006 has_volume =
1007 snd_mixer_selem_has_capture_volume(me) ||
1008 (e->direction_try_other && snd_mixer_selem_has_playback_volume(me));
1011 has_enumeration = snd_mixer_selem_is_enumerated(me);
1013 if ((e->required == PA_ALSA_REQUIRED_SWITCH && !has_switch) ||
1014 (e->required == PA_ALSA_REQUIRED_VOLUME && !has_volume) ||
1015 (e->required == PA_ALSA_REQUIRED_ENUMERATION && !has_enumeration))
1016 return -1;
1018 if (e->required == PA_ALSA_REQUIRED_ANY && !(has_switch || has_volume || has_enumeration))
1019 return -1;
1021 if ((e->required_absent == PA_ALSA_REQUIRED_SWITCH && has_switch) ||
1022 (e->required_absent == PA_ALSA_REQUIRED_VOLUME && has_volume) ||
1023 (e->required_absent == PA_ALSA_REQUIRED_ENUMERATION && has_enumeration))
1024 return -1;
1026 if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration))
1027 return -1;
1029 return 0;
1032 static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
1033 snd_mixer_selem_id_t *sid;
1034 snd_mixer_elem_t *me;
1036 pa_assert(m);
1037 pa_assert(e);
1039 SELEM_INIT(sid, e->alsa_name);
1041 if (!(me = snd_mixer_find_selem(m, sid))) {
1043 if (e->required != PA_ALSA_REQUIRED_IGNORE)
1044 return -1;
1046 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1047 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1048 e->enumeration_use = PA_ALSA_VOLUME_IGNORE;
1050 return 0;
1053 if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
1054 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1056 if (!snd_mixer_selem_has_playback_switch(me)) {
1057 if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
1058 e->direction = PA_ALSA_DIRECTION_INPUT;
1059 else
1060 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1063 } else {
1065 if (!snd_mixer_selem_has_capture_switch(me)) {
1066 if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
1067 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1068 else
1069 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1073 if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
1074 e->direction_try_other = FALSE;
1077 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1079 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1081 if (!snd_mixer_selem_has_playback_volume(me)) {
1082 if (e->direction_try_other && snd_mixer_selem_has_capture_volume(me))
1083 e->direction = PA_ALSA_DIRECTION_INPUT;
1084 else
1085 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1088 } else {
1090 if (!snd_mixer_selem_has_capture_volume(me)) {
1091 if (e->direction_try_other && snd_mixer_selem_has_playback_volume(me))
1092 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1093 else
1094 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1098 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1099 long min_dB = 0, max_dB = 0;
1100 int r;
1102 e->direction_try_other = FALSE;
1104 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1105 e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0;
1106 else
1107 e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0;
1109 if (e->has_dB) {
1110 #ifdef HAVE_VALGRIND_MEMCHECK_H
1111 VALGRIND_MAKE_MEM_DEFINED(&min_dB, sizeof(min_dB));
1112 VALGRIND_MAKE_MEM_DEFINED(&max_dB, sizeof(max_dB));
1113 #endif
1115 e->min_dB = ((double) min_dB) / 100.0;
1116 e->max_dB = ((double) max_dB) / 100.0;
1118 if (min_dB >= max_dB) {
1119 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);
1120 e->has_dB = FALSE;
1124 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1125 r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume);
1126 else
1127 r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
1129 if (r < 0) {
1130 pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1131 return -1;
1135 if (e->min_volume >= e->max_volume) {
1136 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);
1137 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1139 } else {
1140 pa_bool_t is_mono;
1141 pa_channel_position_t p;
1143 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1144 is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
1145 else
1146 is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
1148 if (is_mono) {
1149 e->n_channels = 1;
1151 if (!e->override_map) {
1152 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++)
1153 e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
1154 e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
1157 e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
1158 } else {
1159 e->n_channels = 0;
1160 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1162 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1163 continue;
1165 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1166 e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1167 else
1168 e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1171 if (e->n_channels <= 0) {
1172 pa_log_warn("Volume element %s with no channels?", e->alsa_name);
1173 return -1;
1176 if (!e->override_map) {
1177 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1178 pa_bool_t has_channel;
1180 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1181 continue;
1183 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1184 has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1185 else
1186 has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1188 e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
1192 e->merged_mask = 0;
1193 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++)
1194 e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
1201 if (check_required(e, me) < 0)
1202 return -1;
1204 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1205 pa_alsa_option *o;
1207 PA_LLIST_FOREACH(o, e->options)
1208 o->alsa_idx = pa_streq(o->alsa_name, "on") ? 1 : 0;
1209 } else if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1210 int n;
1211 pa_alsa_option *o;
1213 if ((n = snd_mixer_selem_get_enum_items(me)) < 0) {
1214 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n));
1215 return -1;
1218 PA_LLIST_FOREACH(o, e->options) {
1219 int i;
1221 for (i = 0; i < n; i++) {
1222 char buf[128];
1224 if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
1225 continue;
1227 if (!pa_streq(buf, o->alsa_name))
1228 continue;
1230 o->alsa_idx = i;
1235 return 0;
1238 static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, pa_bool_t prefixed) {
1239 pa_alsa_element *e;
1241 pa_assert(p);
1242 pa_assert(section);
1244 if (prefixed) {
1245 if (!pa_startswith(section, "Element "))
1246 return NULL;
1248 section += 8;
1251 /* This is not an element section, but an enum section? */
1252 if (strchr(section, ':'))
1253 return NULL;
1255 if (p->last_element && pa_streq(p->last_element->alsa_name, section))
1256 return p->last_element;
1258 PA_LLIST_FOREACH(e, p->elements)
1259 if (pa_streq(e->alsa_name, section))
1260 goto finish;
1262 e = pa_xnew0(pa_alsa_element, 1);
1263 e->path = p;
1264 e->alsa_name = pa_xstrdup(section);
1265 e->direction = p->direction;
1267 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
1269 finish:
1270 p->last_element = e;
1271 return e;
1274 static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
1275 char *en;
1276 const char *on;
1277 pa_alsa_option *o;
1278 pa_alsa_element *e;
1280 if (!pa_startswith(section, "Option "))
1281 return NULL;
1283 section += 7;
1285 /* This is not an enum section, but an element section? */
1286 if (!(on = strchr(section, ':')))
1287 return NULL;
1289 en = pa_xstrndup(section, on - section);
1290 on++;
1292 if (p->last_option &&
1293 pa_streq(p->last_option->element->alsa_name, en) &&
1294 pa_streq(p->last_option->alsa_name, on)) {
1295 pa_xfree(en);
1296 return p->last_option;
1299 pa_assert_se(e = element_get(p, en, FALSE));
1300 pa_xfree(en);
1302 PA_LLIST_FOREACH(o, e->options)
1303 if (pa_streq(o->alsa_name, on))
1304 goto finish;
1306 o = pa_xnew0(pa_alsa_option, 1);
1307 o->element = e;
1308 o->alsa_name = pa_xstrdup(on);
1309 o->alsa_idx = -1;
1311 if (p->last_option && p->last_option->element == e)
1312 PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
1313 else
1314 PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
1316 finish:
1317 p->last_option = o;
1318 return o;
1321 static int element_parse_switch(
1322 const char *filename,
1323 unsigned line,
1324 const char *section,
1325 const char *lvalue,
1326 const char *rvalue,
1327 void *data,
1328 void *userdata) {
1330 pa_alsa_path *p = userdata;
1331 pa_alsa_element *e;
1333 pa_assert(p);
1335 if (!(e = element_get(p, section, TRUE))) {
1336 pa_log("[%s:%u] Switch makes no sense in '%s'", filename, line, section);
1337 return -1;
1340 if (pa_streq(rvalue, "ignore"))
1341 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1342 else if (pa_streq(rvalue, "mute"))
1343 e->switch_use = PA_ALSA_SWITCH_MUTE;
1344 else if (pa_streq(rvalue, "off"))
1345 e->switch_use = PA_ALSA_SWITCH_OFF;
1346 else if (pa_streq(rvalue, "on"))
1347 e->switch_use = PA_ALSA_SWITCH_ON;
1348 else if (pa_streq(rvalue, "select"))
1349 e->switch_use = PA_ALSA_SWITCH_SELECT;
1350 else {
1351 pa_log("[%s:%u] Switch invalid of '%s'", filename, line, section);
1352 return -1;
1355 return 0;
1358 static int element_parse_volume(
1359 const char *filename,
1360 unsigned line,
1361 const char *section,
1362 const char *lvalue,
1363 const char *rvalue,
1364 void *data,
1365 void *userdata) {
1367 pa_alsa_path *p = userdata;
1368 pa_alsa_element *e;
1370 pa_assert(p);
1372 if (!(e = element_get(p, section, TRUE))) {
1373 pa_log("[%s:%u] Volume makes no sense in '%s'", filename, line, section);
1374 return -1;
1377 if (pa_streq(rvalue, "ignore"))
1378 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1379 else if (pa_streq(rvalue, "merge"))
1380 e->volume_use = PA_ALSA_VOLUME_MERGE;
1381 else if (pa_streq(rvalue, "off"))
1382 e->volume_use = PA_ALSA_VOLUME_OFF;
1383 else if (pa_streq(rvalue, "zero"))
1384 e->volume_use = PA_ALSA_VOLUME_ZERO;
1385 else {
1386 pa_log("[%s:%u] Volume invalid of '%s'", filename, line, section);
1387 return -1;
1390 return 0;
1393 static int element_parse_enumeration(
1394 const char *filename,
1395 unsigned line,
1396 const char *section,
1397 const char *lvalue,
1398 const char *rvalue,
1399 void *data,
1400 void *userdata) {
1402 pa_alsa_path *p = userdata;
1403 pa_alsa_element *e;
1405 pa_assert(p);
1407 if (!(e = element_get(p, section, TRUE))) {
1408 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename, line, section);
1409 return -1;
1412 if (pa_streq(rvalue, "ignore"))
1413 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1414 else if (pa_streq(rvalue, "select"))
1415 e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
1416 else {
1417 pa_log("[%s:%u] Enumeration invalid of '%s'", filename, line, section);
1418 return -1;
1421 return 0;
1424 static int option_parse_priority(
1425 const char *filename,
1426 unsigned line,
1427 const char *section,
1428 const char *lvalue,
1429 const char *rvalue,
1430 void *data,
1431 void *userdata) {
1433 pa_alsa_path *p = userdata;
1434 pa_alsa_option *o;
1435 uint32_t prio;
1437 pa_assert(p);
1439 if (!(o = option_get(p, section))) {
1440 pa_log("[%s:%u] Priority makes no sense in '%s'", filename, line, section);
1441 return -1;
1444 if (pa_atou(rvalue, &prio) < 0) {
1445 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
1446 return -1;
1449 o->priority = prio;
1450 return 0;
1453 static int option_parse_name(
1454 const char *filename,
1455 unsigned line,
1456 const char *section,
1457 const char *lvalue,
1458 const char *rvalue,
1459 void *data,
1460 void *userdata) {
1462 pa_alsa_path *p = userdata;
1463 pa_alsa_option *o;
1465 pa_assert(p);
1467 if (!(o = option_get(p, section))) {
1468 pa_log("[%s:%u] Name makes no sense in '%s'", filename, line, section);
1469 return -1;
1472 pa_xfree(o->name);
1473 o->name = pa_xstrdup(rvalue);
1475 return 0;
1478 static int element_parse_required(
1479 const char *filename,
1480 unsigned line,
1481 const char *section,
1482 const char *lvalue,
1483 const char *rvalue,
1484 void *data,
1485 void *userdata) {
1487 pa_alsa_path *p = userdata;
1488 pa_alsa_element *e;
1489 pa_alsa_required_t req;
1491 pa_assert(p);
1493 if (!(e = element_get(p, section, TRUE))) {
1494 pa_log("[%s:%u] Required makes no sense in '%s'", filename, line, section);
1495 return -1;
1498 if (pa_streq(rvalue, "ignore"))
1499 req = PA_ALSA_REQUIRED_IGNORE;
1500 else if (pa_streq(rvalue, "switch"))
1501 req = PA_ALSA_REQUIRED_SWITCH;
1502 else if (pa_streq(rvalue, "volume"))
1503 req = PA_ALSA_REQUIRED_VOLUME;
1504 else if (pa_streq(rvalue, "enumeration"))
1505 req = PA_ALSA_REQUIRED_ENUMERATION;
1506 else if (pa_streq(rvalue, "any"))
1507 req = PA_ALSA_REQUIRED_ANY;
1508 else {
1509 pa_log("[%s:%u] Required invalid of '%s'", filename, line, section);
1510 return -1;
1513 if (pa_streq(lvalue, "required-absent"))
1514 e->required_absent = req;
1515 else
1516 e->required = req;
1518 return 0;
1521 static int element_parse_direction(
1522 const char *filename,
1523 unsigned line,
1524 const char *section,
1525 const char *lvalue,
1526 const char *rvalue,
1527 void *data,
1528 void *userdata) {
1530 pa_alsa_path *p = userdata;
1531 pa_alsa_element *e;
1533 pa_assert(p);
1535 if (!(e = element_get(p, section, TRUE))) {
1536 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1537 return -1;
1540 if (pa_streq(rvalue, "playback"))
1541 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1542 else if (pa_streq(rvalue, "capture"))
1543 e->direction = PA_ALSA_DIRECTION_INPUT;
1544 else {
1545 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1546 return -1;
1549 return 0;
1552 static int element_parse_direction_try_other(
1553 const char *filename,
1554 unsigned line,
1555 const char *section,
1556 const char *lvalue,
1557 const char *rvalue,
1558 void *data,
1559 void *userdata) {
1561 pa_alsa_path *p = userdata;
1562 pa_alsa_element *e;
1563 int yes;
1565 if (!(e = element_get(p, section, TRUE))) {
1566 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1567 return -1;
1570 if ((yes = pa_parse_boolean(rvalue)) < 0) {
1571 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1572 return -1;
1575 e->direction_try_other = !!yes;
1576 return 0;
1579 static pa_channel_position_mask_t parse_mask(const char *m) {
1580 pa_channel_position_mask_t v;
1582 if (pa_streq(m, "all-left"))
1583 v = PA_CHANNEL_POSITION_MASK_LEFT;
1584 else if (pa_streq(m, "all-right"))
1585 v = PA_CHANNEL_POSITION_MASK_RIGHT;
1586 else if (pa_streq(m, "all-center"))
1587 v = PA_CHANNEL_POSITION_MASK_CENTER;
1588 else if (pa_streq(m, "all-front"))
1589 v = PA_CHANNEL_POSITION_MASK_FRONT;
1590 else if (pa_streq(m, "all-rear"))
1591 v = PA_CHANNEL_POSITION_MASK_REAR;
1592 else if (pa_streq(m, "all-side"))
1593 v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER;
1594 else if (pa_streq(m, "all-top"))
1595 v = PA_CHANNEL_POSITION_MASK_TOP;
1596 else if (pa_streq(m, "all-no-lfe"))
1597 v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE);
1598 else if (pa_streq(m, "all"))
1599 v = PA_CHANNEL_POSITION_MASK_ALL;
1600 else {
1601 pa_channel_position_t p;
1603 if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
1604 return 0;
1606 v = PA_CHANNEL_POSITION_MASK(p);
1609 return v;
1612 static int element_parse_override_map(
1613 const char *filename,
1614 unsigned line,
1615 const char *section,
1616 const char *lvalue,
1617 const char *rvalue,
1618 void *data,
1619 void *userdata) {
1621 pa_alsa_path *p = userdata;
1622 pa_alsa_element *e;
1623 const char *state = NULL;
1624 unsigned i = 0;
1625 char *n;
1627 if (!(e = element_get(p, section, TRUE))) {
1628 pa_log("[%s:%u] Override map makes no sense in '%s'", filename, line, section);
1629 return -1;
1632 while ((n = pa_split(rvalue, ",", &state))) {
1633 pa_channel_position_mask_t m;
1635 if (!*n)
1636 m = 0;
1637 else {
1638 if ((m = parse_mask(n)) == 0) {
1639 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename, line, n, section);
1640 pa_xfree(n);
1641 return -1;
1645 if (pa_streq(lvalue, "override-map.1"))
1646 e->masks[i++][0] = m;
1647 else
1648 e->masks[i++][1] = m;
1650 /* Later on we might add override-map.3 and so on here ... */
1652 pa_xfree(n);
1655 e->override_map = TRUE;
1657 return 0;
1660 static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
1661 snd_mixer_selem_id_t *sid;
1662 snd_mixer_elem_t *me;
1663 int r;
1665 pa_assert(e);
1666 pa_assert(m);
1668 SELEM_INIT(sid, e->alsa_name);
1669 if (!(me = snd_mixer_find_selem(m, sid))) {
1670 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1671 return -1;
1674 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1676 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1677 r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
1678 else
1679 r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
1681 if (r < 0)
1682 pa_log_warn("Faile to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1684 } else {
1685 pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
1687 if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0)
1688 pa_log_warn("Faile to set enumeration of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1691 return r;
1694 int pa_alsa_setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
1695 pa_alsa_option *o;
1696 uint32_t idx;
1698 pa_assert(s);
1699 pa_assert(m);
1701 PA_IDXSET_FOREACH(o, s->options, idx)
1702 element_set_option(o->element, m, o->alsa_idx);
1704 return 0;
1707 static int option_verify(pa_alsa_option *o) {
1708 static const struct description_map well_known_descriptions[] = {
1709 { "input", N_("Input") },
1710 { "input-docking", N_("Docking Station Input") },
1711 { "input-docking-microphone", N_("Docking Station Microphone") },
1712 { "input-linein", N_("Line-In") },
1713 { "input-microphone", N_("Microphone") },
1714 { "input-microphone-external", N_("External Microphone") },
1715 { "input-microphone-internal", N_("Internal Microphone") },
1716 { "input-radio", N_("Radio") },
1717 { "input-video", N_("Video") },
1718 { "input-agc-on", N_("Automatic Gain Control") },
1719 { "input-agc-off", "" },
1720 { "input-boost-on", N_("Boost") },
1721 { "input-boost-off", "" },
1722 { "output-amplifier-on", N_("Amplifier") },
1723 { "output-amplifier-off", "" }
1726 pa_assert(o);
1728 if (!o->name) {
1729 pa_log("No name set for option %s", o->alsa_name);
1730 return -1;
1733 if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
1734 o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
1735 pa_log("Element %s of option %s not set for select.", o->element->alsa_name, o->name);
1736 return -1;
1739 if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
1740 !pa_streq(o->alsa_name, "on") &&
1741 !pa_streq(o->alsa_name, "off")) {
1742 pa_log("Switch %s options need be named off or on ", o->element->alsa_name);
1743 return -1;
1746 if (!o->description)
1747 o->description = pa_xstrdup(lookup_description(o->name,
1748 well_known_descriptions,
1749 PA_ELEMENTSOF(well_known_descriptions)));
1750 if (!o->description)
1751 o->description = pa_xstrdup(o->name);
1753 return 0;
1756 static int element_verify(pa_alsa_element *e) {
1757 pa_alsa_option *o;
1759 pa_assert(e);
1761 if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
1762 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
1763 pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name);
1764 return -1;
1767 if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1768 pa_log("Element %s cannot set select for both switch and enumeration.", e->alsa_name);
1769 return -1;
1772 PA_LLIST_FOREACH(o, e->options)
1773 if (option_verify(o) < 0)
1774 return -1;
1776 return 0;
1779 static int path_verify(pa_alsa_path *p) {
1780 static const struct description_map well_known_descriptions[] = {
1781 { "analog-input", N_("Analog Input") },
1782 { "analog-input-microphone", N_("Analog Microphone") },
1783 { "analog-input-linein", N_("Analog Line-In") },
1784 { "analog-input-radio", N_("Analog Radio") },
1785 { "analog-input-video", N_("Analog Video") },
1786 { "analog-output", N_("Analog Output") },
1787 { "analog-output-headphones", N_("Analog Headphones") },
1788 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
1789 { "analog-output-mono", N_("Analog Mono Output") }
1792 pa_alsa_element *e;
1794 pa_assert(p);
1796 PA_LLIST_FOREACH(e, p->elements)
1797 if (element_verify(e) < 0)
1798 return -1;
1800 if (!p->description)
1801 p->description = pa_xstrdup(lookup_description(p->name,
1802 well_known_descriptions,
1803 PA_ELEMENTSOF(well_known_descriptions)));
1805 if (!p->description)
1806 p->description = pa_xstrdup(p->name);
1808 return 0;
1811 pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction) {
1812 pa_alsa_path *p;
1813 char *fn;
1814 int r;
1815 const char *n;
1817 pa_config_item items[] = {
1818 /* [General] */
1819 { "priority", pa_config_parse_unsigned, NULL, "General" },
1820 { "description", pa_config_parse_string, NULL, "General" },
1821 { "name", pa_config_parse_string, NULL, "General" },
1823 /* [Option ...] */
1824 { "priority", option_parse_priority, NULL, NULL },
1825 { "name", option_parse_name, NULL, NULL },
1827 /* [Element ...] */
1828 { "switch", element_parse_switch, NULL, NULL },
1829 { "volume", element_parse_volume, NULL, NULL },
1830 { "enumeration", element_parse_enumeration, NULL, NULL },
1831 { "override-map.1", element_parse_override_map, NULL, NULL },
1832 { "override-map.2", element_parse_override_map, NULL, NULL },
1833 /* ... later on we might add override-map.3 and so on here ... */
1834 { "required", element_parse_required, NULL, NULL },
1835 { "required-absent", element_parse_required, NULL, NULL },
1836 { "direction", element_parse_direction, NULL, NULL },
1837 { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
1838 { NULL, NULL, NULL, NULL }
1841 pa_assert(fname);
1843 p = pa_xnew0(pa_alsa_path, 1);
1844 n = pa_path_get_filename(fname);
1845 p->name = pa_xstrndup(n, strcspn(n, "."));
1846 p->direction = direction;
1848 items[0].data = &p->priority;
1849 items[1].data = &p->description;
1850 items[2].data = &p->name;
1852 fn = pa_maybe_prefix_path(fname, PA_ALSA_PATHS_DIR);
1853 r = pa_config_parse(fn, NULL, items, p);
1854 pa_xfree(fn);
1856 if (r < 0)
1857 goto fail;
1859 if (path_verify(p) < 0)
1860 goto fail;
1862 return p;
1864 fail:
1865 pa_alsa_path_free(p);
1866 return NULL;
1869 pa_alsa_path* pa_alsa_path_synthesize(const char*element, pa_alsa_direction_t direction) {
1870 pa_alsa_path *p;
1871 pa_alsa_element *e;
1873 pa_assert(element);
1875 p = pa_xnew0(pa_alsa_path, 1);
1876 p->name = pa_xstrdup(element);
1877 p->direction = direction;
1879 e = pa_xnew0(pa_alsa_element, 1);
1880 e->path = p;
1881 e->alsa_name = pa_xstrdup(element);
1882 e->direction = direction;
1884 e->switch_use = PA_ALSA_SWITCH_MUTE;
1885 e->volume_use = PA_ALSA_VOLUME_MERGE;
1887 PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
1888 return p;
1891 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
1892 pa_alsa_option *o, *n;
1894 pa_assert(e);
1896 for (o = e->options; o; o = n) {
1897 n = o->next;
1899 if (o->alsa_idx < 0) {
1900 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
1901 option_free(o);
1905 return
1906 e->switch_use != PA_ALSA_SWITCH_IGNORE ||
1907 e->volume_use != PA_ALSA_VOLUME_IGNORE ||
1908 e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
1911 static void path_drop_unsupported(pa_alsa_path *p) {
1912 pa_alsa_element *e, *n;
1914 pa_assert(p);
1916 for (e = p->elements; e; e = n) {
1917 n = e->next;
1919 if (!element_drop_unsupported(e)) {
1920 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
1921 element_free(e);
1926 static void path_make_options_unique(pa_alsa_path *p) {
1927 pa_alsa_element *e;
1928 pa_alsa_option *o, *u;
1930 PA_LLIST_FOREACH(e, p->elements) {
1931 PA_LLIST_FOREACH(o, e->options) {
1932 unsigned i;
1933 char *m;
1935 for (u = o->next; u; u = u->next)
1936 if (pa_streq(u->name, o->name))
1937 break;
1939 if (!u)
1940 continue;
1942 m = pa_xstrdup(o->name);
1944 /* OK, this name is not unique, hence let's rename */
1945 for (i = 1, u = o; u; u = u->next) {
1946 char *nn, *nd;
1948 if (!pa_streq(u->name, m))
1949 continue;
1951 nn = pa_sprintf_malloc("%s-%u", m, i);
1952 pa_xfree(u->name);
1953 u->name = nn;
1955 nd = pa_sprintf_malloc("%s %u", u->description, i);
1956 pa_xfree(u->description);
1957 u->description = nd;
1959 i++;
1962 pa_xfree(m);
1967 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
1968 pa_alsa_option *o;
1970 for (; e; e = e->next)
1971 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
1972 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
1973 break;
1975 if (!e)
1976 return FALSE;
1978 for (o = e->options; o; o = o->next) {
1979 pa_alsa_setting *s;
1981 if (template) {
1982 s = pa_xnewdup(pa_alsa_setting, template, 1);
1983 s->options = pa_idxset_copy(template->options);
1984 s->name = pa_sprintf_malloc(_("%s+%s"), template->name, o->name);
1985 s->description =
1986 (template->description[0] && o->description[0])
1987 ? pa_sprintf_malloc(_("%s / %s"), template->description, o->description)
1988 : (template->description[0]
1989 ? pa_xstrdup(template->description)
1990 : pa_xstrdup(o->description));
1992 s->priority = PA_MAX(template->priority, o->priority);
1993 } else {
1994 s = pa_xnew0(pa_alsa_setting, 1);
1995 s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1996 s->name = pa_xstrdup(o->name);
1997 s->description = pa_xstrdup(o->description);
1998 s->priority = o->priority;
2001 pa_idxset_put(s->options, o, NULL);
2003 if (element_create_settings(e->next, s))
2004 /* This is not a leaf, so let's get rid of it */
2005 setting_free(s);
2006 else {
2007 /* This is a leaf, so let's add it */
2008 PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2010 e->path->last_setting = s;
2014 return TRUE;
2017 static void path_create_settings(pa_alsa_path *p) {
2018 pa_assert(p);
2020 element_create_settings(p->elements, NULL);
2023 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) {
2024 pa_alsa_element *e;
2025 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2026 pa_channel_position_t t;
2028 pa_assert(p);
2029 pa_assert(m);
2031 if (p->probed)
2032 return 0;
2034 pa_zero(min_dB);
2035 pa_zero(max_dB);
2037 pa_log_debug("Probing path '%s'", p->name);
2039 PA_LLIST_FOREACH(e, p->elements) {
2040 if (element_probe(e, m) < 0) {
2041 p->supported = FALSE;
2042 pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2043 return -1;
2046 if (ignore_dB)
2047 e->has_dB = FALSE;
2049 if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2051 if (!p->has_volume) {
2052 p->min_volume = e->min_volume;
2053 p->max_volume = e->max_volume;
2056 if (e->has_dB) {
2057 if (!p->has_volume) {
2058 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2059 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2060 min_dB[t] = e->min_dB;
2061 max_dB[t] = e->max_dB;
2064 p->has_dB = TRUE;
2065 } else {
2067 if (p->has_dB) {
2068 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2069 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2070 min_dB[t] += e->min_dB;
2071 max_dB[t] += e->max_dB;
2073 } else
2074 /* Hmm, there's another element before us
2075 * which cannot do dB volumes, so we we need
2076 * to 'neutralize' this slider */
2077 e->volume_use = PA_ALSA_VOLUME_ZERO;
2079 } else if (p->has_volume)
2080 /* We can't use this volume, so let's ignore it */
2081 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2083 p->has_volume = TRUE;
2086 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2087 p->has_mute = TRUE;
2090 path_drop_unsupported(p);
2091 path_make_options_unique(p);
2092 path_create_settings(p);
2094 p->supported = TRUE;
2095 p->probed = TRUE;
2097 p->min_dB = INFINITY;
2098 p->max_dB = -INFINITY;
2100 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2101 if (p->min_dB > min_dB[t])
2102 p->min_dB = min_dB[t];
2104 if (p->max_dB < max_dB[t])
2105 p->max_dB = max_dB[t];
2108 return 0;
2111 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2112 pa_assert(s);
2114 pa_log_debug("Setting %s (%s) priority=%u",
2115 s->name,
2116 pa_strnull(s->description),
2117 s->priority);
2120 void pa_alsa_option_dump(pa_alsa_option *o) {
2121 pa_assert(o);
2123 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2124 o->alsa_name,
2125 pa_strnull(o->name),
2126 pa_strnull(o->description),
2127 o->alsa_idx,
2128 o->priority);
2131 void pa_alsa_element_dump(pa_alsa_element *e) {
2132 pa_alsa_option *o;
2133 pa_assert(e);
2135 pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, enumeration=%i, required=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s",
2136 e->alsa_name,
2137 e->direction,
2138 e->switch_use,
2139 e->volume_use,
2140 e->enumeration_use,
2141 e->required,
2142 e->required_absent,
2143 (long long unsigned) e->merged_mask,
2144 e->n_channels,
2145 pa_yes_no(e->override_map));
2147 PA_LLIST_FOREACH(o, e->options)
2148 pa_alsa_option_dump(o);
2151 void pa_alsa_path_dump(pa_alsa_path *p) {
2152 pa_alsa_element *e;
2153 pa_alsa_setting *s;
2154 pa_assert(p);
2156 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2157 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2158 p->name,
2159 pa_strnull(p->description),
2160 p->direction,
2161 p->priority,
2162 pa_yes_no(p->probed),
2163 pa_yes_no(p->supported),
2164 pa_yes_no(p->has_mute),
2165 pa_yes_no(p->has_volume),
2166 pa_yes_no(p->has_dB),
2167 p->min_volume, p->max_volume,
2168 p->min_dB, p->max_dB);
2170 PA_LLIST_FOREACH(e, p->elements)
2171 pa_alsa_element_dump(e);
2173 PA_LLIST_FOREACH(s, p->settings)
2174 pa_alsa_setting_dump(s);
2177 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2178 snd_mixer_selem_id_t *sid;
2179 snd_mixer_elem_t *me;
2181 pa_assert(e);
2182 pa_assert(m);
2183 pa_assert(cb);
2185 SELEM_INIT(sid, e->alsa_name);
2186 if (!(me = snd_mixer_find_selem(m, sid))) {
2187 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2188 return;
2191 snd_mixer_elem_set_callback(me, cb);
2192 snd_mixer_elem_set_callback_private(me, userdata);
2195 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2196 pa_alsa_element *e;
2198 pa_assert(p);
2199 pa_assert(m);
2200 pa_assert(cb);
2202 PA_LLIST_FOREACH(e, p->elements)
2203 element_set_callback(e, m, cb, userdata);
2206 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2207 pa_alsa_path *p;
2209 pa_assert(ps);
2210 pa_assert(m);
2211 pa_assert(cb);
2213 PA_LLIST_FOREACH(p, ps->paths)
2214 pa_alsa_path_set_callback(p, m, cb, userdata);
2217 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction) {
2218 pa_alsa_path_set *ps;
2219 char **pn = NULL, **en = NULL, **ie;
2221 pa_assert(m);
2222 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2224 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2225 return NULL;
2227 ps = pa_xnew0(pa_alsa_path_set, 1);
2228 ps->direction = direction;
2230 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2231 pn = m->output_path_names;
2232 else if (direction == PA_ALSA_DIRECTION_INPUT)
2233 pn = m->input_path_names;
2235 if (pn) {
2236 char **in;
2238 for (in = pn; *in; in++) {
2239 pa_alsa_path *p;
2240 pa_bool_t duplicate = FALSE;
2241 char **kn, *fn;
2243 for (kn = pn; kn != in; kn++)
2244 if (pa_streq(*kn, *in)) {
2245 duplicate = TRUE;
2246 break;
2249 if (duplicate)
2250 continue;
2252 fn = pa_sprintf_malloc("%s.conf", *in);
2254 if ((p = pa_alsa_path_new(fn, direction))) {
2255 p->path_set = ps;
2256 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2257 ps->last_path = p;
2260 pa_xfree(fn);
2263 return ps;
2266 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2267 en = m->output_element;
2268 else if (direction == PA_ALSA_DIRECTION_INPUT)
2269 en = m->input_element;
2271 if (!en) {
2272 pa_alsa_path_set_free(ps);
2273 return NULL;
2276 for (ie = en; *ie; ie++) {
2277 char **je;
2278 pa_alsa_path *p;
2280 p = pa_alsa_path_synthesize(*ie, direction);
2281 p->path_set = ps;
2283 /* Mark all other passed elements for require-absent */
2284 for (je = en; *je; je++) {
2285 pa_alsa_element *e;
2286 e = pa_xnew0(pa_alsa_element, 1);
2287 e->path = p;
2288 e->alsa_name = pa_xstrdup(*je);
2289 e->direction = direction;
2290 e->required_absent = PA_ALSA_REQUIRED_ANY;
2292 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2293 p->last_element = e;
2296 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2297 ps->last_path = p;
2300 return ps;
2303 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2304 pa_alsa_path *p;
2305 pa_assert(ps);
2307 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2308 (void*) ps,
2309 ps->direction,
2310 pa_yes_no(ps->probed));
2312 PA_LLIST_FOREACH(p, ps->paths)
2313 pa_alsa_path_dump(p);
2316 static void path_set_unify(pa_alsa_path_set *ps) {
2317 pa_alsa_path *p;
2318 pa_bool_t has_dB = TRUE, has_volume = TRUE, has_mute = TRUE;
2319 pa_assert(ps);
2321 /* We have issues dealing with paths that vary too wildly. That
2322 * means for now we have to have all paths support volume/mute/dB
2323 * or none. */
2325 PA_LLIST_FOREACH(p, ps->paths) {
2326 pa_assert(p->probed);
2328 if (!p->has_volume)
2329 has_volume = FALSE;
2330 else if (!p->has_dB)
2331 has_dB = FALSE;
2333 if (!p->has_mute)
2334 has_mute = FALSE;
2337 if (!has_volume || !has_dB || !has_mute) {
2339 if (!has_volume)
2340 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2341 else if (!has_dB)
2342 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2344 if (!has_mute)
2345 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2347 PA_LLIST_FOREACH(p, ps->paths) {
2348 if (!has_volume)
2349 p->has_volume = FALSE;
2350 else if (!has_dB)
2351 p->has_dB = FALSE;
2353 if (!has_mute)
2354 p->has_mute = FALSE;
2359 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
2360 pa_alsa_path *p, *q;
2362 PA_LLIST_FOREACH(p, ps->paths) {
2363 unsigned i;
2364 char *m;
2366 for (q = p->next; q; q = q->next)
2367 if (pa_streq(q->name, p->name))
2368 break;
2370 if (!q)
2371 continue;
2373 m = pa_xstrdup(p->name);
2375 /* OK, this name is not unique, hence let's rename */
2376 for (i = 1, q = p; q; q = q->next) {
2377 char *nn, *nd;
2379 if (!pa_streq(q->name, m))
2380 continue;
2382 nn = pa_sprintf_malloc("%s-%u", m, i);
2383 pa_xfree(q->name);
2384 q->name = nn;
2386 nd = pa_sprintf_malloc("%s %u", q->description, i);
2387 pa_xfree(q->description);
2388 q->description = nd;
2390 i++;
2393 pa_xfree(m);
2397 void pa_alsa_path_set_probe(pa_alsa_path_set *ps, snd_mixer_t *m, pa_bool_t ignore_dB) {
2398 pa_alsa_path *p, *n;
2400 pa_assert(ps);
2402 if (ps->probed)
2403 return;
2405 for (p = ps->paths; p; p = n) {
2406 n = p->next;
2408 if (pa_alsa_path_probe(p, m, ignore_dB) < 0) {
2409 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
2410 pa_alsa_path_free(p);
2414 path_set_unify(ps);
2415 path_set_make_paths_unique(ps);
2416 ps->probed = TRUE;
2419 static void mapping_free(pa_alsa_mapping *m) {
2420 pa_assert(m);
2422 pa_xfree(m->name);
2423 pa_xfree(m->description);
2425 pa_xstrfreev(m->device_strings);
2426 pa_xstrfreev(m->input_path_names);
2427 pa_xstrfreev(m->output_path_names);
2428 pa_xstrfreev(m->input_element);
2429 pa_xstrfreev(m->output_element);
2431 pa_assert(!m->input_pcm);
2432 pa_assert(!m->output_pcm);
2434 pa_xfree(m);
2437 static void profile_free(pa_alsa_profile *p) {
2438 pa_assert(p);
2440 pa_xfree(p->name);
2441 pa_xfree(p->description);
2443 pa_xstrfreev(p->input_mapping_names);
2444 pa_xstrfreev(p->output_mapping_names);
2446 if (p->input_mappings)
2447 pa_idxset_free(p->input_mappings, NULL, NULL);
2449 if (p->output_mappings)
2450 pa_idxset_free(p->output_mappings, NULL, NULL);
2452 pa_xfree(p);
2455 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
2456 pa_assert(ps);
2458 if (ps->profiles) {
2459 pa_alsa_profile *p;
2461 while ((p = pa_hashmap_steal_first(ps->profiles)))
2462 profile_free(p);
2464 pa_hashmap_free(ps->profiles, NULL, NULL);
2467 if (ps->mappings) {
2468 pa_alsa_mapping *m;
2470 while ((m = pa_hashmap_steal_first(ps->mappings)))
2471 mapping_free(m);
2473 pa_hashmap_free(ps->mappings, NULL, NULL);
2476 pa_xfree(ps);
2479 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
2480 pa_alsa_mapping *m;
2482 if (!pa_startswith(name, "Mapping "))
2483 return NULL;
2485 name += 8;
2487 if ((m = pa_hashmap_get(ps->mappings, name)))
2488 return m;
2490 m = pa_xnew0(pa_alsa_mapping, 1);
2491 m->profile_set = ps;
2492 m->name = pa_xstrdup(name);
2493 pa_channel_map_init(&m->channel_map);
2495 pa_hashmap_put(ps->mappings, m->name, m);
2497 return m;
2500 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
2501 pa_alsa_profile *p;
2503 if (!pa_startswith(name, "Profile "))
2504 return NULL;
2506 name += 8;
2508 if ((p = pa_hashmap_get(ps->profiles, name)))
2509 return p;
2511 p = pa_xnew0(pa_alsa_profile, 1);
2512 p->profile_set = ps;
2513 p->name = pa_xstrdup(name);
2515 pa_hashmap_put(ps->profiles, p->name, p);
2517 return p;
2520 static int mapping_parse_device_strings(
2521 const char *filename,
2522 unsigned line,
2523 const char *section,
2524 const char *lvalue,
2525 const char *rvalue,
2526 void *data,
2527 void *userdata) {
2529 pa_alsa_profile_set *ps = userdata;
2530 pa_alsa_mapping *m;
2532 pa_assert(ps);
2534 if (!(m = mapping_get(ps, section))) {
2535 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2536 return -1;
2539 pa_xstrfreev(m->device_strings);
2540 if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
2541 pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
2542 return -1;
2545 return 0;
2548 static int mapping_parse_channel_map(
2549 const char *filename,
2550 unsigned line,
2551 const char *section,
2552 const char *lvalue,
2553 const char *rvalue,
2554 void *data,
2555 void *userdata) {
2557 pa_alsa_profile_set *ps = userdata;
2558 pa_alsa_mapping *m;
2560 pa_assert(ps);
2562 if (!(m = mapping_get(ps, section))) {
2563 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2564 return -1;
2567 if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
2568 pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
2569 return -1;
2572 return 0;
2575 static int mapping_parse_paths(
2576 const char *filename,
2577 unsigned line,
2578 const char *section,
2579 const char *lvalue,
2580 const char *rvalue,
2581 void *data,
2582 void *userdata) {
2584 pa_alsa_profile_set *ps = userdata;
2585 pa_alsa_mapping *m;
2587 pa_assert(ps);
2589 if (!(m = mapping_get(ps, section))) {
2590 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2591 return -1;
2594 if (pa_streq(lvalue, "paths-input")) {
2595 pa_xstrfreev(m->input_path_names);
2596 m->input_path_names = pa_split_spaces_strv(rvalue);
2597 } else {
2598 pa_xstrfreev(m->output_path_names);
2599 m->output_path_names = pa_split_spaces_strv(rvalue);
2602 return 0;
2605 static int mapping_parse_element(
2606 const char *filename,
2607 unsigned line,
2608 const char *section,
2609 const char *lvalue,
2610 const char *rvalue,
2611 void *data,
2612 void *userdata) {
2614 pa_alsa_profile_set *ps = userdata;
2615 pa_alsa_mapping *m;
2617 pa_assert(ps);
2619 if (!(m = mapping_get(ps, section))) {
2620 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2621 return -1;
2624 if (pa_streq(lvalue, "element-input")) {
2625 pa_xstrfreev(m->input_element);
2626 m->input_element = pa_split_spaces_strv(rvalue);
2627 } else {
2628 pa_xstrfreev(m->output_element);
2629 m->output_element = pa_split_spaces_strv(rvalue);
2632 return 0;
2635 static int mapping_parse_direction(
2636 const char *filename,
2637 unsigned line,
2638 const char *section,
2639 const char *lvalue,
2640 const char *rvalue,
2641 void *data,
2642 void *userdata) {
2644 pa_alsa_profile_set *ps = userdata;
2645 pa_alsa_mapping *m;
2647 pa_assert(ps);
2649 if (!(m = mapping_get(ps, section))) {
2650 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2651 return -1;
2654 if (pa_streq(rvalue, "input"))
2655 m->direction = PA_ALSA_DIRECTION_INPUT;
2656 else if (pa_streq(rvalue, "output"))
2657 m->direction = PA_ALSA_DIRECTION_OUTPUT;
2658 else if (pa_streq(rvalue, "any"))
2659 m->direction = PA_ALSA_DIRECTION_ANY;
2660 else {
2661 pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
2662 return -1;
2665 return 0;
2668 static int mapping_parse_description(
2669 const char *filename,
2670 unsigned line,
2671 const char *section,
2672 const char *lvalue,
2673 const char *rvalue,
2674 void *data,
2675 void *userdata) {
2677 pa_alsa_profile_set *ps = userdata;
2678 pa_alsa_profile *p;
2679 pa_alsa_mapping *m;
2681 pa_assert(ps);
2683 if ((m = mapping_get(ps, section))) {
2684 pa_xstrdup(m->description);
2685 m->description = pa_xstrdup(rvalue);
2686 } else if ((p = profile_get(ps, section))) {
2687 pa_xfree(p->description);
2688 p->description = pa_xstrdup(rvalue);
2689 } else {
2690 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2691 return -1;
2694 return 0;
2697 static int mapping_parse_priority(
2698 const char *filename,
2699 unsigned line,
2700 const char *section,
2701 const char *lvalue,
2702 const char *rvalue,
2703 void *data,
2704 void *userdata) {
2706 pa_alsa_profile_set *ps = userdata;
2707 pa_alsa_profile *p;
2708 pa_alsa_mapping *m;
2709 uint32_t prio;
2711 pa_assert(ps);
2713 if (pa_atou(rvalue, &prio) < 0) {
2714 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
2715 return -1;
2718 if ((m = mapping_get(ps, section)))
2719 m->priority = prio;
2720 else if ((p = profile_get(ps, section)))
2721 p->priority = prio;
2722 else {
2723 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2724 return -1;
2727 return 0;
2730 static int profile_parse_mappings(
2731 const char *filename,
2732 unsigned line,
2733 const char *section,
2734 const char *lvalue,
2735 const char *rvalue,
2736 void *data,
2737 void *userdata) {
2739 pa_alsa_profile_set *ps = userdata;
2740 pa_alsa_profile *p;
2742 pa_assert(ps);
2744 if (!(p = profile_get(ps, section))) {
2745 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2746 return -1;
2749 if (pa_streq(lvalue, "input-mappings")) {
2750 pa_xstrfreev(p->input_mapping_names);
2751 p->input_mapping_names = pa_split_spaces_strv(rvalue);
2752 } else {
2753 pa_xstrfreev(p->output_mapping_names);
2754 p->output_mapping_names = pa_split_spaces_strv(rvalue);
2757 return 0;
2760 static int profile_parse_skip_probe(
2761 const char *filename,
2762 unsigned line,
2763 const char *section,
2764 const char *lvalue,
2765 const char *rvalue,
2766 void *data,
2767 void *userdata) {
2769 pa_alsa_profile_set *ps = userdata;
2770 pa_alsa_profile *p;
2771 int b;
2773 pa_assert(ps);
2775 if (!(p = profile_get(ps, section))) {
2776 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2777 return -1;
2780 if ((b = pa_parse_boolean(rvalue)) < 0) {
2781 pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
2782 return -1;
2785 p->supported = b;
2787 return 0;
2790 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
2792 static const struct description_map well_known_descriptions[] = {
2793 { "analog-mono", N_("Analog Mono") },
2794 { "analog-stereo", N_("Analog Stereo") },
2795 { "analog-surround-21", N_("Analog Surround 2.1") },
2796 { "analog-surround-30", N_("Analog Surround 3.0") },
2797 { "analog-surround-31", N_("Analog Surround 3.1") },
2798 { "analog-surround-40", N_("Analog Surround 4.0") },
2799 { "analog-surround-41", N_("Analog Surround 4.1") },
2800 { "analog-surround-50", N_("Analog Surround 5.0") },
2801 { "analog-surround-51", N_("Analog Surround 5.1") },
2802 { "analog-surround-61", N_("Analog Surround 6.0") },
2803 { "analog-surround-61", N_("Analog Surround 6.1") },
2804 { "analog-surround-70", N_("Analog Surround 7.0") },
2805 { "analog-surround-71", N_("Analog Surround 7.1") },
2806 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
2807 { "iec958-surround-40", N_("Digital Surround 4.0 (IEC958)") },
2808 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
2809 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
2810 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
2813 pa_assert(m);
2815 if (!pa_channel_map_valid(&m->channel_map)) {
2816 pa_log("Mapping %s is missing channel map.", m->name);
2817 return -1;
2820 if (!m->device_strings) {
2821 pa_log("Mapping %s is missing device strings.", m->name);
2822 return -1;
2825 if ((m->input_path_names && m->input_element) ||
2826 (m->output_path_names && m->output_element)) {
2827 pa_log("Mapping %s must have either mixer path or mixer elment, not both.", m->name);
2828 return -1;
2831 if (!m->description)
2832 m->description = pa_xstrdup(lookup_description(m->name,
2833 well_known_descriptions,
2834 PA_ELEMENTSOF(well_known_descriptions)));
2836 if (!m->description)
2837 m->description = pa_xstrdup(m->name);
2839 if (bonus) {
2840 if (pa_channel_map_equal(&m->channel_map, bonus))
2841 m->priority += 50;
2842 else if (m->channel_map.channels == bonus->channels)
2843 m->priority += 30;
2846 return 0;
2849 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
2850 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
2852 pa_assert(m);
2854 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
2855 m->name,
2856 pa_strnull(m->description),
2857 m->priority,
2858 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
2859 pa_yes_no(m->supported),
2860 m->direction);
2863 static void profile_set_add_auto_pair(
2864 pa_alsa_profile_set *ps,
2865 pa_alsa_mapping *m, /* output */
2866 pa_alsa_mapping *n /* input */) {
2868 char *name;
2869 pa_alsa_profile *p;
2871 pa_assert(ps);
2872 pa_assert(m || n);
2874 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
2875 return;
2877 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
2878 return;
2880 if (m && n)
2881 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
2882 else if (m)
2883 name = pa_sprintf_malloc("output:%s", m->name);
2884 else
2885 name = pa_sprintf_malloc("input:%s", n->name);
2887 if ((p = pa_hashmap_get(ps->profiles, name))) {
2888 pa_xfree(name);
2889 return;
2892 p = pa_xnew0(pa_alsa_profile, 1);
2893 p->profile_set = ps;
2894 p->name = name;
2896 if (m) {
2897 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2898 pa_idxset_put(p->output_mappings, m, NULL);
2899 p->priority += m->priority * 100;
2902 if (n) {
2903 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2904 pa_idxset_put(p->input_mappings, n, NULL);
2905 p->priority += n->priority;
2908 pa_hashmap_put(ps->profiles, p->name, p);
2911 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
2912 pa_alsa_mapping *m, *n;
2913 void *m_state, *n_state;
2915 pa_assert(ps);
2917 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
2918 profile_set_add_auto_pair(ps, m, NULL);
2920 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
2921 profile_set_add_auto_pair(ps, m, n);
2924 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
2925 profile_set_add_auto_pair(ps, NULL, n);
2928 static int profile_verify(pa_alsa_profile *p) {
2930 static const struct description_map well_known_descriptions[] = {
2931 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
2932 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
2933 { "output:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
2934 { "off", N_("Off") }
2937 pa_assert(p);
2939 /* Replace the output mapping names by the actual mappings */
2940 if (p->output_mapping_names) {
2941 char **name;
2943 pa_assert(!p->output_mappings);
2944 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2946 for (name = p->output_mapping_names; *name; name++) {
2947 pa_alsa_mapping *m;
2948 char **in;
2949 pa_bool_t duplicate = FALSE;
2951 for (in = name + 1; *in; in++)
2952 if (pa_streq(*name, *in)) {
2953 duplicate = TRUE;
2954 break;
2957 if (duplicate)
2958 continue;
2960 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
2961 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
2962 return -1;
2965 pa_idxset_put(p->output_mappings, m, NULL);
2967 if (p->supported)
2968 m->supported++;
2971 pa_xstrfreev(p->output_mapping_names);
2972 p->output_mapping_names = NULL;
2975 /* Replace the input mapping names by the actual mappings */
2976 if (p->input_mapping_names) {
2977 char **name;
2979 pa_assert(!p->input_mappings);
2980 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2982 for (name = p->input_mapping_names; *name; name++) {
2983 pa_alsa_mapping *m;
2984 char **in;
2985 pa_bool_t duplicate = FALSE;
2987 for (in = name + 1; *in; in++)
2988 if (pa_streq(*name, *in)) {
2989 duplicate = TRUE;
2990 break;
2993 if (duplicate)
2994 continue;
2996 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
2997 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
2998 return -1;
3001 pa_idxset_put(p->input_mappings, m, NULL);
3003 if (p->supported)
3004 m->supported++;
3007 pa_xstrfreev(p->input_mapping_names);
3008 p->input_mapping_names = NULL;
3011 if (!p->input_mappings && !p->output_mappings) {
3012 pa_log("Profile '%s' lacks mappings.", p->name);
3013 return -1;
3016 if (!p->description)
3017 p->description = pa_xstrdup(lookup_description(p->name,
3018 well_known_descriptions,
3019 PA_ELEMENTSOF(well_known_descriptions)));
3021 if (!p->description) {
3022 pa_strbuf *sb;
3023 uint32_t idx;
3024 pa_alsa_mapping *m;
3026 sb = pa_strbuf_new();
3028 if (p->output_mappings)
3029 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3030 if (!pa_strbuf_isempty(sb))
3031 pa_strbuf_puts(sb, " + ");
3033 pa_strbuf_printf(sb, "%s Output", m->description);
3036 if (p->input_mappings)
3037 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3038 if (!pa_strbuf_isempty(sb))
3039 pa_strbuf_puts(sb, " + ");
3041 pa_strbuf_printf(sb, "%s Input", m->description);
3044 p->description = pa_strbuf_tostring_free(sb);
3047 return 0;
3050 void pa_alsa_profile_dump(pa_alsa_profile *p) {
3051 uint32_t idx;
3052 pa_alsa_mapping *m;
3053 pa_assert(p);
3055 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3056 p->name,
3057 pa_strnull(p->description),
3058 p->priority,
3059 pa_yes_no(p->supported),
3060 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
3061 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
3063 if (p->input_mappings)
3064 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
3065 pa_log_debug("Input %s", m->name);
3067 if (p->output_mappings)
3068 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
3069 pa_log_debug("Output %s", m->name);
3072 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
3073 pa_alsa_profile_set *ps;
3074 pa_alsa_profile *p;
3075 pa_alsa_mapping *m;
3076 char *fn;
3077 int r;
3078 void *state;
3080 static pa_config_item items[] = {
3081 /* [General] */
3082 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
3084 /* [Mapping ...] */
3085 { "device-strings", mapping_parse_device_strings, NULL, NULL },
3086 { "channel-map", mapping_parse_channel_map, NULL, NULL },
3087 { "paths-input", mapping_parse_paths, NULL, NULL },
3088 { "paths-output", mapping_parse_paths, NULL, NULL },
3089 { "element-input", mapping_parse_element, NULL, NULL },
3090 { "element-output", mapping_parse_element, NULL, NULL },
3091 { "direction", mapping_parse_direction, NULL, NULL },
3093 /* Shared by [Mapping ...] and [Profile ...] */
3094 { "description", mapping_parse_description, NULL, NULL },
3095 { "priority", mapping_parse_priority, NULL, NULL },
3097 /* [Profile ...] */
3098 { "input-mappings", profile_parse_mappings, NULL, NULL },
3099 { "output-mappings", profile_parse_mappings, NULL, NULL },
3100 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
3101 { NULL, NULL, NULL, NULL }
3104 ps = pa_xnew0(pa_alsa_profile_set, 1);
3105 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3106 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3108 items[0].data = &ps->auto_profiles;
3110 if (!fname)
3111 fname = "default.conf";
3113 fn = pa_maybe_prefix_path(fname, PA_ALSA_PROFILE_SETS_DIR);
3114 r = pa_config_parse(fn, NULL, items, ps);
3115 pa_xfree(fn);
3117 if (r < 0)
3118 goto fail;
3120 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3121 if (mapping_verify(m, bonus) < 0)
3122 goto fail;
3124 if (ps->auto_profiles)
3125 profile_set_add_auto(ps);
3127 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3128 if (profile_verify(p) < 0)
3129 goto fail;
3131 return ps;
3133 fail:
3134 pa_alsa_profile_set_free(ps);
3135 return NULL;
3138 void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, const pa_sample_spec *ss) {
3139 void *state;
3140 pa_alsa_profile *p, *last = NULL;
3141 pa_alsa_mapping *m;
3143 pa_assert(ps);
3144 pa_assert(dev_id);
3145 pa_assert(ss);
3147 if (ps->probed)
3148 return;
3150 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
3151 pa_sample_spec try_ss;
3152 pa_channel_map try_map;
3153 uint32_t idx;
3155 /* Is this already marked that it is supported? (i.e. from the config file) */
3156 if (p->supported)
3157 continue;
3159 pa_log_debug("Looking at profile %s", p->name);
3161 /* Close PCMs from the last iteration we don't need anymore */
3162 if (last && last->output_mappings)
3163 PA_IDXSET_FOREACH(m, last->output_mappings, idx) {
3165 if (!m->output_pcm)
3166 break;
3168 if (last->supported)
3169 m->supported++;
3171 if (!p->output_mappings || !pa_idxset_get_by_data(p->output_mappings, m, NULL)) {
3172 snd_pcm_close(m->output_pcm);
3173 m->output_pcm = NULL;
3177 if (last && last->input_mappings)
3178 PA_IDXSET_FOREACH(m, last->input_mappings, idx) {
3180 if (!m->input_pcm)
3181 break;
3183 if (last->supported)
3184 m->supported++;
3186 if (!p->input_mappings || !pa_idxset_get_by_data(p->input_mappings, m, NULL)) {
3187 snd_pcm_close(m->input_pcm);
3188 m->input_pcm = NULL;
3192 p->supported = TRUE;
3194 /* Check if we can open all new ones */
3195 if (p->output_mappings)
3196 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3198 if (m->output_pcm)
3199 continue;
3201 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
3202 try_map = m->channel_map;
3203 try_ss = *ss;
3204 try_ss.channels = try_map.channels;
3206 if (!(m ->output_pcm = pa_alsa_open_by_template(
3207 m->device_strings,
3208 dev_id,
3209 NULL,
3210 &try_ss, &try_map,
3211 SND_PCM_STREAM_PLAYBACK,
3212 NULL, NULL, 0, NULL, NULL,
3213 TRUE))) {
3214 p->supported = FALSE;
3215 break;
3219 if (p->input_mappings && p->supported)
3220 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3222 if (m->input_pcm)
3223 continue;
3225 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
3226 try_map = m->channel_map;
3227 try_ss = *ss;
3228 try_ss.channels = try_map.channels;
3230 if (!(m ->input_pcm = pa_alsa_open_by_template(
3231 m->device_strings,
3232 dev_id,
3233 NULL,
3234 &try_ss, &try_map,
3235 SND_PCM_STREAM_CAPTURE,
3236 NULL, NULL, 0, NULL, NULL,
3237 TRUE))) {
3238 p->supported = FALSE;
3239 break;
3243 last = p;
3245 if (p->supported)
3246 pa_log_debug("Profile %s supported.", p->name);
3249 /* Clean up */
3250 if (last) {
3251 uint32_t idx;
3253 if (last->output_mappings)
3254 PA_IDXSET_FOREACH(m, last->output_mappings, idx)
3255 if (m->output_pcm) {
3257 if (last->supported)
3258 m->supported++;
3260 snd_pcm_close(m->output_pcm);
3261 m->output_pcm = NULL;
3264 if (last->input_mappings)
3265 PA_IDXSET_FOREACH(m, last->input_mappings, idx)
3266 if (m->input_pcm) {
3268 if (last->supported)
3269 m->supported++;
3271 snd_pcm_close(m->input_pcm);
3272 m->input_pcm = NULL;
3276 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3277 if (!p->supported) {
3278 pa_hashmap_remove(ps->profiles, p->name);
3279 profile_free(p);
3282 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3283 if (m->supported <= 0) {
3284 pa_hashmap_remove(ps->mappings, m->name);
3285 mapping_free(m);
3288 ps->probed = TRUE;
3291 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
3292 pa_alsa_profile *p;
3293 pa_alsa_mapping *m;
3294 void *state;
3296 pa_assert(ps);
3298 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u",
3299 (void*)
3301 pa_yes_no(ps->auto_profiles),
3302 pa_yes_no(ps->probed),
3303 pa_hashmap_size(ps->mappings),
3304 pa_hashmap_size(ps->profiles));
3306 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3307 pa_alsa_mapping_dump(m);
3309 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3310 pa_alsa_profile_dump(p);
3313 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps) {
3314 pa_alsa_path *path;
3316 pa_assert(p);
3317 pa_assert(!*p);
3318 pa_assert(ps);
3320 /* if there is no path, we don't want a port list */
3321 if (!ps->paths)
3322 return;
3324 if (!ps->paths->next){
3325 pa_alsa_setting *s;
3327 /* If there is only one path, but no or only one setting, then
3328 * we want a port list either */
3329 if (!ps->paths->settings || !ps->paths->settings->next)
3330 return;
3332 /* Ok, there is only one path, however with multiple settings,
3333 * so let's create a port for each setting */
3334 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3336 PA_LLIST_FOREACH(s, ps->paths->settings) {
3337 pa_device_port *port;
3338 pa_alsa_port_data *data;
3340 port = pa_device_port_new(s->name, s->description, sizeof(pa_alsa_port_data));
3341 port->priority = s->priority;
3343 data = PA_DEVICE_PORT_DATA(port);
3344 data->path = ps->paths;
3345 data->setting = s;
3347 pa_hashmap_put(*p, port->name, port);
3350 } else {
3352 /* We have multiple paths, so let's create a port for each
3353 * one, and each of each settings */
3354 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3356 PA_LLIST_FOREACH(path, ps->paths) {
3358 if (!path->settings || !path->settings->next) {
3359 pa_device_port *port;
3360 pa_alsa_port_data *data;
3362 /* If there is no or just one setting we only need a
3363 * single entry */
3365 port = pa_device_port_new(path->name, path->description, sizeof(pa_alsa_port_data));
3366 port->priority = path->priority * 100;
3369 data = PA_DEVICE_PORT_DATA(port);
3370 data->path = path;
3371 data->setting = path->settings;
3373 pa_hashmap_put(*p, port->name, port);
3374 } else {
3375 pa_alsa_setting *s;
3377 PA_LLIST_FOREACH(s, path->settings) {
3378 pa_device_port *port;
3379 pa_alsa_port_data *data;
3380 char *n, *d;
3382 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
3384 if (s->description[0])
3385 d = pa_sprintf_malloc(_("%s / %s"), path->description, s->description);
3386 else
3387 d = pa_xstrdup(path->description);
3389 port = pa_device_port_new(n, d, sizeof(pa_alsa_port_data));
3390 port->priority = path->priority * 100 + s->priority;
3392 pa_xfree(n);
3393 pa_xfree(d);
3395 data = PA_DEVICE_PORT_DATA(port);
3396 data->path = path;
3397 data->setting = s;
3399 pa_hashmap_put(*p, port->name, port);
3405 pa_log_debug("Added %u ports", pa_hashmap_size(*p));