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