]> code.delx.au - pulseaudio/blob - src/modules/alsa/alsa-mixer.c
alsa: Improve "well known descriptions" for ports
[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
3019 if (a->switch_use != PA_ALSA_SWITCH_IGNORE) {
3020 /* "On" is a subset of "Mute".
3021 * "Off" is a subset of "Mute".
3022 * "On" is a subset of "Select", if there is an "Option:On" in B.
3023 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3024 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3025
3026 if (a->switch_use != b->switch_use) {
3027
3028 if (a->switch_use == PA_ALSA_SWITCH_SELECT || a->switch_use == PA_ALSA_SWITCH_MUTE
3029 || b->switch_use == PA_ALSA_SWITCH_OFF || b->switch_use == PA_ALSA_SWITCH_ON)
3030 return FALSE;
3031
3032 if (b->switch_use == PA_ALSA_SWITCH_SELECT) {
3033 if (a->switch_use == PA_ALSA_SWITCH_ON) {
3034 if (!options_have_option(b->options, "on"))
3035 return FALSE;
3036 } else if (a->switch_use == PA_ALSA_SWITCH_OFF) {
3037 if (!options_have_option(b->options, "off"))
3038 return FALSE;
3039 }
3040 }
3041 } else if (a->switch_use == PA_ALSA_SWITCH_SELECT) {
3042 if (!enumeration_is_subset(a->options, b->options))
3043 return FALSE;
3044 }
3045 }
3046
3047 if (a->enumeration_use != PA_ALSA_ENUMERATION_IGNORE) {
3048 if (b->enumeration_use == PA_ALSA_ENUMERATION_IGNORE)
3049 return FALSE;
3050 if (!enumeration_is_subset(a->options, b->options))
3051 return FALSE;
3052 }
3053
3054 return TRUE;
3055 }
3056
3057 static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
3058 pa_alsa_path *p;
3059 void *state;
3060
3061 pa_assert(ps);
3062 pa_assert(m);
3063
3064 /* If we only have one path, then don't bother */
3065 if (pa_hashmap_size(ps->paths) < 2)
3066 return;
3067
3068 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3069 pa_alsa_path *p2;
3070 void *state2;
3071
3072 PA_HASHMAP_FOREACH(p2, ps->paths, state2) {
3073 pa_alsa_element *ea, *eb;
3074 pa_bool_t is_subset = TRUE;
3075
3076 if (p == p2)
3077 continue;
3078
3079 /* Compare the elements of each set... */
3080 pa_assert_se(ea = p->elements);
3081 pa_assert_se(eb = p2->elements);
3082
3083 while (is_subset) {
3084 if (pa_streq(ea->alsa_name, eb->alsa_name)) {
3085 if (element_is_subset(ea, eb, m)) {
3086 ea = ea->next;
3087 eb = eb->next;
3088 if ((ea && !eb) || (!ea && eb))
3089 is_subset = FALSE;
3090 else if (!ea && !eb)
3091 break;
3092 } else
3093 is_subset = FALSE;
3094
3095 } else
3096 is_subset = FALSE;
3097 }
3098
3099 if (is_subset) {
3100 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p->name, p2->name);
3101 pa_hashmap_remove(ps->paths, p);
3102 break;
3103 }
3104 }
3105 }
3106 }
3107
3108 static pa_alsa_path* path_set_find_path_by_name(pa_alsa_path_set *ps, const char* name, pa_alsa_path *ignore)
3109 {
3110 pa_alsa_path* p;
3111 void *state;
3112
3113 PA_HASHMAP_FOREACH(p, ps->paths, state)
3114 if (p != ignore && pa_streq(p->name, name))
3115 return p;
3116 return NULL;
3117 }
3118
3119 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
3120 pa_alsa_path *p, *q;
3121 void *state, *state2;
3122
3123 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3124 unsigned i;
3125 char *m;
3126
3127 q = path_set_find_path_by_name(ps, p->name, p);
3128
3129 if (!q)
3130 continue;
3131
3132 m = pa_xstrdup(p->name);
3133
3134 /* OK, this name is not unique, hence let's rename */
3135 i = 1;
3136 PA_HASHMAP_FOREACH(q, ps->paths, state2) {
3137 char *nn, *nd;
3138
3139 if (!pa_streq(q->name, m))
3140 continue;
3141
3142 nn = pa_sprintf_malloc("%s-%u", m, i);
3143 pa_xfree(q->name);
3144 q->name = nn;
3145
3146 nd = pa_sprintf_malloc("%s %u", q->description, i);
3147 pa_xfree(q->description);
3148 q->description = nd;
3149
3150 i++;
3151 }
3152
3153 pa_xfree(m);
3154 }
3155 }
3156
3157 static void mapping_free(pa_alsa_mapping *m) {
3158 pa_assert(m);
3159
3160 pa_xfree(m->name);
3161 pa_xfree(m->description);
3162
3163 pa_xstrfreev(m->device_strings);
3164 pa_xstrfreev(m->input_path_names);
3165 pa_xstrfreev(m->output_path_names);
3166 pa_xstrfreev(m->input_element);
3167 pa_xstrfreev(m->output_element);
3168 if (m->input_path_set)
3169 pa_alsa_path_set_free(m->input_path_set);
3170 if (m->output_path_set)
3171 pa_alsa_path_set_free(m->output_path_set);
3172
3173 pa_assert(!m->input_pcm);
3174 pa_assert(!m->output_pcm);
3175
3176 pa_xfree(m);
3177 }
3178
3179 static void profile_free(pa_alsa_profile *p) {
3180 pa_assert(p);
3181
3182 pa_xfree(p->name);
3183 pa_xfree(p->description);
3184
3185 pa_xstrfreev(p->input_mapping_names);
3186 pa_xstrfreev(p->output_mapping_names);
3187
3188 if (p->input_mappings)
3189 pa_idxset_free(p->input_mappings, NULL, NULL);
3190
3191 if (p->output_mappings)
3192 pa_idxset_free(p->output_mappings, NULL, NULL);
3193
3194 pa_xfree(p);
3195 }
3196
3197 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
3198 pa_assert(ps);
3199
3200 if (ps->input_paths) {
3201 pa_alsa_path *p;
3202
3203 while ((p = pa_hashmap_steal_first(ps->input_paths)))
3204 pa_alsa_path_free(p);
3205
3206 pa_hashmap_free(ps->input_paths, NULL, NULL);
3207 }
3208
3209 if (ps->output_paths) {
3210 pa_alsa_path *p;
3211
3212 while ((p = pa_hashmap_steal_first(ps->output_paths)))
3213 pa_alsa_path_free(p);
3214
3215 pa_hashmap_free(ps->output_paths, NULL, NULL);
3216 }
3217
3218 if (ps->profiles) {
3219 pa_alsa_profile *p;
3220
3221 while ((p = pa_hashmap_steal_first(ps->profiles)))
3222 profile_free(p);
3223
3224 pa_hashmap_free(ps->profiles, NULL, NULL);
3225 }
3226
3227 if (ps->mappings) {
3228 pa_alsa_mapping *m;
3229
3230 while ((m = pa_hashmap_steal_first(ps->mappings)))
3231 mapping_free(m);
3232
3233 pa_hashmap_free(ps->mappings, NULL, NULL);
3234 }
3235
3236 if (ps->decibel_fixes) {
3237 pa_alsa_decibel_fix *db_fix;
3238
3239 while ((db_fix = pa_hashmap_steal_first(ps->decibel_fixes)))
3240 decibel_fix_free(db_fix);
3241
3242 pa_hashmap_free(ps->decibel_fixes, NULL, NULL);
3243 }
3244
3245 pa_xfree(ps);
3246 }
3247
3248 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
3249 pa_alsa_mapping *m;
3250
3251 if (!pa_startswith(name, "Mapping "))
3252 return NULL;
3253
3254 name += 8;
3255
3256 if ((m = pa_hashmap_get(ps->mappings, name)))
3257 return m;
3258
3259 m = pa_xnew0(pa_alsa_mapping, 1);
3260 m->profile_set = ps;
3261 m->name = pa_xstrdup(name);
3262 pa_channel_map_init(&m->channel_map);
3263
3264 pa_hashmap_put(ps->mappings, m->name, m);
3265
3266 return m;
3267 }
3268
3269 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
3270 pa_alsa_profile *p;
3271
3272 if (!pa_startswith(name, "Profile "))
3273 return NULL;
3274
3275 name += 8;
3276
3277 if ((p = pa_hashmap_get(ps->profiles, name)))
3278 return p;
3279
3280 p = pa_xnew0(pa_alsa_profile, 1);
3281 p->profile_set = ps;
3282 p->name = pa_xstrdup(name);
3283
3284 pa_hashmap_put(ps->profiles, p->name, p);
3285
3286 return p;
3287 }
3288
3289 static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) {
3290 pa_alsa_decibel_fix *db_fix;
3291
3292 if (!pa_startswith(name, "DecibelFix "))
3293 return NULL;
3294
3295 name += 11;
3296
3297 if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name)))
3298 return db_fix;
3299
3300 db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
3301 db_fix->profile_set = ps;
3302 db_fix->name = pa_xstrdup(name);
3303
3304 pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix);
3305
3306 return db_fix;
3307 }
3308
3309 static int mapping_parse_device_strings(
3310 const char *filename,
3311 unsigned line,
3312 const char *section,
3313 const char *lvalue,
3314 const char *rvalue,
3315 void *data,
3316 void *userdata) {
3317
3318 pa_alsa_profile_set *ps = userdata;
3319 pa_alsa_mapping *m;
3320
3321 pa_assert(ps);
3322
3323 if (!(m = mapping_get(ps, section))) {
3324 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3325 return -1;
3326 }
3327
3328 pa_xstrfreev(m->device_strings);
3329 if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
3330 pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
3331 return -1;
3332 }
3333
3334 return 0;
3335 }
3336
3337 static int mapping_parse_channel_map(
3338 const char *filename,
3339 unsigned line,
3340 const char *section,
3341 const char *lvalue,
3342 const char *rvalue,
3343 void *data,
3344 void *userdata) {
3345
3346 pa_alsa_profile_set *ps = userdata;
3347 pa_alsa_mapping *m;
3348
3349 pa_assert(ps);
3350
3351 if (!(m = mapping_get(ps, section))) {
3352 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3353 return -1;
3354 }
3355
3356 if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
3357 pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
3358 return -1;
3359 }
3360
3361 return 0;
3362 }
3363
3364 static int mapping_parse_paths(
3365 const char *filename,
3366 unsigned line,
3367 const char *section,
3368 const char *lvalue,
3369 const char *rvalue,
3370 void *data,
3371 void *userdata) {
3372
3373 pa_alsa_profile_set *ps = userdata;
3374 pa_alsa_mapping *m;
3375
3376 pa_assert(ps);
3377
3378 if (!(m = mapping_get(ps, section))) {
3379 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3380 return -1;
3381 }
3382
3383 if (pa_streq(lvalue, "paths-input")) {
3384 pa_xstrfreev(m->input_path_names);
3385 m->input_path_names = pa_split_spaces_strv(rvalue);
3386 } else {
3387 pa_xstrfreev(m->output_path_names);
3388 m->output_path_names = pa_split_spaces_strv(rvalue);
3389 }
3390
3391 return 0;
3392 }
3393
3394 static int mapping_parse_element(
3395 const char *filename,
3396 unsigned line,
3397 const char *section,
3398 const char *lvalue,
3399 const char *rvalue,
3400 void *data,
3401 void *userdata) {
3402
3403 pa_alsa_profile_set *ps = userdata;
3404 pa_alsa_mapping *m;
3405
3406 pa_assert(ps);
3407
3408 if (!(m = mapping_get(ps, section))) {
3409 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3410 return -1;
3411 }
3412
3413 if (pa_streq(lvalue, "element-input")) {
3414 pa_xstrfreev(m->input_element);
3415 m->input_element = pa_split_spaces_strv(rvalue);
3416 } else {
3417 pa_xstrfreev(m->output_element);
3418 m->output_element = pa_split_spaces_strv(rvalue);
3419 }
3420
3421 return 0;
3422 }
3423
3424 static int mapping_parse_direction(
3425 const char *filename,
3426 unsigned line,
3427 const char *section,
3428 const char *lvalue,
3429 const char *rvalue,
3430 void *data,
3431 void *userdata) {
3432
3433 pa_alsa_profile_set *ps = userdata;
3434 pa_alsa_mapping *m;
3435
3436 pa_assert(ps);
3437
3438 if (!(m = mapping_get(ps, section))) {
3439 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3440 return -1;
3441 }
3442
3443 if (pa_streq(rvalue, "input"))
3444 m->direction = PA_ALSA_DIRECTION_INPUT;
3445 else if (pa_streq(rvalue, "output"))
3446 m->direction = PA_ALSA_DIRECTION_OUTPUT;
3447 else if (pa_streq(rvalue, "any"))
3448 m->direction = PA_ALSA_DIRECTION_ANY;
3449 else {
3450 pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
3451 return -1;
3452 }
3453
3454 return 0;
3455 }
3456
3457 static int mapping_parse_description(
3458 const char *filename,
3459 unsigned line,
3460 const char *section,
3461 const char *lvalue,
3462 const char *rvalue,
3463 void *data,
3464 void *userdata) {
3465
3466 pa_alsa_profile_set *ps = userdata;
3467 pa_alsa_profile *p;
3468 pa_alsa_mapping *m;
3469
3470 pa_assert(ps);
3471
3472 if ((m = mapping_get(ps, section))) {
3473 pa_xfree(m->description);
3474 m->description = pa_xstrdup(rvalue);
3475 } else if ((p = profile_get(ps, section))) {
3476 pa_xfree(p->description);
3477 p->description = pa_xstrdup(rvalue);
3478 } else {
3479 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3480 return -1;
3481 }
3482
3483 return 0;
3484 }
3485
3486 static int mapping_parse_priority(
3487 const char *filename,
3488 unsigned line,
3489 const char *section,
3490 const char *lvalue,
3491 const char *rvalue,
3492 void *data,
3493 void *userdata) {
3494
3495 pa_alsa_profile_set *ps = userdata;
3496 pa_alsa_profile *p;
3497 pa_alsa_mapping *m;
3498 uint32_t prio;
3499
3500 pa_assert(ps);
3501
3502 if (pa_atou(rvalue, &prio) < 0) {
3503 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
3504 return -1;
3505 }
3506
3507 if ((m = mapping_get(ps, section)))
3508 m->priority = prio;
3509 else if ((p = profile_get(ps, section)))
3510 p->priority = prio;
3511 else {
3512 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3513 return -1;
3514 }
3515
3516 return 0;
3517 }
3518
3519 static int profile_parse_mappings(
3520 const char *filename,
3521 unsigned line,
3522 const char *section,
3523 const char *lvalue,
3524 const char *rvalue,
3525 void *data,
3526 void *userdata) {
3527
3528 pa_alsa_profile_set *ps = userdata;
3529 pa_alsa_profile *p;
3530
3531 pa_assert(ps);
3532
3533 if (!(p = profile_get(ps, section))) {
3534 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3535 return -1;
3536 }
3537
3538 if (pa_streq(lvalue, "input-mappings")) {
3539 pa_xstrfreev(p->input_mapping_names);
3540 p->input_mapping_names = pa_split_spaces_strv(rvalue);
3541 } else {
3542 pa_xstrfreev(p->output_mapping_names);
3543 p->output_mapping_names = pa_split_spaces_strv(rvalue);
3544 }
3545
3546 return 0;
3547 }
3548
3549 static int profile_parse_skip_probe(
3550 const char *filename,
3551 unsigned line,
3552 const char *section,
3553 const char *lvalue,
3554 const char *rvalue,
3555 void *data,
3556 void *userdata) {
3557
3558 pa_alsa_profile_set *ps = userdata;
3559 pa_alsa_profile *p;
3560 int b;
3561
3562 pa_assert(ps);
3563
3564 if (!(p = profile_get(ps, section))) {
3565 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3566 return -1;
3567 }
3568
3569 if ((b = pa_parse_boolean(rvalue)) < 0) {
3570 pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
3571 return -1;
3572 }
3573
3574 p->supported = b;
3575
3576 return 0;
3577 }
3578
3579 static int decibel_fix_parse_db_values(
3580 const char *filename,
3581 unsigned line,
3582 const char *section,
3583 const char *lvalue,
3584 const char *rvalue,
3585 void *data,
3586 void *userdata) {
3587
3588 pa_alsa_profile_set *ps = userdata;
3589 pa_alsa_decibel_fix *db_fix;
3590 char **items;
3591 char *item;
3592 long *db_values;
3593 unsigned n = 8; /* Current size of the db_values table. */
3594 unsigned min_step = 0;
3595 unsigned max_step = 0;
3596 unsigned i = 0; /* Index to the items table. */
3597 unsigned prev_step = 0;
3598 double prev_db = 0;
3599
3600 pa_assert(filename);
3601 pa_assert(section);
3602 pa_assert(lvalue);
3603 pa_assert(rvalue);
3604 pa_assert(ps);
3605
3606 if (!(db_fix = decibel_fix_get(ps, section))) {
3607 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3608 return -1;
3609 }
3610
3611 if (!(items = pa_split_spaces_strv(rvalue))) {
3612 pa_log("[%s:%u] Value missing", pa_strnull(filename), line);
3613 return -1;
3614 }
3615
3616 db_values = pa_xnew(long, n);
3617
3618 while ((item = items[i++])) {
3619 char *s = item; /* Step value string. */
3620 char *d = item; /* dB value string. */
3621 uint32_t step;
3622 double db;
3623
3624 /* Move d forward until it points to a colon or to the end of the item. */
3625 for (; *d && *d != ':'; ++d);
3626
3627 if (d == s) {
3628 /* item started with colon. */
3629 pa_log("[%s:%u] No step value found in %s", filename, line, item);
3630 goto fail;
3631 }
3632
3633 if (!*d || !*(d + 1)) {
3634 /* No colon found, or it was the last character in item. */
3635 pa_log("[%s:%u] No dB value found in %s", filename, line, item);
3636 goto fail;
3637 }
3638
3639 /* pa_atou() needs a null-terminating string. Let's replace the colon
3640 * with a zero byte. */
3641 *d++ = '\0';
3642
3643 if (pa_atou(s, &step) < 0) {
3644 pa_log("[%s:%u] Invalid step value: %s", filename, line, s);
3645 goto fail;
3646 }
3647
3648 if (pa_atod(d, &db) < 0) {
3649 pa_log("[%s:%u] Invalid dB value: %s", filename, line, d);
3650 goto fail;
3651 }
3652
3653 if (step <= prev_step && i != 1) {
3654 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename, line, step, prev_step);
3655 goto fail;
3656 }
3657
3658 if (db < prev_db && i != 1) {
3659 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename, line, db, prev_db);
3660 goto fail;
3661 }
3662
3663 if (i == 1) {
3664 min_step = step;
3665 db_values[0] = (long) (db * 100.0);
3666 prev_step = step;
3667 prev_db = db;
3668 } else {
3669 /* Interpolate linearly. */
3670 double db_increment = (db - prev_db) / (step - prev_step);
3671
3672 for (; prev_step < step; ++prev_step, prev_db += db_increment) {
3673
3674 /* Reallocate the db_values table if it's about to overflow. */
3675 if (prev_step + 1 - min_step == n) {
3676 n *= 2;
3677 db_values = pa_xrenew(long, db_values, n);
3678 }
3679
3680 db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
3681 }
3682 }
3683
3684 max_step = step;
3685 }
3686
3687 db_fix->min_step = min_step;
3688 db_fix->max_step = max_step;
3689 pa_xfree(db_fix->db_values);
3690 db_fix->db_values = db_values;
3691
3692 pa_xstrfreev(items);
3693
3694 return 0;
3695
3696 fail:
3697 pa_xstrfreev(items);
3698 pa_xfree(db_values);
3699
3700 return -1;
3701 }
3702
3703 static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
3704 pa_alsa_direction_t direction) {
3705
3706 pa_alsa_path *p;
3707 void *state;
3708 snd_pcm_t *pcm_handle;
3709 pa_alsa_path_set *ps;
3710 snd_mixer_t *mixer_handle;
3711
3712 if (direction == PA_ALSA_DIRECTION_OUTPUT) {
3713 if (m->output_path_set)
3714 return; /* Already probed */
3715 m->output_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3716 pcm_handle = m->output_pcm;
3717 } else {
3718 if (m->input_path_set)
3719 return; /* Already probed */
3720 m->input_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3721 pcm_handle = m->input_pcm;
3722 }
3723
3724 if (!ps)
3725 return; /* No paths */
3726
3727 pa_assert(pcm_handle);
3728
3729 mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL);
3730 if (!mixer_handle) {
3731 /* Cannot open mixer, remove all entries */
3732 while (pa_hashmap_steal_first(ps->paths));
3733 return;
3734 }
3735
3736
3737 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3738 if (pa_alsa_path_probe(p, mixer_handle, m->profile_set->ignore_dB) < 0) {
3739 pa_hashmap_remove(ps->paths, p);
3740 }
3741 }
3742
3743 path_set_condense(ps, mixer_handle);
3744 path_set_make_paths_unique(ps);
3745
3746 if (mixer_handle)
3747 snd_mixer_close(mixer_handle);
3748
3749 pa_log_debug("Available mixer paths (after tidying):");
3750 pa_alsa_path_set_dump(ps);
3751 }
3752
3753 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
3754
3755 static const struct description_map well_known_descriptions[] = {
3756 { "analog-mono", N_("Analog Mono") },
3757 { "analog-stereo", N_("Analog Stereo") },
3758 { "analog-surround-21", N_("Analog Surround 2.1") },
3759 { "analog-surround-30", N_("Analog Surround 3.0") },
3760 { "analog-surround-31", N_("Analog Surround 3.1") },
3761 { "analog-surround-40", N_("Analog Surround 4.0") },
3762 { "analog-surround-41", N_("Analog Surround 4.1") },
3763 { "analog-surround-50", N_("Analog Surround 5.0") },
3764 { "analog-surround-51", N_("Analog Surround 5.1") },
3765 { "analog-surround-61", N_("Analog Surround 6.0") },
3766 { "analog-surround-61", N_("Analog Surround 6.1") },
3767 { "analog-surround-70", N_("Analog Surround 7.0") },
3768 { "analog-surround-71", N_("Analog Surround 7.1") },
3769 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3770 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3771 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3772 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3773 { "hdmi-stereo", N_("Digital Stereo (HDMI)") },
3774 { "hdmi-surround-51", N_("Digital Surround 5.1 (HDMI)") }
3775 };
3776
3777 pa_assert(m);
3778
3779 if (!pa_channel_map_valid(&m->channel_map)) {
3780 pa_log("Mapping %s is missing channel map.", m->name);
3781 return -1;
3782 }
3783
3784 if (!m->device_strings) {
3785 pa_log("Mapping %s is missing device strings.", m->name);
3786 return -1;
3787 }
3788
3789 if ((m->input_path_names && m->input_element) ||
3790 (m->output_path_names && m->output_element)) {
3791 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
3792 return -1;
3793 }
3794
3795 if (!m->description)
3796 m->description = pa_xstrdup(lookup_description(m->name,
3797 well_known_descriptions,
3798 PA_ELEMENTSOF(well_known_descriptions)));
3799
3800 if (!m->description)
3801 m->description = pa_xstrdup(m->name);
3802
3803 if (bonus) {
3804 if (pa_channel_map_equal(&m->channel_map, bonus))
3805 m->priority += 50;
3806 else if (m->channel_map.channels == bonus->channels)
3807 m->priority += 30;
3808 }
3809
3810 return 0;
3811 }
3812
3813 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
3814 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
3815
3816 pa_assert(m);
3817
3818 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3819 m->name,
3820 pa_strnull(m->description),
3821 m->priority,
3822 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
3823 pa_yes_no(m->supported),
3824 m->direction);
3825 }
3826
3827 static void profile_set_add_auto_pair(
3828 pa_alsa_profile_set *ps,
3829 pa_alsa_mapping *m, /* output */
3830 pa_alsa_mapping *n /* input */) {
3831
3832 char *name;
3833 pa_alsa_profile *p;
3834
3835 pa_assert(ps);
3836 pa_assert(m || n);
3837
3838 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
3839 return;
3840
3841 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
3842 return;
3843
3844 if (m && n)
3845 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
3846 else if (m)
3847 name = pa_sprintf_malloc("output:%s", m->name);
3848 else
3849 name = pa_sprintf_malloc("input:%s", n->name);
3850
3851 if (pa_hashmap_get(ps->profiles, name)) {
3852 pa_xfree(name);
3853 return;
3854 }
3855
3856 p = pa_xnew0(pa_alsa_profile, 1);
3857 p->profile_set = ps;
3858 p->name = name;
3859
3860 if (m) {
3861 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3862 pa_idxset_put(p->output_mappings, m, NULL);
3863 p->priority += m->priority * 100;
3864 }
3865
3866 if (n) {
3867 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3868 pa_idxset_put(p->input_mappings, n, NULL);
3869 p->priority += n->priority;
3870 }
3871
3872 pa_hashmap_put(ps->profiles, p->name, p);
3873 }
3874
3875 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
3876 pa_alsa_mapping *m, *n;
3877 void *m_state, *n_state;
3878
3879 pa_assert(ps);
3880
3881 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
3882 profile_set_add_auto_pair(ps, m, NULL);
3883
3884 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3885 profile_set_add_auto_pair(ps, m, n);
3886 }
3887
3888 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3889 profile_set_add_auto_pair(ps, NULL, n);
3890 }
3891
3892 static int profile_verify(pa_alsa_profile *p) {
3893
3894 static const struct description_map well_known_descriptions[] = {
3895 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3896 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3897 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3898 { "off", N_("Off") }
3899 };
3900
3901 pa_assert(p);
3902
3903 /* Replace the output mapping names by the actual mappings */
3904 if (p->output_mapping_names) {
3905 char **name;
3906
3907 pa_assert(!p->output_mappings);
3908 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3909
3910 for (name = p->output_mapping_names; *name; name++) {
3911 pa_alsa_mapping *m;
3912 char **in;
3913 pa_bool_t duplicate = FALSE;
3914
3915 for (in = name + 1; *in; in++)
3916 if (pa_streq(*name, *in)) {
3917 duplicate = TRUE;
3918 break;
3919 }
3920
3921 if (duplicate)
3922 continue;
3923
3924 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
3925 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
3926 return -1;
3927 }
3928
3929 pa_idxset_put(p->output_mappings, m, NULL);
3930
3931 if (p->supported)
3932 m->supported++;
3933 }
3934
3935 pa_xstrfreev(p->output_mapping_names);
3936 p->output_mapping_names = NULL;
3937 }
3938
3939 /* Replace the input mapping names by the actual mappings */
3940 if (p->input_mapping_names) {
3941 char **name;
3942
3943 pa_assert(!p->input_mappings);
3944 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3945
3946 for (name = p->input_mapping_names; *name; name++) {
3947 pa_alsa_mapping *m;
3948 char **in;
3949 pa_bool_t duplicate = FALSE;
3950
3951 for (in = name + 1; *in; in++)
3952 if (pa_streq(*name, *in)) {
3953 duplicate = TRUE;
3954 break;
3955 }
3956
3957 if (duplicate)
3958 continue;
3959
3960 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
3961 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
3962 return -1;
3963 }
3964
3965 pa_idxset_put(p->input_mappings, m, NULL);
3966
3967 if (p->supported)
3968 m->supported++;
3969 }
3970
3971 pa_xstrfreev(p->input_mapping_names);
3972 p->input_mapping_names = NULL;
3973 }
3974
3975 if (!p->input_mappings && !p->output_mappings) {
3976 pa_log("Profile '%s' lacks mappings.", p->name);
3977 return -1;
3978 }
3979
3980 if (!p->description)
3981 p->description = pa_xstrdup(lookup_description(p->name,
3982 well_known_descriptions,
3983 PA_ELEMENTSOF(well_known_descriptions)));
3984
3985 if (!p->description) {
3986 pa_strbuf *sb;
3987 uint32_t idx;
3988 pa_alsa_mapping *m;
3989
3990 sb = pa_strbuf_new();
3991
3992 if (p->output_mappings)
3993 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3994 if (!pa_strbuf_isempty(sb))
3995 pa_strbuf_puts(sb, " + ");
3996
3997 pa_strbuf_printf(sb, _("%s Output"), m->description);
3998 }
3999
4000 if (p->input_mappings)
4001 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4002 if (!pa_strbuf_isempty(sb))
4003 pa_strbuf_puts(sb, " + ");
4004
4005 pa_strbuf_printf(sb, _("%s Input"), m->description);
4006 }
4007
4008 p->description = pa_strbuf_tostring_free(sb);
4009 }
4010
4011 return 0;
4012 }
4013
4014 void pa_alsa_profile_dump(pa_alsa_profile *p) {
4015 uint32_t idx;
4016 pa_alsa_mapping *m;
4017 pa_assert(p);
4018
4019 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4020 p->name,
4021 pa_strnull(p->description),
4022 p->priority,
4023 pa_yes_no(p->supported),
4024 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
4025 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
4026
4027 if (p->input_mappings)
4028 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4029 pa_log_debug("Input %s", m->name);
4030
4031 if (p->output_mappings)
4032 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4033 pa_log_debug("Output %s", m->name);
4034 }
4035
4036 static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
4037 pa_assert(db_fix);
4038
4039 /* Check that the dB mapping has been configured. Since "db-values" is
4040 * currently the only option in the DecibelFix section, and decibel fix
4041 * objects don't get created if a DecibelFix section is empty, this is
4042 * actually a redundant check. Having this may prevent future bugs,
4043 * however. */
4044 if (!db_fix->db_values) {
4045 pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
4046 return -1;
4047 }
4048
4049 return 0;
4050 }
4051
4052 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
4053 char *db_values = NULL;
4054
4055 pa_assert(db_fix);
4056
4057 if (db_fix->db_values) {
4058 pa_strbuf *buf;
4059 unsigned long i, nsteps;
4060
4061 pa_assert(db_fix->min_step <= db_fix->max_step);
4062 nsteps = db_fix->max_step - db_fix->min_step + 1;
4063
4064 buf = pa_strbuf_new();
4065 for (i = 0; i < nsteps; ++i)
4066 pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
4067
4068 db_values = pa_strbuf_tostring_free(buf);
4069 }
4070
4071 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4072 db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
4073
4074 pa_xfree(db_values);
4075 }
4076
4077 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
4078 pa_alsa_profile_set *ps;
4079 pa_alsa_profile *p;
4080 pa_alsa_mapping *m;
4081 pa_alsa_decibel_fix *db_fix;
4082 char *fn;
4083 int r;
4084 void *state;
4085
4086 static pa_config_item items[] = {
4087 /* [General] */
4088 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
4089
4090 /* [Mapping ...] */
4091 { "device-strings", mapping_parse_device_strings, NULL, NULL },
4092 { "channel-map", mapping_parse_channel_map, NULL, NULL },
4093 { "paths-input", mapping_parse_paths, NULL, NULL },
4094 { "paths-output", mapping_parse_paths, NULL, NULL },
4095 { "element-input", mapping_parse_element, NULL, NULL },
4096 { "element-output", mapping_parse_element, NULL, NULL },
4097 { "direction", mapping_parse_direction, NULL, NULL },
4098
4099 /* Shared by [Mapping ...] and [Profile ...] */
4100 { "description", mapping_parse_description, NULL, NULL },
4101 { "priority", mapping_parse_priority, NULL, NULL },
4102
4103 /* [Profile ...] */
4104 { "input-mappings", profile_parse_mappings, NULL, NULL },
4105 { "output-mappings", profile_parse_mappings, NULL, NULL },
4106 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
4107
4108 /* [DecibelFix ...] */
4109 { "db-values", decibel_fix_parse_db_values, NULL, NULL },
4110 { NULL, NULL, NULL, NULL }
4111 };
4112
4113 ps = pa_xnew0(pa_alsa_profile_set, 1);
4114 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4115 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4116 ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4117 ps->input_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4118 ps->output_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4119
4120 items[0].data = &ps->auto_profiles;
4121
4122 if (!fname)
4123 fname = "default.conf";
4124
4125 fn = pa_maybe_prefix_path(fname,
4126 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
4127 PA_ALSA_PROFILE_SETS_DIR);
4128
4129 r = pa_config_parse(fn, NULL, items, ps);
4130 pa_xfree(fn);
4131
4132 if (r < 0)
4133 goto fail;
4134
4135 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4136 if (mapping_verify(m, bonus) < 0)
4137 goto fail;
4138
4139 if (ps->auto_profiles)
4140 profile_set_add_auto(ps);
4141
4142 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4143 if (profile_verify(p) < 0)
4144 goto fail;
4145
4146 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4147 if (decibel_fix_verify(db_fix) < 0)
4148 goto fail;
4149
4150 return ps;
4151
4152 fail:
4153 pa_alsa_profile_set_free(ps);
4154 return NULL;
4155 }
4156
4157 static void profile_finalize_probing(pa_alsa_profile *to_be_finalized, pa_alsa_profile *next) {
4158 pa_alsa_mapping *m;
4159 uint32_t idx;
4160
4161 if (!to_be_finalized)
4162 return;
4163
4164 if (to_be_finalized->output_mappings)
4165 PA_IDXSET_FOREACH(m, to_be_finalized->output_mappings, idx) {
4166
4167 if (!m->output_pcm)
4168 continue;
4169
4170 if (to_be_finalized->supported)
4171 m->supported++;
4172
4173 /* If this mapping is also in the next profile, we won't close the
4174 * pcm handle here, because it would get immediately reopened
4175 * anyway. */
4176 if (next && next->output_mappings && pa_idxset_get_by_data(next->output_mappings, m, NULL))
4177 continue;
4178
4179 snd_pcm_close(m->output_pcm);
4180 m->output_pcm = NULL;
4181 }
4182
4183 if (to_be_finalized->input_mappings)
4184 PA_IDXSET_FOREACH(m, to_be_finalized->input_mappings, idx) {
4185
4186 if (!m->input_pcm)
4187 continue;
4188
4189 if (to_be_finalized->supported)
4190 m->supported++;
4191
4192 /* If this mapping is also in the next profile, we won't close the
4193 * pcm handle here, because it would get immediately reopened
4194 * anyway. */
4195 if (next && next->input_mappings && pa_idxset_get_by_data(next->input_mappings, m, NULL))
4196 continue;
4197
4198 snd_pcm_close(m->input_pcm);
4199 m->input_pcm = NULL;
4200 }
4201 }
4202
4203 static snd_pcm_t* mapping_open_pcm(pa_alsa_mapping *m,
4204 const pa_sample_spec *ss,
4205 const char *dev_id,
4206 int mode,
4207 unsigned default_n_fragments,
4208 unsigned default_fragment_size_msec) {
4209
4210 pa_sample_spec try_ss = *ss;
4211 pa_channel_map try_map = m->channel_map;
4212 snd_pcm_uframes_t try_period_size, try_buffer_size;
4213
4214 try_ss.channels = try_map.channels;
4215
4216 try_period_size =
4217 pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
4218 pa_frame_size(&try_ss);
4219 try_buffer_size = default_n_fragments * try_period_size;
4220
4221 return pa_alsa_open_by_template(
4222 m->device_strings, dev_id, NULL, &try_ss,
4223 &try_map, mode, &try_period_size,
4224 &try_buffer_size, 0, NULL, NULL, TRUE);
4225 }
4226
4227 void pa_alsa_profile_set_probe(
4228 pa_alsa_profile_set *ps,
4229 const char *dev_id,
4230 const pa_sample_spec *ss,
4231 unsigned default_n_fragments,
4232 unsigned default_fragment_size_msec) {
4233
4234 void *state;
4235 pa_alsa_profile *p, *last = NULL;
4236 pa_alsa_mapping *m;
4237
4238 pa_assert(ps);
4239 pa_assert(dev_id);
4240 pa_assert(ss);
4241
4242 if (ps->probed)
4243 return;
4244
4245 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
4246 uint32_t idx;
4247
4248 /* Skip if this is already marked that it is supported (i.e. from the config file) */
4249 if (!p->supported) {
4250
4251 pa_log_debug("Looking at profile %s", p->name);
4252 profile_finalize_probing(last, p);
4253 p->supported = TRUE;
4254
4255 /* Check if we can open all new ones */
4256 if (p->output_mappings)
4257 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4258
4259 if (m->output_pcm)
4260 continue;
4261
4262 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
4263 if (!(m->output_pcm = mapping_open_pcm(m, ss, dev_id,
4264 SND_PCM_STREAM_PLAYBACK,
4265 default_n_fragments,
4266 default_fragment_size_msec))) {
4267 p->supported = FALSE;
4268 break;
4269 }
4270 }
4271
4272 if (p->input_mappings && p->supported)
4273 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4274
4275 if (m->input_pcm)
4276 continue;
4277
4278 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
4279 if (!(m->input_pcm = mapping_open_pcm(m, ss, dev_id,
4280 SND_PCM_STREAM_CAPTURE,
4281 default_n_fragments,
4282 default_fragment_size_msec))) {
4283 p->supported = FALSE;
4284 break;
4285 }
4286 }
4287
4288 last = p;
4289
4290 if (!p->supported)
4291 continue;
4292 }
4293
4294 pa_log_debug("Profile %s supported.", p->name);
4295
4296 if (p->output_mappings)
4297 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4298 if (m->output_pcm)
4299 mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT);
4300
4301 if (p->input_mappings)
4302 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4303 if (m->input_pcm)
4304 mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT);
4305 }
4306
4307 /* Clean up */
4308 profile_finalize_probing(last, NULL);
4309
4310 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4311 if (!p->supported) {
4312 pa_hashmap_remove(ps->profiles, p->name);
4313 profile_free(p);
4314 }
4315
4316 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4317 if (m->supported <= 0) {
4318 pa_hashmap_remove(ps->mappings, m->name);
4319 mapping_free(m);
4320 }
4321
4322 ps->probed = TRUE;
4323 }
4324
4325 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
4326 pa_alsa_profile *p;
4327 pa_alsa_mapping *m;
4328 pa_alsa_decibel_fix *db_fix;
4329 void *state;
4330
4331 pa_assert(ps);
4332
4333 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4334 (void*)
4335 ps,
4336 pa_yes_no(ps->auto_profiles),
4337 pa_yes_no(ps->probed),
4338 pa_hashmap_size(ps->mappings),
4339 pa_hashmap_size(ps->profiles),
4340 pa_hashmap_size(ps->decibel_fixes));
4341
4342 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4343 pa_alsa_mapping_dump(m);
4344
4345 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4346 pa_alsa_profile_dump(p);
4347
4348 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4349 pa_alsa_decibel_fix_dump(db_fix);
4350 }
4351
4352 static pa_device_port* device_port_alsa_init(pa_hashmap *ports,
4353 const char* name,
4354 const char* description,
4355 pa_alsa_path *path,
4356 pa_alsa_setting *setting,
4357 pa_card_profile *cp,
4358 pa_hashmap *extra,
4359 pa_core *core) {
4360
4361 pa_device_port * p = pa_hashmap_get(ports, name);
4362 if (!p) {
4363 pa_alsa_port_data *data;
4364
4365 p = pa_device_port_new(core, name, description, sizeof(pa_alsa_port_data));
4366 pa_assert(p);
4367 pa_hashmap_put(ports, name, p);
4368 p->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4369
4370 data = PA_DEVICE_PORT_DATA(p);
4371 data->path = path;
4372 data->setting = setting;
4373 }
4374
4375 p->is_input |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_INPUT;
4376 p->is_output |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_OUTPUT;
4377
4378 if (cp)
4379 pa_hashmap_put(p->profiles, cp->name, cp);
4380
4381 if (extra) {
4382 pa_hashmap_put(extra, name, p);
4383 pa_device_port_ref(p);
4384 }
4385
4386 return p;
4387 }
4388
4389 void pa_alsa_path_set_add_ports(
4390 pa_alsa_path_set *ps,
4391 pa_card_profile *cp,
4392 pa_hashmap *ports,
4393 pa_hashmap *extra,
4394 pa_core *core) {
4395
4396 pa_alsa_path *path;
4397 void *state;
4398
4399 pa_assert(ports);
4400
4401 if (!ps)
4402 return;
4403
4404 PA_HASHMAP_FOREACH(path, ps->paths, state) {
4405 if (!path->settings || !path->settings->next) {
4406 /* If there is no or just one setting we only need a
4407 * single entry */
4408 pa_device_port *port = device_port_alsa_init(ports, path->name,
4409 path->description, path, path->settings, cp, extra, core);
4410 port->priority = path->priority * 100;
4411
4412 } else {
4413 pa_alsa_setting *s;
4414 PA_LLIST_FOREACH(s, path->settings) {
4415 pa_device_port *port;
4416 char *n, *d;
4417
4418 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
4419
4420 if (s->description[0])
4421 d = pa_sprintf_malloc(_("%s / %s"), path->description, s->description);
4422 else
4423 d = pa_xstrdup(path->description);
4424
4425 port = device_port_alsa_init(ports, n, d, path, s, cp, extra, core);
4426 port->priority = path->priority * 100 + s->priority;
4427
4428 pa_xfree(n);
4429 pa_xfree(d);
4430 }
4431 }
4432 }
4433 }
4434
4435 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps, pa_card *card) {
4436
4437 pa_assert(p);
4438 pa_assert(!*p);
4439 pa_assert(ps);
4440
4441 if (ps->paths && pa_hashmap_size(ps->paths) > 0) {
4442 pa_assert(card);
4443 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4444 pa_alsa_path_set_add_ports(ps, NULL, card->ports, *p, card->core);
4445 }
4446
4447 pa_log_debug("Added %u ports", *p ? pa_hashmap_size(*p) : 0);
4448 }