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