]> code.delx.au - pulseaudio/blob - src/modules/alsa/alsa-mixer.c
alsa: rework mixer logic
[pulseaudio] / src / modules / alsa / alsa-mixer.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2004-2009 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
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.
11
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.
16
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 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <sys/types.h>
28 #include <limits.h>
29 #include <asoundlib.h>
30
31 #ifdef HAVE_VALGRIND_MEMCHECK_H
32 #include <valgrind/memcheck.h>
33 #endif
34
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>
41
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>
51
52 #include "alsa-mixer.h"
53 #include "alsa-util.h"
54
55 struct description_map {
56 const char *name;
57 const char *description;
58 };
59
60 static const char *lookup_description(const char *name, const struct description_map dm[], unsigned n) {
61 unsigned i;
62
63 for (i = 0; i < n; i++)
64 if (pa_streq(dm[i].name, name))
65 return dm[i].description;
66
67 return NULL;
68 }
69
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;
75
76 snd_mixer_t *mixer;
77
78 pa_mainloop_api *m;
79 pa_defer_event *defer;
80 pa_io_event **ios;
81
82 pa_bool_t polled;
83
84 void (*cb)(void *userdata);
85 void *userdata;
86 };
87
88 static void io_cb(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
89
90 struct pa_alsa_fdlist *fdl = userdata;
91 int err;
92 unsigned i;
93 unsigned short revents;
94
95 pa_assert(a);
96 pa_assert(fdl);
97 pa_assert(fdl->mixer);
98 pa_assert(fdl->fds);
99 pa_assert(fdl->work_fds);
100
101 if (fdl->polled)
102 return;
103
104 fdl->polled = TRUE;
105
106 memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds);
107
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;
119 }
120 }
121
122 pa_assert(i != fdl->num_fds);
123
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;
127 }
128
129 a->defer_enable(fdl->defer, 1);
130
131 if (revents)
132 snd_mixer_handle_events(fdl->mixer);
133 }
134
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;
140
141 pa_assert(a);
142 pa_assert(fdl);
143 pa_assert(fdl->mixer);
144
145 a->defer_enable(fdl->defer, 0);
146
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;
150 }
151 num_fds = (unsigned) n;
152
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);
160 }
161
162 memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds);
163
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;
167 }
168
169 fdl->polled = FALSE;
170
171 if (memcmp(fdl->fds, fdl->work_fds, sizeof(struct pollfd) * num_fds) == 0)
172 return;
173
174 if (fdl->ios) {
175 for (i = 0; i < fdl->num_fds; i++)
176 a->io_free(fdl->ios[i]);
177
178 if (num_fds != fdl->num_fds) {
179 pa_xfree(fdl->ios);
180 fdl->ios = NULL;
181 }
182 }
183
184 if (!fdl->ios)
185 fdl->ios = pa_xnew(pa_io_event*, num_fds);
186
187 /* Swap pointers */
188 temp = fdl->work_fds;
189 fdl->work_fds = fdl->fds;
190 fdl->fds = temp;
191
192 fdl->num_fds = num_fds;
193
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);
199 }
200
201 struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) {
202 struct pa_alsa_fdlist *fdl;
203
204 fdl = pa_xnew0(struct pa_alsa_fdlist, 1);
205
206 return fdl;
207 }
208
209 void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {
210 pa_assert(fdl);
211
212 if (fdl->defer) {
213 pa_assert(fdl->m);
214 fdl->m->defer_free(fdl->defer);
215 }
216
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);
223 }
224
225 if (fdl->fds)
226 pa_xfree(fdl->fds);
227 if (fdl->work_fds)
228 pa_xfree(fdl->work_fds);
229
230 pa_xfree(fdl);
231 }
232
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);
238
239 fdl->mixer = mixer_handle;
240 fdl->m = m;
241 fdl->defer = m->defer_new(m, defer_cb, fdl);
242
243 return 0;
244 }
245
246 static int prepare_mixer(snd_mixer_t *mixer, const char *dev) {
247 int err;
248
249 pa_assert(mixer);
250 pa_assert(dev);
251
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;
255 }
256
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;
260 }
261
262 if ((err = snd_mixer_load(mixer)) < 0) {
263 pa_log_warn("Unable to load mixer: %s", pa_alsa_strerror(err));
264 return -1;
265 }
266
267 pa_log_info("Successfully attached to mixer '%s'", dev);
268 return 0;
269 }
270
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);
277
278 pa_assert(pcm);
279
280 if ((err = snd_mixer_open(&m, 0)) < 0) {
281 pa_log("Error opening mixer: %s", pa_alsa_strerror(err));
282 return NULL;
283 }
284
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);
290
291 return m;
292 }
293
294 /* Then, try by card index */
295 if (snd_pcm_info(pcm, info) >= 0) {
296 char *md;
297 int card_idx;
298
299 if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
300
301 md = pa_sprintf_malloc("hw:%i", card_idx);
302
303 if (!dev || !pa_streq(dev, md))
304 if (prepare_mixer(m, md) >= 0) {
305
306 if (ctl_device)
307 *ctl_device = md;
308 else
309 pa_xfree(md);
310
311 return m;
312 }
313
314 pa_xfree(md);
315 }
316 }
317
318 snd_mixer_close(m);
319 return NULL;
320 }
321
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! */
324
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,
328
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,
332
333 [PA_CHANNEL_POSITION_LFE] = SND_MIXER_SCHN_WOOFER,
334
335 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
336 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
337
338 [PA_CHANNEL_POSITION_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT,
339 [PA_CHANNEL_POSITION_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT,
340
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,
373
374 [PA_CHANNEL_POSITION_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN,
375
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,
379
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
383 };
384
385 static void setting_free(pa_alsa_setting *s) {
386 pa_assert(s);
387
388 if (s->options)
389 pa_idxset_free(s->options, NULL, NULL);
390
391 pa_xfree(s->name);
392 pa_xfree(s->description);
393 pa_xfree(s);
394 }
395
396 static void option_free(pa_alsa_option *o) {
397 pa_assert(o);
398
399 pa_xfree(o->alsa_name);
400 pa_xfree(o->name);
401 pa_xfree(o->description);
402 pa_xfree(o);
403 }
404
405 static void element_free(pa_alsa_element *e) {
406 pa_alsa_option *o;
407 pa_assert(e);
408
409 while ((o = e->options)) {
410 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
411 option_free(o);
412 }
413
414 pa_xfree(e->alsa_name);
415 pa_xfree(e);
416 }
417
418 void pa_alsa_path_free(pa_alsa_path *p) {
419 pa_alsa_element *e;
420 pa_alsa_setting *s;
421
422 pa_assert(p);
423
424 while ((e = p->elements)) {
425 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
426 element_free(e);
427 }
428
429 while ((s = p->settings)) {
430 PA_LLIST_REMOVE(pa_alsa_setting, p->settings, s);
431 setting_free(s);
432 }
433
434 pa_xfree(p->name);
435 pa_xfree(p->description);
436 pa_xfree(p);
437 }
438
439 void pa_alsa_path_set_free(pa_alsa_path_set *ps) {
440 pa_alsa_path *p;
441 pa_assert(ps);
442
443 while ((p = ps->paths)) {
444 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
445 pa_alsa_path_free(p);
446 }
447
448 pa_xfree(ps);
449 }
450
451 static long to_alsa_dB(pa_volume_t v) {
452 return (long) (pa_sw_volume_to_dB(v) * 100.0);
453 }
454
455 static pa_volume_t from_alsa_dB(long v) {
456 return pa_sw_volume_from_dB((double) v / 100.0);
457 }
458
459 static long to_alsa_volume(pa_volume_t v, long min, long max) {
460 long w;
461
462 w = (long) round(((double) v * (double) (max - min)) / PA_VOLUME_NORM) + min;
463 return PA_CLAMP_UNLIKELY(w, min, max);
464 }
465
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));
468 }
469
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)
476
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 unsigned k;
483
484 pa_assert(m);
485 pa_assert(e);
486 pa_assert(cm);
487 pa_assert(v);
488
489 SELEM_INIT(sid, e->alsa_name);
490 if (!(me = snd_mixer_find_selem(m, sid))) {
491 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
492 return -1;
493 }
494
495 pa_cvolume_mute(v, cm->channels);
496
497 /* We take the highest volume of all channels that match */
498
499 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
500 int r;
501 pa_volume_t f;
502
503 if (e->has_dB) {
504 long value = 0;
505
506 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
507 if (snd_mixer_selem_has_playback_channel(me, c))
508 r = snd_mixer_selem_get_playback_dB(me, c, &value);
509 else
510 r = -1;
511 } else {
512 if (snd_mixer_selem_has_capture_channel(me, c))
513 r = snd_mixer_selem_get_capture_dB(me, c, &value);
514 else
515 r = -1;
516 }
517
518 if (r < 0)
519 continue;
520
521 #ifdef HAVE_VALGRIND_MEMCHECK_H
522 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
523 #endif
524
525 f = from_alsa_dB(value);
526
527 } else {
528 long value = 0;
529
530 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
531 if (snd_mixer_selem_has_playback_channel(me, c))
532 r = snd_mixer_selem_get_playback_volume(me, c, &value);
533 else
534 r = -1;
535 } else {
536 if (snd_mixer_selem_has_capture_channel(me, c))
537 r = snd_mixer_selem_get_capture_volume(me, c, &value);
538 else
539 r = -1;
540 }
541
542 if (r < 0)
543 continue;
544
545 f = from_alsa_volume(value, e->min_volume, e->max_volume);
546 }
547
548 for (k = 0; k < cm->channels; k++)
549 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
550 if (v->values[k] < f)
551 v->values[k] = f;
552
553 mask |= e->masks[c][e->n_channels-1];
554 }
555
556 for (k = 0; k < cm->channels; k++)
557 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
558 v->values[k] = PA_VOLUME_NORM;
559
560 return 0;
561 }
562
563 int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
564 pa_alsa_element *e;
565
566 pa_assert(m);
567 pa_assert(p);
568 pa_assert(cm);
569 pa_assert(v);
570
571 if (!p->has_volume)
572 return -1;
573
574 pa_cvolume_reset(v, cm->channels);
575
576 PA_LLIST_FOREACH(e, p->elements) {
577 pa_cvolume ev;
578
579 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
580 continue;
581
582 pa_assert(!p->has_dB || e->has_dB);
583
584 if (element_get_volume(e, m, cm, &ev) < 0)
585 return -1;
586
587 /* If we have no dB information all we can do is take the first element and leave */
588 if (!p->has_dB) {
589 *v = ev;
590 return 0;
591 }
592
593 pa_sw_cvolume_multiply(v, v, &ev);
594 }
595
596 return 0;
597 }
598
599 static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t *b) {
600 snd_mixer_selem_id_t *sid;
601 snd_mixer_elem_t *me;
602 snd_mixer_selem_channel_id_t c;
603
604 pa_assert(m);
605 pa_assert(e);
606 pa_assert(b);
607
608 SELEM_INIT(sid, e->alsa_name);
609 if (!(me = snd_mixer_find_selem(m, sid))) {
610 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
611 return -1;
612 }
613
614 /* We return muted if at least one channel is muted */
615
616 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
617 int r;
618 int value = 0;
619
620 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
621 if (snd_mixer_selem_has_playback_channel(me, c))
622 r = snd_mixer_selem_get_playback_switch(me, c, &value);
623 else
624 r = -1;
625 } else {
626 if (snd_mixer_selem_has_capture_channel(me, c))
627 r = snd_mixer_selem_get_capture_switch(me, c, &value);
628 else
629 r = -1;
630 }
631
632 if (r < 0)
633 continue;
634
635 if (!value) {
636 *b = FALSE;
637 return 0;
638 }
639 }
640
641 *b = TRUE;
642 return 0;
643 }
644
645 int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t *muted) {
646 pa_alsa_element *e;
647
648 pa_assert(m);
649 pa_assert(p);
650 pa_assert(muted);
651
652 if (!p->has_mute)
653 return -1;
654
655 PA_LLIST_FOREACH(e, p->elements) {
656 pa_bool_t b;
657
658 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
659 continue;
660
661 if (element_get_switch(e, m, &b) < 0)
662 return -1;
663
664 if (!b) {
665 *muted = TRUE;
666 return 0;
667 }
668 }
669
670 *muted = FALSE;
671 return 0;
672 }
673
674 static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
675 snd_mixer_selem_id_t *sid;
676 pa_cvolume rv;
677 snd_mixer_elem_t *me;
678 snd_mixer_selem_channel_id_t c;
679 pa_channel_position_mask_t mask = 0;
680 unsigned k;
681
682 pa_assert(m);
683 pa_assert(e);
684 pa_assert(cm);
685 pa_assert(v);
686 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
687
688 SELEM_INIT(sid, e->alsa_name);
689 if (!(me = snd_mixer_find_selem(m, sid))) {
690 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
691 return -1;
692 }
693
694 pa_cvolume_mute(&rv, cm->channels);
695
696 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
697 int r;
698 pa_volume_t f = PA_VOLUME_MUTED;
699
700 for (k = 0; k < cm->channels; k++)
701 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
702 if (v->values[k] > f)
703 f = v->values[k];
704
705 if (e->has_dB) {
706 long value = to_alsa_dB(f);
707
708 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
709 /* If we call set_play_volume() without checking first
710 * if the channel is available, ALSA behaves ver
711 * strangely and doesn't fail the call */
712 if (snd_mixer_selem_has_playback_channel(me, c)) {
713 if ((r = snd_mixer_selem_set_playback_dB(me, c, value, +1)) >= 0)
714 r = snd_mixer_selem_get_playback_dB(me, c, &value);
715 } else
716 r = -1;
717 } else {
718 if (snd_mixer_selem_has_capture_channel(me, c)) {
719 if ((r = snd_mixer_selem_set_capture_dB(me, c, value, +1)) >= 0)
720 r = snd_mixer_selem_get_capture_dB(me, c, &value);
721 } else
722 r = -1;
723 }
724
725 if (r < 0)
726 continue;
727
728 #ifdef HAVE_VALGRIND_MEMCHECK_H
729 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
730 #endif
731
732 f = from_alsa_dB(value);
733
734 } else {
735 long value;
736
737 value = to_alsa_volume(f, e->min_volume, e->max_volume);
738
739 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
740 if (snd_mixer_selem_has_playback_channel(me, c)) {
741 if ((r = snd_mixer_selem_set_playback_volume(me, c, value)) >= 0)
742 r = snd_mixer_selem_get_playback_volume(me, c, &value);
743 } else
744 r = -1;
745 } else {
746 if (snd_mixer_selem_has_capture_channel(me, c)) {
747 if ((r = snd_mixer_selem_set_capture_volume(me, c, value)) >= 0)
748 r = snd_mixer_selem_get_capture_volume(me, c, &value);
749 } else
750 r = -1;
751 }
752
753 if (r < 0)
754 continue;
755
756 f = from_alsa_volume(value, e->min_volume, e->max_volume);
757 }
758
759 for (k = 0; k < cm->channels; k++)
760 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
761 if (rv.values[k] < f)
762 rv.values[k] = f;
763
764 mask |= e->masks[c][e->n_channels-1];
765 }
766
767 for (k = 0; k < cm->channels; k++)
768 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
769 rv.values[k] = PA_VOLUME_NORM;
770
771 *v = rv;
772 return 0;
773 }
774
775 int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
776 pa_alsa_element *e;
777 pa_cvolume rv;
778
779 pa_assert(m);
780 pa_assert(p);
781 pa_assert(cm);
782 pa_assert(v);
783 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
784
785 if (!p->has_volume)
786 return -1;
787
788 rv = *v; /* Remaining adjustment */
789 pa_cvolume_reset(v, cm->channels); /* Adjustment done */
790
791 PA_LLIST_FOREACH(e, p->elements) {
792 pa_cvolume ev;
793
794 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
795 continue;
796
797 pa_assert(!p->has_dB || e->has_dB);
798
799 ev = rv;
800 if (element_set_volume(e, m, cm, &ev) < 0)
801 return -1;
802
803 if (!p->has_dB) {
804 *v = ev;
805 return 0;
806 }
807
808 pa_sw_cvolume_multiply(v, v, &ev);
809 pa_sw_cvolume_divide(&rv, &rv, &ev);
810 }
811
812 return 0;
813 }
814
815 static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t b) {
816 snd_mixer_elem_t *me;
817 snd_mixer_selem_id_t *sid;
818 int r;
819
820 pa_assert(m);
821 pa_assert(e);
822
823 SELEM_INIT(sid, e->alsa_name);
824 if (!(me = snd_mixer_find_selem(m, sid))) {
825 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
826 return -1;
827 }
828
829 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
830 r = snd_mixer_selem_set_playback_switch_all(me, b);
831 else
832 r = snd_mixer_selem_set_capture_switch_all(me, b);
833
834 if (r < 0)
835 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
836
837 return r;
838 }
839
840 int pa_alsa_path_set_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t muted) {
841 pa_alsa_element *e;
842
843 pa_assert(m);
844 pa_assert(p);
845
846 if (!p->has_mute)
847 return -1;
848
849 PA_LLIST_FOREACH(e, p->elements) {
850
851 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
852 continue;
853
854 if (element_set_switch(e, m, !muted) < 0)
855 return -1;
856 }
857
858 return 0;
859 }
860
861 static int element_mute_volume(pa_alsa_element *e, snd_mixer_t *m) {
862 snd_mixer_elem_t *me;
863 snd_mixer_selem_id_t *sid;
864 int r;
865
866 pa_assert(m);
867 pa_assert(e);
868
869 SELEM_INIT(sid, e->alsa_name);
870 if (!(me = snd_mixer_find_selem(m, sid))) {
871 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
872 return -1;
873 }
874
875 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
876 r = snd_mixer_selem_set_playback_volume_all(me, e->min_volume);
877 else
878 r = snd_mixer_selem_set_capture_volume_all(me, e->min_volume);
879
880 if (r < 0)
881 pa_log_warn("Faile to set volume to muted of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
882
883 return r;
884 }
885
886 /* The volume to 0dB */
887 static int element_zero_volume(pa_alsa_element *e, snd_mixer_t *m) {
888 snd_mixer_elem_t *me;
889 snd_mixer_selem_id_t *sid;
890 int r;
891
892 pa_assert(m);
893 pa_assert(e);
894
895 SELEM_INIT(sid, e->alsa_name);
896 if (!(me = snd_mixer_find_selem(m, sid))) {
897 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
898 return -1;
899 }
900
901 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
902 r = snd_mixer_selem_set_playback_dB_all(me, 0, +1);
903 else
904 r = snd_mixer_selem_set_capture_dB_all(me, 0, +1);
905
906 if (r < 0)
907 pa_log_warn("Faile to set volume to 0dB of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
908
909 return r;
910 }
911
912 int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
913 pa_alsa_element *e;
914 int r;
915
916 pa_assert(m);
917 pa_assert(p);
918
919 pa_log_debug("Activating path %s", p->name);
920 pa_alsa_path_dump(p);
921
922 PA_LLIST_FOREACH(e, p->elements) {
923
924 switch (e->switch_use) {
925 case PA_ALSA_SWITCH_MUTE:
926 case PA_ALSA_SWITCH_OFF:
927 r = element_set_switch(e, m, FALSE);
928 break;
929
930 case PA_ALSA_SWITCH_ON:
931 r = element_set_switch(e, m, TRUE);
932 break;
933
934 case PA_ALSA_SWITCH_IGNORE:
935 case PA_ALSA_SWITCH_SELECT:
936 r = 0;
937 break;
938 }
939
940 if (r < 0)
941 return -1;
942
943 switch (e->volume_use) {
944 case PA_ALSA_VOLUME_OFF:
945 case PA_ALSA_VOLUME_MERGE:
946 r = element_mute_volume(e, m);
947 break;
948
949 case PA_ALSA_VOLUME_ZERO:
950 r = element_zero_volume(e, m);
951 break;
952
953 case PA_ALSA_VOLUME_IGNORE:
954 r = 0;
955 break;
956 }
957
958 if (r < 0)
959 return -1;
960 }
961
962 return 0;
963 }
964
965 static int check_required(pa_alsa_element *e, snd_mixer_elem_t *me) {
966 pa_bool_t has_switch;
967 pa_bool_t has_enumeration;
968 pa_bool_t has_volume;
969
970 pa_assert(e);
971 pa_assert(me);
972
973 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
974 has_switch =
975 snd_mixer_selem_has_playback_switch(me) ||
976 (e->direction_try_other && snd_mixer_selem_has_capture_switch(me));
977 } else {
978 has_switch =
979 snd_mixer_selem_has_capture_switch(me) ||
980 (e->direction_try_other && snd_mixer_selem_has_playback_switch(me));
981 }
982
983 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
984 has_volume =
985 snd_mixer_selem_has_playback_volume(me) ||
986 (e->direction_try_other && snd_mixer_selem_has_capture_volume(me));
987 } else {
988 has_volume =
989 snd_mixer_selem_has_capture_volume(me) ||
990 (e->direction_try_other && snd_mixer_selem_has_playback_volume(me));
991 }
992
993 has_enumeration = snd_mixer_selem_is_enumerated(me);
994
995 if ((e->required == PA_ALSA_REQUIRED_SWITCH && !has_switch) ||
996 (e->required == PA_ALSA_REQUIRED_VOLUME && !has_volume) ||
997 (e->required == PA_ALSA_REQUIRED_ENUMERATION && !has_enumeration))
998 return -1;
999
1000 if (e->required == PA_ALSA_REQUIRED_ANY && !(has_switch || has_volume || has_enumeration))
1001 return -1;
1002
1003 if ((e->required_absent == PA_ALSA_REQUIRED_SWITCH && has_switch) ||
1004 (e->required_absent == PA_ALSA_REQUIRED_VOLUME && has_volume) ||
1005 (e->required_absent == PA_ALSA_REQUIRED_ENUMERATION && has_enumeration))
1006 return -1;
1007
1008 if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration))
1009 return -1;
1010
1011 return 0;
1012 }
1013
1014 static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
1015 snd_mixer_selem_id_t *sid;
1016 snd_mixer_elem_t *me;
1017
1018 pa_assert(m);
1019 pa_assert(e);
1020
1021 SELEM_INIT(sid, e->alsa_name);
1022
1023 if (!(me = snd_mixer_find_selem(m, sid))) {
1024
1025 if (e->required != PA_ALSA_REQUIRED_IGNORE)
1026 return -1;
1027
1028 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1029 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1030 e->enumeration_use = PA_ALSA_VOLUME_IGNORE;
1031
1032 return 0;
1033 }
1034
1035 if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
1036 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1037
1038 if (!snd_mixer_selem_has_playback_switch(me)) {
1039 if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
1040 e->direction = PA_ALSA_DIRECTION_INPUT;
1041 else
1042 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1043 }
1044
1045 } else {
1046
1047 if (!snd_mixer_selem_has_capture_switch(me)) {
1048 if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
1049 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1050 else
1051 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1052 }
1053 }
1054
1055 if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
1056 e->direction_try_other = FALSE;
1057 }
1058
1059 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1060
1061 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1062
1063 if (!snd_mixer_selem_has_playback_volume(me)) {
1064 if (e->direction_try_other && snd_mixer_selem_has_capture_volume(me))
1065 e->direction = PA_ALSA_DIRECTION_INPUT;
1066 else
1067 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1068 }
1069
1070 } else {
1071
1072 if (!snd_mixer_selem_has_capture_volume(me)) {
1073 if (e->direction_try_other && snd_mixer_selem_has_playback_volume(me))
1074 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1075 else
1076 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1077 }
1078 }
1079
1080 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1081 long min_dB = 0, max_dB = 0;
1082 int r;
1083
1084 e->direction_try_other = FALSE;
1085
1086 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1087 e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0;
1088 else
1089 e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0;
1090
1091 if (e->has_dB) {
1092 #ifdef HAVE_VALGRIND_MEMCHECK_H
1093 VALGRIND_MAKE_MEM_DEFINED(&min_dB, sizeof(min_dB));
1094 VALGRIND_MAKE_MEM_DEFINED(&max_dB, sizeof(max_dB));
1095 #endif
1096
1097 e->min_dB = ((double) min_dB) / 100.0;
1098 e->max_dB = ((double) max_dB) / 100.0;
1099
1100 if (min_dB >= max_dB) {
1101 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);
1102 e->has_dB = FALSE;
1103 }
1104 }
1105
1106 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1107 r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume);
1108 else
1109 r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
1110
1111 if (r < 0) {
1112 pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1113 return -1;
1114 }
1115
1116
1117 if (e->min_volume >= e->max_volume) {
1118 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);
1119 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1120
1121 } else {
1122 pa_bool_t is_mono;
1123 pa_channel_position_t p;
1124
1125 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1126 is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
1127 else
1128 is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
1129
1130 if (is_mono) {
1131 e->n_channels = 1;
1132
1133 if (!e->override_map) {
1134 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++)
1135 e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
1136 e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
1137 }
1138
1139 e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
1140 } else {
1141 e->n_channels = 0;
1142 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1143
1144 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1145 continue;
1146
1147 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1148 e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1149 else
1150 e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1151 }
1152
1153 if (e->n_channels <= 0) {
1154 pa_log_warn("Volume element %s with no channels?", e->alsa_name);
1155 return -1;
1156 }
1157
1158 if (!e->override_map) {
1159 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1160 pa_bool_t has_channel;
1161
1162 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1163 continue;
1164
1165 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1166 has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1167 else
1168 has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1169
1170 e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
1171 }
1172 }
1173
1174 e->merged_mask = 0;
1175 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++)
1176 e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
1177 }
1178 }
1179 }
1180
1181 }
1182
1183 if (check_required(e, me) < 0)
1184 return -1;
1185
1186 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1187 pa_alsa_option *o;
1188
1189 PA_LLIST_FOREACH(o, e->options)
1190 o->alsa_idx = pa_streq(o->alsa_name, "on") ? 1 : 0;
1191 } else if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1192 int n;
1193 pa_alsa_option *o;
1194
1195 if ((n = snd_mixer_selem_get_enum_items(me)) < 0) {
1196 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n));
1197 return -1;
1198 }
1199
1200 PA_LLIST_FOREACH(o, e->options) {
1201 int i;
1202
1203 for (i = 0; i < n; i++) {
1204 char buf[128];
1205
1206 if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
1207 continue;
1208
1209 if (!pa_streq(buf, o->alsa_name))
1210 continue;
1211
1212 o->alsa_idx = i;
1213 }
1214 }
1215 }
1216
1217 return 0;
1218 }
1219
1220 static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, pa_bool_t prefixed) {
1221 pa_alsa_element *e;
1222
1223 pa_assert(p);
1224 pa_assert(section);
1225
1226 if (prefixed) {
1227 if (!pa_startswith(section, "Element "))
1228 return NULL;
1229
1230 section += 8;
1231 }
1232
1233 /* This is not an element section, but an enum section? */
1234 if (strchr(section, ':'))
1235 return NULL;
1236
1237 if (p->last_element && pa_streq(p->last_element->alsa_name, section))
1238 return p->last_element;
1239
1240 PA_LLIST_FOREACH(e, p->elements)
1241 if (pa_streq(e->alsa_name, section))
1242 goto finish;
1243
1244 e = pa_xnew0(pa_alsa_element, 1);
1245 e->path = p;
1246 e->alsa_name = pa_xstrdup(section);
1247 e->direction = p->direction;
1248
1249 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
1250
1251 finish:
1252 p->last_element = e;
1253 return e;
1254 }
1255
1256 static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
1257 char *en;
1258 const char *on;
1259 pa_alsa_option *o;
1260 pa_alsa_element *e;
1261
1262 if (!pa_startswith(section, "Option "))
1263 return NULL;
1264
1265 section += 7;
1266
1267 /* This is not an enum section, but an element section? */
1268 if (!(on = strchr(section, ':')))
1269 return NULL;
1270
1271 en = pa_xstrndup(section, on - section);
1272 on++;
1273
1274 if (p->last_option &&
1275 pa_streq(p->last_option->element->alsa_name, en) &&
1276 pa_streq(p->last_option->alsa_name, on)) {
1277 pa_xfree(en);
1278 return p->last_option;
1279 }
1280
1281 pa_assert_se(e = element_get(p, en, FALSE));
1282 pa_xfree(en);
1283
1284 PA_LLIST_FOREACH(o, e->options)
1285 if (pa_streq(o->alsa_name, on))
1286 goto finish;
1287
1288 o = pa_xnew0(pa_alsa_option, 1);
1289 o->element = e;
1290 o->alsa_name = pa_xstrdup(on);
1291 o->alsa_idx = -1;
1292
1293 if (p->last_option && p->last_option->element == e)
1294 PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
1295 else
1296 PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
1297
1298 finish:
1299 p->last_option = o;
1300 return o;
1301 }
1302
1303 static int element_parse_switch(
1304 const char *filename,
1305 unsigned line,
1306 const char *section,
1307 const char *lvalue,
1308 const char *rvalue,
1309 void *data,
1310 void *userdata) {
1311
1312 pa_alsa_path *p = userdata;
1313 pa_alsa_element *e;
1314
1315 pa_assert(p);
1316
1317 if (!(e = element_get(p, section, TRUE))) {
1318 pa_log("[%s:%u] Switch makes no sense in '%s'", filename, line, section);
1319 return -1;
1320 }
1321
1322 if (pa_streq(rvalue, "ignore"))
1323 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1324 else if (pa_streq(rvalue, "mute"))
1325 e->switch_use = PA_ALSA_SWITCH_MUTE;
1326 else if (pa_streq(rvalue, "off"))
1327 e->switch_use = PA_ALSA_SWITCH_OFF;
1328 else if (pa_streq(rvalue, "on"))
1329 e->switch_use = PA_ALSA_SWITCH_ON;
1330 else if (pa_streq(rvalue, "select"))
1331 e->switch_use = PA_ALSA_SWITCH_SELECT;
1332 else {
1333 pa_log("[%s:%u] Switch invalid of '%s'", filename, line, section);
1334 return -1;
1335 }
1336
1337 return 0;
1338 }
1339
1340 static int element_parse_volume(
1341 const char *filename,
1342 unsigned line,
1343 const char *section,
1344 const char *lvalue,
1345 const char *rvalue,
1346 void *data,
1347 void *userdata) {
1348
1349 pa_alsa_path *p = userdata;
1350 pa_alsa_element *e;
1351
1352 pa_assert(p);
1353
1354 if (!(e = element_get(p, section, TRUE))) {
1355 pa_log("[%s:%u] Volume makes no sense in '%s'", filename, line, section);
1356 return -1;
1357 }
1358
1359 if (pa_streq(rvalue, "ignore"))
1360 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1361 else if (pa_streq(rvalue, "merge"))
1362 e->volume_use = PA_ALSA_VOLUME_MERGE;
1363 else if (pa_streq(rvalue, "off"))
1364 e->volume_use = PA_ALSA_VOLUME_OFF;
1365 else if (pa_streq(rvalue, "zero"))
1366 e->volume_use = PA_ALSA_VOLUME_ZERO;
1367 else {
1368 pa_log("[%s:%u] Volume invalid of '%s'", filename, line, section);
1369 return -1;
1370 }
1371
1372 return 0;
1373 }
1374
1375 static int element_parse_enumeration(
1376 const char *filename,
1377 unsigned line,
1378 const char *section,
1379 const char *lvalue,
1380 const char *rvalue,
1381 void *data,
1382 void *userdata) {
1383
1384 pa_alsa_path *p = userdata;
1385 pa_alsa_element *e;
1386
1387 pa_assert(p);
1388
1389 if (!(e = element_get(p, section, TRUE))) {
1390 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename, line, section);
1391 return -1;
1392 }
1393
1394 if (pa_streq(rvalue, "ignore"))
1395 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1396 else if (pa_streq(rvalue, "select"))
1397 e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
1398 else {
1399 pa_log("[%s:%u] Enumeration invalid of '%s'", filename, line, section);
1400 return -1;
1401 }
1402
1403 return 0;
1404 }
1405
1406 static int option_parse_priority(
1407 const char *filename,
1408 unsigned line,
1409 const char *section,
1410 const char *lvalue,
1411 const char *rvalue,
1412 void *data,
1413 void *userdata) {
1414
1415 pa_alsa_path *p = userdata;
1416 pa_alsa_option *o;
1417 uint32_t prio;
1418
1419 pa_assert(p);
1420
1421 if (!(o = option_get(p, section))) {
1422 pa_log("[%s:%u] Priority makes no sense in '%s'", filename, line, section);
1423 return -1;
1424 }
1425
1426 if (pa_atou(rvalue, &prio) < 0) {
1427 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
1428 return -1;
1429 }
1430
1431 o->priority = prio;
1432 return 0;
1433 }
1434
1435 static int option_parse_name(
1436 const char *filename,
1437 unsigned line,
1438 const char *section,
1439 const char *lvalue,
1440 const char *rvalue,
1441 void *data,
1442 void *userdata) {
1443
1444 pa_alsa_path *p = userdata;
1445 pa_alsa_option *o;
1446
1447 pa_assert(p);
1448
1449 if (!(o = option_get(p, section))) {
1450 pa_log("[%s:%u] Name makes no sense in '%s'", filename, line, section);
1451 return -1;
1452 }
1453
1454 pa_xfree(o->name);
1455 o->name = pa_xstrdup(rvalue);
1456
1457 return 0;
1458 }
1459
1460 static int element_parse_required(
1461 const char *filename,
1462 unsigned line,
1463 const char *section,
1464 const char *lvalue,
1465 const char *rvalue,
1466 void *data,
1467 void *userdata) {
1468
1469 pa_alsa_path *p = userdata;
1470 pa_alsa_element *e;
1471 pa_alsa_required_t req;
1472
1473 pa_assert(p);
1474
1475 if (!(e = element_get(p, section, TRUE))) {
1476 pa_log("[%s:%u] Required makes no sense in '%s'", filename, line, section);
1477 return -1;
1478 }
1479
1480 if (pa_streq(rvalue, "ignore"))
1481 req = PA_ALSA_REQUIRED_IGNORE;
1482 else if (pa_streq(rvalue, "switch"))
1483 req = PA_ALSA_REQUIRED_SWITCH;
1484 else if (pa_streq(rvalue, "volume"))
1485 req = PA_ALSA_REQUIRED_VOLUME;
1486 else if (pa_streq(rvalue, "enumeration"))
1487 req = PA_ALSA_REQUIRED_ENUMERATION;
1488 else if (pa_streq(rvalue, "any"))
1489 req = PA_ALSA_REQUIRED_ANY;
1490 else {
1491 pa_log("[%s:%u] Required invalid of '%s'", filename, line, section);
1492 return -1;
1493 }
1494
1495 if (pa_streq(lvalue, "required-absent"))
1496 e->required_absent = req;
1497 else
1498 e->required = req;
1499
1500 return 0;
1501 }
1502
1503 static int element_parse_direction(
1504 const char *filename,
1505 unsigned line,
1506 const char *section,
1507 const char *lvalue,
1508 const char *rvalue,
1509 void *data,
1510 void *userdata) {
1511
1512 pa_alsa_path *p = userdata;
1513 pa_alsa_element *e;
1514
1515 pa_assert(p);
1516
1517 if (!(e = element_get(p, section, TRUE))) {
1518 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1519 return -1;
1520 }
1521
1522 if (pa_streq(rvalue, "playback"))
1523 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1524 else if (pa_streq(rvalue, "capture"))
1525 e->direction = PA_ALSA_DIRECTION_INPUT;
1526 else {
1527 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1528 return -1;
1529 }
1530
1531 return 0;
1532 }
1533
1534 static int element_parse_direction_try_other(
1535 const char *filename,
1536 unsigned line,
1537 const char *section,
1538 const char *lvalue,
1539 const char *rvalue,
1540 void *data,
1541 void *userdata) {
1542
1543 pa_alsa_path *p = userdata;
1544 pa_alsa_element *e;
1545 int yes;
1546
1547 if (!(e = element_get(p, section, TRUE))) {
1548 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1549 return -1;
1550 }
1551
1552 if ((yes = pa_parse_boolean(rvalue)) < 0) {
1553 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1554 return -1;
1555 }
1556
1557 e->direction_try_other = !!yes;
1558 return 0;
1559 }
1560
1561 static pa_channel_position_mask_t parse_mask(const char *m) {
1562 pa_channel_position_mask_t v;
1563
1564 if (pa_streq(m, "all-left"))
1565 v = PA_CHANNEL_POSITION_MASK_LEFT;
1566 else if (pa_streq(m, "all-right"))
1567 v = PA_CHANNEL_POSITION_MASK_RIGHT;
1568 else if (pa_streq(m, "all-center"))
1569 v = PA_CHANNEL_POSITION_MASK_CENTER;
1570 else if (pa_streq(m, "all-front"))
1571 v = PA_CHANNEL_POSITION_MASK_FRONT;
1572 else if (pa_streq(m, "all-rear"))
1573 v = PA_CHANNEL_POSITION_MASK_REAR;
1574 else if (pa_streq(m, "all-side"))
1575 v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER;
1576 else if (pa_streq(m, "all-top"))
1577 v = PA_CHANNEL_POSITION_MASK_TOP;
1578 else if (pa_streq(m, "all-no-lfe"))
1579 v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE);
1580 else if (pa_streq(m, "all"))
1581 v = PA_CHANNEL_POSITION_MASK_ALL;
1582 else {
1583 pa_channel_position_t p;
1584
1585 if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
1586 return 0;
1587
1588 v = PA_CHANNEL_POSITION_MASK(p);
1589 }
1590
1591 return v;
1592 }
1593
1594 static int element_parse_override_map(
1595 const char *filename,
1596 unsigned line,
1597 const char *section,
1598 const char *lvalue,
1599 const char *rvalue,
1600 void *data,
1601 void *userdata) {
1602
1603 pa_alsa_path *p = userdata;
1604 pa_alsa_element *e;
1605 const char *state = NULL;
1606 unsigned i = 0;
1607 char *n;
1608
1609 if (!(e = element_get(p, section, TRUE))) {
1610 pa_log("[%s:%u] Override map makes no sense in '%s'", filename, line, section);
1611 return -1;
1612 }
1613
1614 while ((n = pa_split(rvalue, ",", &state))) {
1615 pa_channel_position_mask_t m;
1616
1617 if (!*n)
1618 m = 0;
1619 else {
1620 if ((m = parse_mask(n)) == 0) {
1621 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename, line, n, section);
1622 pa_xfree(n);
1623 return -1;
1624 }
1625 }
1626
1627 if (pa_streq(lvalue, "override-map.1"))
1628 e->masks[i++][0] = m;
1629 else
1630 e->masks[i++][1] = m;
1631
1632 /* Later on we might add override-map.3 and so on here ... */
1633
1634 pa_xfree(n);
1635 }
1636
1637 e->override_map = TRUE;
1638
1639 return 0;
1640 }
1641
1642 static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
1643 snd_mixer_selem_id_t *sid;
1644 snd_mixer_elem_t *me;
1645 int r;
1646
1647 pa_assert(e);
1648 pa_assert(m);
1649
1650 SELEM_INIT(sid, e->alsa_name);
1651 if (!(me = snd_mixer_find_selem(m, sid))) {
1652 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1653 return -1;
1654 }
1655
1656 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1657
1658 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1659 r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
1660 else
1661 r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
1662
1663 if (r < 0)
1664 pa_log_warn("Faile to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1665
1666 } else {
1667 pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
1668
1669 if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0)
1670 pa_log_warn("Faile to set enumeration of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1671 }
1672
1673 return r;
1674 }
1675
1676 int pa_alsa_setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
1677 pa_alsa_option *o;
1678 uint32_t idx;
1679
1680 pa_assert(s);
1681 pa_assert(m);
1682
1683 PA_IDXSET_FOREACH(o, s->options, idx)
1684 element_set_option(o->element, m, o->alsa_idx);
1685
1686 return 0;
1687 }
1688
1689 static int option_verify(pa_alsa_option *o) {
1690 static const struct description_map well_known_descriptions[] = {
1691 { "input", N_("Input") },
1692 { "input-docking", N_("Docking Station Input") },
1693 { "input-docking-microphone", N_("Docking Station Microphone") },
1694 { "input-linein", N_("Line-In") },
1695 { "input-microphone", N_("Microphone") },
1696 { "input-microphone-external", N_("External Microphone") },
1697 { "input-microphone-internal", N_("Internal Microphone") },
1698 { "input-radio", N_("Radio") },
1699 { "input-video", N_("Video") },
1700 { "input-agc-on", N_("Automatic Gain Control") },
1701 { "input-agc-off", "" },
1702 { "input-boost-on", N_("Boost") },
1703 { "input-boost-off", "" },
1704 { "output-amplifier-on", N_("Amplifier") },
1705 { "output-amplifier-off", "" }
1706 };
1707
1708 pa_assert(o);
1709
1710 if (!o->name) {
1711 pa_log("No name set for option %s", o->alsa_name);
1712 return -1;
1713 }
1714
1715 if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
1716 o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
1717 pa_log("Element %s of option %s not set for select.", o->element->alsa_name, o->name);
1718 return -1;
1719 }
1720
1721 if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
1722 !pa_streq(o->alsa_name, "on") &&
1723 !pa_streq(o->alsa_name, "off")) {
1724 pa_log("Switch %s options need be named off or on ", o->element->alsa_name);
1725 return -1;
1726 }
1727
1728 if (!o->description)
1729 o->description = pa_xstrdup(lookup_description(o->name,
1730 well_known_descriptions,
1731 PA_ELEMENTSOF(well_known_descriptions)));
1732 if (!o->description)
1733 o->description = pa_xstrdup(o->name);
1734
1735 return 0;
1736 }
1737
1738 static int element_verify(pa_alsa_element *e) {
1739 pa_alsa_option *o;
1740
1741 pa_assert(e);
1742
1743 if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
1744 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
1745 pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name);
1746 return -1;
1747 }
1748
1749 if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1750 pa_log("Element %s cannot set select for both switch and enumeration.", e->alsa_name);
1751 return -1;
1752 }
1753
1754 PA_LLIST_FOREACH(o, e->options)
1755 if (option_verify(o) < 0)
1756 return -1;
1757
1758 return 0;
1759 }
1760
1761 static int path_verify(pa_alsa_path *p) {
1762 static const struct description_map well_known_descriptions[] = {
1763 { "analog-input", N_("Analog Input") },
1764 { "analog-input-microphone", N_("Analog Microphone") },
1765 { "analog-input-linein", N_("Analog Line-In") },
1766 { "analog-input-radio", N_("Analog Radio") },
1767 { "analog-input-video", N_("Analog Video") },
1768 { "analog-output", N_("Analog Output") },
1769 { "analog-output-headphones", N_("Analog Headphones") },
1770 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
1771 { "analog-output-mono", N_("Analog Mono Output") }
1772 };
1773
1774 pa_alsa_element *e;
1775
1776 pa_assert(p);
1777
1778 PA_LLIST_FOREACH(e, p->elements)
1779 if (element_verify(e) < 0)
1780 return -1;
1781
1782 if (!p->description)
1783 p->description = pa_xstrdup(lookup_description(p->name,
1784 well_known_descriptions,
1785 PA_ELEMENTSOF(well_known_descriptions)));
1786
1787 if (!p->description)
1788 p->description = pa_xstrdup(p->name);
1789
1790 return 0;
1791 }
1792
1793 pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction) {
1794 pa_alsa_path *p;
1795 char *fn;
1796 int r;
1797 const char *n;
1798
1799 pa_config_item items[] = {
1800 /* [General] */
1801 { "priority", pa_config_parse_unsigned, NULL, "General" },
1802 { "description", pa_config_parse_string, NULL, "General" },
1803 { "name", pa_config_parse_string, NULL, "General" },
1804
1805 /* [Option ...] */
1806 { "priority", option_parse_priority, NULL, NULL },
1807 { "name", option_parse_name, NULL, NULL },
1808
1809 /* [Element ...] */
1810 { "switch", element_parse_switch, NULL, NULL },
1811 { "volume", element_parse_volume, NULL, NULL },
1812 { "enumeration", element_parse_enumeration, NULL, NULL },
1813 { "override-map.1", element_parse_override_map, NULL, NULL },
1814 { "override-map.2", element_parse_override_map, NULL, NULL },
1815 /* ... later on we might add override-map.3 and so on here ... */
1816 { "required", element_parse_required, NULL, NULL },
1817 { "required-absent", element_parse_required, NULL, NULL },
1818 { "direction", element_parse_direction, NULL, NULL },
1819 { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
1820 { NULL, NULL, NULL, NULL }
1821 };
1822
1823 pa_assert(fname);
1824
1825 p = pa_xnew0(pa_alsa_path, 1);
1826 n = pa_path_get_filename(fname);
1827 p->name = pa_xstrndup(n, strcspn(n, "."));
1828 p->direction = direction;
1829
1830 items[0].data = &p->priority;
1831 items[1].data = &p->description;
1832 items[2].data = &p->name;
1833
1834 fn = pa_maybe_prefix_path(fname, PA_ALSA_PATHS_DIR);
1835 r = pa_config_parse(fn, NULL, items, p);
1836 pa_xfree(fn);
1837
1838 if (r < 0)
1839 goto fail;
1840
1841 if (path_verify(p) < 0)
1842 goto fail;
1843
1844 return p;
1845
1846 fail:
1847 pa_alsa_path_free(p);
1848 return NULL;
1849 }
1850
1851 pa_alsa_path* pa_alsa_path_synthesize(const char*element, pa_alsa_direction_t direction) {
1852 pa_alsa_path *p;
1853 pa_alsa_element *e;
1854
1855 pa_assert(element);
1856
1857 p = pa_xnew0(pa_alsa_path, 1);
1858 p->name = pa_xstrdup(element);
1859 p->direction = direction;
1860
1861 e = pa_xnew0(pa_alsa_element, 1);
1862 e->path = p;
1863 e->alsa_name = pa_xstrdup(element);
1864 e->direction = direction;
1865
1866 e->switch_use = PA_ALSA_SWITCH_MUTE;
1867 e->volume_use = PA_ALSA_VOLUME_MERGE;
1868
1869 PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
1870 return p;
1871 }
1872
1873 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
1874 pa_alsa_option *o, *n;
1875
1876 pa_assert(e);
1877
1878 for (o = e->options; o; o = n) {
1879 n = o->next;
1880
1881 if (o->alsa_idx < 0) {
1882 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
1883 option_free(o);
1884 }
1885 }
1886
1887 return
1888 e->switch_use != PA_ALSA_SWITCH_IGNORE ||
1889 e->volume_use != PA_ALSA_VOLUME_IGNORE ||
1890 e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
1891 }
1892
1893 static void path_drop_unsupported(pa_alsa_path *p) {
1894 pa_alsa_element *e, *n;
1895
1896 pa_assert(p);
1897
1898 for (e = p->elements; e; e = n) {
1899 n = e->next;
1900
1901 if (!element_drop_unsupported(e)) {
1902 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
1903 element_free(e);
1904 }
1905 }
1906 }
1907
1908 static void path_make_options_unique(pa_alsa_path *p) {
1909 pa_alsa_element *e;
1910 pa_alsa_option *o, *u;
1911
1912 PA_LLIST_FOREACH(e, p->elements) {
1913 PA_LLIST_FOREACH(o, e->options) {
1914 unsigned i;
1915 char *m;
1916
1917 for (u = o->next; u; u = u->next)
1918 if (pa_streq(u->name, o->name))
1919 break;
1920
1921 if (!u)
1922 continue;
1923
1924 m = pa_xstrdup(o->name);
1925
1926 /* OK, this name is not unique, hence let's rename */
1927 for (i = 1, u = o; u; u = u->next) {
1928 char *nn, *nd;
1929
1930 if (!pa_streq(u->name, m))
1931 continue;
1932
1933 nn = pa_sprintf_malloc("%s-%u", m, i);
1934 pa_xfree(u->name);
1935 u->name = nn;
1936
1937 nd = pa_sprintf_malloc("%s %u", u->description, i);
1938 pa_xfree(u->description);
1939 u->description = nd;
1940
1941 i++;
1942 }
1943
1944 pa_xfree(m);
1945 }
1946 }
1947 }
1948
1949 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
1950 pa_alsa_option *o;
1951
1952 for (; e; e = e->next)
1953 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
1954 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
1955 break;
1956
1957 if (!e)
1958 return FALSE;
1959
1960 for (o = e->options; o; o = o->next) {
1961 pa_alsa_setting *s;
1962
1963 if (template) {
1964 s = pa_xnewdup(pa_alsa_setting, template, 1);
1965 s->options = pa_idxset_copy(template->options);
1966 s->name = pa_sprintf_malloc(_("%s+%s"), template->name, o->name);
1967 s->description =
1968 (template->description[0] && o->description[0])
1969 ? pa_sprintf_malloc(_("%s / %s"), template->description, o->description)
1970 : (template->description[0]
1971 ? pa_xstrdup(template->description)
1972 : pa_xstrdup(o->description));
1973
1974 s->priority = PA_MAX(template->priority, o->priority);
1975 } else {
1976 s = pa_xnew0(pa_alsa_setting, 1);
1977 s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1978 s->name = pa_xstrdup(o->name);
1979 s->description = pa_xstrdup(o->description);
1980 s->priority = o->priority;
1981 }
1982
1983 pa_idxset_put(s->options, o, NULL);
1984
1985 if (element_create_settings(e->next, s))
1986 /* This is not a leaf, so let's get rid of it */
1987 setting_free(s);
1988 else {
1989 /* This is a leaf, so let's add it */
1990 PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
1991
1992 e->path->last_setting = s;
1993 }
1994 }
1995
1996 return TRUE;
1997 }
1998
1999 static void path_create_settings(pa_alsa_path *p) {
2000 pa_assert(p);
2001
2002 element_create_settings(p->elements, NULL);
2003 }
2004
2005 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) {
2006 pa_alsa_element *e;
2007 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2008 pa_channel_position_t t;
2009
2010 pa_assert(p);
2011 pa_assert(m);
2012
2013 if (p->probed)
2014 return 0;
2015
2016 pa_zero(min_dB);
2017 pa_zero(max_dB);
2018
2019 pa_log_debug("Probing path '%s'", p->name);
2020
2021 PA_LLIST_FOREACH(e, p->elements) {
2022 if (element_probe(e, m) < 0) {
2023 p->supported = FALSE;
2024 pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2025 return -1;
2026 }
2027
2028 if (ignore_dB)
2029 e->has_dB = FALSE;
2030
2031 if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2032
2033 if (!p->has_volume) {
2034 p->min_volume = e->min_volume;
2035 p->max_volume = e->max_volume;
2036 }
2037
2038 if (e->has_dB) {
2039 if (!p->has_volume) {
2040 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2041 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2042 min_dB[t] = e->min_dB;
2043 max_dB[t] = e->max_dB;
2044 }
2045
2046 p->has_dB = TRUE;
2047 } else {
2048
2049 if (p->has_dB) {
2050 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2051 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2052 min_dB[t] += e->min_dB;
2053 max_dB[t] += e->max_dB;
2054 }
2055 } else
2056 /* Hmm, there's another element before us
2057 * which cannot do dB volumes, so we we need
2058 * to 'neutralize' this slider */
2059 e->volume_use = PA_ALSA_VOLUME_ZERO;
2060 }
2061 } else if (p->has_volume)
2062 /* We can't use this volume, so let's ignore it */
2063 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2064
2065 p->has_volume = TRUE;
2066 }
2067
2068 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2069 p->has_mute = TRUE;
2070 }
2071
2072 path_drop_unsupported(p);
2073 path_make_options_unique(p);
2074 path_create_settings(p);
2075
2076 p->supported = TRUE;
2077 p->probed = TRUE;
2078
2079 p->min_dB = INFINITY;
2080 p->max_dB = -INFINITY;
2081
2082 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2083 if (p->min_dB > min_dB[t])
2084 p->min_dB = min_dB[t];
2085
2086 if (p->max_dB < max_dB[t])
2087 p->max_dB = max_dB[t];
2088 }
2089
2090 return 0;
2091 }
2092
2093 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2094 pa_assert(s);
2095
2096 pa_log_debug("Setting %s (%s) priority=%u",
2097 s->name,
2098 pa_strnull(s->description),
2099 s->priority);
2100 }
2101
2102 void pa_alsa_option_dump(pa_alsa_option *o) {
2103 pa_assert(o);
2104
2105 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2106 o->alsa_name,
2107 pa_strnull(o->name),
2108 pa_strnull(o->description),
2109 o->alsa_idx,
2110 o->priority);
2111 }
2112
2113 void pa_alsa_element_dump(pa_alsa_element *e) {
2114 pa_alsa_option *o;
2115 pa_assert(e);
2116
2117 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",
2118 e->alsa_name,
2119 e->direction,
2120 e->switch_use,
2121 e->volume_use,
2122 e->enumeration_use,
2123 e->required,
2124 e->required_absent,
2125 (long long unsigned) e->merged_mask,
2126 e->n_channels,
2127 pa_yes_no(e->override_map));
2128
2129 PA_LLIST_FOREACH(o, e->options)
2130 pa_alsa_option_dump(o);
2131 }
2132
2133 void pa_alsa_path_dump(pa_alsa_path *p) {
2134 pa_alsa_element *e;
2135 pa_alsa_setting *s;
2136 pa_assert(p);
2137
2138 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2139 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2140 p->name,
2141 pa_strnull(p->description),
2142 p->direction,
2143 p->priority,
2144 pa_yes_no(p->probed),
2145 pa_yes_no(p->supported),
2146 pa_yes_no(p->has_mute),
2147 pa_yes_no(p->has_volume),
2148 pa_yes_no(p->has_dB),
2149 p->min_volume, p->max_volume,
2150 p->min_dB, p->max_dB);
2151
2152 PA_LLIST_FOREACH(e, p->elements)
2153 pa_alsa_element_dump(e);
2154
2155 PA_LLIST_FOREACH(s, p->settings)
2156 pa_alsa_setting_dump(s);
2157 }
2158
2159 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2160 snd_mixer_selem_id_t *sid;
2161 snd_mixer_elem_t *me;
2162
2163 pa_assert(e);
2164 pa_assert(m);
2165 pa_assert(cb);
2166
2167 SELEM_INIT(sid, e->alsa_name);
2168 if (!(me = snd_mixer_find_selem(m, sid))) {
2169 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2170 return;
2171 }
2172
2173 snd_mixer_elem_set_callback(me, cb);
2174 snd_mixer_elem_set_callback_private(me, userdata);
2175 }
2176
2177 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2178 pa_alsa_element *e;
2179
2180 pa_assert(p);
2181 pa_assert(m);
2182 pa_assert(cb);
2183
2184 PA_LLIST_FOREACH(e, p->elements)
2185 element_set_callback(e, m, cb, userdata);
2186 }
2187
2188 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2189 pa_alsa_path *p;
2190
2191 pa_assert(ps);
2192 pa_assert(m);
2193 pa_assert(cb);
2194
2195 PA_LLIST_FOREACH(p, ps->paths)
2196 pa_alsa_path_set_callback(p, m, cb, userdata);
2197 }
2198
2199 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction) {
2200 pa_alsa_path_set *ps;
2201 char **pn = NULL, **en = NULL, **ie;
2202
2203 pa_assert(m);
2204 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2205
2206 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2207 return NULL;
2208
2209 ps = pa_xnew0(pa_alsa_path_set, 1);
2210 ps->direction = direction;
2211
2212 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2213 pn = m->output_path_names;
2214 else if (direction == PA_ALSA_DIRECTION_INPUT)
2215 pn = m->input_path_names;
2216
2217 if (pn) {
2218 char **in;
2219
2220 for (in = pn; *in; in++) {
2221 pa_alsa_path *p;
2222 pa_bool_t duplicate = FALSE;
2223 char **kn, *fn;
2224
2225 for (kn = pn; kn != in; kn++)
2226 if (pa_streq(*kn, *in)) {
2227 duplicate = TRUE;
2228 break;
2229 }
2230
2231 if (duplicate)
2232 continue;
2233
2234 fn = pa_sprintf_malloc("%s.conf", *in);
2235
2236 if ((p = pa_alsa_path_new(fn, direction))) {
2237 p->path_set = ps;
2238 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2239 ps->last_path = p;
2240 }
2241
2242 pa_xfree(fn);
2243 }
2244
2245 return ps;
2246 }
2247
2248 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2249 en = m->output_element;
2250 else if (direction == PA_ALSA_DIRECTION_INPUT)
2251 en = m->input_element;
2252
2253 if (!en) {
2254 pa_alsa_path_set_free(ps);
2255 return NULL;
2256 }
2257
2258 for (ie = en; *ie; ie++) {
2259 char **je;
2260 pa_alsa_path *p;
2261
2262 p = pa_alsa_path_synthesize(*ie, direction);
2263 p->path_set = ps;
2264
2265 /* Mark all other passed elements for require-absent */
2266 for (je = en; *je; je++) {
2267 pa_alsa_element *e;
2268 e = pa_xnew0(pa_alsa_element, 1);
2269 e->path = p;
2270 e->alsa_name = pa_xstrdup(*je);
2271 e->direction = direction;
2272 e->required_absent = PA_ALSA_REQUIRED_ANY;
2273
2274 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2275 p->last_element = e;
2276 }
2277
2278 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2279 ps->last_path = p;
2280 }
2281
2282 return ps;
2283 }
2284
2285 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2286 pa_alsa_path *p;
2287 pa_assert(ps);
2288
2289 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2290 (void*) ps,
2291 ps->direction,
2292 pa_yes_no(ps->probed));
2293
2294 PA_LLIST_FOREACH(p, ps->paths)
2295 pa_alsa_path_dump(p);
2296 }
2297
2298 static void path_set_unify(pa_alsa_path_set *ps) {
2299 pa_alsa_path *p;
2300 pa_bool_t has_dB = TRUE, has_volume = TRUE, has_mute = TRUE;
2301 pa_assert(ps);
2302
2303 /* We have issues dealing with paths that vary too wildly. That
2304 * means for now we have to have all paths support volume/mute/dB
2305 * or none. */
2306
2307 PA_LLIST_FOREACH(p, ps->paths) {
2308 pa_assert(p->probed);
2309
2310 if (!p->has_volume)
2311 has_volume = FALSE;
2312 else if (!p->has_dB)
2313 has_dB = FALSE;
2314
2315 if (!p->has_mute)
2316 has_mute = FALSE;
2317 }
2318
2319 if (!has_volume || !has_dB || !has_mute) {
2320
2321 if (!has_volume)
2322 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2323 else if (!has_dB)
2324 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2325
2326 if (!has_mute)
2327 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2328
2329 PA_LLIST_FOREACH(p, ps->paths) {
2330 if (!has_volume)
2331 p->has_volume = FALSE;
2332 else if (!has_dB)
2333 p->has_dB = FALSE;
2334
2335 if (!has_mute)
2336 p->has_mute = FALSE;
2337 }
2338 }
2339 }
2340
2341 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
2342 pa_alsa_path *p, *q;
2343
2344 PA_LLIST_FOREACH(p, ps->paths) {
2345 unsigned i;
2346 char *m;
2347
2348 for (q = p->next; q; q = q->next)
2349 if (pa_streq(q->name, p->name))
2350 break;
2351
2352 if (!q)
2353 continue;
2354
2355 m = pa_xstrdup(p->name);
2356
2357 /* OK, this name is not unique, hence let's rename */
2358 for (i = 1, q = p; q; q = q->next) {
2359 char *nn, *nd;
2360
2361 if (!pa_streq(q->name, m))
2362 continue;
2363
2364 nn = pa_sprintf_malloc("%s-%u", m, i);
2365 pa_xfree(q->name);
2366 q->name = nn;
2367
2368 nd = pa_sprintf_malloc("%s %u", q->description, i);
2369 pa_xfree(q->description);
2370 q->description = nd;
2371
2372 i++;
2373 }
2374
2375 pa_xfree(m);
2376 }
2377 }
2378
2379 void pa_alsa_path_set_probe(pa_alsa_path_set *ps, snd_mixer_t *m, pa_bool_t ignore_dB) {
2380 pa_alsa_path *p, *n;
2381
2382 pa_assert(ps);
2383
2384 if (ps->probed)
2385 return;
2386
2387 for (p = ps->paths; p; p = n) {
2388 n = p->next;
2389
2390 if (pa_alsa_path_probe(p, m, ignore_dB) < 0) {
2391 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
2392 pa_alsa_path_free(p);
2393 }
2394 }
2395
2396 path_set_unify(ps);
2397 path_set_make_paths_unique(ps);
2398 ps->probed = TRUE;
2399 }
2400
2401 static void mapping_free(pa_alsa_mapping *m) {
2402 pa_assert(m);
2403
2404 pa_xfree(m->name);
2405 pa_xfree(m->description);
2406
2407 pa_xstrfreev(m->device_strings);
2408 pa_xstrfreev(m->input_path_names);
2409 pa_xstrfreev(m->output_path_names);
2410 pa_xstrfreev(m->input_element);
2411 pa_xstrfreev(m->output_element);
2412
2413 pa_assert(!m->input_pcm);
2414 pa_assert(!m->output_pcm);
2415
2416 pa_xfree(m);
2417 }
2418
2419 static void profile_free(pa_alsa_profile *p) {
2420 pa_assert(p);
2421
2422 pa_xfree(p->name);
2423 pa_xfree(p->description);
2424
2425 pa_xstrfreev(p->input_mapping_names);
2426 pa_xstrfreev(p->output_mapping_names);
2427
2428 if (p->input_mappings)
2429 pa_idxset_free(p->input_mappings, NULL, NULL);
2430
2431 if (p->output_mappings)
2432 pa_idxset_free(p->output_mappings, NULL, NULL);
2433
2434 pa_xfree(p);
2435 }
2436
2437 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
2438 pa_assert(ps);
2439
2440 if (ps->profiles) {
2441 pa_alsa_profile *p;
2442
2443 while ((p = pa_hashmap_steal_first(ps->profiles)))
2444 profile_free(p);
2445
2446 pa_hashmap_free(ps->profiles, NULL, NULL);
2447 }
2448
2449 if (ps->mappings) {
2450 pa_alsa_mapping *m;
2451
2452 while ((m = pa_hashmap_steal_first(ps->mappings)))
2453 mapping_free(m);
2454
2455 pa_hashmap_free(ps->mappings, NULL, NULL);
2456 }
2457
2458 pa_xfree(ps);
2459 }
2460
2461 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
2462 pa_alsa_mapping *m;
2463
2464 if (!pa_startswith(name, "Mapping "))
2465 return NULL;
2466
2467 name += 8;
2468
2469 if ((m = pa_hashmap_get(ps->mappings, name)))
2470 return m;
2471
2472 m = pa_xnew0(pa_alsa_mapping, 1);
2473 m->profile_set = ps;
2474 m->name = pa_xstrdup(name);
2475 pa_channel_map_init(&m->channel_map);
2476
2477 pa_hashmap_put(ps->mappings, m->name, m);
2478
2479 return m;
2480 }
2481
2482 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
2483 pa_alsa_profile *p;
2484
2485 if (!pa_startswith(name, "Profile "))
2486 return NULL;
2487
2488 name += 8;
2489
2490 if ((p = pa_hashmap_get(ps->profiles, name)))
2491 return p;
2492
2493 p = pa_xnew0(pa_alsa_profile, 1);
2494 p->profile_set = ps;
2495 p->name = pa_xstrdup(name);
2496
2497 pa_hashmap_put(ps->profiles, p->name, p);
2498
2499 return p;
2500 }
2501
2502 static int mapping_parse_device_strings(
2503 const char *filename,
2504 unsigned line,
2505 const char *section,
2506 const char *lvalue,
2507 const char *rvalue,
2508 void *data,
2509 void *userdata) {
2510
2511 pa_alsa_profile_set *ps = userdata;
2512 pa_alsa_mapping *m;
2513
2514 pa_assert(ps);
2515
2516 if (!(m = mapping_get(ps, section))) {
2517 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2518 return -1;
2519 }
2520
2521 pa_xstrfreev(m->device_strings);
2522 if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
2523 pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
2524 return -1;
2525 }
2526
2527 return 0;
2528 }
2529
2530 static int mapping_parse_channel_map(
2531 const char *filename,
2532 unsigned line,
2533 const char *section,
2534 const char *lvalue,
2535 const char *rvalue,
2536 void *data,
2537 void *userdata) {
2538
2539 pa_alsa_profile_set *ps = userdata;
2540 pa_alsa_mapping *m;
2541
2542 pa_assert(ps);
2543
2544 if (!(m = mapping_get(ps, section))) {
2545 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2546 return -1;
2547 }
2548
2549 if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
2550 pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
2551 return -1;
2552 }
2553
2554 return 0;
2555 }
2556
2557 static int mapping_parse_paths(
2558 const char *filename,
2559 unsigned line,
2560 const char *section,
2561 const char *lvalue,
2562 const char *rvalue,
2563 void *data,
2564 void *userdata) {
2565
2566 pa_alsa_profile_set *ps = userdata;
2567 pa_alsa_mapping *m;
2568
2569 pa_assert(ps);
2570
2571 if (!(m = mapping_get(ps, section))) {
2572 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2573 return -1;
2574 }
2575
2576 if (pa_streq(lvalue, "paths-input")) {
2577 pa_xstrfreev(m->input_path_names);
2578 m->input_path_names = pa_split_spaces_strv(rvalue);
2579 } else {
2580 pa_xstrfreev(m->output_path_names);
2581 m->output_path_names = pa_split_spaces_strv(rvalue);
2582 }
2583
2584 return 0;
2585 }
2586
2587 static int mapping_parse_element(
2588 const char *filename,
2589 unsigned line,
2590 const char *section,
2591 const char *lvalue,
2592 const char *rvalue,
2593 void *data,
2594 void *userdata) {
2595
2596 pa_alsa_profile_set *ps = userdata;
2597 pa_alsa_mapping *m;
2598
2599 pa_assert(ps);
2600
2601 if (!(m = mapping_get(ps, section))) {
2602 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2603 return -1;
2604 }
2605
2606 if (pa_streq(lvalue, "element-input")) {
2607 pa_xstrfreev(m->input_element);
2608 m->input_element = pa_split_spaces_strv(rvalue);
2609 } else {
2610 pa_xstrfreev(m->output_element);
2611 m->output_element = pa_split_spaces_strv(rvalue);
2612 }
2613
2614 return 0;
2615 }
2616
2617 static int mapping_parse_direction(
2618 const char *filename,
2619 unsigned line,
2620 const char *section,
2621 const char *lvalue,
2622 const char *rvalue,
2623 void *data,
2624 void *userdata) {
2625
2626 pa_alsa_profile_set *ps = userdata;
2627 pa_alsa_mapping *m;
2628
2629 pa_assert(ps);
2630
2631 if (!(m = mapping_get(ps, section))) {
2632 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2633 return -1;
2634 }
2635
2636 if (pa_streq(rvalue, "input"))
2637 m->direction = PA_ALSA_DIRECTION_INPUT;
2638 else if (pa_streq(rvalue, "output"))
2639 m->direction = PA_ALSA_DIRECTION_OUTPUT;
2640 else if (pa_streq(rvalue, "any"))
2641 m->direction = PA_ALSA_DIRECTION_ANY;
2642 else {
2643 pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
2644 return -1;
2645 }
2646
2647 return 0;
2648 }
2649
2650 static int mapping_parse_description(
2651 const char *filename,
2652 unsigned line,
2653 const char *section,
2654 const char *lvalue,
2655 const char *rvalue,
2656 void *data,
2657 void *userdata) {
2658
2659 pa_alsa_profile_set *ps = userdata;
2660 pa_alsa_profile *p;
2661 pa_alsa_mapping *m;
2662
2663 pa_assert(ps);
2664
2665 if ((m = mapping_get(ps, section))) {
2666 pa_xstrdup(m->description);
2667 m->description = pa_xstrdup(rvalue);
2668 } else if ((p = profile_get(ps, section))) {
2669 pa_xfree(p->description);
2670 p->description = pa_xstrdup(rvalue);
2671 } else {
2672 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2673 return -1;
2674 }
2675
2676 return 0;
2677 }
2678
2679 static int mapping_parse_priority(
2680 const char *filename,
2681 unsigned line,
2682 const char *section,
2683 const char *lvalue,
2684 const char *rvalue,
2685 void *data,
2686 void *userdata) {
2687
2688 pa_alsa_profile_set *ps = userdata;
2689 pa_alsa_profile *p;
2690 pa_alsa_mapping *m;
2691 uint32_t prio;
2692
2693 pa_assert(ps);
2694
2695 if (pa_atou(rvalue, &prio) < 0) {
2696 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
2697 return -1;
2698 }
2699
2700 if ((m = mapping_get(ps, section)))
2701 m->priority = prio;
2702 else if ((p = profile_get(ps, section)))
2703 p->priority = prio;
2704 else {
2705 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2706 return -1;
2707 }
2708
2709 return 0;
2710 }
2711
2712 static int profile_parse_mappings(
2713 const char *filename,
2714 unsigned line,
2715 const char *section,
2716 const char *lvalue,
2717 const char *rvalue,
2718 void *data,
2719 void *userdata) {
2720
2721 pa_alsa_profile_set *ps = userdata;
2722 pa_alsa_profile *p;
2723
2724 pa_assert(ps);
2725
2726 if (!(p = profile_get(ps, section))) {
2727 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2728 return -1;
2729 }
2730
2731 if (pa_streq(lvalue, "input-mappings")) {
2732 pa_xstrfreev(p->input_mapping_names);
2733 p->input_mapping_names = pa_split_spaces_strv(rvalue);
2734 } else {
2735 pa_xstrfreev(p->output_mapping_names);
2736 p->output_mapping_names = pa_split_spaces_strv(rvalue);
2737 }
2738
2739 return 0;
2740 }
2741
2742 static int profile_parse_skip_probe(
2743 const char *filename,
2744 unsigned line,
2745 const char *section,
2746 const char *lvalue,
2747 const char *rvalue,
2748 void *data,
2749 void *userdata) {
2750
2751 pa_alsa_profile_set *ps = userdata;
2752 pa_alsa_profile *p;
2753 int b;
2754
2755 pa_assert(ps);
2756
2757 if (!(p = profile_get(ps, section))) {
2758 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2759 return -1;
2760 }
2761
2762 if ((b = pa_parse_boolean(rvalue)) < 0) {
2763 pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
2764 return -1;
2765 }
2766
2767 p->supported = b;
2768
2769 return 0;
2770 }
2771
2772 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
2773
2774 static const struct description_map well_known_descriptions[] = {
2775 { "analog-mono", N_("Analog Mono") },
2776 { "analog-stereo", N_("Analog Stereo") },
2777 { "analog-surround-21", N_("Analog Surround 2.1") },
2778 { "analog-surround-30", N_("Analog Surround 3.0") },
2779 { "analog-surround-31", N_("Analog Surround 3.1") },
2780 { "analog-surround-40", N_("Analog Surround 4.0") },
2781 { "analog-surround-41", N_("Analog Surround 4.1") },
2782 { "analog-surround-50", N_("Analog Surround 5.0") },
2783 { "analog-surround-51", N_("Analog Surround 5.1") },
2784 { "analog-surround-61", N_("Analog Surround 6.0") },
2785 { "analog-surround-61", N_("Analog Surround 6.1") },
2786 { "analog-surround-70", N_("Analog Surround 7.0") },
2787 { "analog-surround-71", N_("Analog Surround 7.1") },
2788 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
2789 { "iec958-surround-40", N_("Digital Surround 4.0 (IEC958)") },
2790 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
2791 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
2792 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
2793 };
2794
2795 pa_assert(m);
2796
2797 if (!pa_channel_map_valid(&m->channel_map)) {
2798 pa_log("Mapping %s is missing channel map.", m->name);
2799 return -1;
2800 }
2801
2802 if (!m->device_strings) {
2803 pa_log("Mapping %s is missing device strings.", m->name);
2804 return -1;
2805 }
2806
2807 if ((m->input_path_names && m->input_element) ||
2808 (m->output_path_names && m->output_element)) {
2809 pa_log("Mapping %s must have either mixer path or mixer elment, not both.", m->name);
2810 return -1;
2811 }
2812
2813 if (!m->description)
2814 m->description = pa_xstrdup(lookup_description(m->name,
2815 well_known_descriptions,
2816 PA_ELEMENTSOF(well_known_descriptions)));
2817
2818 if (!m->description)
2819 m->description = pa_xstrdup(m->name);
2820
2821 if (bonus) {
2822 if (pa_channel_map_equal(&m->channel_map, bonus))
2823 m->priority += 5000;
2824 else if (m->channel_map.channels == bonus->channels)
2825 m->priority += 4000;
2826 }
2827
2828 return 0;
2829 }
2830
2831 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
2832 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
2833
2834 pa_assert(m);
2835
2836 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
2837 m->name,
2838 pa_strnull(m->description),
2839 m->priority,
2840 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
2841 pa_yes_no(m->supported),
2842 m->direction);
2843 }
2844
2845 static void profile_set_add_auto_pair(
2846 pa_alsa_profile_set *ps,
2847 pa_alsa_mapping *m, /* output */
2848 pa_alsa_mapping *n /* input */) {
2849
2850 char *name;
2851 pa_alsa_profile *p;
2852
2853 pa_assert(ps);
2854 pa_assert(m || n);
2855
2856 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
2857 return;
2858
2859 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
2860 return;
2861
2862 if (m && n)
2863 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
2864 else if (m)
2865 name = pa_sprintf_malloc("output:%s", m->name);
2866 else
2867 name = pa_sprintf_malloc("input:%s", n->name);
2868
2869 if ((p = pa_hashmap_get(ps->profiles, name))) {
2870 pa_xfree(name);
2871 return;
2872 }
2873
2874 p = pa_xnew0(pa_alsa_profile, 1);
2875 p->profile_set = ps;
2876 p->name = name;
2877
2878 if (m) {
2879 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2880 pa_idxset_put(p->output_mappings, m, NULL);
2881 p->priority += m->priority * 100;
2882 }
2883
2884 if (n) {
2885 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2886 pa_idxset_put(p->input_mappings, n, NULL);
2887 p->priority += n->priority;
2888 }
2889
2890 pa_hashmap_put(ps->profiles, p->name, p);
2891 }
2892
2893 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
2894 pa_alsa_mapping *m, *n;
2895 void *m_state, *n_state;
2896
2897 pa_assert(ps);
2898
2899 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
2900 profile_set_add_auto_pair(ps, m, NULL);
2901
2902 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
2903 profile_set_add_auto_pair(ps, m, n);
2904 }
2905
2906 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
2907 profile_set_add_auto_pair(ps, NULL, n);
2908 }
2909
2910 static int profile_verify(pa_alsa_profile *p) {
2911
2912 static const struct description_map well_known_descriptions[] = {
2913 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
2914 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
2915 { "output:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
2916 { "off", N_("Off") }
2917 };
2918
2919 pa_assert(p);
2920
2921 /* Replace the output mapping names by the actual mappings */
2922 if (p->output_mapping_names) {
2923 char **name;
2924
2925 pa_assert(!p->output_mappings);
2926 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2927
2928 for (name = p->output_mapping_names; *name; name++) {
2929 pa_alsa_mapping *m;
2930 char **in;
2931 pa_bool_t duplicate = FALSE;
2932
2933 for (in = p->output_mapping_names; *in; in++)
2934 if (pa_streq(*name, *in)) {
2935 duplicate = TRUE;
2936 break;
2937 }
2938
2939 if (duplicate)
2940 continue;
2941
2942 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
2943 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
2944 return -1;
2945 }
2946
2947 pa_idxset_put(p->output_mappings, m, NULL);
2948 }
2949
2950 pa_xstrfreev(p->output_mapping_names);
2951 p->output_mapping_names = NULL;
2952 }
2953
2954 /* Replace the input mapping names by the actual mappings */
2955 if (p->input_mapping_names) {
2956 char **name;
2957
2958 pa_assert(!p->input_mappings);
2959 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2960
2961 for (name = p->input_mapping_names; *name; name++) {
2962 pa_alsa_mapping *m;
2963 char **in;
2964 pa_bool_t duplicate = FALSE;
2965
2966 for (in = p->input_mapping_names; *in; in++)
2967 if (pa_streq(*name, *in)) {
2968 duplicate = TRUE;
2969 break;
2970 }
2971
2972 if (duplicate)
2973 continue;
2974
2975 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
2976 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
2977 return -1;
2978 }
2979
2980 pa_idxset_put(p->input_mappings, m, NULL);
2981 }
2982
2983 pa_xstrfreev(p->input_mapping_names);
2984 p->input_mapping_names = NULL;
2985 }
2986
2987 if (!p->input_mappings && !p->output_mappings) {
2988 pa_log("Profile '%s' lacks mappings.", p->name);
2989 return -1;
2990 }
2991
2992 if (!p->description)
2993 p->description = pa_xstrdup(lookup_description(p->name,
2994 well_known_descriptions,
2995 PA_ELEMENTSOF(well_known_descriptions)));
2996
2997 if (!p->description) {
2998 pa_strbuf *sb;
2999 uint32_t idx;
3000 pa_alsa_mapping *m;
3001
3002 sb = pa_strbuf_new();
3003
3004 if (p->output_mappings)
3005 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3006 if (!pa_strbuf_isempty(sb))
3007 pa_strbuf_puts(sb, " + ");
3008
3009 pa_strbuf_printf(sb, "%s Output", m->description);
3010 }
3011
3012 if (p->input_mappings)
3013 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3014 if (!pa_strbuf_isempty(sb))
3015 pa_strbuf_puts(sb, " + ");
3016
3017 pa_strbuf_printf(sb, "%s Input", m->description);
3018 }
3019
3020 p->description = pa_strbuf_tostring_free(sb);
3021 }
3022
3023 return 0;
3024 }
3025
3026 void pa_alsa_profile_dump(pa_alsa_profile *p) {
3027 uint32_t idx;
3028 pa_alsa_mapping *m;
3029 pa_assert(p);
3030
3031 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3032 p->name,
3033 pa_strnull(p->description),
3034 p->priority,
3035 pa_yes_no(p->supported),
3036 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
3037 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
3038
3039 if (p->input_mappings)
3040 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
3041 pa_log_debug("Input %s", m->name);
3042
3043 if (p->output_mappings)
3044 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
3045 pa_log_debug("Output %s", m->name);
3046 }
3047
3048 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
3049 pa_alsa_profile_set *ps;
3050 pa_alsa_profile *p;
3051 pa_alsa_mapping *m;
3052 char *fn;
3053 int r;
3054 void *state;
3055
3056 static pa_config_item items[] = {
3057 /* [General] */
3058 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
3059
3060 /* [Mapping ...] */
3061 { "device-strings", mapping_parse_device_strings, NULL, NULL },
3062 { "channel-map", mapping_parse_channel_map, NULL, NULL },
3063 { "paths-input", mapping_parse_paths, NULL, NULL },
3064 { "paths-output", mapping_parse_paths, NULL, NULL },
3065 { "element-input", mapping_parse_element, NULL, NULL },
3066 { "element-output", mapping_parse_element, NULL, NULL },
3067 { "direction", mapping_parse_direction, NULL, NULL },
3068
3069 /* Shared by [Mapping ...] and [Profile ...] */
3070 { "description", mapping_parse_description, NULL, NULL },
3071 { "priority", mapping_parse_priority, NULL, NULL },
3072
3073 /* [Profile ...] */
3074 { "input-mappings", profile_parse_mappings, NULL, NULL },
3075 { "output-mappings", profile_parse_mappings, NULL, NULL },
3076 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
3077 { NULL, NULL, NULL, NULL }
3078 };
3079
3080 ps = pa_xnew0(pa_alsa_profile_set, 1);
3081 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3082 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3083
3084 items[0].data = &ps->auto_profiles;
3085
3086 if (!fname)
3087 fname = "default.conf";
3088
3089 fn = pa_maybe_prefix_path(fname, PA_ALSA_PROFILE_SETS_DIR);
3090 r = pa_config_parse(fn, NULL, items, ps);
3091 pa_xfree(fn);
3092
3093 if (r < 0)
3094 goto fail;
3095
3096 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3097 if (mapping_verify(m, bonus) < 0)
3098 goto fail;
3099
3100 if (ps->auto_profiles)
3101 profile_set_add_auto(ps);
3102
3103 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3104 if (profile_verify(p) < 0)
3105 goto fail;
3106
3107 return ps;
3108
3109 fail:
3110 pa_alsa_profile_set_free(ps);
3111 return NULL;
3112 }
3113
3114 void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, const pa_sample_spec *ss) {
3115 void *state;
3116 pa_alsa_profile *p, *last = NULL;
3117 pa_alsa_mapping *m;
3118
3119 pa_assert(ps);
3120 pa_assert(dev_id);
3121 pa_assert(ss);
3122
3123 if (ps->probed)
3124 return;
3125
3126 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
3127 pa_sample_spec try_ss;
3128 pa_channel_map try_map;
3129 uint32_t idx;
3130
3131 /* Is this already marked that it is supported? (i.e. from the config file) */
3132 if (p->supported)
3133 continue;
3134
3135 pa_log_debug("Looking at profile %s", p->name);
3136
3137 /* Close PCMs from the last iteration we don't need anymore */
3138 if (last && last->output_mappings)
3139 PA_IDXSET_FOREACH(m, last->output_mappings, idx) {
3140
3141 if (!m->output_pcm)
3142 break;
3143
3144 if (last->supported)
3145 m->supported++;
3146
3147 if (!p->output_mappings || !pa_idxset_get_by_data(p->output_mappings, m, NULL)) {
3148 snd_pcm_close(m->output_pcm);
3149 m->output_pcm = NULL;
3150 }
3151 }
3152
3153 if (last && last->input_mappings)
3154 PA_IDXSET_FOREACH(m, last->input_mappings, idx) {
3155
3156 if (!m->input_pcm)
3157 break;
3158
3159 if (last->supported)
3160 m->supported++;
3161
3162 if (!p->input_mappings || !pa_idxset_get_by_data(p->input_mappings, m, NULL)) {
3163 snd_pcm_close(m->input_pcm);
3164 m->input_pcm = NULL;
3165 }
3166 }
3167
3168 p->supported = TRUE;
3169
3170 /* Check if we can open all new ones */
3171 if (p->output_mappings)
3172 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3173
3174 if (m->output_pcm)
3175 continue;
3176
3177 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
3178 try_map = m->channel_map;
3179 try_ss = *ss;
3180 try_ss.channels = try_map.channels;
3181
3182 if (!(m ->output_pcm = pa_alsa_open_by_device_string_strv(
3183 m->device_strings,
3184 dev_id,
3185 NULL,
3186 &try_ss, &try_map,
3187 SND_PCM_STREAM_PLAYBACK,
3188 NULL, NULL, 0, NULL, NULL,
3189 TRUE))) {
3190 p->supported = FALSE;
3191 break;
3192 }
3193 }
3194
3195 if (p->input_mappings && p->supported)
3196 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3197
3198 if (m->input_pcm)
3199 continue;
3200
3201 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
3202 try_map = m->channel_map;
3203 try_ss = *ss;
3204 try_ss.channels = try_map.channels;
3205
3206 if (!(m ->input_pcm = pa_alsa_open_by_device_string_strv(
3207 m->device_strings,
3208 dev_id,
3209 NULL,
3210 &try_ss, &try_map,
3211 SND_PCM_STREAM_CAPTURE,
3212 NULL, NULL, 0, NULL, NULL,
3213 TRUE))) {
3214 p->supported = FALSE;
3215 break;
3216 }
3217 }
3218
3219 last = p;
3220
3221 if (p->supported)
3222 pa_log_debug("Profile %s supported.", p->name);
3223 }
3224
3225 /* Clean up */
3226 if (last) {
3227 uint32_t idx;
3228
3229 if (last->output_mappings)
3230 PA_IDXSET_FOREACH(m, last->output_mappings, idx)
3231 if (m->output_pcm) {
3232
3233 if (last->supported)
3234 m->supported++;
3235
3236 snd_pcm_close(m->output_pcm);
3237 m->output_pcm = NULL;
3238 }
3239
3240 if (last->input_mappings)
3241 PA_IDXSET_FOREACH(m, last->input_mappings, idx)
3242 if (m->input_pcm) {
3243
3244 if (last->supported)
3245 m->supported++;
3246
3247 snd_pcm_close(m->input_pcm);
3248 m->input_pcm = NULL;
3249 }
3250 }
3251
3252 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3253 if (!p->supported) {
3254 pa_hashmap_remove(ps->profiles, p->name);
3255 profile_free(p);
3256 }
3257
3258 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3259 if (m->supported <= 0) {
3260 pa_hashmap_remove(ps->mappings, m->name);
3261 mapping_free(m);
3262 }
3263
3264 ps->probed = TRUE;
3265 }
3266
3267 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
3268 pa_alsa_profile *p;
3269 pa_alsa_mapping *m;
3270 void *state;
3271
3272 pa_assert(ps);
3273
3274 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u",
3275 (void*)
3276 ps,
3277 pa_yes_no(ps->auto_profiles),
3278 pa_yes_no(ps->probed),
3279 pa_hashmap_size(ps->mappings),
3280 pa_hashmap_size(ps->profiles));
3281
3282 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3283 pa_alsa_mapping_dump(m);
3284
3285 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3286 pa_alsa_profile_dump(p);
3287 }
3288
3289 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps) {
3290 pa_alsa_path *path;
3291
3292 pa_assert(p);
3293 pa_assert(!*p);
3294 pa_assert(ps);
3295
3296 /* if there is no path, we don't want a port list */
3297 if (!ps->paths)
3298 return;
3299
3300 if (!ps->paths->next){
3301 pa_alsa_setting *s;
3302
3303 /* If there is only one path, but no or only one setting, then
3304 * we want a port list either */
3305 if (!ps->paths->settings || !ps->paths->settings->next)
3306 return;
3307
3308 /* Ok, there is only one path, however with multiple settings,
3309 * so let's create a port for each setting */
3310 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3311
3312 PA_LLIST_FOREACH(s, ps->paths->settings) {
3313 pa_device_port *port;
3314 pa_alsa_port_data *data;
3315
3316 port = pa_device_port_new(s->name, s->description, sizeof(pa_alsa_port_data));
3317 port->priority = s->priority;
3318
3319 data = PA_DEVICE_PORT_DATA(port);
3320 data->path = ps->paths;
3321 data->setting = s;
3322
3323 pa_hashmap_put(*p, port->name, port);
3324 }
3325
3326 } else {
3327
3328 /* We have multiple paths, so let's create a port for each
3329 * one, and each of each settings */
3330 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3331
3332 PA_LLIST_FOREACH(path, ps->paths) {
3333
3334 if (!path->settings || !path->settings->next) {
3335 pa_device_port *port;
3336 pa_alsa_port_data *data;
3337
3338 /* If there is no or just one setting we only need a
3339 * single entry */
3340
3341 port = pa_device_port_new(path->name, path->description, sizeof(pa_alsa_port_data));
3342 port->priority = path->priority * 100;
3343
3344
3345 data = PA_DEVICE_PORT_DATA(port);
3346 data->path = path;
3347 data->setting = path->settings;
3348
3349 pa_hashmap_put(*p, port->name, port);
3350 } else {
3351 pa_alsa_setting *s;
3352
3353 PA_LLIST_FOREACH(s, path->settings) {
3354 pa_device_port *port;
3355 pa_alsa_port_data *data;
3356 char *n, *d;
3357
3358 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
3359
3360 if (s->description[0])
3361 d = pa_sprintf_malloc(_("%s / %s"), path->description, s->description);
3362 else
3363 d = pa_xstrdup(path->description);
3364
3365 port = pa_device_port_new(n, d, sizeof(pa_alsa_port_data));
3366 port->priority = path->priority * 100 + s->priority;
3367
3368 pa_xfree(n);
3369 pa_xfree(d);
3370
3371 data = PA_DEVICE_PORT_DATA(port);
3372 data->path = path;
3373 data->setting = s;
3374
3375 pa_hashmap_put(*p, port->name, port);
3376 }
3377 }
3378 }
3379 }
3380
3381 pa_log_debug("Added %u ports", pa_hashmap_size(*p));
3382 }