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