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