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