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