]> code.delx.au - pulseaudio/blob - src/modules/alsa/alsa-mixer.c
alsa-mixer: Add DecibelFix section to the profile set config file format.
[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 struct pa_alsa_mixer_pdata {
247 pa_rtpoll *rtpoll;
248 pa_rtpoll_item *poll_item;
249 snd_mixer_t *mixer;
250 };
251
252
253 struct pa_alsa_mixer_pdata *pa_alsa_mixer_pdata_new(void) {
254 struct pa_alsa_mixer_pdata *pd;
255
256 pd = pa_xnew0(struct pa_alsa_mixer_pdata, 1);
257
258 return pd;
259 }
260
261 void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata *pd) {
262 pa_assert(pd);
263
264 if (pd->poll_item) {
265 pa_rtpoll_item_free(pd->poll_item);
266 }
267
268 pa_xfree(pd);
269 }
270
271 static int rtpoll_work_cb(pa_rtpoll_item *i) {
272 struct pa_alsa_mixer_pdata *pd;
273 struct pollfd *p;
274 unsigned n_fds;
275 unsigned short revents = 0;
276 int err;
277
278 pd = pa_rtpoll_item_get_userdata(i);
279 pa_assert_fp(pd);
280 pa_assert_fp(i == pd->poll_item);
281
282 p = pa_rtpoll_item_get_pollfd(i, &n_fds);
283
284 if ((err = snd_mixer_poll_descriptors_revents(pd->mixer, p, n_fds, &revents)) < 0) {
285 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
286 pa_rtpoll_item_free(i);
287 return -1;
288 }
289
290 if (revents) {
291 snd_mixer_handle_events(pd->mixer);
292 pa_rtpoll_item_free(i);
293 pa_alsa_set_mixer_rtpoll(pd, pd->mixer, pd->rtpoll);
294 }
295
296 return 0;
297 }
298
299 int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata *pd, snd_mixer_t *mixer, pa_rtpoll *rtp) {
300 pa_rtpoll_item *i;
301 struct pollfd *p;
302 int err, n;
303
304 pa_assert(pd);
305 pa_assert(mixer);
306 pa_assert(rtp);
307
308 if ((n = snd_mixer_poll_descriptors_count(mixer)) < 0) {
309 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
310 return -1;
311 }
312
313 i = pa_rtpoll_item_new(rtp, PA_RTPOLL_LATE, (unsigned) n);
314
315 p = pa_rtpoll_item_get_pollfd(i, NULL);
316
317 memset(p, 0, sizeof(struct pollfd) * n);
318
319 if ((err = snd_mixer_poll_descriptors(mixer, p, (unsigned) n)) < 0) {
320 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
321 pa_rtpoll_item_free(i);
322 return -1;
323 }
324
325 pd->rtpoll = rtp;
326 pd->poll_item = i;
327 pd->mixer = mixer;
328
329 pa_rtpoll_item_set_userdata(i, pd);
330 pa_rtpoll_item_set_work_callback(i, rtpoll_work_cb);
331
332 return 0;
333 }
334
335 static int prepare_mixer(snd_mixer_t *mixer, const char *dev) {
336 int err;
337
338 pa_assert(mixer);
339 pa_assert(dev);
340
341 if ((err = snd_mixer_attach(mixer, dev)) < 0) {
342 pa_log_info("Unable to attach to mixer %s: %s", dev, pa_alsa_strerror(err));
343 return -1;
344 }
345
346 if ((err = snd_mixer_selem_register(mixer, NULL, NULL)) < 0) {
347 pa_log_warn("Unable to register mixer: %s", pa_alsa_strerror(err));
348 return -1;
349 }
350
351 if ((err = snd_mixer_load(mixer)) < 0) {
352 pa_log_warn("Unable to load mixer: %s", pa_alsa_strerror(err));
353 return -1;
354 }
355
356 pa_log_info("Successfully attached to mixer '%s'", dev);
357 return 0;
358 }
359
360 snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device) {
361 int err;
362 snd_mixer_t *m;
363 const char *dev;
364 snd_pcm_info_t* info;
365 snd_pcm_info_alloca(&info);
366
367 pa_assert(pcm);
368
369 if ((err = snd_mixer_open(&m, 0)) < 0) {
370 pa_log("Error opening mixer: %s", pa_alsa_strerror(err));
371 return NULL;
372 }
373
374 /* First, try by name */
375 if ((dev = snd_pcm_name(pcm)))
376 if (prepare_mixer(m, dev) >= 0) {
377 if (ctl_device)
378 *ctl_device = pa_xstrdup(dev);
379
380 return m;
381 }
382
383 /* Then, try by card index */
384 if (snd_pcm_info(pcm, info) >= 0) {
385 char *md;
386 int card_idx;
387
388 if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
389
390 md = pa_sprintf_malloc("hw:%i", card_idx);
391
392 if (!dev || !pa_streq(dev, md))
393 if (prepare_mixer(m, md) >= 0) {
394
395 if (ctl_device)
396 *ctl_device = md;
397 else
398 pa_xfree(md);
399
400 return m;
401 }
402
403 pa_xfree(md);
404 }
405 }
406
407 snd_mixer_close(m);
408 return NULL;
409 }
410
411 static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_MAX] = {
412 [PA_CHANNEL_POSITION_MONO] = SND_MIXER_SCHN_MONO, /* The ALSA name is just an alias! */
413
414 [PA_CHANNEL_POSITION_FRONT_CENTER] = SND_MIXER_SCHN_FRONT_CENTER,
415 [PA_CHANNEL_POSITION_FRONT_LEFT] = SND_MIXER_SCHN_FRONT_LEFT,
416 [PA_CHANNEL_POSITION_FRONT_RIGHT] = SND_MIXER_SCHN_FRONT_RIGHT,
417
418 [PA_CHANNEL_POSITION_REAR_CENTER] = SND_MIXER_SCHN_REAR_CENTER,
419 [PA_CHANNEL_POSITION_REAR_LEFT] = SND_MIXER_SCHN_REAR_LEFT,
420 [PA_CHANNEL_POSITION_REAR_RIGHT] = SND_MIXER_SCHN_REAR_RIGHT,
421
422 [PA_CHANNEL_POSITION_LFE] = SND_MIXER_SCHN_WOOFER,
423
424 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
425 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
426
427 [PA_CHANNEL_POSITION_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT,
428 [PA_CHANNEL_POSITION_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT,
429
430 [PA_CHANNEL_POSITION_AUX0] = SND_MIXER_SCHN_UNKNOWN,
431 [PA_CHANNEL_POSITION_AUX1] = SND_MIXER_SCHN_UNKNOWN,
432 [PA_CHANNEL_POSITION_AUX2] = SND_MIXER_SCHN_UNKNOWN,
433 [PA_CHANNEL_POSITION_AUX3] = SND_MIXER_SCHN_UNKNOWN,
434 [PA_CHANNEL_POSITION_AUX4] = SND_MIXER_SCHN_UNKNOWN,
435 [PA_CHANNEL_POSITION_AUX5] = SND_MIXER_SCHN_UNKNOWN,
436 [PA_CHANNEL_POSITION_AUX6] = SND_MIXER_SCHN_UNKNOWN,
437 [PA_CHANNEL_POSITION_AUX7] = SND_MIXER_SCHN_UNKNOWN,
438 [PA_CHANNEL_POSITION_AUX8] = SND_MIXER_SCHN_UNKNOWN,
439 [PA_CHANNEL_POSITION_AUX9] = SND_MIXER_SCHN_UNKNOWN,
440 [PA_CHANNEL_POSITION_AUX10] = SND_MIXER_SCHN_UNKNOWN,
441 [PA_CHANNEL_POSITION_AUX11] = SND_MIXER_SCHN_UNKNOWN,
442 [PA_CHANNEL_POSITION_AUX12] = SND_MIXER_SCHN_UNKNOWN,
443 [PA_CHANNEL_POSITION_AUX13] = SND_MIXER_SCHN_UNKNOWN,
444 [PA_CHANNEL_POSITION_AUX14] = SND_MIXER_SCHN_UNKNOWN,
445 [PA_CHANNEL_POSITION_AUX15] = SND_MIXER_SCHN_UNKNOWN,
446 [PA_CHANNEL_POSITION_AUX16] = SND_MIXER_SCHN_UNKNOWN,
447 [PA_CHANNEL_POSITION_AUX17] = SND_MIXER_SCHN_UNKNOWN,
448 [PA_CHANNEL_POSITION_AUX18] = SND_MIXER_SCHN_UNKNOWN,
449 [PA_CHANNEL_POSITION_AUX19] = SND_MIXER_SCHN_UNKNOWN,
450 [PA_CHANNEL_POSITION_AUX20] = SND_MIXER_SCHN_UNKNOWN,
451 [PA_CHANNEL_POSITION_AUX21] = SND_MIXER_SCHN_UNKNOWN,
452 [PA_CHANNEL_POSITION_AUX22] = SND_MIXER_SCHN_UNKNOWN,
453 [PA_CHANNEL_POSITION_AUX23] = SND_MIXER_SCHN_UNKNOWN,
454 [PA_CHANNEL_POSITION_AUX24] = SND_MIXER_SCHN_UNKNOWN,
455 [PA_CHANNEL_POSITION_AUX25] = SND_MIXER_SCHN_UNKNOWN,
456 [PA_CHANNEL_POSITION_AUX26] = SND_MIXER_SCHN_UNKNOWN,
457 [PA_CHANNEL_POSITION_AUX27] = SND_MIXER_SCHN_UNKNOWN,
458 [PA_CHANNEL_POSITION_AUX28] = SND_MIXER_SCHN_UNKNOWN,
459 [PA_CHANNEL_POSITION_AUX29] = SND_MIXER_SCHN_UNKNOWN,
460 [PA_CHANNEL_POSITION_AUX30] = SND_MIXER_SCHN_UNKNOWN,
461 [PA_CHANNEL_POSITION_AUX31] = SND_MIXER_SCHN_UNKNOWN,
462
463 [PA_CHANNEL_POSITION_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN,
464
465 [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SND_MIXER_SCHN_UNKNOWN,
466 [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SND_MIXER_SCHN_UNKNOWN,
467 [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SND_MIXER_SCHN_UNKNOWN,
468
469 [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SND_MIXER_SCHN_UNKNOWN,
470 [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SND_MIXER_SCHN_UNKNOWN,
471 [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SND_MIXER_SCHN_UNKNOWN
472 };
473
474 static void setting_free(pa_alsa_setting *s) {
475 pa_assert(s);
476
477 if (s->options)
478 pa_idxset_free(s->options, NULL, NULL);
479
480 pa_xfree(s->name);
481 pa_xfree(s->description);
482 pa_xfree(s);
483 }
484
485 static void option_free(pa_alsa_option *o) {
486 pa_assert(o);
487
488 pa_xfree(o->alsa_name);
489 pa_xfree(o->name);
490 pa_xfree(o->description);
491 pa_xfree(o);
492 }
493
494 static void element_free(pa_alsa_element *e) {
495 pa_alsa_option *o;
496 pa_assert(e);
497
498 while ((o = e->options)) {
499 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
500 option_free(o);
501 }
502
503 pa_xfree(e->alsa_name);
504 pa_xfree(e);
505 }
506
507 void pa_alsa_path_free(pa_alsa_path *p) {
508 pa_alsa_element *e;
509 pa_alsa_setting *s;
510
511 pa_assert(p);
512
513 while ((e = p->elements)) {
514 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
515 element_free(e);
516 }
517
518 while ((s = p->settings)) {
519 PA_LLIST_REMOVE(pa_alsa_setting, p->settings, s);
520 setting_free(s);
521 }
522
523 pa_xfree(p->name);
524 pa_xfree(p->description);
525 pa_xfree(p);
526 }
527
528 void pa_alsa_path_set_free(pa_alsa_path_set *ps) {
529 pa_alsa_path *p;
530 pa_assert(ps);
531
532 while ((p = ps->paths)) {
533 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
534 pa_alsa_path_free(p);
535 }
536
537 pa_xfree(ps);
538 }
539
540 static long to_alsa_dB(pa_volume_t v) {
541 return (long) (pa_sw_volume_to_dB(v) * 100.0);
542 }
543
544 static pa_volume_t from_alsa_dB(long v) {
545 return pa_sw_volume_from_dB((double) v / 100.0);
546 }
547
548 static long to_alsa_volume(pa_volume_t v, long min, long max) {
549 long w;
550
551 w = (long) round(((double) v * (double) (max - min)) / PA_VOLUME_NORM) + min;
552 return PA_CLAMP_UNLIKELY(w, min, max);
553 }
554
555 static pa_volume_t from_alsa_volume(long v, long min, long max) {
556 return (pa_volume_t) round(((double) (v - min) * PA_VOLUME_NORM) / (double) (max - min));
557 }
558
559 #define SELEM_INIT(sid, name) \
560 do { \
561 snd_mixer_selem_id_alloca(&(sid)); \
562 snd_mixer_selem_id_set_name((sid), (name)); \
563 snd_mixer_selem_id_set_index((sid), 0); \
564 } while(FALSE)
565
566 static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
567 snd_mixer_selem_id_t *sid;
568 snd_mixer_elem_t *me;
569 snd_mixer_selem_channel_id_t c;
570 pa_channel_position_mask_t mask = 0;
571 unsigned k;
572
573 pa_assert(m);
574 pa_assert(e);
575 pa_assert(cm);
576 pa_assert(v);
577
578 SELEM_INIT(sid, e->alsa_name);
579 if (!(me = snd_mixer_find_selem(m, sid))) {
580 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
581 return -1;
582 }
583
584 pa_cvolume_mute(v, cm->channels);
585
586 /* We take the highest volume of all channels that match */
587
588 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
589 int r;
590 pa_volume_t f;
591
592 if (e->has_dB) {
593 long value = 0;
594
595 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
596 if (snd_mixer_selem_has_playback_channel(me, c))
597 r = snd_mixer_selem_get_playback_dB(me, c, &value);
598 else
599 r = -1;
600 } else {
601 if (snd_mixer_selem_has_capture_channel(me, c))
602 r = snd_mixer_selem_get_capture_dB(me, c, &value);
603 else
604 r = -1;
605 }
606
607 if (r < 0)
608 continue;
609
610 #ifdef HAVE_VALGRIND_MEMCHECK_H
611 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
612 #endif
613
614 f = from_alsa_dB(value);
615
616 } else {
617 long value = 0;
618
619 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
620 if (snd_mixer_selem_has_playback_channel(me, c))
621 r = snd_mixer_selem_get_playback_volume(me, c, &value);
622 else
623 r = -1;
624 } else {
625 if (snd_mixer_selem_has_capture_channel(me, c))
626 r = snd_mixer_selem_get_capture_volume(me, c, &value);
627 else
628 r = -1;
629 }
630
631 if (r < 0)
632 continue;
633
634 f = from_alsa_volume(value, e->min_volume, e->max_volume);
635 }
636
637 for (k = 0; k < cm->channels; k++)
638 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
639 if (v->values[k] < f)
640 v->values[k] = f;
641
642 mask |= e->masks[c][e->n_channels-1];
643 }
644
645 for (k = 0; k < cm->channels; k++)
646 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
647 v->values[k] = PA_VOLUME_NORM;
648
649 return 0;
650 }
651
652 int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
653 pa_alsa_element *e;
654
655 pa_assert(m);
656 pa_assert(p);
657 pa_assert(cm);
658 pa_assert(v);
659
660 if (!p->has_volume)
661 return -1;
662
663 pa_cvolume_reset(v, cm->channels);
664
665 PA_LLIST_FOREACH(e, p->elements) {
666 pa_cvolume ev;
667
668 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
669 continue;
670
671 pa_assert(!p->has_dB || e->has_dB);
672
673 if (element_get_volume(e, m, cm, &ev) < 0)
674 return -1;
675
676 /* If we have no dB information all we can do is take the first element and leave */
677 if (!p->has_dB) {
678 *v = ev;
679 return 0;
680 }
681
682 pa_sw_cvolume_multiply(v, v, &ev);
683 }
684
685 return 0;
686 }
687
688 static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t *b) {
689 snd_mixer_selem_id_t *sid;
690 snd_mixer_elem_t *me;
691 snd_mixer_selem_channel_id_t c;
692
693 pa_assert(m);
694 pa_assert(e);
695 pa_assert(b);
696
697 SELEM_INIT(sid, e->alsa_name);
698 if (!(me = snd_mixer_find_selem(m, sid))) {
699 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
700 return -1;
701 }
702
703 /* We return muted if at least one channel is muted */
704
705 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
706 int r;
707 int value = 0;
708
709 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
710 if (snd_mixer_selem_has_playback_channel(me, c))
711 r = snd_mixer_selem_get_playback_switch(me, c, &value);
712 else
713 r = -1;
714 } else {
715 if (snd_mixer_selem_has_capture_channel(me, c))
716 r = snd_mixer_selem_get_capture_switch(me, c, &value);
717 else
718 r = -1;
719 }
720
721 if (r < 0)
722 continue;
723
724 if (!value) {
725 *b = FALSE;
726 return 0;
727 }
728 }
729
730 *b = TRUE;
731 return 0;
732 }
733
734 int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t *muted) {
735 pa_alsa_element *e;
736
737 pa_assert(m);
738 pa_assert(p);
739 pa_assert(muted);
740
741 if (!p->has_mute)
742 return -1;
743
744 PA_LLIST_FOREACH(e, p->elements) {
745 pa_bool_t b;
746
747 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
748 continue;
749
750 if (element_get_switch(e, m, &b) < 0)
751 return -1;
752
753 if (!b) {
754 *muted = TRUE;
755 return 0;
756 }
757 }
758
759 *muted = FALSE;
760 return 0;
761 }
762
763 static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, pa_bool_t write_to_hw) {
764
765 snd_mixer_selem_id_t *sid;
766 pa_cvolume rv;
767 snd_mixer_elem_t *me;
768 snd_mixer_selem_channel_id_t c;
769 pa_channel_position_mask_t mask = 0;
770 unsigned k;
771
772 pa_assert(m);
773 pa_assert(e);
774 pa_assert(cm);
775 pa_assert(v);
776 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
777
778 SELEM_INIT(sid, e->alsa_name);
779 if (!(me = snd_mixer_find_selem(m, sid))) {
780 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
781 return -1;
782 }
783
784 pa_cvolume_mute(&rv, cm->channels);
785
786 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
787 int r;
788 pa_volume_t f = PA_VOLUME_MUTED;
789 pa_bool_t found = FALSE;
790
791 for (k = 0; k < cm->channels; k++)
792 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) {
793 found = TRUE;
794 if (v->values[k] > f)
795 f = v->values[k];
796 }
797
798 if (!found) {
799 /* Hmm, so this channel does not exist in the volume
800 * struct, so let's bind it to the overall max of the
801 * volume. */
802 f = pa_cvolume_max(v);
803 }
804
805 if (e->has_dB) {
806 long value = to_alsa_dB(f);
807 int rounding = value > 0 ? -1 : +1;
808
809 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
810 /* If we call set_play_volume() without checking first
811 * if the channel is available, ALSA behaves ver
812 * strangely and doesn't fail the call */
813 if (snd_mixer_selem_has_playback_channel(me, c)) {
814 if (write_to_hw) {
815 if ((r = snd_mixer_selem_set_playback_dB(me, c, value, rounding)) >= 0)
816 r = snd_mixer_selem_get_playback_dB(me, c, &value);
817 } else {
818 long alsa_val;
819 if ((r = snd_mixer_selem_ask_playback_dB_vol(me, value, rounding, &alsa_val)) >= 0)
820 r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value);
821 }
822 } else
823 r = -1;
824 } else {
825 if (snd_mixer_selem_has_capture_channel(me, c)) {
826 if (write_to_hw) {
827 if ((r = snd_mixer_selem_set_capture_dB(me, c, value, rounding)) >= 0)
828 r = snd_mixer_selem_get_capture_dB(me, c, &value);
829 } else {
830 long alsa_val;
831 if ((r = snd_mixer_selem_ask_capture_dB_vol(me, value, rounding, &alsa_val)) >= 0)
832 r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value);
833 }
834 } else
835 r = -1;
836 }
837
838 if (r < 0)
839 continue;
840
841 #ifdef HAVE_VALGRIND_MEMCHECK_H
842 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
843 #endif
844
845 f = from_alsa_dB(value);
846
847 } else {
848 long value;
849
850 value = to_alsa_volume(f, e->min_volume, e->max_volume);
851
852 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
853 if (snd_mixer_selem_has_playback_channel(me, c)) {
854 if ((r = snd_mixer_selem_set_playback_volume(me, c, value)) >= 0)
855 r = snd_mixer_selem_get_playback_volume(me, c, &value);
856 } else
857 r = -1;
858 } else {
859 if (snd_mixer_selem_has_capture_channel(me, c)) {
860 if ((r = snd_mixer_selem_set_capture_volume(me, c, value)) >= 0)
861 r = snd_mixer_selem_get_capture_volume(me, c, &value);
862 } else
863 r = -1;
864 }
865
866 if (r < 0)
867 continue;
868
869 f = from_alsa_volume(value, e->min_volume, e->max_volume);
870 }
871
872 for (k = 0; k < cm->channels; k++)
873 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
874 if (rv.values[k] < f)
875 rv.values[k] = f;
876
877 mask |= e->masks[c][e->n_channels-1];
878 }
879
880 for (k = 0; k < cm->channels; k++)
881 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
882 rv.values[k] = PA_VOLUME_NORM;
883
884 *v = rv;
885 return 0;
886 }
887
888 int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, pa_bool_t write_to_hw) {
889
890 pa_alsa_element *e;
891 pa_cvolume rv;
892
893 pa_assert(m);
894 pa_assert(p);
895 pa_assert(cm);
896 pa_assert(v);
897 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
898
899 if (!p->has_volume)
900 return -1;
901
902 rv = *v; /* Remaining adjustment */
903 pa_cvolume_reset(v, cm->channels); /* Adjustment done */
904
905 PA_LLIST_FOREACH(e, p->elements) {
906 pa_cvolume ev;
907
908 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
909 continue;
910
911 pa_assert(!p->has_dB || e->has_dB);
912
913 ev = rv;
914 if (element_set_volume(e, m, cm, &ev, write_to_hw) < 0)
915 return -1;
916
917 if (!p->has_dB) {
918 *v = ev;
919 return 0;
920 }
921
922 pa_sw_cvolume_multiply(v, v, &ev);
923 pa_sw_cvolume_divide(&rv, &rv, &ev);
924 }
925
926 return 0;
927 }
928
929 static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t b) {
930 snd_mixer_elem_t *me;
931 snd_mixer_selem_id_t *sid;
932 int r;
933
934 pa_assert(m);
935 pa_assert(e);
936
937 SELEM_INIT(sid, e->alsa_name);
938 if (!(me = snd_mixer_find_selem(m, sid))) {
939 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
940 return -1;
941 }
942
943 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
944 r = snd_mixer_selem_set_playback_switch_all(me, b);
945 else
946 r = snd_mixer_selem_set_capture_switch_all(me, b);
947
948 if (r < 0)
949 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
950
951 return r;
952 }
953
954 int pa_alsa_path_set_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t muted) {
955 pa_alsa_element *e;
956
957 pa_assert(m);
958 pa_assert(p);
959
960 if (!p->has_mute)
961 return -1;
962
963 PA_LLIST_FOREACH(e, p->elements) {
964
965 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
966 continue;
967
968 if (element_set_switch(e, m, !muted) < 0)
969 return -1;
970 }
971
972 return 0;
973 }
974
975 static int element_mute_volume(pa_alsa_element *e, snd_mixer_t *m) {
976 snd_mixer_elem_t *me;
977 snd_mixer_selem_id_t *sid;
978 int r;
979
980 pa_assert(m);
981 pa_assert(e);
982
983 SELEM_INIT(sid, e->alsa_name);
984 if (!(me = snd_mixer_find_selem(m, sid))) {
985 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
986 return -1;
987 }
988
989 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
990 r = snd_mixer_selem_set_playback_volume_all(me, e->min_volume);
991 else
992 r = snd_mixer_selem_set_capture_volume_all(me, e->min_volume);
993
994 if (r < 0)
995 pa_log_warn("Failed to set volume to muted of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
996
997 return r;
998 }
999
1000 /* The volume to 0dB */
1001 static int element_zero_volume(pa_alsa_element *e, snd_mixer_t *m) {
1002 snd_mixer_elem_t *me;
1003 snd_mixer_selem_id_t *sid;
1004 int r;
1005
1006 pa_assert(m);
1007 pa_assert(e);
1008
1009 SELEM_INIT(sid, e->alsa_name);
1010 if (!(me = snd_mixer_find_selem(m, sid))) {
1011 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1012 return -1;
1013 }
1014
1015 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1016 r = snd_mixer_selem_set_playback_dB_all(me, 0, +1);
1017 else
1018 r = snd_mixer_selem_set_capture_dB_all(me, 0, +1);
1019
1020 if (r < 0)
1021 pa_log_warn("Failed to set volume to 0dB of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1022
1023 return r;
1024 }
1025
1026 int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
1027 pa_alsa_element *e;
1028 int r = 0;
1029
1030 pa_assert(m);
1031 pa_assert(p);
1032
1033 pa_log_debug("Activating path %s", p->name);
1034 pa_alsa_path_dump(p);
1035
1036 PA_LLIST_FOREACH(e, p->elements) {
1037
1038 switch (e->switch_use) {
1039 case PA_ALSA_SWITCH_OFF:
1040 r = element_set_switch(e, m, FALSE);
1041 break;
1042
1043 case PA_ALSA_SWITCH_ON:
1044 r = element_set_switch(e, m, TRUE);
1045 break;
1046
1047 case PA_ALSA_SWITCH_MUTE:
1048 case PA_ALSA_SWITCH_IGNORE:
1049 case PA_ALSA_SWITCH_SELECT:
1050 r = 0;
1051 break;
1052 }
1053
1054 if (r < 0)
1055 return -1;
1056
1057 switch (e->volume_use) {
1058 case PA_ALSA_VOLUME_OFF:
1059 r = element_mute_volume(e, m);
1060 break;
1061
1062 case PA_ALSA_VOLUME_ZERO:
1063 r = element_zero_volume(e, m);
1064 break;
1065
1066 case PA_ALSA_VOLUME_MERGE:
1067 case PA_ALSA_VOLUME_IGNORE:
1068 r = 0;
1069 break;
1070 }
1071
1072 if (r < 0)
1073 return -1;
1074 }
1075
1076 return 0;
1077 }
1078
1079 static int check_required(pa_alsa_element *e, snd_mixer_elem_t *me) {
1080 pa_bool_t has_switch;
1081 pa_bool_t has_enumeration;
1082 pa_bool_t has_volume;
1083
1084 pa_assert(e);
1085 pa_assert(me);
1086
1087 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1088 has_switch =
1089 snd_mixer_selem_has_playback_switch(me) ||
1090 (e->direction_try_other && snd_mixer_selem_has_capture_switch(me));
1091 } else {
1092 has_switch =
1093 snd_mixer_selem_has_capture_switch(me) ||
1094 (e->direction_try_other && snd_mixer_selem_has_playback_switch(me));
1095 }
1096
1097 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1098 has_volume =
1099 snd_mixer_selem_has_playback_volume(me) ||
1100 (e->direction_try_other && snd_mixer_selem_has_capture_volume(me));
1101 } else {
1102 has_volume =
1103 snd_mixer_selem_has_capture_volume(me) ||
1104 (e->direction_try_other && snd_mixer_selem_has_playback_volume(me));
1105 }
1106
1107 has_enumeration = snd_mixer_selem_is_enumerated(me);
1108
1109 if ((e->required == PA_ALSA_REQUIRED_SWITCH && !has_switch) ||
1110 (e->required == PA_ALSA_REQUIRED_VOLUME && !has_volume) ||
1111 (e->required == PA_ALSA_REQUIRED_ENUMERATION && !has_enumeration))
1112 return -1;
1113
1114 if (e->required == PA_ALSA_REQUIRED_ANY && !(has_switch || has_volume || has_enumeration))
1115 return -1;
1116
1117 if ((e->required_absent == PA_ALSA_REQUIRED_SWITCH && has_switch) ||
1118 (e->required_absent == PA_ALSA_REQUIRED_VOLUME && has_volume) ||
1119 (e->required_absent == PA_ALSA_REQUIRED_ENUMERATION && has_enumeration))
1120 return -1;
1121
1122 if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration))
1123 return -1;
1124
1125 if (e->required_any != PA_ALSA_REQUIRED_IGNORE) {
1126 switch (e->required_any) {
1127 case PA_ALSA_REQUIRED_VOLUME:
1128 e->path->req_any_present |= (e->volume_use != PA_ALSA_VOLUME_IGNORE);
1129 break;
1130 case PA_ALSA_REQUIRED_SWITCH:
1131 e->path->req_any_present |= (e->switch_use != PA_ALSA_SWITCH_IGNORE);
1132 break;
1133 case PA_ALSA_REQUIRED_ENUMERATION:
1134 e->path->req_any_present |= (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
1135 break;
1136 case PA_ALSA_REQUIRED_ANY:
1137 e->path->req_any_present |=
1138 (e->volume_use != PA_ALSA_VOLUME_IGNORE) ||
1139 (e->switch_use != PA_ALSA_SWITCH_IGNORE) ||
1140 (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
1141 break;
1142 }
1143 }
1144
1145 if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1146 pa_alsa_option *o;
1147 PA_LLIST_FOREACH(o, e->options) {
1148 e->path->req_any_present |= (o->required_any != PA_ALSA_REQUIRED_IGNORE) &&
1149 (o->alsa_idx >= 0);
1150 if (o->required != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx < 0)
1151 return -1;
1152 if (o->required_absent != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx >= 0)
1153 return -1;
1154 }
1155 }
1156
1157 return 0;
1158 }
1159
1160 static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
1161 snd_mixer_selem_id_t *sid;
1162 snd_mixer_elem_t *me;
1163
1164 pa_assert(m);
1165 pa_assert(e);
1166
1167 SELEM_INIT(sid, e->alsa_name);
1168
1169 if (!(me = snd_mixer_find_selem(m, sid))) {
1170
1171 if (e->required != PA_ALSA_REQUIRED_IGNORE)
1172 return -1;
1173
1174 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1175 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1176 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1177
1178 return 0;
1179 }
1180
1181 if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
1182 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1183
1184 if (!snd_mixer_selem_has_playback_switch(me)) {
1185 if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
1186 e->direction = PA_ALSA_DIRECTION_INPUT;
1187 else
1188 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1189 }
1190
1191 } else {
1192
1193 if (!snd_mixer_selem_has_capture_switch(me)) {
1194 if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
1195 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1196 else
1197 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1198 }
1199 }
1200
1201 if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
1202 e->direction_try_other = FALSE;
1203 }
1204
1205 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1206
1207 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1208
1209 if (!snd_mixer_selem_has_playback_volume(me)) {
1210 if (e->direction_try_other && snd_mixer_selem_has_capture_volume(me))
1211 e->direction = PA_ALSA_DIRECTION_INPUT;
1212 else
1213 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1214 }
1215
1216 } else {
1217
1218 if (!snd_mixer_selem_has_capture_volume(me)) {
1219 if (e->direction_try_other && snd_mixer_selem_has_playback_volume(me))
1220 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1221 else
1222 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1223 }
1224 }
1225
1226 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1227 long min_dB = 0, max_dB = 0;
1228 int r;
1229
1230 e->direction_try_other = FALSE;
1231
1232 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1233 e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0;
1234 else
1235 e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0;
1236
1237 if (e->has_dB) {
1238 #ifdef HAVE_VALGRIND_MEMCHECK_H
1239 VALGRIND_MAKE_MEM_DEFINED(&min_dB, sizeof(min_dB));
1240 VALGRIND_MAKE_MEM_DEFINED(&max_dB, sizeof(max_dB));
1241 #endif
1242
1243 e->min_dB = ((double) min_dB) / 100.0;
1244 e->max_dB = ((double) max_dB) / 100.0;
1245
1246 if (min_dB >= max_dB) {
1247 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);
1248 e->has_dB = FALSE;
1249 }
1250 }
1251
1252 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1253 r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume);
1254 else
1255 r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
1256
1257 if (r < 0) {
1258 pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1259 return -1;
1260 }
1261
1262
1263 if (e->min_volume >= e->max_volume) {
1264 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);
1265 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1266
1267 } else {
1268 pa_bool_t is_mono;
1269 pa_channel_position_t p;
1270
1271 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1272 is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
1273 else
1274 is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
1275
1276 if (is_mono) {
1277 e->n_channels = 1;
1278
1279 if (!e->override_map) {
1280 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++)
1281 e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
1282 e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
1283 }
1284
1285 e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
1286 } else {
1287 e->n_channels = 0;
1288 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1289
1290 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1291 continue;
1292
1293 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1294 e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1295 else
1296 e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1297 }
1298
1299 if (e->n_channels <= 0) {
1300 pa_log_warn("Volume element %s with no channels?", e->alsa_name);
1301 return -1;
1302 }
1303
1304 if (!e->override_map) {
1305 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1306 pa_bool_t has_channel;
1307
1308 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1309 continue;
1310
1311 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1312 has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1313 else
1314 has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1315
1316 e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
1317 }
1318 }
1319
1320 e->merged_mask = 0;
1321 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++)
1322 e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
1323 }
1324 }
1325 }
1326
1327 }
1328
1329 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1330 pa_alsa_option *o;
1331
1332 PA_LLIST_FOREACH(o, e->options)
1333 o->alsa_idx = pa_streq(o->alsa_name, "on") ? 1 : 0;
1334 } else if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1335 int n;
1336 pa_alsa_option *o;
1337
1338 if ((n = snd_mixer_selem_get_enum_items(me)) < 0) {
1339 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n));
1340 return -1;
1341 }
1342
1343 PA_LLIST_FOREACH(o, e->options) {
1344 int i;
1345
1346 for (i = 0; i < n; i++) {
1347 char buf[128];
1348
1349 if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
1350 continue;
1351
1352 if (!pa_streq(buf, o->alsa_name))
1353 continue;
1354
1355 o->alsa_idx = i;
1356 }
1357 }
1358 }
1359
1360 if (check_required(e, me) < 0)
1361 return -1;
1362
1363 return 0;
1364 }
1365
1366 static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, pa_bool_t prefixed) {
1367 pa_alsa_element *e;
1368
1369 pa_assert(p);
1370 pa_assert(section);
1371
1372 if (prefixed) {
1373 if (!pa_startswith(section, "Element "))
1374 return NULL;
1375
1376 section += 8;
1377 }
1378
1379 /* This is not an element section, but an enum section? */
1380 if (strchr(section, ':'))
1381 return NULL;
1382
1383 if (p->last_element && pa_streq(p->last_element->alsa_name, section))
1384 return p->last_element;
1385
1386 PA_LLIST_FOREACH(e, p->elements)
1387 if (pa_streq(e->alsa_name, section))
1388 goto finish;
1389
1390 e = pa_xnew0(pa_alsa_element, 1);
1391 e->path = p;
1392 e->alsa_name = pa_xstrdup(section);
1393 e->direction = p->direction;
1394
1395 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
1396
1397 finish:
1398 p->last_element = e;
1399 return e;
1400 }
1401
1402 static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
1403 char *en;
1404 const char *on;
1405 pa_alsa_option *o;
1406 pa_alsa_element *e;
1407
1408 if (!pa_startswith(section, "Option "))
1409 return NULL;
1410
1411 section += 7;
1412
1413 /* This is not an enum section, but an element section? */
1414 if (!(on = strchr(section, ':')))
1415 return NULL;
1416
1417 en = pa_xstrndup(section, on - section);
1418 on++;
1419
1420 if (p->last_option &&
1421 pa_streq(p->last_option->element->alsa_name, en) &&
1422 pa_streq(p->last_option->alsa_name, on)) {
1423 pa_xfree(en);
1424 return p->last_option;
1425 }
1426
1427 pa_assert_se(e = element_get(p, en, FALSE));
1428 pa_xfree(en);
1429
1430 PA_LLIST_FOREACH(o, e->options)
1431 if (pa_streq(o->alsa_name, on))
1432 goto finish;
1433
1434 o = pa_xnew0(pa_alsa_option, 1);
1435 o->element = e;
1436 o->alsa_name = pa_xstrdup(on);
1437 o->alsa_idx = -1;
1438
1439 if (p->last_option && p->last_option->element == e)
1440 PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
1441 else
1442 PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
1443
1444 finish:
1445 p->last_option = o;
1446 return o;
1447 }
1448
1449 static int element_parse_switch(
1450 const char *filename,
1451 unsigned line,
1452 const char *section,
1453 const char *lvalue,
1454 const char *rvalue,
1455 void *data,
1456 void *userdata) {
1457
1458 pa_alsa_path *p = userdata;
1459 pa_alsa_element *e;
1460
1461 pa_assert(p);
1462
1463 if (!(e = element_get(p, section, TRUE))) {
1464 pa_log("[%s:%u] Switch makes no sense in '%s'", filename, line, section);
1465 return -1;
1466 }
1467
1468 if (pa_streq(rvalue, "ignore"))
1469 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1470 else if (pa_streq(rvalue, "mute"))
1471 e->switch_use = PA_ALSA_SWITCH_MUTE;
1472 else if (pa_streq(rvalue, "off"))
1473 e->switch_use = PA_ALSA_SWITCH_OFF;
1474 else if (pa_streq(rvalue, "on"))
1475 e->switch_use = PA_ALSA_SWITCH_ON;
1476 else if (pa_streq(rvalue, "select"))
1477 e->switch_use = PA_ALSA_SWITCH_SELECT;
1478 else {
1479 pa_log("[%s:%u] Switch invalid of '%s'", filename, line, section);
1480 return -1;
1481 }
1482
1483 return 0;
1484 }
1485
1486 static int element_parse_volume(
1487 const char *filename,
1488 unsigned line,
1489 const char *section,
1490 const char *lvalue,
1491 const char *rvalue,
1492 void *data,
1493 void *userdata) {
1494
1495 pa_alsa_path *p = userdata;
1496 pa_alsa_element *e;
1497
1498 pa_assert(p);
1499
1500 if (!(e = element_get(p, section, TRUE))) {
1501 pa_log("[%s:%u] Volume makes no sense in '%s'", filename, line, section);
1502 return -1;
1503 }
1504
1505 if (pa_streq(rvalue, "ignore"))
1506 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1507 else if (pa_streq(rvalue, "merge"))
1508 e->volume_use = PA_ALSA_VOLUME_MERGE;
1509 else if (pa_streq(rvalue, "off"))
1510 e->volume_use = PA_ALSA_VOLUME_OFF;
1511 else if (pa_streq(rvalue, "zero"))
1512 e->volume_use = PA_ALSA_VOLUME_ZERO;
1513 else {
1514 pa_log("[%s:%u] Volume invalid of '%s'", filename, line, section);
1515 return -1;
1516 }
1517
1518 return 0;
1519 }
1520
1521 static int element_parse_enumeration(
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) {
1529
1530 pa_alsa_path *p = userdata;
1531 pa_alsa_element *e;
1532
1533 pa_assert(p);
1534
1535 if (!(e = element_get(p, section, TRUE))) {
1536 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename, line, section);
1537 return -1;
1538 }
1539
1540 if (pa_streq(rvalue, "ignore"))
1541 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1542 else if (pa_streq(rvalue, "select"))
1543 e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
1544 else {
1545 pa_log("[%s:%u] Enumeration invalid of '%s'", filename, line, section);
1546 return -1;
1547 }
1548
1549 return 0;
1550 }
1551
1552 static int option_parse_priority(
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) {
1560
1561 pa_alsa_path *p = userdata;
1562 pa_alsa_option *o;
1563 uint32_t prio;
1564
1565 pa_assert(p);
1566
1567 if (!(o = option_get(p, section))) {
1568 pa_log("[%s:%u] Priority makes no sense in '%s'", filename, line, section);
1569 return -1;
1570 }
1571
1572 if (pa_atou(rvalue, &prio) < 0) {
1573 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
1574 return -1;
1575 }
1576
1577 o->priority = prio;
1578 return 0;
1579 }
1580
1581 static int option_parse_name(
1582 const char *filename,
1583 unsigned line,
1584 const char *section,
1585 const char *lvalue,
1586 const char *rvalue,
1587 void *data,
1588 void *userdata) {
1589
1590 pa_alsa_path *p = userdata;
1591 pa_alsa_option *o;
1592
1593 pa_assert(p);
1594
1595 if (!(o = option_get(p, section))) {
1596 pa_log("[%s:%u] Name makes no sense in '%s'", filename, line, section);
1597 return -1;
1598 }
1599
1600 pa_xfree(o->name);
1601 o->name = pa_xstrdup(rvalue);
1602
1603 return 0;
1604 }
1605
1606 static int element_parse_required(
1607 const char *filename,
1608 unsigned line,
1609 const char *section,
1610 const char *lvalue,
1611 const char *rvalue,
1612 void *data,
1613 void *userdata) {
1614
1615 pa_alsa_path *p = userdata;
1616 pa_alsa_element *e;
1617 pa_alsa_option *o;
1618 pa_alsa_required_t req;
1619
1620 pa_assert(p);
1621
1622 e = element_get(p, section, TRUE);
1623 o = option_get(p, section);
1624 if (!e && !o) {
1625 pa_log("[%s:%u] Required makes no sense in '%s'", filename, line, section);
1626 return -1;
1627 }
1628
1629 if (pa_streq(rvalue, "ignore"))
1630 req = PA_ALSA_REQUIRED_IGNORE;
1631 else if (pa_streq(rvalue, "switch") && e)
1632 req = PA_ALSA_REQUIRED_SWITCH;
1633 else if (pa_streq(rvalue, "volume") && e)
1634 req = PA_ALSA_REQUIRED_VOLUME;
1635 else if (pa_streq(rvalue, "enumeration"))
1636 req = PA_ALSA_REQUIRED_ENUMERATION;
1637 else if (pa_streq(rvalue, "any"))
1638 req = PA_ALSA_REQUIRED_ANY;
1639 else {
1640 pa_log("[%s:%u] Required invalid of '%s'", filename, line, section);
1641 return -1;
1642 }
1643
1644 if (pa_streq(lvalue, "required-absent")) {
1645 if (e)
1646 e->required_absent = req;
1647 if (o)
1648 o->required_absent = req;
1649 }
1650 else if (pa_streq(lvalue, "required-any")) {
1651 if (e) {
1652 e->required_any = req;
1653 e->path->has_req_any = TRUE;
1654 }
1655 if (o) {
1656 o->required_any = req;
1657 o->element->path->has_req_any = TRUE;
1658 }
1659 }
1660 else {
1661 if (e)
1662 e->required = req;
1663 if (o)
1664 o->required = req;
1665 }
1666
1667 return 0;
1668 }
1669
1670 static int element_parse_direction(
1671 const char *filename,
1672 unsigned line,
1673 const char *section,
1674 const char *lvalue,
1675 const char *rvalue,
1676 void *data,
1677 void *userdata) {
1678
1679 pa_alsa_path *p = userdata;
1680 pa_alsa_element *e;
1681
1682 pa_assert(p);
1683
1684 if (!(e = element_get(p, section, TRUE))) {
1685 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1686 return -1;
1687 }
1688
1689 if (pa_streq(rvalue, "playback"))
1690 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1691 else if (pa_streq(rvalue, "capture"))
1692 e->direction = PA_ALSA_DIRECTION_INPUT;
1693 else {
1694 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1695 return -1;
1696 }
1697
1698 return 0;
1699 }
1700
1701 static int element_parse_direction_try_other(
1702 const char *filename,
1703 unsigned line,
1704 const char *section,
1705 const char *lvalue,
1706 const char *rvalue,
1707 void *data,
1708 void *userdata) {
1709
1710 pa_alsa_path *p = userdata;
1711 pa_alsa_element *e;
1712 int yes;
1713
1714 if (!(e = element_get(p, section, TRUE))) {
1715 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1716 return -1;
1717 }
1718
1719 if ((yes = pa_parse_boolean(rvalue)) < 0) {
1720 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1721 return -1;
1722 }
1723
1724 e->direction_try_other = !!yes;
1725 return 0;
1726 }
1727
1728 static pa_channel_position_mask_t parse_mask(const char *m) {
1729 pa_channel_position_mask_t v;
1730
1731 if (pa_streq(m, "all-left"))
1732 v = PA_CHANNEL_POSITION_MASK_LEFT;
1733 else if (pa_streq(m, "all-right"))
1734 v = PA_CHANNEL_POSITION_MASK_RIGHT;
1735 else if (pa_streq(m, "all-center"))
1736 v = PA_CHANNEL_POSITION_MASK_CENTER;
1737 else if (pa_streq(m, "all-front"))
1738 v = PA_CHANNEL_POSITION_MASK_FRONT;
1739 else if (pa_streq(m, "all-rear"))
1740 v = PA_CHANNEL_POSITION_MASK_REAR;
1741 else if (pa_streq(m, "all-side"))
1742 v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER;
1743 else if (pa_streq(m, "all-top"))
1744 v = PA_CHANNEL_POSITION_MASK_TOP;
1745 else if (pa_streq(m, "all-no-lfe"))
1746 v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE);
1747 else if (pa_streq(m, "all"))
1748 v = PA_CHANNEL_POSITION_MASK_ALL;
1749 else {
1750 pa_channel_position_t p;
1751
1752 if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
1753 return 0;
1754
1755 v = PA_CHANNEL_POSITION_MASK(p);
1756 }
1757
1758 return v;
1759 }
1760
1761 static int element_parse_override_map(
1762 const char *filename,
1763 unsigned line,
1764 const char *section,
1765 const char *lvalue,
1766 const char *rvalue,
1767 void *data,
1768 void *userdata) {
1769
1770 pa_alsa_path *p = userdata;
1771 pa_alsa_element *e;
1772 const char *state = NULL;
1773 unsigned i = 0;
1774 char *n;
1775
1776 if (!(e = element_get(p, section, TRUE))) {
1777 pa_log("[%s:%u] Override map makes no sense in '%s'", filename, line, section);
1778 return -1;
1779 }
1780
1781 while ((n = pa_split(rvalue, ",", &state))) {
1782 pa_channel_position_mask_t m;
1783
1784 if (!*n)
1785 m = 0;
1786 else {
1787 if ((m = parse_mask(n)) == 0) {
1788 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename, line, n, section);
1789 pa_xfree(n);
1790 return -1;
1791 }
1792 }
1793
1794 if (pa_streq(lvalue, "override-map.1"))
1795 e->masks[i++][0] = m;
1796 else
1797 e->masks[i++][1] = m;
1798
1799 /* Later on we might add override-map.3 and so on here ... */
1800
1801 pa_xfree(n);
1802 }
1803
1804 e->override_map = TRUE;
1805
1806 return 0;
1807 }
1808
1809 static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
1810 snd_mixer_selem_id_t *sid;
1811 snd_mixer_elem_t *me;
1812 int r;
1813
1814 pa_assert(e);
1815 pa_assert(m);
1816
1817 SELEM_INIT(sid, e->alsa_name);
1818 if (!(me = snd_mixer_find_selem(m, sid))) {
1819 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1820 return -1;
1821 }
1822
1823 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1824
1825 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1826 r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
1827 else
1828 r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
1829
1830 if (r < 0)
1831 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1832
1833 } else {
1834 pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
1835
1836 if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0)
1837 pa_log_warn("Failed to set enumeration of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1838 }
1839
1840 return r;
1841 }
1842
1843 int pa_alsa_setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
1844 pa_alsa_option *o;
1845 uint32_t idx;
1846
1847 pa_assert(s);
1848 pa_assert(m);
1849
1850 PA_IDXSET_FOREACH(o, s->options, idx)
1851 element_set_option(o->element, m, o->alsa_idx);
1852
1853 return 0;
1854 }
1855
1856 static int option_verify(pa_alsa_option *o) {
1857 static const struct description_map well_known_descriptions[] = {
1858 { "input", N_("Input") },
1859 { "input-docking", N_("Docking Station Input") },
1860 { "input-docking-microphone", N_("Docking Station Microphone") },
1861 { "input-docking-linein", N_("Docking Station Line-In") },
1862 { "input-linein", N_("Line-In") },
1863 { "input-microphone", N_("Microphone") },
1864 { "input-microphone-front", N_("Front Microphone") },
1865 { "input-microphone-rear", N_("Rear Microphone") },
1866 { "input-microphone-external", N_("External Microphone") },
1867 { "input-microphone-internal", N_("Internal Microphone") },
1868 { "input-radio", N_("Radio") },
1869 { "input-video", N_("Video") },
1870 { "input-agc-on", N_("Automatic Gain Control") },
1871 { "input-agc-off", N_("No Automatic Gain Control") },
1872 { "input-boost-on", N_("Boost") },
1873 { "input-boost-off", N_("No Boost") },
1874 { "output-amplifier-on", N_("Amplifier") },
1875 { "output-amplifier-off", N_("No Amplifier") },
1876 { "output-bass-boost-on", N_("Bass Boost") },
1877 { "output-bass-boost-off", N_("No Bass Boost") },
1878 { "output-speaker", N_("Speaker") },
1879 { "output-headphones", N_("Headphones") }
1880 };
1881
1882 pa_assert(o);
1883
1884 if (!o->name) {
1885 pa_log("No name set for option %s", o->alsa_name);
1886 return -1;
1887 }
1888
1889 if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
1890 o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
1891 pa_log("Element %s of option %s not set for select.", o->element->alsa_name, o->name);
1892 return -1;
1893 }
1894
1895 if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
1896 !pa_streq(o->alsa_name, "on") &&
1897 !pa_streq(o->alsa_name, "off")) {
1898 pa_log("Switch %s options need be named off or on ", o->element->alsa_name);
1899 return -1;
1900 }
1901
1902 if (!o->description)
1903 o->description = pa_xstrdup(lookup_description(o->name,
1904 well_known_descriptions,
1905 PA_ELEMENTSOF(well_known_descriptions)));
1906 if (!o->description)
1907 o->description = pa_xstrdup(o->name);
1908
1909 return 0;
1910 }
1911
1912 static int element_verify(pa_alsa_element *e) {
1913 pa_alsa_option *o;
1914
1915 pa_assert(e);
1916
1917 // pa_log_debug("Element %s, path %s: r=%d, r-any=%d, r-abs=%d", e->alsa_name, e->path->name, e->required, e->required_any, e->required_absent);
1918 if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
1919 (e->required_any != PA_ALSA_REQUIRED_IGNORE && e->required_any == e->required_absent) ||
1920 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required_any != PA_ALSA_REQUIRED_IGNORE) ||
1921 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
1922 pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name);
1923 return -1;
1924 }
1925
1926 if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1927 pa_log("Element %s cannot set select for both switch and enumeration.", e->alsa_name);
1928 return -1;
1929 }
1930
1931 PA_LLIST_FOREACH(o, e->options)
1932 if (option_verify(o) < 0)
1933 return -1;
1934
1935 return 0;
1936 }
1937
1938 static int path_verify(pa_alsa_path *p) {
1939 static const struct description_map well_known_descriptions[] = {
1940 { "analog-input", N_("Analog Input") },
1941 { "analog-input-microphone", N_("Analog Microphone") },
1942 { "analog-input-microphone-front", N_("Front Microphone") },
1943 { "analog-input-microphone-rear", N_("Rear Microphone") },
1944 { "analog-input-microphone-dock", N_("Docking Station Microphone") },
1945 { "analog-input-microphone-internal", N_("Internal Microphone") },
1946 { "analog-input-linein", N_("Analog Line-In") },
1947 { "analog-input-radio", N_("Analog Radio") },
1948 { "analog-input-video", N_("Analog Video") },
1949 { "analog-output", N_("Analog Output") },
1950 { "analog-output-headphones", N_("Analog Headphones") },
1951 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
1952 { "analog-output-mono", N_("Analog Mono Output") },
1953 { "analog-output-speaker", N_("Analog Speakers") },
1954 { "iec958-stereo-output", N_("Digital Output (IEC958)") },
1955 { "iec958-passthrough-output", N_("Digital Passthrough (IEC958)") }
1956 };
1957
1958 pa_alsa_element *e;
1959
1960 pa_assert(p);
1961
1962 PA_LLIST_FOREACH(e, p->elements)
1963 if (element_verify(e) < 0)
1964 return -1;
1965
1966 if (!p->description)
1967 p->description = pa_xstrdup(lookup_description(p->name,
1968 well_known_descriptions,
1969 PA_ELEMENTSOF(well_known_descriptions)));
1970
1971 if (!p->description)
1972 p->description = pa_xstrdup(p->name);
1973
1974 return 0;
1975 }
1976
1977 pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction) {
1978 pa_alsa_path *p;
1979 char *fn;
1980 int r;
1981 const char *n;
1982
1983 pa_config_item items[] = {
1984 /* [General] */
1985 { "priority", pa_config_parse_unsigned, NULL, "General" },
1986 { "description", pa_config_parse_string, NULL, "General" },
1987 { "name", pa_config_parse_string, NULL, "General" },
1988
1989 /* [Option ...] */
1990 { "priority", option_parse_priority, NULL, NULL },
1991 { "name", option_parse_name, NULL, NULL },
1992
1993 /* [Element ...] */
1994 { "switch", element_parse_switch, NULL, NULL },
1995 { "volume", element_parse_volume, NULL, NULL },
1996 { "enumeration", element_parse_enumeration, NULL, NULL },
1997 { "override-map.1", element_parse_override_map, NULL, NULL },
1998 { "override-map.2", element_parse_override_map, NULL, NULL },
1999 /* ... later on we might add override-map.3 and so on here ... */
2000 { "required", element_parse_required, NULL, NULL },
2001 { "required-any", element_parse_required, NULL, NULL },
2002 { "required-absent", element_parse_required, NULL, NULL },
2003 { "direction", element_parse_direction, NULL, NULL },
2004 { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
2005 { NULL, NULL, NULL, NULL }
2006 };
2007
2008 pa_assert(fname);
2009
2010 p = pa_xnew0(pa_alsa_path, 1);
2011 n = pa_path_get_filename(fname);
2012 p->name = pa_xstrndup(n, strcspn(n, "."));
2013 p->direction = direction;
2014
2015 items[0].data = &p->priority;
2016 items[1].data = &p->description;
2017 items[2].data = &p->name;
2018
2019 fn = pa_maybe_prefix_path(fname,
2020 #if defined(__linux__) && !defined(__OPTIMIZE__)
2021 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/paths/" :
2022 #endif
2023 PA_ALSA_PATHS_DIR);
2024
2025 r = pa_config_parse(fn, NULL, items, p);
2026 pa_xfree(fn);
2027
2028 if (r < 0)
2029 goto fail;
2030
2031 if (path_verify(p) < 0)
2032 goto fail;
2033
2034 return p;
2035
2036 fail:
2037 pa_alsa_path_free(p);
2038 return NULL;
2039 }
2040
2041 pa_alsa_path* pa_alsa_path_synthesize(const char*element, pa_alsa_direction_t direction) {
2042 pa_alsa_path *p;
2043 pa_alsa_element *e;
2044
2045 pa_assert(element);
2046
2047 p = pa_xnew0(pa_alsa_path, 1);
2048 p->name = pa_xstrdup(element);
2049 p->direction = direction;
2050
2051 e = pa_xnew0(pa_alsa_element, 1);
2052 e->path = p;
2053 e->alsa_name = pa_xstrdup(element);
2054 e->direction = direction;
2055
2056 e->switch_use = PA_ALSA_SWITCH_MUTE;
2057 e->volume_use = PA_ALSA_VOLUME_MERGE;
2058
2059 PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
2060 p->last_element = e;
2061 return p;
2062 }
2063
2064 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
2065 pa_alsa_option *o, *n;
2066
2067 pa_assert(e);
2068
2069 for (o = e->options; o; o = n) {
2070 n = o->next;
2071
2072 if (o->alsa_idx < 0) {
2073 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
2074 option_free(o);
2075 }
2076 }
2077
2078 return
2079 e->switch_use != PA_ALSA_SWITCH_IGNORE ||
2080 e->volume_use != PA_ALSA_VOLUME_IGNORE ||
2081 e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
2082 }
2083
2084 static void path_drop_unsupported(pa_alsa_path *p) {
2085 pa_alsa_element *e, *n;
2086
2087 pa_assert(p);
2088
2089 for (e = p->elements; e; e = n) {
2090 n = e->next;
2091
2092 if (!element_drop_unsupported(e)) {
2093 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
2094 element_free(e);
2095 }
2096 }
2097 }
2098
2099 static void path_make_options_unique(pa_alsa_path *p) {
2100 pa_alsa_element *e;
2101 pa_alsa_option *o, *u;
2102
2103 PA_LLIST_FOREACH(e, p->elements) {
2104 PA_LLIST_FOREACH(o, e->options) {
2105 unsigned i;
2106 char *m;
2107
2108 for (u = o->next; u; u = u->next)
2109 if (pa_streq(u->name, o->name))
2110 break;
2111
2112 if (!u)
2113 continue;
2114
2115 m = pa_xstrdup(o->name);
2116
2117 /* OK, this name is not unique, hence let's rename */
2118 for (i = 1, u = o; u; u = u->next) {
2119 char *nn, *nd;
2120
2121 if (!pa_streq(u->name, m))
2122 continue;
2123
2124 nn = pa_sprintf_malloc("%s-%u", m, i);
2125 pa_xfree(u->name);
2126 u->name = nn;
2127
2128 nd = pa_sprintf_malloc("%s %u", u->description, i);
2129 pa_xfree(u->description);
2130 u->description = nd;
2131
2132 i++;
2133 }
2134
2135 pa_xfree(m);
2136 }
2137 }
2138 }
2139
2140 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
2141 pa_alsa_option *o;
2142
2143 for (; e; e = e->next)
2144 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
2145 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
2146 break;
2147
2148 if (!e)
2149 return FALSE;
2150
2151 for (o = e->options; o; o = o->next) {
2152 pa_alsa_setting *s;
2153
2154 if (template) {
2155 s = pa_xnewdup(pa_alsa_setting, template, 1);
2156 s->options = pa_idxset_copy(template->options);
2157 s->name = pa_sprintf_malloc(_("%s+%s"), template->name, o->name);
2158 s->description =
2159 (template->description[0] && o->description[0])
2160 ? pa_sprintf_malloc(_("%s / %s"), template->description, o->description)
2161 : (template->description[0]
2162 ? pa_xstrdup(template->description)
2163 : pa_xstrdup(o->description));
2164
2165 s->priority = PA_MAX(template->priority, o->priority);
2166 } else {
2167 s = pa_xnew0(pa_alsa_setting, 1);
2168 s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2169 s->name = pa_xstrdup(o->name);
2170 s->description = pa_xstrdup(o->description);
2171 s->priority = o->priority;
2172 }
2173
2174 pa_idxset_put(s->options, o, NULL);
2175
2176 if (element_create_settings(e->next, s))
2177 /* This is not a leaf, so let's get rid of it */
2178 setting_free(s);
2179 else {
2180 /* This is a leaf, so let's add it */
2181 PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2182
2183 e->path->last_setting = s;
2184 }
2185 }
2186
2187 return TRUE;
2188 }
2189
2190 static void path_create_settings(pa_alsa_path *p) {
2191 pa_assert(p);
2192
2193 element_create_settings(p->elements, NULL);
2194 }
2195
2196 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) {
2197 pa_alsa_element *e;
2198 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2199 pa_channel_position_t t;
2200
2201 pa_assert(p);
2202 pa_assert(m);
2203
2204 if (p->probed)
2205 return 0;
2206
2207 pa_zero(min_dB);
2208 pa_zero(max_dB);
2209
2210 pa_log_debug("Probing path '%s'", p->name);
2211
2212 PA_LLIST_FOREACH(e, p->elements) {
2213 if (element_probe(e, m) < 0) {
2214 p->supported = FALSE;
2215 pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2216 return -1;
2217 }
2218 pa_log_debug("Probe of element '%s' succeeded (volume=%d, switch=%d, enumeration=%d).", e->alsa_name, e->volume_use, e->switch_use, e->enumeration_use);
2219
2220 if (ignore_dB)
2221 e->has_dB = FALSE;
2222
2223 if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2224
2225 if (!p->has_volume) {
2226 p->min_volume = e->min_volume;
2227 p->max_volume = e->max_volume;
2228 }
2229
2230 if (e->has_dB) {
2231 if (!p->has_volume) {
2232 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2233 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2234 min_dB[t] = e->min_dB;
2235 max_dB[t] = e->max_dB;
2236 }
2237
2238 p->has_dB = TRUE;
2239 } else {
2240
2241 if (p->has_dB) {
2242 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2243 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2244 min_dB[t] += e->min_dB;
2245 max_dB[t] += e->max_dB;
2246 }
2247 } else {
2248 /* Hmm, there's another element before us
2249 * which cannot do dB volumes, so we we need
2250 * to 'neutralize' this slider */
2251 e->volume_use = PA_ALSA_VOLUME_ZERO;
2252 pa_log_info("Zeroing volume of '%s' on path '%s'", e->alsa_name, p->name);
2253 }
2254 }
2255 } else if (p->has_volume) {
2256 /* We can't use this volume, so let's ignore it */
2257 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2258 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e->alsa_name, p->name);
2259 }
2260 p->has_volume = TRUE;
2261 }
2262
2263 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2264 p->has_mute = TRUE;
2265 }
2266
2267 if (p->has_req_any && !p->req_any_present) {
2268 p->supported = FALSE;
2269 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name);
2270 return -1;
2271 }
2272
2273 path_drop_unsupported(p);
2274 path_make_options_unique(p);
2275 path_create_settings(p);
2276
2277 p->supported = TRUE;
2278 p->probed = TRUE;
2279
2280 p->min_dB = INFINITY;
2281 p->max_dB = -INFINITY;
2282
2283 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2284 if (p->min_dB > min_dB[t])
2285 p->min_dB = min_dB[t];
2286
2287 if (p->max_dB < max_dB[t])
2288 p->max_dB = max_dB[t];
2289 }
2290
2291 return 0;
2292 }
2293
2294 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2295 pa_assert(s);
2296
2297 pa_log_debug("Setting %s (%s) priority=%u",
2298 s->name,
2299 pa_strnull(s->description),
2300 s->priority);
2301 }
2302
2303 void pa_alsa_option_dump(pa_alsa_option *o) {
2304 pa_assert(o);
2305
2306 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2307 o->alsa_name,
2308 pa_strnull(o->name),
2309 pa_strnull(o->description),
2310 o->alsa_idx,
2311 o->priority);
2312 }
2313
2314 void pa_alsa_element_dump(pa_alsa_element *e) {
2315 pa_alsa_option *o;
2316 pa_assert(e);
2317
2318 pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s",
2319 e->alsa_name,
2320 e->direction,
2321 e->switch_use,
2322 e->volume_use,
2323 e->enumeration_use,
2324 e->required,
2325 e->required_any,
2326 e->required_absent,
2327 (long long unsigned) e->merged_mask,
2328 e->n_channels,
2329 pa_yes_no(e->override_map));
2330
2331 PA_LLIST_FOREACH(o, e->options)
2332 pa_alsa_option_dump(o);
2333 }
2334
2335 void pa_alsa_path_dump(pa_alsa_path *p) {
2336 pa_alsa_element *e;
2337 pa_alsa_setting *s;
2338 pa_assert(p);
2339
2340 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2341 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2342 p->name,
2343 pa_strnull(p->description),
2344 p->direction,
2345 p->priority,
2346 pa_yes_no(p->probed),
2347 pa_yes_no(p->supported),
2348 pa_yes_no(p->has_mute),
2349 pa_yes_no(p->has_volume),
2350 pa_yes_no(p->has_dB),
2351 p->min_volume, p->max_volume,
2352 p->min_dB, p->max_dB);
2353
2354 PA_LLIST_FOREACH(e, p->elements)
2355 pa_alsa_element_dump(e);
2356
2357 PA_LLIST_FOREACH(s, p->settings)
2358 pa_alsa_setting_dump(s);
2359 }
2360
2361 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2362 snd_mixer_selem_id_t *sid;
2363 snd_mixer_elem_t *me;
2364
2365 pa_assert(e);
2366 pa_assert(m);
2367 pa_assert(cb);
2368
2369 SELEM_INIT(sid, e->alsa_name);
2370 if (!(me = snd_mixer_find_selem(m, sid))) {
2371 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2372 return;
2373 }
2374
2375 snd_mixer_elem_set_callback(me, cb);
2376 snd_mixer_elem_set_callback_private(me, userdata);
2377 }
2378
2379 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2380 pa_alsa_element *e;
2381
2382 pa_assert(p);
2383 pa_assert(m);
2384 pa_assert(cb);
2385
2386 PA_LLIST_FOREACH(e, p->elements)
2387 element_set_callback(e, m, cb, userdata);
2388 }
2389
2390 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2391 pa_alsa_path *p;
2392
2393 pa_assert(ps);
2394 pa_assert(m);
2395 pa_assert(cb);
2396
2397 PA_LLIST_FOREACH(p, ps->paths)
2398 pa_alsa_path_set_callback(p, m, cb, userdata);
2399 }
2400
2401 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction) {
2402 pa_alsa_path_set *ps;
2403 char **pn = NULL, **en = NULL, **ie;
2404
2405 pa_assert(m);
2406 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2407
2408 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2409 return NULL;
2410
2411 ps = pa_xnew0(pa_alsa_path_set, 1);
2412 ps->direction = direction;
2413
2414 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2415 pn = m->output_path_names;
2416 else if (direction == PA_ALSA_DIRECTION_INPUT)
2417 pn = m->input_path_names;
2418
2419 if (pn) {
2420 char **in;
2421
2422 for (in = pn; *in; in++) {
2423 pa_alsa_path *p;
2424 pa_bool_t duplicate = FALSE;
2425 char **kn, *fn;
2426
2427 for (kn = pn; kn != in; kn++)
2428 if (pa_streq(*kn, *in)) {
2429 duplicate = TRUE;
2430 break;
2431 }
2432
2433 if (duplicate)
2434 continue;
2435
2436 fn = pa_sprintf_malloc("%s.conf", *in);
2437
2438 if ((p = pa_alsa_path_new(fn, direction))) {
2439 p->path_set = ps;
2440 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2441 ps->last_path = p;
2442 }
2443
2444 pa_xfree(fn);
2445 }
2446
2447 return ps;
2448 }
2449
2450 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2451 en = m->output_element;
2452 else if (direction == PA_ALSA_DIRECTION_INPUT)
2453 en = m->input_element;
2454
2455 if (!en) {
2456 pa_alsa_path_set_free(ps);
2457 return NULL;
2458 }
2459
2460 for (ie = en; *ie; ie++) {
2461 char **je;
2462 pa_alsa_path *p;
2463
2464 p = pa_alsa_path_synthesize(*ie, direction);
2465 p->path_set = ps;
2466
2467 /* Mark all other passed elements for require-absent */
2468 for (je = en; *je; je++) {
2469 pa_alsa_element *e;
2470
2471 if (je == ie)
2472 continue;
2473
2474 e = pa_xnew0(pa_alsa_element, 1);
2475 e->path = p;
2476 e->alsa_name = pa_xstrdup(*je);
2477 e->direction = direction;
2478 e->required_absent = PA_ALSA_REQUIRED_ANY;
2479
2480 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2481 p->last_element = e;
2482 }
2483
2484 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2485 ps->last_path = p;
2486 }
2487
2488 return ps;
2489 }
2490
2491 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2492 pa_alsa_path *p;
2493 pa_assert(ps);
2494
2495 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2496 (void*) ps,
2497 ps->direction,
2498 pa_yes_no(ps->probed));
2499
2500 PA_LLIST_FOREACH(p, ps->paths)
2501 pa_alsa_path_dump(p);
2502 }
2503
2504 static void path_set_unify(pa_alsa_path_set *ps) {
2505 pa_alsa_path *p;
2506 pa_bool_t has_dB = TRUE, has_volume = TRUE, has_mute = TRUE;
2507 pa_assert(ps);
2508
2509 /* We have issues dealing with paths that vary too wildly. That
2510 * means for now we have to have all paths support volume/mute/dB
2511 * or none. */
2512
2513 PA_LLIST_FOREACH(p, ps->paths) {
2514 pa_assert(p->probed);
2515
2516 if (!p->has_volume)
2517 has_volume = FALSE;
2518 else if (!p->has_dB)
2519 has_dB = FALSE;
2520
2521 if (!p->has_mute)
2522 has_mute = FALSE;
2523 }
2524
2525 if (!has_volume || !has_dB || !has_mute) {
2526
2527 if (!has_volume)
2528 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2529 else if (!has_dB)
2530 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2531
2532 if (!has_mute)
2533 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2534
2535 PA_LLIST_FOREACH(p, ps->paths) {
2536 if (!has_volume)
2537 p->has_volume = FALSE;
2538 else if (!has_dB)
2539 p->has_dB = FALSE;
2540
2541 if (!has_mute)
2542 p->has_mute = FALSE;
2543 }
2544 }
2545 }
2546
2547 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
2548 pa_alsa_path *p, *q;
2549
2550 PA_LLIST_FOREACH(p, ps->paths) {
2551 unsigned i;
2552 char *m;
2553
2554 for (q = p->next; q; q = q->next)
2555 if (pa_streq(q->name, p->name))
2556 break;
2557
2558 if (!q)
2559 continue;
2560
2561 m = pa_xstrdup(p->name);
2562
2563 /* OK, this name is not unique, hence let's rename */
2564 for (i = 1, q = p; q; q = q->next) {
2565 char *nn, *nd;
2566
2567 if (!pa_streq(q->name, m))
2568 continue;
2569
2570 nn = pa_sprintf_malloc("%s-%u", m, i);
2571 pa_xfree(q->name);
2572 q->name = nn;
2573
2574 nd = pa_sprintf_malloc("%s %u", q->description, i);
2575 pa_xfree(q->description);
2576 q->description = nd;
2577
2578 i++;
2579 }
2580
2581 pa_xfree(m);
2582 }
2583 }
2584
2585 void pa_alsa_path_set_probe(pa_alsa_path_set *ps, snd_mixer_t *m, pa_bool_t ignore_dB) {
2586 pa_alsa_path *p, *n;
2587
2588 pa_assert(ps);
2589
2590 if (ps->probed)
2591 return;
2592
2593 for (p = ps->paths; p; p = n) {
2594 n = p->next;
2595
2596 if (pa_alsa_path_probe(p, m, ignore_dB) < 0) {
2597 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
2598 pa_alsa_path_free(p);
2599 }
2600 }
2601
2602 path_set_unify(ps);
2603 path_set_make_paths_unique(ps);
2604 ps->probed = TRUE;
2605 }
2606
2607 static void mapping_free(pa_alsa_mapping *m) {
2608 pa_assert(m);
2609
2610 pa_xfree(m->name);
2611 pa_xfree(m->description);
2612
2613 pa_xstrfreev(m->device_strings);
2614 pa_xstrfreev(m->input_path_names);
2615 pa_xstrfreev(m->output_path_names);
2616 pa_xstrfreev(m->input_element);
2617 pa_xstrfreev(m->output_element);
2618
2619 pa_assert(!m->input_pcm);
2620 pa_assert(!m->output_pcm);
2621
2622 pa_xfree(m);
2623 }
2624
2625 static void profile_free(pa_alsa_profile *p) {
2626 pa_assert(p);
2627
2628 pa_xfree(p->name);
2629 pa_xfree(p->description);
2630
2631 pa_xstrfreev(p->input_mapping_names);
2632 pa_xstrfreev(p->output_mapping_names);
2633
2634 if (p->input_mappings)
2635 pa_idxset_free(p->input_mappings, NULL, NULL);
2636
2637 if (p->output_mappings)
2638 pa_idxset_free(p->output_mappings, NULL, NULL);
2639
2640 pa_xfree(p);
2641 }
2642
2643 static void decibel_fix_free(pa_alsa_decibel_fix *db_fix) {
2644 pa_assert(db_fix);
2645
2646 pa_xfree(db_fix->name);
2647 pa_xfree(db_fix->db_values);
2648
2649 pa_xfree(db_fix);
2650 }
2651
2652 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
2653 pa_assert(ps);
2654
2655 if (ps->profiles) {
2656 pa_alsa_profile *p;
2657
2658 while ((p = pa_hashmap_steal_first(ps->profiles)))
2659 profile_free(p);
2660
2661 pa_hashmap_free(ps->profiles, NULL, NULL);
2662 }
2663
2664 if (ps->mappings) {
2665 pa_alsa_mapping *m;
2666
2667 while ((m = pa_hashmap_steal_first(ps->mappings)))
2668 mapping_free(m);
2669
2670 pa_hashmap_free(ps->mappings, NULL, NULL);
2671 }
2672
2673 if (ps->decibel_fixes) {
2674 pa_alsa_decibel_fix *db_fix;
2675
2676 while ((db_fix = pa_hashmap_steal_first(ps->decibel_fixes)))
2677 decibel_fix_free(db_fix);
2678
2679 pa_hashmap_free(ps->decibel_fixes, NULL, NULL);
2680 }
2681
2682 pa_xfree(ps);
2683 }
2684
2685 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
2686 pa_alsa_mapping *m;
2687
2688 if (!pa_startswith(name, "Mapping "))
2689 return NULL;
2690
2691 name += 8;
2692
2693 if ((m = pa_hashmap_get(ps->mappings, name)))
2694 return m;
2695
2696 m = pa_xnew0(pa_alsa_mapping, 1);
2697 m->profile_set = ps;
2698 m->name = pa_xstrdup(name);
2699 pa_channel_map_init(&m->channel_map);
2700
2701 pa_hashmap_put(ps->mappings, m->name, m);
2702
2703 return m;
2704 }
2705
2706 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
2707 pa_alsa_profile *p;
2708
2709 if (!pa_startswith(name, "Profile "))
2710 return NULL;
2711
2712 name += 8;
2713
2714 if ((p = pa_hashmap_get(ps->profiles, name)))
2715 return p;
2716
2717 p = pa_xnew0(pa_alsa_profile, 1);
2718 p->profile_set = ps;
2719 p->name = pa_xstrdup(name);
2720
2721 pa_hashmap_put(ps->profiles, p->name, p);
2722
2723 return p;
2724 }
2725
2726 static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) {
2727 pa_alsa_decibel_fix *db_fix;
2728
2729 if (!pa_startswith(name, "DecibelFix "))
2730 return NULL;
2731
2732 name += 11;
2733
2734 if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name)))
2735 return db_fix;
2736
2737 db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
2738 db_fix->profile_set = ps;
2739 db_fix->name = pa_xstrdup(name);
2740
2741 pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix);
2742
2743 return db_fix;
2744 }
2745
2746 static int mapping_parse_device_strings(
2747 const char *filename,
2748 unsigned line,
2749 const char *section,
2750 const char *lvalue,
2751 const char *rvalue,
2752 void *data,
2753 void *userdata) {
2754
2755 pa_alsa_profile_set *ps = userdata;
2756 pa_alsa_mapping *m;
2757
2758 pa_assert(ps);
2759
2760 if (!(m = mapping_get(ps, section))) {
2761 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2762 return -1;
2763 }
2764
2765 pa_xstrfreev(m->device_strings);
2766 if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
2767 pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
2768 return -1;
2769 }
2770
2771 return 0;
2772 }
2773
2774 static int mapping_parse_channel_map(
2775 const char *filename,
2776 unsigned line,
2777 const char *section,
2778 const char *lvalue,
2779 const char *rvalue,
2780 void *data,
2781 void *userdata) {
2782
2783 pa_alsa_profile_set *ps = userdata;
2784 pa_alsa_mapping *m;
2785
2786 pa_assert(ps);
2787
2788 if (!(m = mapping_get(ps, section))) {
2789 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2790 return -1;
2791 }
2792
2793 if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
2794 pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
2795 return -1;
2796 }
2797
2798 return 0;
2799 }
2800
2801 static int mapping_parse_paths(
2802 const char *filename,
2803 unsigned line,
2804 const char *section,
2805 const char *lvalue,
2806 const char *rvalue,
2807 void *data,
2808 void *userdata) {
2809
2810 pa_alsa_profile_set *ps = userdata;
2811 pa_alsa_mapping *m;
2812
2813 pa_assert(ps);
2814
2815 if (!(m = mapping_get(ps, section))) {
2816 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2817 return -1;
2818 }
2819
2820 if (pa_streq(lvalue, "paths-input")) {
2821 pa_xstrfreev(m->input_path_names);
2822 m->input_path_names = pa_split_spaces_strv(rvalue);
2823 } else {
2824 pa_xstrfreev(m->output_path_names);
2825 m->output_path_names = pa_split_spaces_strv(rvalue);
2826 }
2827
2828 return 0;
2829 }
2830
2831 static int mapping_parse_element(
2832 const char *filename,
2833 unsigned line,
2834 const char *section,
2835 const char *lvalue,
2836 const char *rvalue,
2837 void *data,
2838 void *userdata) {
2839
2840 pa_alsa_profile_set *ps = userdata;
2841 pa_alsa_mapping *m;
2842
2843 pa_assert(ps);
2844
2845 if (!(m = mapping_get(ps, section))) {
2846 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2847 return -1;
2848 }
2849
2850 if (pa_streq(lvalue, "element-input")) {
2851 pa_xstrfreev(m->input_element);
2852 m->input_element = pa_split_spaces_strv(rvalue);
2853 } else {
2854 pa_xstrfreev(m->output_element);
2855 m->output_element = pa_split_spaces_strv(rvalue);
2856 }
2857
2858 return 0;
2859 }
2860
2861 static int mapping_parse_direction(
2862 const char *filename,
2863 unsigned line,
2864 const char *section,
2865 const char *lvalue,
2866 const char *rvalue,
2867 void *data,
2868 void *userdata) {
2869
2870 pa_alsa_profile_set *ps = userdata;
2871 pa_alsa_mapping *m;
2872
2873 pa_assert(ps);
2874
2875 if (!(m = mapping_get(ps, section))) {
2876 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2877 return -1;
2878 }
2879
2880 if (pa_streq(rvalue, "input"))
2881 m->direction = PA_ALSA_DIRECTION_INPUT;
2882 else if (pa_streq(rvalue, "output"))
2883 m->direction = PA_ALSA_DIRECTION_OUTPUT;
2884 else if (pa_streq(rvalue, "any"))
2885 m->direction = PA_ALSA_DIRECTION_ANY;
2886 else {
2887 pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
2888 return -1;
2889 }
2890
2891 return 0;
2892 }
2893
2894 static int mapping_parse_description(
2895 const char *filename,
2896 unsigned line,
2897 const char *section,
2898 const char *lvalue,
2899 const char *rvalue,
2900 void *data,
2901 void *userdata) {
2902
2903 pa_alsa_profile_set *ps = userdata;
2904 pa_alsa_profile *p;
2905 pa_alsa_mapping *m;
2906
2907 pa_assert(ps);
2908
2909 if ((m = mapping_get(ps, section))) {
2910 pa_xfree(m->description);
2911 m->description = pa_xstrdup(rvalue);
2912 } else if ((p = profile_get(ps, section))) {
2913 pa_xfree(p->description);
2914 p->description = pa_xstrdup(rvalue);
2915 } else {
2916 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2917 return -1;
2918 }
2919
2920 return 0;
2921 }
2922
2923 static int mapping_parse_priority(
2924 const char *filename,
2925 unsigned line,
2926 const char *section,
2927 const char *lvalue,
2928 const char *rvalue,
2929 void *data,
2930 void *userdata) {
2931
2932 pa_alsa_profile_set *ps = userdata;
2933 pa_alsa_profile *p;
2934 pa_alsa_mapping *m;
2935 uint32_t prio;
2936
2937 pa_assert(ps);
2938
2939 if (pa_atou(rvalue, &prio) < 0) {
2940 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
2941 return -1;
2942 }
2943
2944 if ((m = mapping_get(ps, section)))
2945 m->priority = prio;
2946 else if ((p = profile_get(ps, section)))
2947 p->priority = prio;
2948 else {
2949 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2950 return -1;
2951 }
2952
2953 return 0;
2954 }
2955
2956 static int profile_parse_mappings(
2957 const char *filename,
2958 unsigned line,
2959 const char *section,
2960 const char *lvalue,
2961 const char *rvalue,
2962 void *data,
2963 void *userdata) {
2964
2965 pa_alsa_profile_set *ps = userdata;
2966 pa_alsa_profile *p;
2967
2968 pa_assert(ps);
2969
2970 if (!(p = profile_get(ps, section))) {
2971 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2972 return -1;
2973 }
2974
2975 if (pa_streq(lvalue, "input-mappings")) {
2976 pa_xstrfreev(p->input_mapping_names);
2977 p->input_mapping_names = pa_split_spaces_strv(rvalue);
2978 } else {
2979 pa_xstrfreev(p->output_mapping_names);
2980 p->output_mapping_names = pa_split_spaces_strv(rvalue);
2981 }
2982
2983 return 0;
2984 }
2985
2986 static int profile_parse_skip_probe(
2987 const char *filename,
2988 unsigned line,
2989 const char *section,
2990 const char *lvalue,
2991 const char *rvalue,
2992 void *data,
2993 void *userdata) {
2994
2995 pa_alsa_profile_set *ps = userdata;
2996 pa_alsa_profile *p;
2997 int b;
2998
2999 pa_assert(ps);
3000
3001 if (!(p = profile_get(ps, section))) {
3002 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3003 return -1;
3004 }
3005
3006 if ((b = pa_parse_boolean(rvalue)) < 0) {
3007 pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
3008 return -1;
3009 }
3010
3011 p->supported = b;
3012
3013 return 0;
3014 }
3015
3016 static int decibel_fix_parse_db_values(
3017 const char *filename,
3018 unsigned line,
3019 const char *section,
3020 const char *lvalue,
3021 const char *rvalue,
3022 void *data,
3023 void *userdata) {
3024
3025 pa_alsa_profile_set *ps = userdata;
3026 pa_alsa_decibel_fix *db_fix;
3027 char **items;
3028 char *item;
3029 long *db_values;
3030 unsigned n = 8; /* Current size of the db_values table. */
3031 unsigned min_step = 0;
3032 unsigned max_step = 0;
3033 unsigned i = 0; /* Index to the items table. */
3034 unsigned prev_step = 0;
3035 double prev_db = 0;
3036
3037 pa_assert(filename);
3038 pa_assert(section);
3039 pa_assert(lvalue);
3040 pa_assert(rvalue);
3041 pa_assert(ps);
3042
3043 if (!(db_fix = decibel_fix_get(ps, section))) {
3044 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3045 return -1;
3046 }
3047
3048 if (!(items = pa_split_spaces_strv(rvalue))) {
3049 pa_log("[%s:%u] Value missing", pa_strnull(filename), line);
3050 return -1;
3051 }
3052
3053 db_values = pa_xnew(long, n);
3054
3055 while ((item = items[i++])) {
3056 char *s = item; /* Step value string. */
3057 char *d = item; /* dB value string. */
3058 uint32_t step;
3059 double db;
3060
3061 /* Move d forward until it points to a colon or to the end of the item. */
3062 for (; *d && *d != ':'; ++d);
3063
3064 if (d == s) {
3065 /* item started with colon. */
3066 pa_log("[%s:%u] No step value found in %s", filename, line, item);
3067 goto fail;
3068 }
3069
3070 if (!*d || !*(d + 1)) {
3071 /* No colon found, or it was the last character in item. */
3072 pa_log("[%s:%u] No dB value found in %s", filename, line, item);
3073 goto fail;
3074 }
3075
3076 /* pa_atou() needs a null-terminating string. Let's replace the colon
3077 * with a zero byte. */
3078 *d++ = '\0';
3079
3080 if (pa_atou(s, &step) < 0) {
3081 pa_log("[%s:%u] Invalid step value: %s", filename, line, s);
3082 goto fail;
3083 }
3084
3085 if (pa_atod(d, &db) < 0) {
3086 pa_log("[%s:%u] Invalid dB value: %s", filename, line, d);
3087 goto fail;
3088 }
3089
3090 if (step <= prev_step && i != 1) {
3091 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename, line, step, prev_step);
3092 goto fail;
3093 }
3094
3095 if (db < prev_db && i != 1) {
3096 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename, line, db, prev_db);
3097 goto fail;
3098 }
3099
3100 if (i == 1) {
3101 min_step = step;
3102 db_values[0] = (long) (db * 100.0);
3103 prev_step = step;
3104 prev_db = db;
3105 } else {
3106 /* Interpolate linearly. */
3107 double db_increment = (db - prev_db) / (step - prev_step);
3108
3109 for (; prev_step < step; ++prev_step, prev_db += db_increment) {
3110
3111 /* Reallocate the db_values table if it's about to overflow. */
3112 if (prev_step + 1 - min_step == n) {
3113 n *= 2;
3114 db_values = pa_xrenew(long, db_values, n);
3115 }
3116
3117 db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
3118 }
3119 }
3120
3121 max_step = step;
3122 }
3123
3124 db_fix->min_step = min_step;
3125 db_fix->max_step = max_step;
3126 pa_xfree(db_fix->db_values);
3127 db_fix->db_values = db_values;
3128
3129 pa_xstrfreev(items);
3130
3131 return 0;
3132
3133 fail:
3134 pa_xstrfreev(items);
3135 pa_xfree(db_values);
3136
3137 return -1;
3138 }
3139
3140 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
3141
3142 static const struct description_map well_known_descriptions[] = {
3143 { "analog-mono", N_("Analog Mono") },
3144 { "analog-stereo", N_("Analog Stereo") },
3145 { "analog-surround-21", N_("Analog Surround 2.1") },
3146 { "analog-surround-30", N_("Analog Surround 3.0") },
3147 { "analog-surround-31", N_("Analog Surround 3.1") },
3148 { "analog-surround-40", N_("Analog Surround 4.0") },
3149 { "analog-surround-41", N_("Analog Surround 4.1") },
3150 { "analog-surround-50", N_("Analog Surround 5.0") },
3151 { "analog-surround-51", N_("Analog Surround 5.1") },
3152 { "analog-surround-61", N_("Analog Surround 6.0") },
3153 { "analog-surround-61", N_("Analog Surround 6.1") },
3154 { "analog-surround-70", N_("Analog Surround 7.0") },
3155 { "analog-surround-71", N_("Analog Surround 7.1") },
3156 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3157 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3158 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3159 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3160 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3161 };
3162
3163 pa_assert(m);
3164
3165 if (!pa_channel_map_valid(&m->channel_map)) {
3166 pa_log("Mapping %s is missing channel map.", m->name);
3167 return -1;
3168 }
3169
3170 if (!m->device_strings) {
3171 pa_log("Mapping %s is missing device strings.", m->name);
3172 return -1;
3173 }
3174
3175 if ((m->input_path_names && m->input_element) ||
3176 (m->output_path_names && m->output_element)) {
3177 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
3178 return -1;
3179 }
3180
3181 if (!m->description)
3182 m->description = pa_xstrdup(lookup_description(m->name,
3183 well_known_descriptions,
3184 PA_ELEMENTSOF(well_known_descriptions)));
3185
3186 if (!m->description)
3187 m->description = pa_xstrdup(m->name);
3188
3189 if (bonus) {
3190 if (pa_channel_map_equal(&m->channel_map, bonus))
3191 m->priority += 50;
3192 else if (m->channel_map.channels == bonus->channels)
3193 m->priority += 30;
3194 }
3195
3196 return 0;
3197 }
3198
3199 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
3200 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
3201
3202 pa_assert(m);
3203
3204 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3205 m->name,
3206 pa_strnull(m->description),
3207 m->priority,
3208 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
3209 pa_yes_no(m->supported),
3210 m->direction);
3211 }
3212
3213 static void profile_set_add_auto_pair(
3214 pa_alsa_profile_set *ps,
3215 pa_alsa_mapping *m, /* output */
3216 pa_alsa_mapping *n /* input */) {
3217
3218 char *name;
3219 pa_alsa_profile *p;
3220
3221 pa_assert(ps);
3222 pa_assert(m || n);
3223
3224 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
3225 return;
3226
3227 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
3228 return;
3229
3230 if (m && n)
3231 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
3232 else if (m)
3233 name = pa_sprintf_malloc("output:%s", m->name);
3234 else
3235 name = pa_sprintf_malloc("input:%s", n->name);
3236
3237 if (pa_hashmap_get(ps->profiles, name)) {
3238 pa_xfree(name);
3239 return;
3240 }
3241
3242 p = pa_xnew0(pa_alsa_profile, 1);
3243 p->profile_set = ps;
3244 p->name = name;
3245
3246 if (m) {
3247 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3248 pa_idxset_put(p->output_mappings, m, NULL);
3249 p->priority += m->priority * 100;
3250 }
3251
3252 if (n) {
3253 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3254 pa_idxset_put(p->input_mappings, n, NULL);
3255 p->priority += n->priority;
3256 }
3257
3258 pa_hashmap_put(ps->profiles, p->name, p);
3259 }
3260
3261 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
3262 pa_alsa_mapping *m, *n;
3263 void *m_state, *n_state;
3264
3265 pa_assert(ps);
3266
3267 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
3268 profile_set_add_auto_pair(ps, m, NULL);
3269
3270 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3271 profile_set_add_auto_pair(ps, m, n);
3272 }
3273
3274 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3275 profile_set_add_auto_pair(ps, NULL, n);
3276 }
3277
3278 static int profile_verify(pa_alsa_profile *p) {
3279
3280 static const struct description_map well_known_descriptions[] = {
3281 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3282 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3283 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3284 { "off", N_("Off") }
3285 };
3286
3287 pa_assert(p);
3288
3289 /* Replace the output mapping names by the actual mappings */
3290 if (p->output_mapping_names) {
3291 char **name;
3292
3293 pa_assert(!p->output_mappings);
3294 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3295
3296 for (name = p->output_mapping_names; *name; name++) {
3297 pa_alsa_mapping *m;
3298 char **in;
3299 pa_bool_t duplicate = FALSE;
3300
3301 for (in = name + 1; *in; in++)
3302 if (pa_streq(*name, *in)) {
3303 duplicate = TRUE;
3304 break;
3305 }
3306
3307 if (duplicate)
3308 continue;
3309
3310 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
3311 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
3312 return -1;
3313 }
3314
3315 pa_idxset_put(p->output_mappings, m, NULL);
3316
3317 if (p->supported)
3318 m->supported++;
3319 }
3320
3321 pa_xstrfreev(p->output_mapping_names);
3322 p->output_mapping_names = NULL;
3323 }
3324
3325 /* Replace the input mapping names by the actual mappings */
3326 if (p->input_mapping_names) {
3327 char **name;
3328
3329 pa_assert(!p->input_mappings);
3330 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3331
3332 for (name = p->input_mapping_names; *name; name++) {
3333 pa_alsa_mapping *m;
3334 char **in;
3335 pa_bool_t duplicate = FALSE;
3336
3337 for (in = name + 1; *in; in++)
3338 if (pa_streq(*name, *in)) {
3339 duplicate = TRUE;
3340 break;
3341 }
3342
3343 if (duplicate)
3344 continue;
3345
3346 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
3347 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
3348 return -1;
3349 }
3350
3351 pa_idxset_put(p->input_mappings, m, NULL);
3352
3353 if (p->supported)
3354 m->supported++;
3355 }
3356
3357 pa_xstrfreev(p->input_mapping_names);
3358 p->input_mapping_names = NULL;
3359 }
3360
3361 if (!p->input_mappings && !p->output_mappings) {
3362 pa_log("Profile '%s' lacks mappings.", p->name);
3363 return -1;
3364 }
3365
3366 if (!p->description)
3367 p->description = pa_xstrdup(lookup_description(p->name,
3368 well_known_descriptions,
3369 PA_ELEMENTSOF(well_known_descriptions)));
3370
3371 if (!p->description) {
3372 pa_strbuf *sb;
3373 uint32_t idx;
3374 pa_alsa_mapping *m;
3375
3376 sb = pa_strbuf_new();
3377
3378 if (p->output_mappings)
3379 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3380 if (!pa_strbuf_isempty(sb))
3381 pa_strbuf_puts(sb, " + ");
3382
3383 pa_strbuf_printf(sb, _("%s Output"), m->description);
3384 }
3385
3386 if (p->input_mappings)
3387 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3388 if (!pa_strbuf_isempty(sb))
3389 pa_strbuf_puts(sb, " + ");
3390
3391 pa_strbuf_printf(sb, _("%s Input"), m->description);
3392 }
3393
3394 p->description = pa_strbuf_tostring_free(sb);
3395 }
3396
3397 return 0;
3398 }
3399
3400 void pa_alsa_profile_dump(pa_alsa_profile *p) {
3401 uint32_t idx;
3402 pa_alsa_mapping *m;
3403 pa_assert(p);
3404
3405 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3406 p->name,
3407 pa_strnull(p->description),
3408 p->priority,
3409 pa_yes_no(p->supported),
3410 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
3411 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
3412
3413 if (p->input_mappings)
3414 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
3415 pa_log_debug("Input %s", m->name);
3416
3417 if (p->output_mappings)
3418 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
3419 pa_log_debug("Output %s", m->name);
3420 }
3421
3422 static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
3423 pa_assert(db_fix);
3424
3425 /* Check that the dB mapping has been configured. Since "db-values" is
3426 * currently the only option in the DecibelFix section, and decibel fix
3427 * objects don't get created if a DecibelFix section is empty, this is
3428 * actually a redundant check. Having this may prevent future bugs,
3429 * however. */
3430 if (!db_fix->db_values) {
3431 pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
3432 return -1;
3433 }
3434
3435 return 0;
3436 }
3437
3438 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
3439 char *db_values = NULL;
3440
3441 pa_assert(db_fix);
3442
3443 if (db_fix->db_values) {
3444 pa_strbuf *buf;
3445 long i;
3446 long max_i = db_fix->max_step - db_fix->min_step;
3447
3448 buf = pa_strbuf_new();
3449 pa_strbuf_printf(buf, "[%li]:%0.2f", db_fix->min_step, db_fix->db_values[0] / 100.0);
3450
3451 for (i = 1; i <= max_i; ++i)
3452 pa_strbuf_printf(buf, " [%li]:%0.2f", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
3453
3454 db_values = pa_strbuf_tostring_free(buf);
3455 }
3456
3457 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
3458 db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
3459
3460 pa_xfree(db_values);
3461 }
3462
3463 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
3464 pa_alsa_profile_set *ps;
3465 pa_alsa_profile *p;
3466 pa_alsa_mapping *m;
3467 pa_alsa_decibel_fix *db_fix;
3468 char *fn;
3469 int r;
3470 void *state;
3471
3472 static pa_config_item items[] = {
3473 /* [General] */
3474 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
3475
3476 /* [Mapping ...] */
3477 { "device-strings", mapping_parse_device_strings, NULL, NULL },
3478 { "channel-map", mapping_parse_channel_map, NULL, NULL },
3479 { "paths-input", mapping_parse_paths, NULL, NULL },
3480 { "paths-output", mapping_parse_paths, NULL, NULL },
3481 { "element-input", mapping_parse_element, NULL, NULL },
3482 { "element-output", mapping_parse_element, NULL, NULL },
3483 { "direction", mapping_parse_direction, NULL, NULL },
3484
3485 /* Shared by [Mapping ...] and [Profile ...] */
3486 { "description", mapping_parse_description, NULL, NULL },
3487 { "priority", mapping_parse_priority, NULL, NULL },
3488
3489 /* [Profile ...] */
3490 { "input-mappings", profile_parse_mappings, NULL, NULL },
3491 { "output-mappings", profile_parse_mappings, NULL, NULL },
3492 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
3493
3494 /* [DecibelFix ...] */
3495 { "db-values", decibel_fix_parse_db_values, NULL, NULL },
3496 { NULL, NULL, NULL, NULL }
3497 };
3498
3499 ps = pa_xnew0(pa_alsa_profile_set, 1);
3500 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3501 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3502 ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3503
3504 items[0].data = &ps->auto_profiles;
3505
3506 if (!fname)
3507 fname = "default.conf";
3508
3509 fn = pa_maybe_prefix_path(fname,
3510 #if defined(__linux__) && !defined(__OPTIMIZE__)
3511 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
3512 #endif
3513 PA_ALSA_PROFILE_SETS_DIR);
3514
3515 r = pa_config_parse(fn, NULL, items, ps);
3516 pa_xfree(fn);
3517
3518 if (r < 0)
3519 goto fail;
3520
3521 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3522 if (mapping_verify(m, bonus) < 0)
3523 goto fail;
3524
3525 if (ps->auto_profiles)
3526 profile_set_add_auto(ps);
3527
3528 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3529 if (profile_verify(p) < 0)
3530 goto fail;
3531
3532 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
3533 if (decibel_fix_verify(db_fix) < 0)
3534 goto fail;
3535
3536 return ps;
3537
3538 fail:
3539 pa_alsa_profile_set_free(ps);
3540 return NULL;
3541 }
3542
3543 void pa_alsa_profile_set_probe(
3544 pa_alsa_profile_set *ps,
3545 const char *dev_id,
3546 const pa_sample_spec *ss,
3547 unsigned default_n_fragments,
3548 unsigned default_fragment_size_msec) {
3549
3550 void *state;
3551 pa_alsa_profile *p, *last = NULL;
3552 pa_alsa_mapping *m;
3553
3554 pa_assert(ps);
3555 pa_assert(dev_id);
3556 pa_assert(ss);
3557
3558 if (ps->probed)
3559 return;
3560
3561 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
3562 pa_sample_spec try_ss;
3563 pa_channel_map try_map;
3564 snd_pcm_uframes_t try_period_size, try_buffer_size;
3565 uint32_t idx;
3566
3567 /* Is this already marked that it is supported? (i.e. from the config file) */
3568 if (p->supported)
3569 continue;
3570
3571 pa_log_debug("Looking at profile %s", p->name);
3572
3573 /* Close PCMs from the last iteration we don't need anymore */
3574 if (last && last->output_mappings)
3575 PA_IDXSET_FOREACH(m, last->output_mappings, idx) {
3576
3577 if (!m->output_pcm)
3578 break;
3579
3580 if (last->supported)
3581 m->supported++;
3582
3583 if (!p->output_mappings || !pa_idxset_get_by_data(p->output_mappings, m, NULL)) {
3584 snd_pcm_close(m->output_pcm);
3585 m->output_pcm = NULL;
3586 }
3587 }
3588
3589 if (last && last->input_mappings)
3590 PA_IDXSET_FOREACH(m, last->input_mappings, idx) {
3591
3592 if (!m->input_pcm)
3593 break;
3594
3595 if (last->supported)
3596 m->supported++;
3597
3598 if (!p->input_mappings || !pa_idxset_get_by_data(p->input_mappings, m, NULL)) {
3599 snd_pcm_close(m->input_pcm);
3600 m->input_pcm = NULL;
3601 }
3602 }
3603
3604 p->supported = TRUE;
3605
3606 /* Check if we can open all new ones */
3607 if (p->output_mappings)
3608 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3609
3610 if (m->output_pcm)
3611 continue;
3612
3613 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
3614 try_map = m->channel_map;
3615 try_ss = *ss;
3616 try_ss.channels = try_map.channels;
3617
3618 try_period_size =
3619 pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
3620 pa_frame_size(&try_ss);
3621 try_buffer_size = default_n_fragments * try_period_size;
3622
3623 if (!(m ->output_pcm = pa_alsa_open_by_template(
3624 m->device_strings,
3625 dev_id,
3626 NULL,
3627 &try_ss, &try_map,
3628 SND_PCM_STREAM_PLAYBACK,
3629 &try_period_size, &try_buffer_size, 0, NULL, NULL,
3630 TRUE))) {
3631 p->supported = FALSE;
3632 break;
3633 }
3634 }
3635
3636 if (p->input_mappings && p->supported)
3637 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3638
3639 if (m->input_pcm)
3640 continue;
3641
3642 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
3643 try_map = m->channel_map;
3644 try_ss = *ss;
3645 try_ss.channels = try_map.channels;
3646
3647 try_period_size =
3648 pa_usec_to_bytes(default_fragment_size_msec*PA_USEC_PER_MSEC, &try_ss) /
3649 pa_frame_size(&try_ss);
3650 try_buffer_size = default_n_fragments * try_period_size;
3651
3652 if (!(m ->input_pcm = pa_alsa_open_by_template(
3653 m->device_strings,
3654 dev_id,
3655 NULL,
3656 &try_ss, &try_map,
3657 SND_PCM_STREAM_CAPTURE,
3658 &try_period_size, &try_buffer_size, 0, NULL, NULL,
3659 TRUE))) {
3660 p->supported = FALSE;
3661 break;
3662 }
3663 }
3664
3665 last = p;
3666
3667 if (p->supported)
3668 pa_log_debug("Profile %s supported.", p->name);
3669 }
3670
3671 /* Clean up */
3672 if (last) {
3673 uint32_t idx;
3674
3675 if (last->output_mappings)
3676 PA_IDXSET_FOREACH(m, last->output_mappings, idx)
3677 if (m->output_pcm) {
3678
3679 if (last->supported)
3680 m->supported++;
3681
3682 snd_pcm_close(m->output_pcm);
3683 m->output_pcm = NULL;
3684 }
3685
3686 if (last->input_mappings)
3687 PA_IDXSET_FOREACH(m, last->input_mappings, idx)
3688 if (m->input_pcm) {
3689
3690 if (last->supported)
3691 m->supported++;
3692
3693 snd_pcm_close(m->input_pcm);
3694 m->input_pcm = NULL;
3695 }
3696 }
3697
3698 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3699 if (!p->supported) {
3700 pa_hashmap_remove(ps->profiles, p->name);
3701 profile_free(p);
3702 }
3703
3704 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3705 if (m->supported <= 0) {
3706 pa_hashmap_remove(ps->mappings, m->name);
3707 mapping_free(m);
3708 }
3709
3710 ps->probed = TRUE;
3711 }
3712
3713 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
3714 pa_alsa_profile *p;
3715 pa_alsa_mapping *m;
3716 pa_alsa_decibel_fix *db_fix;
3717 void *state;
3718
3719 pa_assert(ps);
3720
3721 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
3722 (void*)
3723 ps,
3724 pa_yes_no(ps->auto_profiles),
3725 pa_yes_no(ps->probed),
3726 pa_hashmap_size(ps->mappings),
3727 pa_hashmap_size(ps->profiles),
3728 pa_hashmap_size(ps->decibel_fixes));
3729
3730 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3731 pa_alsa_mapping_dump(m);
3732
3733 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3734 pa_alsa_profile_dump(p);
3735
3736 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
3737 pa_alsa_decibel_fix_dump(db_fix);
3738 }
3739
3740 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps) {
3741 pa_alsa_path *path;
3742
3743 pa_assert(p);
3744 pa_assert(!*p);
3745 pa_assert(ps);
3746
3747 /* if there is no path, we don't want a port list */
3748 if (!ps->paths)
3749 return;
3750
3751 if (!ps->paths->next){
3752 pa_alsa_setting *s;
3753
3754 /* If there is only one path, but no or only one setting, then
3755 * we want a port list either */
3756 if (!ps->paths->settings || !ps->paths->settings->next)
3757 return;
3758
3759 /* Ok, there is only one path, however with multiple settings,
3760 * so let's create a port for each setting */
3761 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3762
3763 PA_LLIST_FOREACH(s, ps->paths->settings) {
3764 pa_device_port *port;
3765 pa_alsa_port_data *data;
3766
3767 port = pa_device_port_new(s->name, s->description, sizeof(pa_alsa_port_data));
3768 port->priority = s->priority;
3769
3770 data = PA_DEVICE_PORT_DATA(port);
3771 data->path = ps->paths;
3772 data->setting = s;
3773
3774 pa_hashmap_put(*p, port->name, port);
3775 }
3776
3777 } else {
3778
3779 /* We have multiple paths, so let's create a port for each
3780 * one, and each of each settings */
3781 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3782
3783 PA_LLIST_FOREACH(path, ps->paths) {
3784
3785 if (!path->settings || !path->settings->next) {
3786 pa_device_port *port;
3787 pa_alsa_port_data *data;
3788
3789 /* If there is no or just one setting we only need a
3790 * single entry */
3791
3792 port = pa_device_port_new(path->name, path->description, sizeof(pa_alsa_port_data));
3793 port->priority = path->priority * 100;
3794
3795
3796 data = PA_DEVICE_PORT_DATA(port);
3797 data->path = path;
3798 data->setting = path->settings;
3799
3800 pa_hashmap_put(*p, port->name, port);
3801 } else {
3802 pa_alsa_setting *s;
3803
3804 PA_LLIST_FOREACH(s, path->settings) {
3805 pa_device_port *port;
3806 pa_alsa_port_data *data;
3807 char *n, *d;
3808
3809 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
3810
3811 if (s->description[0])
3812 d = pa_sprintf_malloc(_("%s / %s"), path->description, s->description);
3813 else
3814 d = pa_xstrdup(path->description);
3815
3816 port = pa_device_port_new(n, d, sizeof(pa_alsa_port_data));
3817 port->priority = path->priority * 100 + s->priority;
3818
3819 pa_xfree(n);
3820 pa_xfree(d);
3821
3822 data = PA_DEVICE_PORT_DATA(port);
3823 data->path = path;
3824 data->setting = s;
3825
3826 pa_hashmap_put(*p, port->name, port);
3827 }
3828 }
3829 }
3830 }
3831
3832 pa_log_debug("Added %u ports", pa_hashmap_size(*p));
3833 }