alsa: when probing for profiles configure buffer/period sizes since some broken drive...
[pulseaudio-mirror.git] / src / modules / alsa / alsa-mixer.c
blob685169b998358e09346fda92720498a44a870a3f
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 = 0;
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,
1853 #if defined(__linux__) && !defined(__OPTIMIZE__)
1854 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/paths/" :
1855 #endif
1856 PA_ALSA_PATHS_DIR);
1858 r = pa_config_parse(fn, NULL, items, p);
1859 pa_xfree(fn);
1861 if (r < 0)
1862 goto fail;
1864 if (path_verify(p) < 0)
1865 goto fail;
1867 return p;
1869 fail:
1870 pa_alsa_path_free(p);
1871 return NULL;
1874 pa_alsa_path* pa_alsa_path_synthesize(const char*element, pa_alsa_direction_t direction) {
1875 pa_alsa_path *p;
1876 pa_alsa_element *e;
1878 pa_assert(element);
1880 p = pa_xnew0(pa_alsa_path, 1);
1881 p->name = pa_xstrdup(element);
1882 p->direction = direction;
1884 e = pa_xnew0(pa_alsa_element, 1);
1885 e->path = p;
1886 e->alsa_name = pa_xstrdup(element);
1887 e->direction = direction;
1889 e->switch_use = PA_ALSA_SWITCH_MUTE;
1890 e->volume_use = PA_ALSA_VOLUME_MERGE;
1892 PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
1893 return p;
1896 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
1897 pa_alsa_option *o, *n;
1899 pa_assert(e);
1901 for (o = e->options; o; o = n) {
1902 n = o->next;
1904 if (o->alsa_idx < 0) {
1905 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
1906 option_free(o);
1910 return
1911 e->switch_use != PA_ALSA_SWITCH_IGNORE ||
1912 e->volume_use != PA_ALSA_VOLUME_IGNORE ||
1913 e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
1916 static void path_drop_unsupported(pa_alsa_path *p) {
1917 pa_alsa_element *e, *n;
1919 pa_assert(p);
1921 for (e = p->elements; e; e = n) {
1922 n = e->next;
1924 if (!element_drop_unsupported(e)) {
1925 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
1926 element_free(e);
1931 static void path_make_options_unique(pa_alsa_path *p) {
1932 pa_alsa_element *e;
1933 pa_alsa_option *o, *u;
1935 PA_LLIST_FOREACH(e, p->elements) {
1936 PA_LLIST_FOREACH(o, e->options) {
1937 unsigned i;
1938 char *m;
1940 for (u = o->next; u; u = u->next)
1941 if (pa_streq(u->name, o->name))
1942 break;
1944 if (!u)
1945 continue;
1947 m = pa_xstrdup(o->name);
1949 /* OK, this name is not unique, hence let's rename */
1950 for (i = 1, u = o; u; u = u->next) {
1951 char *nn, *nd;
1953 if (!pa_streq(u->name, m))
1954 continue;
1956 nn = pa_sprintf_malloc("%s-%u", m, i);
1957 pa_xfree(u->name);
1958 u->name = nn;
1960 nd = pa_sprintf_malloc("%s %u", u->description, i);
1961 pa_xfree(u->description);
1962 u->description = nd;
1964 i++;
1967 pa_xfree(m);
1972 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
1973 pa_alsa_option *o;
1975 for (; e; e = e->next)
1976 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
1977 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
1978 break;
1980 if (!e)
1981 return FALSE;
1983 for (o = e->options; o; o = o->next) {
1984 pa_alsa_setting *s;
1986 if (template) {
1987 s = pa_xnewdup(pa_alsa_setting, template, 1);
1988 s->options = pa_idxset_copy(template->options);
1989 s->name = pa_sprintf_malloc(_("%s+%s"), template->name, o->name);
1990 s->description =
1991 (template->description[0] && o->description[0])
1992 ? pa_sprintf_malloc(_("%s / %s"), template->description, o->description)
1993 : (template->description[0]
1994 ? pa_xstrdup(template->description)
1995 : pa_xstrdup(o->description));
1997 s->priority = PA_MAX(template->priority, o->priority);
1998 } else {
1999 s = pa_xnew0(pa_alsa_setting, 1);
2000 s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2001 s->name = pa_xstrdup(o->name);
2002 s->description = pa_xstrdup(o->description);
2003 s->priority = o->priority;
2006 pa_idxset_put(s->options, o, NULL);
2008 if (element_create_settings(e->next, s))
2009 /* This is not a leaf, so let's get rid of it */
2010 setting_free(s);
2011 else {
2012 /* This is a leaf, so let's add it */
2013 PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2015 e->path->last_setting = s;
2019 return TRUE;
2022 static void path_create_settings(pa_alsa_path *p) {
2023 pa_assert(p);
2025 element_create_settings(p->elements, NULL);
2028 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) {
2029 pa_alsa_element *e;
2030 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2031 pa_channel_position_t t;
2033 pa_assert(p);
2034 pa_assert(m);
2036 if (p->probed)
2037 return 0;
2039 pa_zero(min_dB);
2040 pa_zero(max_dB);
2042 pa_log_debug("Probing path '%s'", p->name);
2044 PA_LLIST_FOREACH(e, p->elements) {
2045 if (element_probe(e, m) < 0) {
2046 p->supported = FALSE;
2047 pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2048 return -1;
2051 if (ignore_dB)
2052 e->has_dB = FALSE;
2054 if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2056 if (!p->has_volume) {
2057 p->min_volume = e->min_volume;
2058 p->max_volume = e->max_volume;
2061 if (e->has_dB) {
2062 if (!p->has_volume) {
2063 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2064 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2065 min_dB[t] = e->min_dB;
2066 max_dB[t] = e->max_dB;
2069 p->has_dB = TRUE;
2070 } else {
2072 if (p->has_dB) {
2073 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2074 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2075 min_dB[t] += e->min_dB;
2076 max_dB[t] += e->max_dB;
2078 } else
2079 /* Hmm, there's another element before us
2080 * which cannot do dB volumes, so we we need
2081 * to 'neutralize' this slider */
2082 e->volume_use = PA_ALSA_VOLUME_ZERO;
2084 } else if (p->has_volume)
2085 /* We can't use this volume, so let's ignore it */
2086 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2088 p->has_volume = TRUE;
2091 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2092 p->has_mute = TRUE;
2095 path_drop_unsupported(p);
2096 path_make_options_unique(p);
2097 path_create_settings(p);
2099 p->supported = TRUE;
2100 p->probed = TRUE;
2102 p->min_dB = INFINITY;
2103 p->max_dB = -INFINITY;
2105 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2106 if (p->min_dB > min_dB[t])
2107 p->min_dB = min_dB[t];
2109 if (p->max_dB < max_dB[t])
2110 p->max_dB = max_dB[t];
2113 return 0;
2116 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2117 pa_assert(s);
2119 pa_log_debug("Setting %s (%s) priority=%u",
2120 s->name,
2121 pa_strnull(s->description),
2122 s->priority);
2125 void pa_alsa_option_dump(pa_alsa_option *o) {
2126 pa_assert(o);
2128 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2129 o->alsa_name,
2130 pa_strnull(o->name),
2131 pa_strnull(o->description),
2132 o->alsa_idx,
2133 o->priority);
2136 void pa_alsa_element_dump(pa_alsa_element *e) {
2137 pa_alsa_option *o;
2138 pa_assert(e);
2140 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",
2141 e->alsa_name,
2142 e->direction,
2143 e->switch_use,
2144 e->volume_use,
2145 e->enumeration_use,
2146 e->required,
2147 e->required_absent,
2148 (long long unsigned) e->merged_mask,
2149 e->n_channels,
2150 pa_yes_no(e->override_map));
2152 PA_LLIST_FOREACH(o, e->options)
2153 pa_alsa_option_dump(o);
2156 void pa_alsa_path_dump(pa_alsa_path *p) {
2157 pa_alsa_element *e;
2158 pa_alsa_setting *s;
2159 pa_assert(p);
2161 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2162 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2163 p->name,
2164 pa_strnull(p->description),
2165 p->direction,
2166 p->priority,
2167 pa_yes_no(p->probed),
2168 pa_yes_no(p->supported),
2169 pa_yes_no(p->has_mute),
2170 pa_yes_no(p->has_volume),
2171 pa_yes_no(p->has_dB),
2172 p->min_volume, p->max_volume,
2173 p->min_dB, p->max_dB);
2175 PA_LLIST_FOREACH(e, p->elements)
2176 pa_alsa_element_dump(e);
2178 PA_LLIST_FOREACH(s, p->settings)
2179 pa_alsa_setting_dump(s);
2182 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2183 snd_mixer_selem_id_t *sid;
2184 snd_mixer_elem_t *me;
2186 pa_assert(e);
2187 pa_assert(m);
2188 pa_assert(cb);
2190 SELEM_INIT(sid, e->alsa_name);
2191 if (!(me = snd_mixer_find_selem(m, sid))) {
2192 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2193 return;
2196 snd_mixer_elem_set_callback(me, cb);
2197 snd_mixer_elem_set_callback_private(me, userdata);
2200 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2201 pa_alsa_element *e;
2203 pa_assert(p);
2204 pa_assert(m);
2205 pa_assert(cb);
2207 PA_LLIST_FOREACH(e, p->elements)
2208 element_set_callback(e, m, cb, userdata);
2211 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2212 pa_alsa_path *p;
2214 pa_assert(ps);
2215 pa_assert(m);
2216 pa_assert(cb);
2218 PA_LLIST_FOREACH(p, ps->paths)
2219 pa_alsa_path_set_callback(p, m, cb, userdata);
2222 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction) {
2223 pa_alsa_path_set *ps;
2224 char **pn = NULL, **en = NULL, **ie;
2226 pa_assert(m);
2227 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2229 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2230 return NULL;
2232 ps = pa_xnew0(pa_alsa_path_set, 1);
2233 ps->direction = direction;
2235 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2236 pn = m->output_path_names;
2237 else if (direction == PA_ALSA_DIRECTION_INPUT)
2238 pn = m->input_path_names;
2240 if (pn) {
2241 char **in;
2243 for (in = pn; *in; in++) {
2244 pa_alsa_path *p;
2245 pa_bool_t duplicate = FALSE;
2246 char **kn, *fn;
2248 for (kn = pn; kn != in; kn++)
2249 if (pa_streq(*kn, *in)) {
2250 duplicate = TRUE;
2251 break;
2254 if (duplicate)
2255 continue;
2257 fn = pa_sprintf_malloc("%s.conf", *in);
2259 if ((p = pa_alsa_path_new(fn, direction))) {
2260 p->path_set = ps;
2261 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2262 ps->last_path = p;
2265 pa_xfree(fn);
2268 return ps;
2271 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2272 en = m->output_element;
2273 else if (direction == PA_ALSA_DIRECTION_INPUT)
2274 en = m->input_element;
2276 if (!en) {
2277 pa_alsa_path_set_free(ps);
2278 return NULL;
2281 for (ie = en; *ie; ie++) {
2282 char **je;
2283 pa_alsa_path *p;
2285 p = pa_alsa_path_synthesize(*ie, direction);
2286 p->path_set = ps;
2288 /* Mark all other passed elements for require-absent */
2289 for (je = en; *je; je++) {
2290 pa_alsa_element *e;
2291 e = pa_xnew0(pa_alsa_element, 1);
2292 e->path = p;
2293 e->alsa_name = pa_xstrdup(*je);
2294 e->direction = direction;
2295 e->required_absent = PA_ALSA_REQUIRED_ANY;
2297 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2298 p->last_element = e;
2301 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2302 ps->last_path = p;
2305 return ps;
2308 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2309 pa_alsa_path *p;
2310 pa_assert(ps);
2312 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2313 (void*) ps,
2314 ps->direction,
2315 pa_yes_no(ps->probed));
2317 PA_LLIST_FOREACH(p, ps->paths)
2318 pa_alsa_path_dump(p);
2321 static void path_set_unify(pa_alsa_path_set *ps) {
2322 pa_alsa_path *p;
2323 pa_bool_t has_dB = TRUE, has_volume = TRUE, has_mute = TRUE;
2324 pa_assert(ps);
2326 /* We have issues dealing with paths that vary too wildly. That
2327 * means for now we have to have all paths support volume/mute/dB
2328 * or none. */
2330 PA_LLIST_FOREACH(p, ps->paths) {
2331 pa_assert(p->probed);
2333 if (!p->has_volume)
2334 has_volume = FALSE;
2335 else if (!p->has_dB)
2336 has_dB = FALSE;
2338 if (!p->has_mute)
2339 has_mute = FALSE;
2342 if (!has_volume || !has_dB || !has_mute) {
2344 if (!has_volume)
2345 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2346 else if (!has_dB)
2347 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2349 if (!has_mute)
2350 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2352 PA_LLIST_FOREACH(p, ps->paths) {
2353 if (!has_volume)
2354 p->has_volume = FALSE;
2355 else if (!has_dB)
2356 p->has_dB = FALSE;
2358 if (!has_mute)
2359 p->has_mute = FALSE;
2364 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
2365 pa_alsa_path *p, *q;
2367 PA_LLIST_FOREACH(p, ps->paths) {
2368 unsigned i;
2369 char *m;
2371 for (q = p->next; q; q = q->next)
2372 if (pa_streq(q->name, p->name))
2373 break;
2375 if (!q)
2376 continue;
2378 m = pa_xstrdup(p->name);
2380 /* OK, this name is not unique, hence let's rename */
2381 for (i = 1, q = p; q; q = q->next) {
2382 char *nn, *nd;
2384 if (!pa_streq(q->name, m))
2385 continue;
2387 nn = pa_sprintf_malloc("%s-%u", m, i);
2388 pa_xfree(q->name);
2389 q->name = nn;
2391 nd = pa_sprintf_malloc("%s %u", q->description, i);
2392 pa_xfree(q->description);
2393 q->description = nd;
2395 i++;
2398 pa_xfree(m);
2402 void pa_alsa_path_set_probe(pa_alsa_path_set *ps, snd_mixer_t *m, pa_bool_t ignore_dB) {
2403 pa_alsa_path *p, *n;
2405 pa_assert(ps);
2407 if (ps->probed)
2408 return;
2410 for (p = ps->paths; p; p = n) {
2411 n = p->next;
2413 if (pa_alsa_path_probe(p, m, ignore_dB) < 0) {
2414 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
2415 pa_alsa_path_free(p);
2419 path_set_unify(ps);
2420 path_set_make_paths_unique(ps);
2421 ps->probed = TRUE;
2424 static void mapping_free(pa_alsa_mapping *m) {
2425 pa_assert(m);
2427 pa_xfree(m->name);
2428 pa_xfree(m->description);
2430 pa_xstrfreev(m->device_strings);
2431 pa_xstrfreev(m->input_path_names);
2432 pa_xstrfreev(m->output_path_names);
2433 pa_xstrfreev(m->input_element);
2434 pa_xstrfreev(m->output_element);
2436 pa_assert(!m->input_pcm);
2437 pa_assert(!m->output_pcm);
2439 pa_xfree(m);
2442 static void profile_free(pa_alsa_profile *p) {
2443 pa_assert(p);
2445 pa_xfree(p->name);
2446 pa_xfree(p->description);
2448 pa_xstrfreev(p->input_mapping_names);
2449 pa_xstrfreev(p->output_mapping_names);
2451 if (p->input_mappings)
2452 pa_idxset_free(p->input_mappings, NULL, NULL);
2454 if (p->output_mappings)
2455 pa_idxset_free(p->output_mappings, NULL, NULL);
2457 pa_xfree(p);
2460 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
2461 pa_assert(ps);
2463 if (ps->profiles) {
2464 pa_alsa_profile *p;
2466 while ((p = pa_hashmap_steal_first(ps->profiles)))
2467 profile_free(p);
2469 pa_hashmap_free(ps->profiles, NULL, NULL);
2472 if (ps->mappings) {
2473 pa_alsa_mapping *m;
2475 while ((m = pa_hashmap_steal_first(ps->mappings)))
2476 mapping_free(m);
2478 pa_hashmap_free(ps->mappings, NULL, NULL);
2481 pa_xfree(ps);
2484 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
2485 pa_alsa_mapping *m;
2487 if (!pa_startswith(name, "Mapping "))
2488 return NULL;
2490 name += 8;
2492 if ((m = pa_hashmap_get(ps->mappings, name)))
2493 return m;
2495 m = pa_xnew0(pa_alsa_mapping, 1);
2496 m->profile_set = ps;
2497 m->name = pa_xstrdup(name);
2498 pa_channel_map_init(&m->channel_map);
2500 pa_hashmap_put(ps->mappings, m->name, m);
2502 return m;
2505 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
2506 pa_alsa_profile *p;
2508 if (!pa_startswith(name, "Profile "))
2509 return NULL;
2511 name += 8;
2513 if ((p = pa_hashmap_get(ps->profiles, name)))
2514 return p;
2516 p = pa_xnew0(pa_alsa_profile, 1);
2517 p->profile_set = ps;
2518 p->name = pa_xstrdup(name);
2520 pa_hashmap_put(ps->profiles, p->name, p);
2522 return p;
2525 static int mapping_parse_device_strings(
2526 const char *filename,
2527 unsigned line,
2528 const char *section,
2529 const char *lvalue,
2530 const char *rvalue,
2531 void *data,
2532 void *userdata) {
2534 pa_alsa_profile_set *ps = userdata;
2535 pa_alsa_mapping *m;
2537 pa_assert(ps);
2539 if (!(m = mapping_get(ps, section))) {
2540 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2541 return -1;
2544 pa_xstrfreev(m->device_strings);
2545 if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
2546 pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
2547 return -1;
2550 return 0;
2553 static int mapping_parse_channel_map(
2554 const char *filename,
2555 unsigned line,
2556 const char *section,
2557 const char *lvalue,
2558 const char *rvalue,
2559 void *data,
2560 void *userdata) {
2562 pa_alsa_profile_set *ps = userdata;
2563 pa_alsa_mapping *m;
2565 pa_assert(ps);
2567 if (!(m = mapping_get(ps, section))) {
2568 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2569 return -1;
2572 if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
2573 pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
2574 return -1;
2577 return 0;
2580 static int mapping_parse_paths(
2581 const char *filename,
2582 unsigned line,
2583 const char *section,
2584 const char *lvalue,
2585 const char *rvalue,
2586 void *data,
2587 void *userdata) {
2589 pa_alsa_profile_set *ps = userdata;
2590 pa_alsa_mapping *m;
2592 pa_assert(ps);
2594 if (!(m = mapping_get(ps, section))) {
2595 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2596 return -1;
2599 if (pa_streq(lvalue, "paths-input")) {
2600 pa_xstrfreev(m->input_path_names);
2601 m->input_path_names = pa_split_spaces_strv(rvalue);
2602 } else {
2603 pa_xstrfreev(m->output_path_names);
2604 m->output_path_names = pa_split_spaces_strv(rvalue);
2607 return 0;
2610 static int mapping_parse_element(
2611 const char *filename,
2612 unsigned line,
2613 const char *section,
2614 const char *lvalue,
2615 const char *rvalue,
2616 void *data,
2617 void *userdata) {
2619 pa_alsa_profile_set *ps = userdata;
2620 pa_alsa_mapping *m;
2622 pa_assert(ps);
2624 if (!(m = mapping_get(ps, section))) {
2625 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2626 return -1;
2629 if (pa_streq(lvalue, "element-input")) {
2630 pa_xstrfreev(m->input_element);
2631 m->input_element = pa_split_spaces_strv(rvalue);
2632 } else {
2633 pa_xstrfreev(m->output_element);
2634 m->output_element = pa_split_spaces_strv(rvalue);
2637 return 0;
2640 static int mapping_parse_direction(
2641 const char *filename,
2642 unsigned line,
2643 const char *section,
2644 const char *lvalue,
2645 const char *rvalue,
2646 void *data,
2647 void *userdata) {
2649 pa_alsa_profile_set *ps = userdata;
2650 pa_alsa_mapping *m;
2652 pa_assert(ps);
2654 if (!(m = mapping_get(ps, section))) {
2655 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2656 return -1;
2659 if (pa_streq(rvalue, "input"))
2660 m->direction = PA_ALSA_DIRECTION_INPUT;
2661 else if (pa_streq(rvalue, "output"))
2662 m->direction = PA_ALSA_DIRECTION_OUTPUT;
2663 else if (pa_streq(rvalue, "any"))
2664 m->direction = PA_ALSA_DIRECTION_ANY;
2665 else {
2666 pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
2667 return -1;
2670 return 0;
2673 static int mapping_parse_description(
2674 const char *filename,
2675 unsigned line,
2676 const char *section,
2677 const char *lvalue,
2678 const char *rvalue,
2679 void *data,
2680 void *userdata) {
2682 pa_alsa_profile_set *ps = userdata;
2683 pa_alsa_profile *p;
2684 pa_alsa_mapping *m;
2686 pa_assert(ps);
2688 if ((m = mapping_get(ps, section))) {
2689 pa_xstrdup(m->description);
2690 m->description = pa_xstrdup(rvalue);
2691 } else if ((p = profile_get(ps, section))) {
2692 pa_xfree(p->description);
2693 p->description = pa_xstrdup(rvalue);
2694 } else {
2695 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2696 return -1;
2699 return 0;
2702 static int mapping_parse_priority(
2703 const char *filename,
2704 unsigned line,
2705 const char *section,
2706 const char *lvalue,
2707 const char *rvalue,
2708 void *data,
2709 void *userdata) {
2711 pa_alsa_profile_set *ps = userdata;
2712 pa_alsa_profile *p;
2713 pa_alsa_mapping *m;
2714 uint32_t prio;
2716 pa_assert(ps);
2718 if (pa_atou(rvalue, &prio) < 0) {
2719 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
2720 return -1;
2723 if ((m = mapping_get(ps, section)))
2724 m->priority = prio;
2725 else if ((p = profile_get(ps, section)))
2726 p->priority = prio;
2727 else {
2728 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2729 return -1;
2732 return 0;
2735 static int profile_parse_mappings(
2736 const char *filename,
2737 unsigned line,
2738 const char *section,
2739 const char *lvalue,
2740 const char *rvalue,
2741 void *data,
2742 void *userdata) {
2744 pa_alsa_profile_set *ps = userdata;
2745 pa_alsa_profile *p;
2747 pa_assert(ps);
2749 if (!(p = profile_get(ps, section))) {
2750 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2751 return -1;
2754 if (pa_streq(lvalue, "input-mappings")) {
2755 pa_xstrfreev(p->input_mapping_names);
2756 p->input_mapping_names = pa_split_spaces_strv(rvalue);
2757 } else {
2758 pa_xstrfreev(p->output_mapping_names);
2759 p->output_mapping_names = pa_split_spaces_strv(rvalue);
2762 return 0;
2765 static int profile_parse_skip_probe(
2766 const char *filename,
2767 unsigned line,
2768 const char *section,
2769 const char *lvalue,
2770 const char *rvalue,
2771 void *data,
2772 void *userdata) {
2774 pa_alsa_profile_set *ps = userdata;
2775 pa_alsa_profile *p;
2776 int b;
2778 pa_assert(ps);
2780 if (!(p = profile_get(ps, section))) {
2781 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2782 return -1;
2785 if ((b = pa_parse_boolean(rvalue)) < 0) {
2786 pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
2787 return -1;
2790 p->supported = b;
2792 return 0;
2795 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
2797 static const struct description_map well_known_descriptions[] = {
2798 { "analog-mono", N_("Analog Mono") },
2799 { "analog-stereo", N_("Analog Stereo") },
2800 { "analog-surround-21", N_("Analog Surround 2.1") },
2801 { "analog-surround-30", N_("Analog Surround 3.0") },
2802 { "analog-surround-31", N_("Analog Surround 3.1") },
2803 { "analog-surround-40", N_("Analog Surround 4.0") },
2804 { "analog-surround-41", N_("Analog Surround 4.1") },
2805 { "analog-surround-50", N_("Analog Surround 5.0") },
2806 { "analog-surround-51", N_("Analog Surround 5.1") },
2807 { "analog-surround-61", N_("Analog Surround 6.0") },
2808 { "analog-surround-61", N_("Analog Surround 6.1") },
2809 { "analog-surround-70", N_("Analog Surround 7.0") },
2810 { "analog-surround-71", N_("Analog Surround 7.1") },
2811 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
2812 { "iec958-surround-40", N_("Digital Surround 4.0 (IEC958)") },
2813 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
2814 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
2815 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
2818 pa_assert(m);
2820 if (!pa_channel_map_valid(&m->channel_map)) {
2821 pa_log("Mapping %s is missing channel map.", m->name);
2822 return -1;
2825 if (!m->device_strings) {
2826 pa_log("Mapping %s is missing device strings.", m->name);
2827 return -1;
2830 if ((m->input_path_names && m->input_element) ||
2831 (m->output_path_names && m->output_element)) {
2832 pa_log("Mapping %s must have either mixer path or mixer elment, not both.", m->name);
2833 return -1;
2836 if (!m->description)
2837 m->description = pa_xstrdup(lookup_description(m->name,
2838 well_known_descriptions,
2839 PA_ELEMENTSOF(well_known_descriptions)));
2841 if (!m->description)
2842 m->description = pa_xstrdup(m->name);
2844 if (bonus) {
2845 if (pa_channel_map_equal(&m->channel_map, bonus))
2846 m->priority += 50;
2847 else if (m->channel_map.channels == bonus->channels)
2848 m->priority += 30;
2851 return 0;
2854 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
2855 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
2857 pa_assert(m);
2859 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
2860 m->name,
2861 pa_strnull(m->description),
2862 m->priority,
2863 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
2864 pa_yes_no(m->supported),
2865 m->direction);
2868 static void profile_set_add_auto_pair(
2869 pa_alsa_profile_set *ps,
2870 pa_alsa_mapping *m, /* output */
2871 pa_alsa_mapping *n /* input */) {
2873 char *name;
2874 pa_alsa_profile *p;
2876 pa_assert(ps);
2877 pa_assert(m || n);
2879 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
2880 return;
2882 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
2883 return;
2885 if (m && n)
2886 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
2887 else if (m)
2888 name = pa_sprintf_malloc("output:%s", m->name);
2889 else
2890 name = pa_sprintf_malloc("input:%s", n->name);
2892 if (pa_hashmap_get(ps->profiles, name)) {
2893 pa_xfree(name);
2894 return;
2897 p = pa_xnew0(pa_alsa_profile, 1);
2898 p->profile_set = ps;
2899 p->name = name;
2901 if (m) {
2902 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2903 pa_idxset_put(p->output_mappings, m, NULL);
2904 p->priority += m->priority * 100;
2907 if (n) {
2908 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2909 pa_idxset_put(p->input_mappings, n, NULL);
2910 p->priority += n->priority;
2913 pa_hashmap_put(ps->profiles, p->name, p);
2916 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
2917 pa_alsa_mapping *m, *n;
2918 void *m_state, *n_state;
2920 pa_assert(ps);
2922 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
2923 profile_set_add_auto_pair(ps, m, NULL);
2925 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
2926 profile_set_add_auto_pair(ps, m, n);
2929 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
2930 profile_set_add_auto_pair(ps, NULL, n);
2933 static int profile_verify(pa_alsa_profile *p) {
2935 static const struct description_map well_known_descriptions[] = {
2936 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
2937 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
2938 { "output:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
2939 { "off", N_("Off") }
2942 pa_assert(p);
2944 /* Replace the output mapping names by the actual mappings */
2945 if (p->output_mapping_names) {
2946 char **name;
2948 pa_assert(!p->output_mappings);
2949 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2951 for (name = p->output_mapping_names; *name; name++) {
2952 pa_alsa_mapping *m;
2953 char **in;
2954 pa_bool_t duplicate = FALSE;
2956 for (in = name + 1; *in; in++)
2957 if (pa_streq(*name, *in)) {
2958 duplicate = TRUE;
2959 break;
2962 if (duplicate)
2963 continue;
2965 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
2966 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
2967 return -1;
2970 pa_idxset_put(p->output_mappings, m, NULL);
2972 if (p->supported)
2973 m->supported++;
2976 pa_xstrfreev(p->output_mapping_names);
2977 p->output_mapping_names = NULL;
2980 /* Replace the input mapping names by the actual mappings */
2981 if (p->input_mapping_names) {
2982 char **name;
2984 pa_assert(!p->input_mappings);
2985 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2987 for (name = p->input_mapping_names; *name; name++) {
2988 pa_alsa_mapping *m;
2989 char **in;
2990 pa_bool_t duplicate = FALSE;
2992 for (in = name + 1; *in; in++)
2993 if (pa_streq(*name, *in)) {
2994 duplicate = TRUE;
2995 break;
2998 if (duplicate)
2999 continue;
3001 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
3002 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
3003 return -1;
3006 pa_idxset_put(p->input_mappings, m, NULL);
3008 if (p->supported)
3009 m->supported++;
3012 pa_xstrfreev(p->input_mapping_names);
3013 p->input_mapping_names = NULL;
3016 if (!p->input_mappings && !p->output_mappings) {
3017 pa_log("Profile '%s' lacks mappings.", p->name);
3018 return -1;
3021 if (!p->description)
3022 p->description = pa_xstrdup(lookup_description(p->name,
3023 well_known_descriptions,
3024 PA_ELEMENTSOF(well_known_descriptions)));
3026 if (!p->description) {
3027 pa_strbuf *sb;
3028 uint32_t idx;
3029 pa_alsa_mapping *m;
3031 sb = pa_strbuf_new();
3033 if (p->output_mappings)
3034 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3035 if (!pa_strbuf_isempty(sb))
3036 pa_strbuf_puts(sb, " + ");
3038 pa_strbuf_printf(sb, "%s Output", m->description);
3041 if (p->input_mappings)
3042 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3043 if (!pa_strbuf_isempty(sb))
3044 pa_strbuf_puts(sb, " + ");
3046 pa_strbuf_printf(sb, "%s Input", m->description);
3049 p->description = pa_strbuf_tostring_free(sb);
3052 return 0;
3055 void pa_alsa_profile_dump(pa_alsa_profile *p) {
3056 uint32_t idx;
3057 pa_alsa_mapping *m;
3058 pa_assert(p);
3060 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3061 p->name,
3062 pa_strnull(p->description),
3063 p->priority,
3064 pa_yes_no(p->supported),
3065 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
3066 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
3068 if (p->input_mappings)
3069 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
3070 pa_log_debug("Input %s", m->name);
3072 if (p->output_mappings)
3073 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
3074 pa_log_debug("Output %s", m->name);
3077 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
3078 pa_alsa_profile_set *ps;
3079 pa_alsa_profile *p;
3080 pa_alsa_mapping *m;
3081 char *fn;
3082 int r;
3083 void *state;
3085 static pa_config_item items[] = {
3086 /* [General] */
3087 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
3089 /* [Mapping ...] */
3090 { "device-strings", mapping_parse_device_strings, NULL, NULL },
3091 { "channel-map", mapping_parse_channel_map, NULL, NULL },
3092 { "paths-input", mapping_parse_paths, NULL, NULL },
3093 { "paths-output", mapping_parse_paths, NULL, NULL },
3094 { "element-input", mapping_parse_element, NULL, NULL },
3095 { "element-output", mapping_parse_element, NULL, NULL },
3096 { "direction", mapping_parse_direction, NULL, NULL },
3098 /* Shared by [Mapping ...] and [Profile ...] */
3099 { "description", mapping_parse_description, NULL, NULL },
3100 { "priority", mapping_parse_priority, NULL, NULL },
3102 /* [Profile ...] */
3103 { "input-mappings", profile_parse_mappings, NULL, NULL },
3104 { "output-mappings", profile_parse_mappings, NULL, NULL },
3105 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
3106 { NULL, NULL, NULL, NULL }
3109 ps = pa_xnew0(pa_alsa_profile_set, 1);
3110 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3111 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3113 items[0].data = &ps->auto_profiles;
3115 if (!fname)
3116 fname = "default.conf";
3118 fn = pa_maybe_prefix_path(fname,
3119 #if defined(__linux__) && !defined(__OPTIMIZE__)
3120 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
3121 #endif
3122 PA_ALSA_PROFILE_SETS_DIR);
3124 r = pa_config_parse(fn, NULL, items, ps);
3125 pa_xfree(fn);
3127 if (r < 0)
3128 goto fail;
3130 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3131 if (mapping_verify(m, bonus) < 0)
3132 goto fail;
3134 if (ps->auto_profiles)
3135 profile_set_add_auto(ps);
3137 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3138 if (profile_verify(p) < 0)
3139 goto fail;
3141 return ps;
3143 fail:
3144 pa_alsa_profile_set_free(ps);
3145 return NULL;
3148 void pa_alsa_profile_set_probe(
3149 pa_alsa_profile_set *ps,
3150 const char *dev_id,
3151 const pa_sample_spec *ss,
3152 unsigned default_n_fragments,
3153 unsigned default_fragment_size_msec) {
3155 void *state;
3156 pa_alsa_profile *p, *last = NULL;
3157 pa_alsa_mapping *m;
3159 pa_assert(ps);
3160 pa_assert(dev_id);
3161 pa_assert(ss);
3163 if (ps->probed)
3164 return;
3166 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
3167 pa_sample_spec try_ss;
3168 pa_channel_map try_map;
3169 snd_pcm_uframes_t try_period_size, try_buffer_size;
3170 uint32_t idx;
3172 /* Is this already marked that it is supported? (i.e. from the config file) */
3173 if (p->supported)
3174 continue;
3176 pa_log_debug("Looking at profile %s", p->name);
3178 /* Close PCMs from the last iteration we don't need anymore */
3179 if (last && last->output_mappings)
3180 PA_IDXSET_FOREACH(m, last->output_mappings, idx) {
3182 if (!m->output_pcm)
3183 break;
3185 if (last->supported)
3186 m->supported++;
3188 if (!p->output_mappings || !pa_idxset_get_by_data(p->output_mappings, m, NULL)) {
3189 snd_pcm_close(m->output_pcm);
3190 m->output_pcm = NULL;
3194 if (last && last->input_mappings)
3195 PA_IDXSET_FOREACH(m, last->input_mappings, idx) {
3197 if (!m->input_pcm)
3198 break;
3200 if (last->supported)
3201 m->supported++;
3203 if (!p->input_mappings || !pa_idxset_get_by_data(p->input_mappings, m, NULL)) {
3204 snd_pcm_close(m->input_pcm);
3205 m->input_pcm = NULL;
3209 p->supported = TRUE;
3211 /* Check if we can open all new ones */
3212 if (p->output_mappings)
3213 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3215 if (m->output_pcm)
3216 continue;
3218 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
3219 try_map = m->channel_map;
3220 try_ss = *ss;
3221 try_ss.channels = try_map.channels;
3223 try_period_size =
3224 pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
3225 pa_frame_size(&try_ss);
3226 try_buffer_size = default_n_fragments * try_period_size;
3228 if (!(m ->output_pcm = pa_alsa_open_by_template(
3229 m->device_strings,
3230 dev_id,
3231 NULL,
3232 &try_ss, &try_map,
3233 SND_PCM_STREAM_PLAYBACK,
3234 &try_period_size, &try_buffer_size, 0, NULL, NULL,
3235 TRUE))) {
3236 p->supported = FALSE;
3237 break;
3241 if (p->input_mappings && p->supported)
3242 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3244 if (m->input_pcm)
3245 continue;
3247 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
3248 try_map = m->channel_map;
3249 try_ss = *ss;
3250 try_ss.channels = try_map.channels;
3252 try_period_size =
3253 pa_usec_to_bytes(default_fragment_size_msec*PA_USEC_PER_MSEC, &try_ss) /
3254 pa_frame_size(&try_ss);
3255 try_buffer_size = default_n_fragments * try_period_size;
3257 if (!(m ->input_pcm = pa_alsa_open_by_template(
3258 m->device_strings,
3259 dev_id,
3260 NULL,
3261 &try_ss, &try_map,
3262 SND_PCM_STREAM_CAPTURE,
3263 &try_period_size, &try_buffer_size, 0, NULL, NULL,
3264 TRUE))) {
3265 p->supported = FALSE;
3266 break;
3270 last = p;
3272 if (p->supported)
3273 pa_log_debug("Profile %s supported.", p->name);
3276 /* Clean up */
3277 if (last) {
3278 uint32_t idx;
3280 if (last->output_mappings)
3281 PA_IDXSET_FOREACH(m, last->output_mappings, idx)
3282 if (m->output_pcm) {
3284 if (last->supported)
3285 m->supported++;
3287 snd_pcm_close(m->output_pcm);
3288 m->output_pcm = NULL;
3291 if (last->input_mappings)
3292 PA_IDXSET_FOREACH(m, last->input_mappings, idx)
3293 if (m->input_pcm) {
3295 if (last->supported)
3296 m->supported++;
3298 snd_pcm_close(m->input_pcm);
3299 m->input_pcm = NULL;
3303 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3304 if (!p->supported) {
3305 pa_hashmap_remove(ps->profiles, p->name);
3306 profile_free(p);
3309 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3310 if (m->supported <= 0) {
3311 pa_hashmap_remove(ps->mappings, m->name);
3312 mapping_free(m);
3315 ps->probed = TRUE;
3318 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
3319 pa_alsa_profile *p;
3320 pa_alsa_mapping *m;
3321 void *state;
3323 pa_assert(ps);
3325 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u",
3326 (void*)
3328 pa_yes_no(ps->auto_profiles),
3329 pa_yes_no(ps->probed),
3330 pa_hashmap_size(ps->mappings),
3331 pa_hashmap_size(ps->profiles));
3333 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3334 pa_alsa_mapping_dump(m);
3336 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3337 pa_alsa_profile_dump(p);
3340 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps) {
3341 pa_alsa_path *path;
3343 pa_assert(p);
3344 pa_assert(!*p);
3345 pa_assert(ps);
3347 /* if there is no path, we don't want a port list */
3348 if (!ps->paths)
3349 return;
3351 if (!ps->paths->next){
3352 pa_alsa_setting *s;
3354 /* If there is only one path, but no or only one setting, then
3355 * we want a port list either */
3356 if (!ps->paths->settings || !ps->paths->settings->next)
3357 return;
3359 /* Ok, there is only one path, however with multiple settings,
3360 * so let's create a port for each setting */
3361 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3363 PA_LLIST_FOREACH(s, ps->paths->settings) {
3364 pa_device_port *port;
3365 pa_alsa_port_data *data;
3367 port = pa_device_port_new(s->name, s->description, sizeof(pa_alsa_port_data));
3368 port->priority = s->priority;
3370 data = PA_DEVICE_PORT_DATA(port);
3371 data->path = ps->paths;
3372 data->setting = s;
3374 pa_hashmap_put(*p, port->name, port);
3377 } else {
3379 /* We have multiple paths, so let's create a port for each
3380 * one, and each of each settings */
3381 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3383 PA_LLIST_FOREACH(path, ps->paths) {
3385 if (!path->settings || !path->settings->next) {
3386 pa_device_port *port;
3387 pa_alsa_port_data *data;
3389 /* If there is no or just one setting we only need a
3390 * single entry */
3392 port = pa_device_port_new(path->name, path->description, sizeof(pa_alsa_port_data));
3393 port->priority = path->priority * 100;
3396 data = PA_DEVICE_PORT_DATA(port);
3397 data->path = path;
3398 data->setting = path->settings;
3400 pa_hashmap_put(*p, port->name, port);
3401 } else {
3402 pa_alsa_setting *s;
3404 PA_LLIST_FOREACH(s, path->settings) {
3405 pa_device_port *port;
3406 pa_alsa_port_data *data;
3407 char *n, *d;
3409 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
3411 if (s->description[0])
3412 d = pa_sprintf_malloc(_("%s / %s"), path->description, s->description);
3413 else
3414 d = pa_xstrdup(path->description);
3416 port = pa_device_port_new(n, d, sizeof(pa_alsa_port_data));
3417 port->priority = path->priority * 100 + s->priority;
3419 pa_xfree(n);
3420 pa_xfree(d);
3422 data = PA_DEVICE_PORT_DATA(port);
3423 data->path = path;
3424 data->setting = s;
3426 pa_hashmap_put(*p, port->name, port);
3432 pa_log_debug("Added %u ports", pa_hashmap_size(*p));