]> code.delx.au - pulseaudio/blob - src/modules/alsa/alsa-mixer.c
alsa-mixer: Take override-maps into account in subset elimination
[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_("Microphone") },
2303 { "analog-input-microphone-front", N_("Front Microphone") },
2304 { "analog-input-microphone-rear", N_("Rear Microphone") },
2305 { "analog-input-microphone-dock", N_("Dock Microphone") },
2306 { "analog-input-microphone-internal", N_("Internal Microphone") },
2307 { "analog-input-linein", N_("Line In") },
2308 { "analog-input-radio", N_("Radio") },
2309 { "analog-input-video", N_("Video") },
2310 { "analog-output", N_("Analog Output") },
2311 { "analog-output-headphones", N_("Headphones") },
2312 { "analog-output-lfe-on-mono", N_("LFE on Separate Mono Output") },
2313 { "analog-output-lineout", N_("Line Out") },
2314 { "analog-output-mono", N_("Analog Mono Output") },
2315 { "analog-output-speaker", N_("Speakers") },
2316 { "hdmi-output", N_("HDMI / DisplayPort") },
2317 { "iec958-stereo-output", N_("Digital Output (S/PDIF)") },
2318 { "iec958-passthrough-output", N_("Digital Passthrough (S/PDIF)") }
2319 };
2320
2321 pa_alsa_element *e;
2322
2323 pa_assert(p);
2324
2325 PA_LLIST_FOREACH(e, p->elements)
2326 if (element_verify(e) < 0)
2327 return -1;
2328
2329 if (!p->description)
2330 p->description = pa_xstrdup(lookup_description(p->name,
2331 well_known_descriptions,
2332 PA_ELEMENTSOF(well_known_descriptions)));
2333
2334 if (!p->description)
2335 p->description = pa_xstrdup(p->name);
2336
2337 return 0;
2338 }
2339
2340 static const char *get_default_paths_dir(void) {
2341 if (pa_run_from_build_tree())
2342 return PA_BUILDDIR "/modules/alsa/mixer/paths/";
2343 else
2344 return PA_ALSA_PATHS_DIR;
2345 }
2346
2347 pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa_direction_t direction) {
2348 pa_alsa_path *p;
2349 char *fn;
2350 int r;
2351 const char *n;
2352
2353 pa_config_item items[] = {
2354 /* [General] */
2355 { "priority", pa_config_parse_unsigned, NULL, "General" },
2356 { "description", pa_config_parse_string, NULL, "General" },
2357 { "name", pa_config_parse_string, NULL, "General" },
2358
2359 /* [Option ...] */
2360 { "priority", option_parse_priority, NULL, NULL },
2361 { "name", option_parse_name, NULL, NULL },
2362
2363 /* [Element ...] */
2364 { "switch", element_parse_switch, NULL, NULL },
2365 { "volume", element_parse_volume, NULL, NULL },
2366 { "enumeration", element_parse_enumeration, NULL, NULL },
2367 { "override-map.1", element_parse_override_map, NULL, NULL },
2368 { "override-map.2", element_parse_override_map, NULL, NULL },
2369 /* ... later on we might add override-map.3 and so on here ... */
2370 { "required", element_parse_required, NULL, NULL },
2371 { "required-any", element_parse_required, NULL, NULL },
2372 { "required-absent", element_parse_required, NULL, NULL },
2373 { "direction", element_parse_direction, NULL, NULL },
2374 { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
2375 { "volume-limit", element_parse_volume_limit, NULL, NULL },
2376 { NULL, NULL, NULL, NULL }
2377 };
2378
2379 pa_assert(fname);
2380
2381 p = pa_xnew0(pa_alsa_path, 1);
2382 n = pa_path_get_filename(fname);
2383 p->name = pa_xstrndup(n, strcspn(n, "."));
2384 p->direction = direction;
2385
2386 items[0].data = &p->priority;
2387 items[1].data = &p->description;
2388 items[2].data = &p->name;
2389
2390 if (!paths_dir)
2391 paths_dir = get_default_paths_dir();
2392
2393 fn = pa_maybe_prefix_path(fname, paths_dir);
2394
2395 r = pa_config_parse(fn, NULL, items, p);
2396 pa_xfree(fn);
2397
2398 if (r < 0)
2399 goto fail;
2400
2401 if (path_verify(p) < 0)
2402 goto fail;
2403
2404 return p;
2405
2406 fail:
2407 pa_alsa_path_free(p);
2408 return NULL;
2409 }
2410
2411 pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction) {
2412 pa_alsa_path *p;
2413 pa_alsa_element *e;
2414
2415 pa_assert(element);
2416
2417 p = pa_xnew0(pa_alsa_path, 1);
2418 p->name = pa_xstrdup(element);
2419 p->direction = direction;
2420
2421 e = pa_xnew0(pa_alsa_element, 1);
2422 e->path = p;
2423 e->alsa_name = pa_xstrdup(element);
2424 e->direction = direction;
2425 e->volume_limit = -1;
2426
2427 e->switch_use = PA_ALSA_SWITCH_MUTE;
2428 e->volume_use = PA_ALSA_VOLUME_MERGE;
2429
2430 PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
2431 p->last_element = e;
2432 return p;
2433 }
2434
2435 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
2436 pa_alsa_option *o, *n;
2437
2438 pa_assert(e);
2439
2440 for (o = e->options; o; o = n) {
2441 n = o->next;
2442
2443 if (o->alsa_idx < 0) {
2444 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
2445 option_free(o);
2446 }
2447 }
2448
2449 return
2450 e->switch_use != PA_ALSA_SWITCH_IGNORE ||
2451 e->volume_use != PA_ALSA_VOLUME_IGNORE ||
2452 e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
2453 }
2454
2455 static void path_drop_unsupported(pa_alsa_path *p) {
2456 pa_alsa_element *e, *n;
2457
2458 pa_assert(p);
2459
2460 for (e = p->elements; e; e = n) {
2461 n = e->next;
2462
2463 if (!element_drop_unsupported(e)) {
2464 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
2465 element_free(e);
2466 }
2467 }
2468 }
2469
2470 static void path_make_options_unique(pa_alsa_path *p) {
2471 pa_alsa_element *e;
2472 pa_alsa_option *o, *u;
2473
2474 PA_LLIST_FOREACH(e, p->elements) {
2475 PA_LLIST_FOREACH(o, e->options) {
2476 unsigned i;
2477 char *m;
2478
2479 for (u = o->next; u; u = u->next)
2480 if (pa_streq(u->name, o->name))
2481 break;
2482
2483 if (!u)
2484 continue;
2485
2486 m = pa_xstrdup(o->name);
2487
2488 /* OK, this name is not unique, hence let's rename */
2489 for (i = 1, u = o; u; u = u->next) {
2490 char *nn, *nd;
2491
2492 if (!pa_streq(u->name, m))
2493 continue;
2494
2495 nn = pa_sprintf_malloc("%s-%u", m, i);
2496 pa_xfree(u->name);
2497 u->name = nn;
2498
2499 nd = pa_sprintf_malloc("%s %u", u->description, i);
2500 pa_xfree(u->description);
2501 u->description = nd;
2502
2503 i++;
2504 }
2505
2506 pa_xfree(m);
2507 }
2508 }
2509 }
2510
2511 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
2512 pa_alsa_option *o;
2513
2514 for (; e; e = e->next)
2515 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
2516 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
2517 break;
2518
2519 if (!e)
2520 return FALSE;
2521
2522 for (o = e->options; o; o = o->next) {
2523 pa_alsa_setting *s;
2524
2525 if (template) {
2526 s = pa_xnewdup(pa_alsa_setting, template, 1);
2527 s->options = pa_idxset_copy(template->options);
2528 s->name = pa_sprintf_malloc("%s+%s", template->name, o->name);
2529 s->description =
2530 (template->description[0] && o->description[0])
2531 ? pa_sprintf_malloc("%s / %s", template->description, o->description)
2532 : (template->description[0]
2533 ? pa_xstrdup(template->description)
2534 : pa_xstrdup(o->description));
2535
2536 s->priority = PA_MAX(template->priority, o->priority);
2537 } else {
2538 s = pa_xnew0(pa_alsa_setting, 1);
2539 s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2540 s->name = pa_xstrdup(o->name);
2541 s->description = pa_xstrdup(o->description);
2542 s->priority = o->priority;
2543 }
2544
2545 pa_idxset_put(s->options, o, NULL);
2546
2547 if (element_create_settings(e->next, s))
2548 /* This is not a leaf, so let's get rid of it */
2549 setting_free(s);
2550 else {
2551 /* This is a leaf, so let's add it */
2552 PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2553
2554 e->path->last_setting = s;
2555 }
2556 }
2557
2558 return TRUE;
2559 }
2560
2561 static void path_create_settings(pa_alsa_path *p) {
2562 pa_assert(p);
2563
2564 element_create_settings(p->elements, NULL);
2565 }
2566
2567 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) {
2568 pa_alsa_element *e;
2569 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2570 pa_channel_position_t t;
2571 pa_channel_position_mask_t path_volume_channels = 0;
2572
2573 pa_assert(p);
2574 pa_assert(m);
2575
2576 if (p->probed)
2577 return p->supported ? 0 : -1;
2578 p->probed = TRUE;
2579
2580 pa_zero(min_dB);
2581 pa_zero(max_dB);
2582
2583 pa_log_debug("Probing path '%s'", p->name);
2584
2585 PA_LLIST_FOREACH(e, p->elements) {
2586 if (element_probe(e, m) < 0) {
2587 p->supported = FALSE;
2588 pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2589 return -1;
2590 }
2591 pa_log_debug("Probe of element '%s' succeeded (volume=%d, switch=%d, enumeration=%d).", e->alsa_name, e->volume_use, e->switch_use, e->enumeration_use);
2592
2593 if (ignore_dB)
2594 e->has_dB = FALSE;
2595
2596 if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2597
2598 if (!p->has_volume) {
2599 p->min_volume = e->min_volume;
2600 p->max_volume = e->max_volume;
2601 }
2602
2603 if (e->has_dB) {
2604 if (!p->has_volume) {
2605 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2606 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2607 min_dB[t] = e->min_dB;
2608 max_dB[t] = e->max_dB;
2609 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2610 }
2611
2612 p->has_dB = TRUE;
2613 } else {
2614
2615 if (p->has_dB) {
2616 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2617 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2618 min_dB[t] += e->min_dB;
2619 max_dB[t] += e->max_dB;
2620 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2621 }
2622 } else {
2623 /* Hmm, there's another element before us
2624 * which cannot do dB volumes, so we we need
2625 * to 'neutralize' this slider */
2626 e->volume_use = PA_ALSA_VOLUME_ZERO;
2627 pa_log_info("Zeroing volume of '%s' on path '%s'", e->alsa_name, p->name);
2628 }
2629 }
2630 } else if (p->has_volume) {
2631 /* We can't use this volume, so let's ignore it */
2632 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2633 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e->alsa_name, p->name);
2634 }
2635 p->has_volume = TRUE;
2636 }
2637
2638 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2639 p->has_mute = TRUE;
2640 }
2641
2642 if (p->has_req_any && !p->req_any_present) {
2643 p->supported = FALSE;
2644 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name);
2645 return -1;
2646 }
2647
2648 path_drop_unsupported(p);
2649 path_make_options_unique(p);
2650 path_create_settings(p);
2651
2652 p->supported = TRUE;
2653
2654 p->min_dB = INFINITY;
2655 p->max_dB = -INFINITY;
2656
2657 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2658 if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) {
2659 if (p->min_dB > min_dB[t])
2660 p->min_dB = min_dB[t];
2661
2662 if (p->max_dB < max_dB[t])
2663 p->max_dB = max_dB[t];
2664 }
2665 }
2666
2667 return 0;
2668 }
2669
2670 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2671 pa_assert(s);
2672
2673 pa_log_debug("Setting %s (%s) priority=%u",
2674 s->name,
2675 pa_strnull(s->description),
2676 s->priority);
2677 }
2678
2679 void pa_alsa_option_dump(pa_alsa_option *o) {
2680 pa_assert(o);
2681
2682 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2683 o->alsa_name,
2684 pa_strnull(o->name),
2685 pa_strnull(o->description),
2686 o->alsa_idx,
2687 o->priority);
2688 }
2689
2690 void pa_alsa_element_dump(pa_alsa_element *e) {
2691 pa_alsa_option *o;
2692 pa_assert(e);
2693
2694 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",
2695 e->alsa_name,
2696 e->direction,
2697 e->switch_use,
2698 e->volume_use,
2699 e->volume_limit,
2700 e->enumeration_use,
2701 e->required,
2702 e->required_any,
2703 e->required_absent,
2704 (long long unsigned) e->merged_mask,
2705 e->n_channels,
2706 pa_yes_no(e->override_map));
2707
2708 PA_LLIST_FOREACH(o, e->options)
2709 pa_alsa_option_dump(o);
2710 }
2711
2712 void pa_alsa_path_dump(pa_alsa_path *p) {
2713 pa_alsa_element *e;
2714 pa_alsa_setting *s;
2715 pa_assert(p);
2716
2717 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2718 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2719 p->name,
2720 pa_strnull(p->description),
2721 p->direction,
2722 p->priority,
2723 pa_yes_no(p->probed),
2724 pa_yes_no(p->supported),
2725 pa_yes_no(p->has_mute),
2726 pa_yes_no(p->has_volume),
2727 pa_yes_no(p->has_dB),
2728 p->min_volume, p->max_volume,
2729 p->min_dB, p->max_dB);
2730
2731 PA_LLIST_FOREACH(e, p->elements)
2732 pa_alsa_element_dump(e);
2733
2734 PA_LLIST_FOREACH(s, p->settings)
2735 pa_alsa_setting_dump(s);
2736 }
2737
2738 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2739 snd_mixer_selem_id_t *sid;
2740 snd_mixer_elem_t *me;
2741
2742 pa_assert(e);
2743 pa_assert(m);
2744 pa_assert(cb);
2745
2746 SELEM_INIT(sid, e->alsa_name);
2747 if (!(me = snd_mixer_find_selem(m, sid))) {
2748 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2749 return;
2750 }
2751
2752 snd_mixer_elem_set_callback(me, cb);
2753 snd_mixer_elem_set_callback_private(me, userdata);
2754 }
2755
2756 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2757 pa_alsa_element *e;
2758
2759 pa_assert(p);
2760 pa_assert(m);
2761 pa_assert(cb);
2762
2763 PA_LLIST_FOREACH(e, p->elements)
2764 element_set_callback(e, m, cb, userdata);
2765 }
2766
2767 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2768 pa_alsa_path *p;
2769 void *state;
2770
2771 pa_assert(ps);
2772 pa_assert(m);
2773 pa_assert(cb);
2774
2775 PA_HASHMAP_FOREACH(p, ps->paths, state)
2776 pa_alsa_path_set_callback(p, m, cb, userdata);
2777 }
2778
2779 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction, const char *paths_dir) {
2780 pa_alsa_path_set *ps;
2781 char **pn = NULL, **en = NULL, **ie;
2782 pa_alsa_decibel_fix *db_fix;
2783 void *state, *state2;
2784 pa_hashmap *cache;
2785
2786 pa_assert(m);
2787 pa_assert(m->profile_set);
2788 pa_assert(m->profile_set->decibel_fixes);
2789 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2790
2791 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2792 return NULL;
2793
2794 ps = pa_xnew0(pa_alsa_path_set, 1);
2795 ps->direction = direction;
2796 ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2797
2798 if (direction == PA_ALSA_DIRECTION_OUTPUT) {
2799 pn = m->output_path_names;
2800 cache = m->profile_set->output_paths;
2801 }
2802 else if (direction == PA_ALSA_DIRECTION_INPUT) {
2803 pn = m->input_path_names;
2804 cache = m->profile_set->input_paths;
2805 }
2806
2807 if (pn) {
2808 char **in;
2809
2810 for (in = pn; *in; in++) {
2811 pa_alsa_path *p = NULL;
2812 pa_bool_t duplicate = FALSE;
2813 char **kn;
2814
2815 for (kn = pn; kn < in; kn++)
2816 if (pa_streq(*kn, *in)) {
2817 duplicate = TRUE;
2818 break;
2819 }
2820
2821 if (duplicate)
2822 continue;
2823
2824 p = pa_hashmap_get(cache, *in);
2825 if (!p) {
2826 char *fn = pa_sprintf_malloc("%s.conf", *in);
2827 p = pa_alsa_path_new(paths_dir, fn, direction);
2828 pa_xfree(fn);
2829 if (p)
2830 pa_hashmap_put(cache, *in, p);
2831 }
2832 pa_assert(pa_hashmap_get(cache, *in) == p);
2833 if (p)
2834 pa_hashmap_put(ps->paths, p, p);
2835
2836 }
2837
2838 goto finish;
2839 }
2840
2841 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2842 en = m->output_element;
2843 else if (direction == PA_ALSA_DIRECTION_INPUT)
2844 en = m->input_element;
2845
2846 if (!en) {
2847 pa_alsa_path_set_free(ps);
2848 return NULL;
2849 }
2850
2851 for (ie = en; *ie; ie++) {
2852 char **je;
2853 pa_alsa_path *p;
2854
2855 p = pa_alsa_path_synthesize(*ie, direction);
2856
2857 /* Mark all other passed elements for require-absent */
2858 for (je = en; *je; je++) {
2859 pa_alsa_element *e;
2860
2861 if (je == ie)
2862 continue;
2863
2864 e = pa_xnew0(pa_alsa_element, 1);
2865 e->path = p;
2866 e->alsa_name = pa_xstrdup(*je);
2867 e->direction = direction;
2868 e->required_absent = PA_ALSA_REQUIRED_ANY;
2869 e->volume_limit = -1;
2870
2871 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2872 p->last_element = e;
2873 }
2874
2875 pa_hashmap_put(ps->paths, *ie, p);
2876 }
2877
2878 finish:
2879 /* Assign decibel fixes to elements. */
2880 PA_HASHMAP_FOREACH(db_fix, m->profile_set->decibel_fixes, state) {
2881 pa_alsa_path *p;
2882
2883 PA_HASHMAP_FOREACH(p, ps->paths, state2) {
2884 pa_alsa_element *e;
2885
2886 PA_LLIST_FOREACH(e, p->elements) {
2887 if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_name)) {
2888 /* The profile set that contains the dB fix may be freed
2889 * before the element, so we have to copy the dB fix
2890 * object. */
2891 e->db_fix = pa_xnewdup(pa_alsa_decibel_fix, db_fix, 1);
2892 e->db_fix->profile_set = NULL;
2893 e->db_fix->name = pa_xstrdup(db_fix->name);
2894 e->db_fix->db_values = pa_xmemdup(db_fix->db_values, (db_fix->max_step - db_fix->min_step + 1) * sizeof(long));
2895 }
2896 }
2897 }
2898 }
2899
2900 return ps;
2901 }
2902
2903 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2904 pa_alsa_path *p;
2905 void *state;
2906 pa_assert(ps);
2907
2908 pa_log_debug("Path Set %p, direction=%i",
2909 (void*) ps,
2910 ps->direction);
2911
2912 PA_HASHMAP_FOREACH(p, ps->paths, state)
2913 pa_alsa_path_dump(p);
2914 }
2915
2916
2917 static pa_bool_t options_have_option(pa_alsa_option *options, const char *alsa_name) {
2918 pa_alsa_option *o;
2919
2920 pa_assert(options);
2921 pa_assert(alsa_name);
2922
2923 PA_LLIST_FOREACH(o, options) {
2924 if (pa_streq(o->alsa_name, alsa_name))
2925 return TRUE;
2926 }
2927 return FALSE;
2928 }
2929
2930 static pa_bool_t enumeration_is_subset(pa_alsa_option *a_options, pa_alsa_option *b_options) {
2931 pa_alsa_option *oa, *ob;
2932
2933 if (!a_options) return TRUE;
2934 if (!b_options) return FALSE;
2935
2936 /* If there is an option A offers that B does not, then A is not a subset of B. */
2937 PA_LLIST_FOREACH(oa, a_options) {
2938 pa_bool_t found = FALSE;
2939 PA_LLIST_FOREACH(ob, b_options) {
2940 if (pa_streq(oa->alsa_name, ob->alsa_name)) {
2941 found = TRUE;
2942 break;
2943 }
2944 }
2945 if (!found)
2946 return FALSE;
2947 }
2948 return TRUE;
2949 }
2950
2951 /**
2952 * Compares two elements to see if a is a subset of b
2953 */
2954 static pa_bool_t element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_t *m) {
2955 pa_assert(a);
2956 pa_assert(b);
2957 pa_assert(m);
2958
2959 /* General rules:
2960 * Every state is a subset of itself (with caveats for volume_limits and options)
2961 * IGNORE is a subset of every other state */
2962
2963 /* Check the volume_use */
2964 if (a->volume_use != PA_ALSA_VOLUME_IGNORE) {
2965
2966 /* "Constant" is subset of "Constant" only when their constant values are equal */
2967 if (a->volume_use == PA_ALSA_VOLUME_CONSTANT && b->volume_use == PA_ALSA_VOLUME_CONSTANT && a->constant_volume != b->constant_volume)
2968 return FALSE;
2969
2970 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
2971 if (a->volume_use != b->volume_use && b->volume_use != PA_ALSA_VOLUME_MERGE)
2972 return FALSE;
2973
2974 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
2975 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
2976 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
2977 if (b->volume_use == PA_ALSA_VOLUME_MERGE && b->volume_limit >= 0) {
2978 long a_limit;
2979
2980 if (a->volume_use == PA_ALSA_VOLUME_CONSTANT)
2981 a_limit = a->constant_volume;
2982 else if (a->volume_use == PA_ALSA_VOLUME_ZERO) {
2983 long dB = 0;
2984
2985 if (a->db_fix) {
2986 int rounding = (a->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1);
2987 a_limit = decibel_fix_get_step(a->db_fix, &dB, rounding);
2988 } else {
2989 snd_mixer_selem_id_t *sid;
2990 snd_mixer_elem_t *me;
2991
2992 SELEM_INIT(sid, a->alsa_name);
2993 if (!(me = snd_mixer_find_selem(m, sid))) {
2994 pa_log_warn("Element %s seems to have disappeared.", a->alsa_name);
2995 return FALSE;
2996 }
2997
2998 if (a->direction == PA_ALSA_DIRECTION_OUTPUT) {
2999 if (snd_mixer_selem_ask_playback_dB_vol(me, dB, +1, &a_limit) < 0)
3000 return FALSE;
3001 } else {
3002 if (snd_mixer_selem_ask_capture_dB_vol(me, dB, -1, &a_limit) < 0)
3003 return FALSE;
3004 }
3005 }
3006 } else if (a->volume_use == PA_ALSA_VOLUME_OFF)
3007 a_limit = a->min_volume;
3008 else if (a->volume_use == PA_ALSA_VOLUME_MERGE)
3009 a_limit = a->volume_limit;
3010 else
3011 /* This should never be reached */
3012 pa_assert(FALSE);
3013
3014 if (a_limit > b->volume_limit)
3015 return FALSE;
3016 }
3017
3018 if (a->volume_use == PA_ALSA_VOLUME_MERGE) {
3019 int s;
3020 /* If override-maps are different, they're not subsets */
3021 if (a->n_channels != b->n_channels)
3022 return FALSE;
3023 for (s = 0; s < SND_MIXER_SCHN_LAST; s++)
3024 if (a->masks[s][a->n_channels-1] != b->masks[s][b->n_channels-1]) {
3025 pa_log_debug("Element %s is not a subset - mask a: 0x%lx, mask b: 0x%lx, at channel %d",
3026 a->alsa_name, a->masks[s][a->n_channels-1], b->masks[s][b->n_channels-1], s);
3027 return FALSE;
3028 }
3029 }
3030 }
3031
3032 if (a->switch_use != PA_ALSA_SWITCH_IGNORE) {
3033 /* "On" is a subset of "Mute".
3034 * "Off" is a subset of "Mute".
3035 * "On" is a subset of "Select", if there is an "Option:On" in B.
3036 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3037 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3038
3039 if (a->switch_use != b->switch_use) {
3040
3041 if (a->switch_use == PA_ALSA_SWITCH_SELECT || a->switch_use == PA_ALSA_SWITCH_MUTE
3042 || b->switch_use == PA_ALSA_SWITCH_OFF || b->switch_use == PA_ALSA_SWITCH_ON)
3043 return FALSE;
3044
3045 if (b->switch_use == PA_ALSA_SWITCH_SELECT) {
3046 if (a->switch_use == PA_ALSA_SWITCH_ON) {
3047 if (!options_have_option(b->options, "on"))
3048 return FALSE;
3049 } else if (a->switch_use == PA_ALSA_SWITCH_OFF) {
3050 if (!options_have_option(b->options, "off"))
3051 return FALSE;
3052 }
3053 }
3054 } else if (a->switch_use == PA_ALSA_SWITCH_SELECT) {
3055 if (!enumeration_is_subset(a->options, b->options))
3056 return FALSE;
3057 }
3058 }
3059
3060 if (a->enumeration_use != PA_ALSA_ENUMERATION_IGNORE) {
3061 if (b->enumeration_use == PA_ALSA_ENUMERATION_IGNORE)
3062 return FALSE;
3063 if (!enumeration_is_subset(a->options, b->options))
3064 return FALSE;
3065 }
3066
3067 return TRUE;
3068 }
3069
3070 static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
3071 pa_alsa_path *p;
3072 void *state;
3073
3074 pa_assert(ps);
3075 pa_assert(m);
3076
3077 /* If we only have one path, then don't bother */
3078 if (pa_hashmap_size(ps->paths) < 2)
3079 return;
3080
3081 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3082 pa_alsa_path *p2;
3083 void *state2;
3084
3085 PA_HASHMAP_FOREACH(p2, ps->paths, state2) {
3086 pa_alsa_element *ea, *eb;
3087 pa_bool_t is_subset = TRUE;
3088
3089 if (p == p2)
3090 continue;
3091
3092 /* Compare the elements of each set... */
3093 pa_assert_se(ea = p->elements);
3094 pa_assert_se(eb = p2->elements);
3095
3096 while (is_subset) {
3097 if (pa_streq(ea->alsa_name, eb->alsa_name)) {
3098 if (element_is_subset(ea, eb, m)) {
3099 ea = ea->next;
3100 eb = eb->next;
3101 if ((ea && !eb) || (!ea && eb))
3102 is_subset = FALSE;
3103 else if (!ea && !eb)
3104 break;
3105 } else
3106 is_subset = FALSE;
3107
3108 } else
3109 is_subset = FALSE;
3110 }
3111
3112 if (is_subset) {
3113 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p->name, p2->name);
3114 pa_hashmap_remove(ps->paths, p);
3115 break;
3116 }
3117 }
3118 }
3119 }
3120
3121 static pa_alsa_path* path_set_find_path_by_name(pa_alsa_path_set *ps, const char* name, pa_alsa_path *ignore)
3122 {
3123 pa_alsa_path* p;
3124 void *state;
3125
3126 PA_HASHMAP_FOREACH(p, ps->paths, state)
3127 if (p != ignore && pa_streq(p->name, name))
3128 return p;
3129 return NULL;
3130 }
3131
3132 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
3133 pa_alsa_path *p, *q;
3134 void *state, *state2;
3135
3136 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3137 unsigned i;
3138 char *m;
3139
3140 q = path_set_find_path_by_name(ps, p->name, p);
3141
3142 if (!q)
3143 continue;
3144
3145 m = pa_xstrdup(p->name);
3146
3147 /* OK, this name is not unique, hence let's rename */
3148 i = 1;
3149 PA_HASHMAP_FOREACH(q, ps->paths, state2) {
3150 char *nn, *nd;
3151
3152 if (!pa_streq(q->name, m))
3153 continue;
3154
3155 nn = pa_sprintf_malloc("%s-%u", m, i);
3156 pa_xfree(q->name);
3157 q->name = nn;
3158
3159 nd = pa_sprintf_malloc("%s %u", q->description, i);
3160 pa_xfree(q->description);
3161 q->description = nd;
3162
3163 i++;
3164 }
3165
3166 pa_xfree(m);
3167 }
3168 }
3169
3170 static void mapping_free(pa_alsa_mapping *m) {
3171 pa_assert(m);
3172
3173 pa_xfree(m->name);
3174 pa_xfree(m->description);
3175
3176 pa_xstrfreev(m->device_strings);
3177 pa_xstrfreev(m->input_path_names);
3178 pa_xstrfreev(m->output_path_names);
3179 pa_xstrfreev(m->input_element);
3180 pa_xstrfreev(m->output_element);
3181 if (m->input_path_set)
3182 pa_alsa_path_set_free(m->input_path_set);
3183 if (m->output_path_set)
3184 pa_alsa_path_set_free(m->output_path_set);
3185
3186 pa_assert(!m->input_pcm);
3187 pa_assert(!m->output_pcm);
3188
3189 pa_xfree(m);
3190 }
3191
3192 static void profile_free(pa_alsa_profile *p) {
3193 pa_assert(p);
3194
3195 pa_xfree(p->name);
3196 pa_xfree(p->description);
3197
3198 pa_xstrfreev(p->input_mapping_names);
3199 pa_xstrfreev(p->output_mapping_names);
3200
3201 if (p->input_mappings)
3202 pa_idxset_free(p->input_mappings, NULL, NULL);
3203
3204 if (p->output_mappings)
3205 pa_idxset_free(p->output_mappings, NULL, NULL);
3206
3207 pa_xfree(p);
3208 }
3209
3210 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
3211 pa_assert(ps);
3212
3213 if (ps->input_paths) {
3214 pa_alsa_path *p;
3215
3216 while ((p = pa_hashmap_steal_first(ps->input_paths)))
3217 pa_alsa_path_free(p);
3218
3219 pa_hashmap_free(ps->input_paths, NULL, NULL);
3220 }
3221
3222 if (ps->output_paths) {
3223 pa_alsa_path *p;
3224
3225 while ((p = pa_hashmap_steal_first(ps->output_paths)))
3226 pa_alsa_path_free(p);
3227
3228 pa_hashmap_free(ps->output_paths, NULL, NULL);
3229 }
3230
3231 if (ps->profiles) {
3232 pa_alsa_profile *p;
3233
3234 while ((p = pa_hashmap_steal_first(ps->profiles)))
3235 profile_free(p);
3236
3237 pa_hashmap_free(ps->profiles, NULL, NULL);
3238 }
3239
3240 if (ps->mappings) {
3241 pa_alsa_mapping *m;
3242
3243 while ((m = pa_hashmap_steal_first(ps->mappings)))
3244 mapping_free(m);
3245
3246 pa_hashmap_free(ps->mappings, NULL, NULL);
3247 }
3248
3249 if (ps->decibel_fixes) {
3250 pa_alsa_decibel_fix *db_fix;
3251
3252 while ((db_fix = pa_hashmap_steal_first(ps->decibel_fixes)))
3253 decibel_fix_free(db_fix);
3254
3255 pa_hashmap_free(ps->decibel_fixes, NULL, NULL);
3256 }
3257
3258 pa_xfree(ps);
3259 }
3260
3261 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
3262 pa_alsa_mapping *m;
3263
3264 if (!pa_startswith(name, "Mapping "))
3265 return NULL;
3266
3267 name += 8;
3268
3269 if ((m = pa_hashmap_get(ps->mappings, name)))
3270 return m;
3271
3272 m = pa_xnew0(pa_alsa_mapping, 1);
3273 m->profile_set = ps;
3274 m->name = pa_xstrdup(name);
3275 pa_channel_map_init(&m->channel_map);
3276
3277 pa_hashmap_put(ps->mappings, m->name, m);
3278
3279 return m;
3280 }
3281
3282 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
3283 pa_alsa_profile *p;
3284
3285 if (!pa_startswith(name, "Profile "))
3286 return NULL;
3287
3288 name += 8;
3289
3290 if ((p = pa_hashmap_get(ps->profiles, name)))
3291 return p;
3292
3293 p = pa_xnew0(pa_alsa_profile, 1);
3294 p->profile_set = ps;
3295 p->name = pa_xstrdup(name);
3296
3297 pa_hashmap_put(ps->profiles, p->name, p);
3298
3299 return p;
3300 }
3301
3302 static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) {
3303 pa_alsa_decibel_fix *db_fix;
3304
3305 if (!pa_startswith(name, "DecibelFix "))
3306 return NULL;
3307
3308 name += 11;
3309
3310 if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name)))
3311 return db_fix;
3312
3313 db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
3314 db_fix->profile_set = ps;
3315 db_fix->name = pa_xstrdup(name);
3316
3317 pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix);
3318
3319 return db_fix;
3320 }
3321
3322 static int mapping_parse_device_strings(
3323 const char *filename,
3324 unsigned line,
3325 const char *section,
3326 const char *lvalue,
3327 const char *rvalue,
3328 void *data,
3329 void *userdata) {
3330
3331 pa_alsa_profile_set *ps = userdata;
3332 pa_alsa_mapping *m;
3333
3334 pa_assert(ps);
3335
3336 if (!(m = mapping_get(ps, section))) {
3337 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3338 return -1;
3339 }
3340
3341 pa_xstrfreev(m->device_strings);
3342 if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
3343 pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
3344 return -1;
3345 }
3346
3347 return 0;
3348 }
3349
3350 static int mapping_parse_channel_map(
3351 const char *filename,
3352 unsigned line,
3353 const char *section,
3354 const char *lvalue,
3355 const char *rvalue,
3356 void *data,
3357 void *userdata) {
3358
3359 pa_alsa_profile_set *ps = userdata;
3360 pa_alsa_mapping *m;
3361
3362 pa_assert(ps);
3363
3364 if (!(m = mapping_get(ps, section))) {
3365 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3366 return -1;
3367 }
3368
3369 if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
3370 pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
3371 return -1;
3372 }
3373
3374 return 0;
3375 }
3376
3377 static int mapping_parse_paths(
3378 const char *filename,
3379 unsigned line,
3380 const char *section,
3381 const char *lvalue,
3382 const char *rvalue,
3383 void *data,
3384 void *userdata) {
3385
3386 pa_alsa_profile_set *ps = userdata;
3387 pa_alsa_mapping *m;
3388
3389 pa_assert(ps);
3390
3391 if (!(m = mapping_get(ps, section))) {
3392 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3393 return -1;
3394 }
3395
3396 if (pa_streq(lvalue, "paths-input")) {
3397 pa_xstrfreev(m->input_path_names);
3398 m->input_path_names = pa_split_spaces_strv(rvalue);
3399 } else {
3400 pa_xstrfreev(m->output_path_names);
3401 m->output_path_names = pa_split_spaces_strv(rvalue);
3402 }
3403
3404 return 0;
3405 }
3406
3407 static int mapping_parse_element(
3408 const char *filename,
3409 unsigned line,
3410 const char *section,
3411 const char *lvalue,
3412 const char *rvalue,
3413 void *data,
3414 void *userdata) {
3415
3416 pa_alsa_profile_set *ps = userdata;
3417 pa_alsa_mapping *m;
3418
3419 pa_assert(ps);
3420
3421 if (!(m = mapping_get(ps, section))) {
3422 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3423 return -1;
3424 }
3425
3426 if (pa_streq(lvalue, "element-input")) {
3427 pa_xstrfreev(m->input_element);
3428 m->input_element = pa_split_spaces_strv(rvalue);
3429 } else {
3430 pa_xstrfreev(m->output_element);
3431 m->output_element = pa_split_spaces_strv(rvalue);
3432 }
3433
3434 return 0;
3435 }
3436
3437 static int mapping_parse_direction(
3438 const char *filename,
3439 unsigned line,
3440 const char *section,
3441 const char *lvalue,
3442 const char *rvalue,
3443 void *data,
3444 void *userdata) {
3445
3446 pa_alsa_profile_set *ps = userdata;
3447 pa_alsa_mapping *m;
3448
3449 pa_assert(ps);
3450
3451 if (!(m = mapping_get(ps, section))) {
3452 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3453 return -1;
3454 }
3455
3456 if (pa_streq(rvalue, "input"))
3457 m->direction = PA_ALSA_DIRECTION_INPUT;
3458 else if (pa_streq(rvalue, "output"))
3459 m->direction = PA_ALSA_DIRECTION_OUTPUT;
3460 else if (pa_streq(rvalue, "any"))
3461 m->direction = PA_ALSA_DIRECTION_ANY;
3462 else {
3463 pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
3464 return -1;
3465 }
3466
3467 return 0;
3468 }
3469
3470 static int mapping_parse_description(
3471 const char *filename,
3472 unsigned line,
3473 const char *section,
3474 const char *lvalue,
3475 const char *rvalue,
3476 void *data,
3477 void *userdata) {
3478
3479 pa_alsa_profile_set *ps = userdata;
3480 pa_alsa_profile *p;
3481 pa_alsa_mapping *m;
3482
3483 pa_assert(ps);
3484
3485 if ((m = mapping_get(ps, section))) {
3486 pa_xfree(m->description);
3487 m->description = pa_xstrdup(rvalue);
3488 } else if ((p = profile_get(ps, section))) {
3489 pa_xfree(p->description);
3490 p->description = pa_xstrdup(rvalue);
3491 } else {
3492 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3493 return -1;
3494 }
3495
3496 return 0;
3497 }
3498
3499 static int mapping_parse_priority(
3500 const char *filename,
3501 unsigned line,
3502 const char *section,
3503 const char *lvalue,
3504 const char *rvalue,
3505 void *data,
3506 void *userdata) {
3507
3508 pa_alsa_profile_set *ps = userdata;
3509 pa_alsa_profile *p;
3510 pa_alsa_mapping *m;
3511 uint32_t prio;
3512
3513 pa_assert(ps);
3514
3515 if (pa_atou(rvalue, &prio) < 0) {
3516 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
3517 return -1;
3518 }
3519
3520 if ((m = mapping_get(ps, section)))
3521 m->priority = prio;
3522 else if ((p = profile_get(ps, section)))
3523 p->priority = prio;
3524 else {
3525 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3526 return -1;
3527 }
3528
3529 return 0;
3530 }
3531
3532 static int profile_parse_mappings(
3533 const char *filename,
3534 unsigned line,
3535 const char *section,
3536 const char *lvalue,
3537 const char *rvalue,
3538 void *data,
3539 void *userdata) {
3540
3541 pa_alsa_profile_set *ps = userdata;
3542 pa_alsa_profile *p;
3543
3544 pa_assert(ps);
3545
3546 if (!(p = profile_get(ps, section))) {
3547 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3548 return -1;
3549 }
3550
3551 if (pa_streq(lvalue, "input-mappings")) {
3552 pa_xstrfreev(p->input_mapping_names);
3553 p->input_mapping_names = pa_split_spaces_strv(rvalue);
3554 } else {
3555 pa_xstrfreev(p->output_mapping_names);
3556 p->output_mapping_names = pa_split_spaces_strv(rvalue);
3557 }
3558
3559 return 0;
3560 }
3561
3562 static int profile_parse_skip_probe(
3563 const char *filename,
3564 unsigned line,
3565 const char *section,
3566 const char *lvalue,
3567 const char *rvalue,
3568 void *data,
3569 void *userdata) {
3570
3571 pa_alsa_profile_set *ps = userdata;
3572 pa_alsa_profile *p;
3573 int b;
3574
3575 pa_assert(ps);
3576
3577 if (!(p = profile_get(ps, section))) {
3578 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3579 return -1;
3580 }
3581
3582 if ((b = pa_parse_boolean(rvalue)) < 0) {
3583 pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
3584 return -1;
3585 }
3586
3587 p->supported = b;
3588
3589 return 0;
3590 }
3591
3592 static int decibel_fix_parse_db_values(
3593 const char *filename,
3594 unsigned line,
3595 const char *section,
3596 const char *lvalue,
3597 const char *rvalue,
3598 void *data,
3599 void *userdata) {
3600
3601 pa_alsa_profile_set *ps = userdata;
3602 pa_alsa_decibel_fix *db_fix;
3603 char **items;
3604 char *item;
3605 long *db_values;
3606 unsigned n = 8; /* Current size of the db_values table. */
3607 unsigned min_step = 0;
3608 unsigned max_step = 0;
3609 unsigned i = 0; /* Index to the items table. */
3610 unsigned prev_step = 0;
3611 double prev_db = 0;
3612
3613 pa_assert(filename);
3614 pa_assert(section);
3615 pa_assert(lvalue);
3616 pa_assert(rvalue);
3617 pa_assert(ps);
3618
3619 if (!(db_fix = decibel_fix_get(ps, section))) {
3620 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3621 return -1;
3622 }
3623
3624 if (!(items = pa_split_spaces_strv(rvalue))) {
3625 pa_log("[%s:%u] Value missing", pa_strnull(filename), line);
3626 return -1;
3627 }
3628
3629 db_values = pa_xnew(long, n);
3630
3631 while ((item = items[i++])) {
3632 char *s = item; /* Step value string. */
3633 char *d = item; /* dB value string. */
3634 uint32_t step;
3635 double db;
3636
3637 /* Move d forward until it points to a colon or to the end of the item. */
3638 for (; *d && *d != ':'; ++d);
3639
3640 if (d == s) {
3641 /* item started with colon. */
3642 pa_log("[%s:%u] No step value found in %s", filename, line, item);
3643 goto fail;
3644 }
3645
3646 if (!*d || !*(d + 1)) {
3647 /* No colon found, or it was the last character in item. */
3648 pa_log("[%s:%u] No dB value found in %s", filename, line, item);
3649 goto fail;
3650 }
3651
3652 /* pa_atou() needs a null-terminating string. Let's replace the colon
3653 * with a zero byte. */
3654 *d++ = '\0';
3655
3656 if (pa_atou(s, &step) < 0) {
3657 pa_log("[%s:%u] Invalid step value: %s", filename, line, s);
3658 goto fail;
3659 }
3660
3661 if (pa_atod(d, &db) < 0) {
3662 pa_log("[%s:%u] Invalid dB value: %s", filename, line, d);
3663 goto fail;
3664 }
3665
3666 if (step <= prev_step && i != 1) {
3667 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename, line, step, prev_step);
3668 goto fail;
3669 }
3670
3671 if (db < prev_db && i != 1) {
3672 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename, line, db, prev_db);
3673 goto fail;
3674 }
3675
3676 if (i == 1) {
3677 min_step = step;
3678 db_values[0] = (long) (db * 100.0);
3679 prev_step = step;
3680 prev_db = db;
3681 } else {
3682 /* Interpolate linearly. */
3683 double db_increment = (db - prev_db) / (step - prev_step);
3684
3685 for (; prev_step < step; ++prev_step, prev_db += db_increment) {
3686
3687 /* Reallocate the db_values table if it's about to overflow. */
3688 if (prev_step + 1 - min_step == n) {
3689 n *= 2;
3690 db_values = pa_xrenew(long, db_values, n);
3691 }
3692
3693 db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
3694 }
3695 }
3696
3697 max_step = step;
3698 }
3699
3700 db_fix->min_step = min_step;
3701 db_fix->max_step = max_step;
3702 pa_xfree(db_fix->db_values);
3703 db_fix->db_values = db_values;
3704
3705 pa_xstrfreev(items);
3706
3707 return 0;
3708
3709 fail:
3710 pa_xstrfreev(items);
3711 pa_xfree(db_values);
3712
3713 return -1;
3714 }
3715
3716 static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
3717 pa_alsa_direction_t direction) {
3718
3719 pa_alsa_path *p;
3720 void *state;
3721 snd_pcm_t *pcm_handle;
3722 pa_alsa_path_set *ps;
3723 snd_mixer_t *mixer_handle;
3724
3725 if (direction == PA_ALSA_DIRECTION_OUTPUT) {
3726 if (m->output_path_set)
3727 return; /* Already probed */
3728 m->output_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3729 pcm_handle = m->output_pcm;
3730 } else {
3731 if (m->input_path_set)
3732 return; /* Already probed */
3733 m->input_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3734 pcm_handle = m->input_pcm;
3735 }
3736
3737 if (!ps)
3738 return; /* No paths */
3739
3740 pa_assert(pcm_handle);
3741
3742 mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL);
3743 if (!mixer_handle) {
3744 /* Cannot open mixer, remove all entries */
3745 while (pa_hashmap_steal_first(ps->paths));
3746 return;
3747 }
3748
3749
3750 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3751 if (pa_alsa_path_probe(p, mixer_handle, m->profile_set->ignore_dB) < 0) {
3752 pa_hashmap_remove(ps->paths, p);
3753 }
3754 }
3755
3756 path_set_condense(ps, mixer_handle);
3757 path_set_make_paths_unique(ps);
3758
3759 if (mixer_handle)
3760 snd_mixer_close(mixer_handle);
3761
3762 pa_log_debug("Available mixer paths (after tidying):");
3763 pa_alsa_path_set_dump(ps);
3764 }
3765
3766 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
3767
3768 static const struct description_map well_known_descriptions[] = {
3769 { "analog-mono", N_("Analog Mono") },
3770 { "analog-stereo", N_("Analog Stereo") },
3771 { "analog-surround-21", N_("Analog Surround 2.1") },
3772 { "analog-surround-30", N_("Analog Surround 3.0") },
3773 { "analog-surround-31", N_("Analog Surround 3.1") },
3774 { "analog-surround-40", N_("Analog Surround 4.0") },
3775 { "analog-surround-41", N_("Analog Surround 4.1") },
3776 { "analog-surround-50", N_("Analog Surround 5.0") },
3777 { "analog-surround-51", N_("Analog Surround 5.1") },
3778 { "analog-surround-61", N_("Analog Surround 6.0") },
3779 { "analog-surround-61", N_("Analog Surround 6.1") },
3780 { "analog-surround-70", N_("Analog Surround 7.0") },
3781 { "analog-surround-71", N_("Analog Surround 7.1") },
3782 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3783 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3784 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3785 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3786 { "hdmi-stereo", N_("Digital Stereo (HDMI)") },
3787 { "hdmi-surround-51", N_("Digital Surround 5.1 (HDMI)") }
3788 };
3789
3790 pa_assert(m);
3791
3792 if (!pa_channel_map_valid(&m->channel_map)) {
3793 pa_log("Mapping %s is missing channel map.", m->name);
3794 return -1;
3795 }
3796
3797 if (!m->device_strings) {
3798 pa_log("Mapping %s is missing device strings.", m->name);
3799 return -1;
3800 }
3801
3802 if ((m->input_path_names && m->input_element) ||
3803 (m->output_path_names && m->output_element)) {
3804 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
3805 return -1;
3806 }
3807
3808 if (!m->description)
3809 m->description = pa_xstrdup(lookup_description(m->name,
3810 well_known_descriptions,
3811 PA_ELEMENTSOF(well_known_descriptions)));
3812
3813 if (!m->description)
3814 m->description = pa_xstrdup(m->name);
3815
3816 if (bonus) {
3817 if (pa_channel_map_equal(&m->channel_map, bonus))
3818 m->priority += 50;
3819 else if (m->channel_map.channels == bonus->channels)
3820 m->priority += 30;
3821 }
3822
3823 return 0;
3824 }
3825
3826 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
3827 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
3828
3829 pa_assert(m);
3830
3831 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3832 m->name,
3833 pa_strnull(m->description),
3834 m->priority,
3835 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
3836 pa_yes_no(m->supported),
3837 m->direction);
3838 }
3839
3840 static void profile_set_add_auto_pair(
3841 pa_alsa_profile_set *ps,
3842 pa_alsa_mapping *m, /* output */
3843 pa_alsa_mapping *n /* input */) {
3844
3845 char *name;
3846 pa_alsa_profile *p;
3847
3848 pa_assert(ps);
3849 pa_assert(m || n);
3850
3851 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
3852 return;
3853
3854 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
3855 return;
3856
3857 if (m && n)
3858 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
3859 else if (m)
3860 name = pa_sprintf_malloc("output:%s", m->name);
3861 else
3862 name = pa_sprintf_malloc("input:%s", n->name);
3863
3864 if (pa_hashmap_get(ps->profiles, name)) {
3865 pa_xfree(name);
3866 return;
3867 }
3868
3869 p = pa_xnew0(pa_alsa_profile, 1);
3870 p->profile_set = ps;
3871 p->name = name;
3872
3873 if (m) {
3874 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3875 pa_idxset_put(p->output_mappings, m, NULL);
3876 p->priority += m->priority * 100;
3877 }
3878
3879 if (n) {
3880 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3881 pa_idxset_put(p->input_mappings, n, NULL);
3882 p->priority += n->priority;
3883 }
3884
3885 pa_hashmap_put(ps->profiles, p->name, p);
3886 }
3887
3888 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
3889 pa_alsa_mapping *m, *n;
3890 void *m_state, *n_state;
3891
3892 pa_assert(ps);
3893
3894 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
3895 profile_set_add_auto_pair(ps, m, NULL);
3896
3897 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3898 profile_set_add_auto_pair(ps, m, n);
3899 }
3900
3901 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3902 profile_set_add_auto_pair(ps, NULL, n);
3903 }
3904
3905 static int profile_verify(pa_alsa_profile *p) {
3906
3907 static const struct description_map well_known_descriptions[] = {
3908 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3909 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3910 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3911 { "off", N_("Off") }
3912 };
3913
3914 pa_assert(p);
3915
3916 /* Replace the output mapping names by the actual mappings */
3917 if (p->output_mapping_names) {
3918 char **name;
3919
3920 pa_assert(!p->output_mappings);
3921 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3922
3923 for (name = p->output_mapping_names; *name; name++) {
3924 pa_alsa_mapping *m;
3925 char **in;
3926 pa_bool_t duplicate = FALSE;
3927
3928 for (in = name + 1; *in; in++)
3929 if (pa_streq(*name, *in)) {
3930 duplicate = TRUE;
3931 break;
3932 }
3933
3934 if (duplicate)
3935 continue;
3936
3937 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
3938 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
3939 return -1;
3940 }
3941
3942 pa_idxset_put(p->output_mappings, m, NULL);
3943
3944 if (p->supported)
3945 m->supported++;
3946 }
3947
3948 pa_xstrfreev(p->output_mapping_names);
3949 p->output_mapping_names = NULL;
3950 }
3951
3952 /* Replace the input mapping names by the actual mappings */
3953 if (p->input_mapping_names) {
3954 char **name;
3955
3956 pa_assert(!p->input_mappings);
3957 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3958
3959 for (name = p->input_mapping_names; *name; name++) {
3960 pa_alsa_mapping *m;
3961 char **in;
3962 pa_bool_t duplicate = FALSE;
3963
3964 for (in = name + 1; *in; in++)
3965 if (pa_streq(*name, *in)) {
3966 duplicate = TRUE;
3967 break;
3968 }
3969
3970 if (duplicate)
3971 continue;
3972
3973 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
3974 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
3975 return -1;
3976 }
3977
3978 pa_idxset_put(p->input_mappings, m, NULL);
3979
3980 if (p->supported)
3981 m->supported++;
3982 }
3983
3984 pa_xstrfreev(p->input_mapping_names);
3985 p->input_mapping_names = NULL;
3986 }
3987
3988 if (!p->input_mappings && !p->output_mappings) {
3989 pa_log("Profile '%s' lacks mappings.", p->name);
3990 return -1;
3991 }
3992
3993 if (!p->description)
3994 p->description = pa_xstrdup(lookup_description(p->name,
3995 well_known_descriptions,
3996 PA_ELEMENTSOF(well_known_descriptions)));
3997
3998 if (!p->description) {
3999 pa_strbuf *sb;
4000 uint32_t idx;
4001 pa_alsa_mapping *m;
4002
4003 sb = pa_strbuf_new();
4004
4005 if (p->output_mappings)
4006 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4007 if (!pa_strbuf_isempty(sb))
4008 pa_strbuf_puts(sb, " + ");
4009
4010 pa_strbuf_printf(sb, _("%s Output"), m->description);
4011 }
4012
4013 if (p->input_mappings)
4014 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4015 if (!pa_strbuf_isempty(sb))
4016 pa_strbuf_puts(sb, " + ");
4017
4018 pa_strbuf_printf(sb, _("%s Input"), m->description);
4019 }
4020
4021 p->description = pa_strbuf_tostring_free(sb);
4022 }
4023
4024 return 0;
4025 }
4026
4027 void pa_alsa_profile_dump(pa_alsa_profile *p) {
4028 uint32_t idx;
4029 pa_alsa_mapping *m;
4030 pa_assert(p);
4031
4032 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4033 p->name,
4034 pa_strnull(p->description),
4035 p->priority,
4036 pa_yes_no(p->supported),
4037 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
4038 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
4039
4040 if (p->input_mappings)
4041 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4042 pa_log_debug("Input %s", m->name);
4043
4044 if (p->output_mappings)
4045 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4046 pa_log_debug("Output %s", m->name);
4047 }
4048
4049 static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
4050 pa_assert(db_fix);
4051
4052 /* Check that the dB mapping has been configured. Since "db-values" is
4053 * currently the only option in the DecibelFix section, and decibel fix
4054 * objects don't get created if a DecibelFix section is empty, this is
4055 * actually a redundant check. Having this may prevent future bugs,
4056 * however. */
4057 if (!db_fix->db_values) {
4058 pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
4059 return -1;
4060 }
4061
4062 return 0;
4063 }
4064
4065 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
4066 char *db_values = NULL;
4067
4068 pa_assert(db_fix);
4069
4070 if (db_fix->db_values) {
4071 pa_strbuf *buf;
4072 unsigned long i, nsteps;
4073
4074 pa_assert(db_fix->min_step <= db_fix->max_step);
4075 nsteps = db_fix->max_step - db_fix->min_step + 1;
4076
4077 buf = pa_strbuf_new();
4078 for (i = 0; i < nsteps; ++i)
4079 pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
4080
4081 db_values = pa_strbuf_tostring_free(buf);
4082 }
4083
4084 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4085 db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
4086
4087 pa_xfree(db_values);
4088 }
4089
4090 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
4091 pa_alsa_profile_set *ps;
4092 pa_alsa_profile *p;
4093 pa_alsa_mapping *m;
4094 pa_alsa_decibel_fix *db_fix;
4095 char *fn;
4096 int r;
4097 void *state;
4098
4099 static pa_config_item items[] = {
4100 /* [General] */
4101 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
4102
4103 /* [Mapping ...] */
4104 { "device-strings", mapping_parse_device_strings, NULL, NULL },
4105 { "channel-map", mapping_parse_channel_map, NULL, NULL },
4106 { "paths-input", mapping_parse_paths, NULL, NULL },
4107 { "paths-output", mapping_parse_paths, NULL, NULL },
4108 { "element-input", mapping_parse_element, NULL, NULL },
4109 { "element-output", mapping_parse_element, NULL, NULL },
4110 { "direction", mapping_parse_direction, NULL, NULL },
4111
4112 /* Shared by [Mapping ...] and [Profile ...] */
4113 { "description", mapping_parse_description, NULL, NULL },
4114 { "priority", mapping_parse_priority, NULL, NULL },
4115
4116 /* [Profile ...] */
4117 { "input-mappings", profile_parse_mappings, NULL, NULL },
4118 { "output-mappings", profile_parse_mappings, NULL, NULL },
4119 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
4120
4121 /* [DecibelFix ...] */
4122 { "db-values", decibel_fix_parse_db_values, NULL, NULL },
4123 { NULL, NULL, NULL, NULL }
4124 };
4125
4126 ps = pa_xnew0(pa_alsa_profile_set, 1);
4127 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4128 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4129 ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4130 ps->input_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4131 ps->output_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4132
4133 items[0].data = &ps->auto_profiles;
4134
4135 if (!fname)
4136 fname = "default.conf";
4137
4138 fn = pa_maybe_prefix_path(fname,
4139 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
4140 PA_ALSA_PROFILE_SETS_DIR);
4141
4142 r = pa_config_parse(fn, NULL, items, ps);
4143 pa_xfree(fn);
4144
4145 if (r < 0)
4146 goto fail;
4147
4148 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4149 if (mapping_verify(m, bonus) < 0)
4150 goto fail;
4151
4152 if (ps->auto_profiles)
4153 profile_set_add_auto(ps);
4154
4155 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4156 if (profile_verify(p) < 0)
4157 goto fail;
4158
4159 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4160 if (decibel_fix_verify(db_fix) < 0)
4161 goto fail;
4162
4163 return ps;
4164
4165 fail:
4166 pa_alsa_profile_set_free(ps);
4167 return NULL;
4168 }
4169
4170 static void profile_finalize_probing(pa_alsa_profile *to_be_finalized, pa_alsa_profile *next) {
4171 pa_alsa_mapping *m;
4172 uint32_t idx;
4173
4174 if (!to_be_finalized)
4175 return;
4176
4177 if (to_be_finalized->output_mappings)
4178 PA_IDXSET_FOREACH(m, to_be_finalized->output_mappings, idx) {
4179
4180 if (!m->output_pcm)
4181 continue;
4182
4183 if (to_be_finalized->supported)
4184 m->supported++;
4185
4186 /* If this mapping is also in the next profile, we won't close the
4187 * pcm handle here, because it would get immediately reopened
4188 * anyway. */
4189 if (next && next->output_mappings && pa_idxset_get_by_data(next->output_mappings, m, NULL))
4190 continue;
4191
4192 snd_pcm_close(m->output_pcm);
4193 m->output_pcm = NULL;
4194 }
4195
4196 if (to_be_finalized->input_mappings)
4197 PA_IDXSET_FOREACH(m, to_be_finalized->input_mappings, idx) {
4198
4199 if (!m->input_pcm)
4200 continue;
4201
4202 if (to_be_finalized->supported)
4203 m->supported++;
4204
4205 /* If this mapping is also in the next profile, we won't close the
4206 * pcm handle here, because it would get immediately reopened
4207 * anyway. */
4208 if (next && next->input_mappings && pa_idxset_get_by_data(next->input_mappings, m, NULL))
4209 continue;
4210
4211 snd_pcm_close(m->input_pcm);
4212 m->input_pcm = NULL;
4213 }
4214 }
4215
4216 static snd_pcm_t* mapping_open_pcm(pa_alsa_mapping *m,
4217 const pa_sample_spec *ss,
4218 const char *dev_id,
4219 int mode,
4220 unsigned default_n_fragments,
4221 unsigned default_fragment_size_msec) {
4222
4223 pa_sample_spec try_ss = *ss;
4224 pa_channel_map try_map = m->channel_map;
4225 snd_pcm_uframes_t try_period_size, try_buffer_size;
4226
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 return pa_alsa_open_by_template(
4235 m->device_strings, dev_id, NULL, &try_ss,
4236 &try_map, mode, &try_period_size,
4237 &try_buffer_size, 0, NULL, NULL, TRUE);
4238 }
4239
4240 static void paths_drop_unsupported(pa_hashmap* h) {
4241
4242 void* state = NULL;
4243 const void* key;
4244 pa_alsa_path* p;
4245
4246 pa_assert(h);
4247 p = pa_hashmap_iterate(h, &state, &key);
4248 while (p) {
4249 if (p->supported <= 0) {
4250 pa_hashmap_remove(h, key);
4251 pa_alsa_path_free(p);
4252 }
4253 p = pa_hashmap_iterate(h, &state, &key);
4254 }
4255 }
4256
4257 void pa_alsa_profile_set_probe(
4258 pa_alsa_profile_set *ps,
4259 const char *dev_id,
4260 const pa_sample_spec *ss,
4261 unsigned default_n_fragments,
4262 unsigned default_fragment_size_msec) {
4263
4264 void *state;
4265 pa_alsa_profile *p, *last = NULL;
4266 pa_alsa_mapping *m;
4267
4268 pa_assert(ps);
4269 pa_assert(dev_id);
4270 pa_assert(ss);
4271
4272 if (ps->probed)
4273 return;
4274
4275 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
4276 uint32_t idx;
4277
4278 /* Skip if this is already marked that it is supported (i.e. from the config file) */
4279 if (!p->supported) {
4280
4281 pa_log_debug("Looking at profile %s", p->name);
4282 profile_finalize_probing(last, p);
4283 p->supported = TRUE;
4284
4285 /* Check if we can open all new ones */
4286 if (p->output_mappings)
4287 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4288
4289 if (m->output_pcm)
4290 continue;
4291
4292 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
4293 if (!(m->output_pcm = mapping_open_pcm(m, ss, dev_id,
4294 SND_PCM_STREAM_PLAYBACK,
4295 default_n_fragments,
4296 default_fragment_size_msec))) {
4297 p->supported = FALSE;
4298 break;
4299 }
4300 }
4301
4302 if (p->input_mappings && p->supported)
4303 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4304
4305 if (m->input_pcm)
4306 continue;
4307
4308 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
4309 if (!(m->input_pcm = mapping_open_pcm(m, ss, dev_id,
4310 SND_PCM_STREAM_CAPTURE,
4311 default_n_fragments,
4312 default_fragment_size_msec))) {
4313 p->supported = FALSE;
4314 break;
4315 }
4316 }
4317
4318 last = p;
4319
4320 if (!p->supported)
4321 continue;
4322 }
4323
4324 pa_log_debug("Profile %s supported.", p->name);
4325
4326 if (p->output_mappings)
4327 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4328 if (m->output_pcm)
4329 mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT);
4330
4331 if (p->input_mappings)
4332 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4333 if (m->input_pcm)
4334 mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT);
4335 }
4336
4337 /* Clean up */
4338 profile_finalize_probing(last, NULL);
4339
4340 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4341 if (!p->supported) {
4342 pa_hashmap_remove(ps->profiles, p->name);
4343 profile_free(p);
4344 }
4345
4346 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4347 if (m->supported <= 0) {
4348 pa_hashmap_remove(ps->mappings, m->name);
4349 mapping_free(m);
4350 }
4351
4352 paths_drop_unsupported(ps->input_paths);
4353 paths_drop_unsupported(ps->output_paths);
4354
4355 ps->probed = TRUE;
4356 }
4357
4358 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
4359 pa_alsa_profile *p;
4360 pa_alsa_mapping *m;
4361 pa_alsa_decibel_fix *db_fix;
4362 void *state;
4363
4364 pa_assert(ps);
4365
4366 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4367 (void*)
4368 ps,
4369 pa_yes_no(ps->auto_profiles),
4370 pa_yes_no(ps->probed),
4371 pa_hashmap_size(ps->mappings),
4372 pa_hashmap_size(ps->profiles),
4373 pa_hashmap_size(ps->decibel_fixes));
4374
4375 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4376 pa_alsa_mapping_dump(m);
4377
4378 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4379 pa_alsa_profile_dump(p);
4380
4381 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4382 pa_alsa_decibel_fix_dump(db_fix);
4383 }
4384
4385 static pa_device_port* device_port_alsa_init(pa_hashmap *ports,
4386 const char* name,
4387 const char* description,
4388 pa_alsa_path *path,
4389 pa_alsa_setting *setting,
4390 pa_card_profile *cp,
4391 pa_hashmap *extra,
4392 pa_core *core) {
4393
4394 pa_device_port * p = pa_hashmap_get(ports, name);
4395 if (!p) {
4396 pa_alsa_port_data *data;
4397
4398 p = pa_device_port_new(core, name, description, sizeof(pa_alsa_port_data));
4399 pa_assert(p);
4400 pa_hashmap_put(ports, name, p);
4401 p->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4402
4403 data = PA_DEVICE_PORT_DATA(p);
4404 data->path = path;
4405 data->setting = setting;
4406 }
4407
4408 p->is_input |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_INPUT;
4409 p->is_output |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_OUTPUT;
4410
4411 if (cp)
4412 pa_hashmap_put(p->profiles, cp->name, cp);
4413
4414 if (extra) {
4415 pa_hashmap_put(extra, name, p);
4416 pa_device_port_ref(p);
4417 }
4418
4419 return p;
4420 }
4421
4422 void pa_alsa_path_set_add_ports(
4423 pa_alsa_path_set *ps,
4424 pa_card_profile *cp,
4425 pa_hashmap *ports,
4426 pa_hashmap *extra,
4427 pa_core *core) {
4428
4429 pa_alsa_path *path;
4430 void *state;
4431
4432 pa_assert(ports);
4433
4434 if (!ps)
4435 return;
4436
4437 PA_HASHMAP_FOREACH(path, ps->paths, state) {
4438 if (!path->settings || !path->settings->next) {
4439 /* If there is no or just one setting we only need a
4440 * single entry */
4441 pa_device_port *port = device_port_alsa_init(ports, path->name,
4442 path->description, path, path->settings, cp, extra, core);
4443 port->priority = path->priority * 100;
4444
4445 } else {
4446 pa_alsa_setting *s;
4447 PA_LLIST_FOREACH(s, path->settings) {
4448 pa_device_port *port;
4449 char *n, *d;
4450
4451 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
4452
4453 if (s->description[0])
4454 d = pa_sprintf_malloc("%s / %s", path->description, s->description);
4455 else
4456 d = pa_xstrdup(path->description);
4457
4458 port = device_port_alsa_init(ports, n, d, path, s, cp, extra, core);
4459 port->priority = path->priority * 100 + s->priority;
4460
4461 pa_xfree(n);
4462 pa_xfree(d);
4463 }
4464 }
4465 }
4466 }
4467
4468 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps, pa_card *card) {
4469
4470 pa_assert(p);
4471 pa_assert(!*p);
4472 pa_assert(ps);
4473
4474 if (ps->paths && pa_hashmap_size(ps->paths) > 0) {
4475 pa_assert(card);
4476 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4477 pa_alsa_path_set_add_ports(ps, NULL, card->ports, *p, card->core);
4478 }
4479
4480 pa_log_debug("Added %u ports", *p ? pa_hashmap_size(*p) : 0);
4481 }