]> code.delx.au - pulseaudio/blob - src/modules/alsa/alsa-mixer.c
alsa: Jack detection kcontrol implementation
[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][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->path = p;
1728 j->name = pa_xstrdup(section);
1729 j->alsa_name = pa_sprintf_malloc("%s Jack", section);
1730 PA_LLIST_INSERT_AFTER(pa_alsa_jack, p->jacks, p->last_jack, j);
1731
1732 finish:
1733 p->last_jack = j;
1734 return j;
1735 }
1736
1737
1738 static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
1739 char *en;
1740 const char *on;
1741 pa_alsa_option *o;
1742 pa_alsa_element *e;
1743
1744 if (!pa_startswith(section, "Option "))
1745 return NULL;
1746
1747 section += 7;
1748
1749 /* This is not an enum section, but an element section? */
1750 if (!(on = strchr(section, ':')))
1751 return NULL;
1752
1753 en = pa_xstrndup(section, on - section);
1754 on++;
1755
1756 if (p->last_option &&
1757 pa_streq(p->last_option->element->alsa_name, en) &&
1758 pa_streq(p->last_option->alsa_name, on)) {
1759 pa_xfree(en);
1760 return p->last_option;
1761 }
1762
1763 pa_assert_se(e = element_get(p, en, FALSE));
1764 pa_xfree(en);
1765
1766 PA_LLIST_FOREACH(o, e->options)
1767 if (pa_streq(o->alsa_name, on))
1768 goto finish;
1769
1770 o = pa_xnew0(pa_alsa_option, 1);
1771 o->element = e;
1772 o->alsa_name = pa_xstrdup(on);
1773 o->alsa_idx = -1;
1774
1775 if (p->last_option && p->last_option->element == e)
1776 PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
1777 else
1778 PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
1779
1780 finish:
1781 p->last_option = o;
1782 return o;
1783 }
1784
1785 static int element_parse_switch(
1786 const char *filename,
1787 unsigned line,
1788 const char *section,
1789 const char *lvalue,
1790 const char *rvalue,
1791 void *data,
1792 void *userdata) {
1793
1794 pa_alsa_path *p = userdata;
1795 pa_alsa_element *e;
1796
1797 pa_assert(p);
1798
1799 if (!(e = element_get(p, section, TRUE))) {
1800 pa_log("[%s:%u] Switch makes no sense in '%s'", filename, line, section);
1801 return -1;
1802 }
1803
1804 if (pa_streq(rvalue, "ignore"))
1805 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1806 else if (pa_streq(rvalue, "mute"))
1807 e->switch_use = PA_ALSA_SWITCH_MUTE;
1808 else if (pa_streq(rvalue, "off"))
1809 e->switch_use = PA_ALSA_SWITCH_OFF;
1810 else if (pa_streq(rvalue, "on"))
1811 e->switch_use = PA_ALSA_SWITCH_ON;
1812 else if (pa_streq(rvalue, "select"))
1813 e->switch_use = PA_ALSA_SWITCH_SELECT;
1814 else {
1815 pa_log("[%s:%u] Switch invalid of '%s'", filename, line, section);
1816 return -1;
1817 }
1818
1819 return 0;
1820 }
1821
1822 static int element_parse_volume(
1823 const char *filename,
1824 unsigned line,
1825 const char *section,
1826 const char *lvalue,
1827 const char *rvalue,
1828 void *data,
1829 void *userdata) {
1830
1831 pa_alsa_path *p = userdata;
1832 pa_alsa_element *e;
1833
1834 pa_assert(p);
1835
1836 if (!(e = element_get(p, section, TRUE))) {
1837 pa_log("[%s:%u] Volume makes no sense in '%s'", filename, line, section);
1838 return -1;
1839 }
1840
1841 if (pa_streq(rvalue, "ignore"))
1842 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1843 else if (pa_streq(rvalue, "merge"))
1844 e->volume_use = PA_ALSA_VOLUME_MERGE;
1845 else if (pa_streq(rvalue, "off"))
1846 e->volume_use = PA_ALSA_VOLUME_OFF;
1847 else if (pa_streq(rvalue, "zero"))
1848 e->volume_use = PA_ALSA_VOLUME_ZERO;
1849 else {
1850 uint32_t constant;
1851
1852 if (pa_atou(rvalue, &constant) >= 0) {
1853 e->volume_use = PA_ALSA_VOLUME_CONSTANT;
1854 e->constant_volume = constant;
1855 } else {
1856 pa_log("[%s:%u] Volume invalid of '%s'", filename, line, section);
1857 return -1;
1858 }
1859 }
1860
1861 return 0;
1862 }
1863
1864 static int element_parse_enumeration(
1865 const char *filename,
1866 unsigned line,
1867 const char *section,
1868 const char *lvalue,
1869 const char *rvalue,
1870 void *data,
1871 void *userdata) {
1872
1873 pa_alsa_path *p = userdata;
1874 pa_alsa_element *e;
1875
1876 pa_assert(p);
1877
1878 if (!(e = element_get(p, section, TRUE))) {
1879 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename, line, section);
1880 return -1;
1881 }
1882
1883 if (pa_streq(rvalue, "ignore"))
1884 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1885 else if (pa_streq(rvalue, "select"))
1886 e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
1887 else {
1888 pa_log("[%s:%u] Enumeration invalid of '%s'", filename, line, section);
1889 return -1;
1890 }
1891
1892 return 0;
1893 }
1894
1895 static int option_parse_priority(
1896 const char *filename,
1897 unsigned line,
1898 const char *section,
1899 const char *lvalue,
1900 const char *rvalue,
1901 void *data,
1902 void *userdata) {
1903
1904 pa_alsa_path *p = userdata;
1905 pa_alsa_option *o;
1906 uint32_t prio;
1907
1908 pa_assert(p);
1909
1910 if (!(o = option_get(p, section))) {
1911 pa_log("[%s:%u] Priority makes no sense in '%s'", filename, line, section);
1912 return -1;
1913 }
1914
1915 if (pa_atou(rvalue, &prio) < 0) {
1916 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
1917 return -1;
1918 }
1919
1920 o->priority = prio;
1921 return 0;
1922 }
1923
1924 static int option_parse_name(
1925 const char *filename,
1926 unsigned line,
1927 const char *section,
1928 const char *lvalue,
1929 const char *rvalue,
1930 void *data,
1931 void *userdata) {
1932
1933 pa_alsa_path *p = userdata;
1934 pa_alsa_option *o;
1935
1936 pa_assert(p);
1937
1938 if (!(o = option_get(p, section))) {
1939 pa_log("[%s:%u] Name makes no sense in '%s'", filename, line, section);
1940 return -1;
1941 }
1942
1943 pa_xfree(o->name);
1944 o->name = pa_xstrdup(rvalue);
1945
1946 return 0;
1947 }
1948
1949 static int element_parse_required(
1950 const char *filename,
1951 unsigned line,
1952 const char *section,
1953 const char *lvalue,
1954 const char *rvalue,
1955 void *data,
1956 void *userdata) {
1957
1958 pa_alsa_path *p = userdata;
1959 pa_alsa_element *e;
1960 pa_alsa_option *o;
1961 pa_alsa_jack *j;
1962 pa_alsa_required_t req;
1963
1964 pa_assert(p);
1965
1966 e = element_get(p, section, TRUE);
1967 o = option_get(p, section);
1968 j = jack_get(p, section);
1969 if (!e && !o && !j) {
1970 pa_log("[%s:%u] Required makes no sense in '%s'", filename, line, section);
1971 return -1;
1972 }
1973
1974 if (pa_streq(rvalue, "ignore"))
1975 req = PA_ALSA_REQUIRED_IGNORE;
1976 else if (pa_streq(rvalue, "switch") && e)
1977 req = PA_ALSA_REQUIRED_SWITCH;
1978 else if (pa_streq(rvalue, "volume") && e)
1979 req = PA_ALSA_REQUIRED_VOLUME;
1980 else if (pa_streq(rvalue, "enumeration"))
1981 req = PA_ALSA_REQUIRED_ENUMERATION;
1982 else if (pa_streq(rvalue, "any"))
1983 req = PA_ALSA_REQUIRED_ANY;
1984 else {
1985 pa_log("[%s:%u] Required invalid of '%s'", filename, line, section);
1986 return -1;
1987 }
1988
1989 if (pa_streq(lvalue, "required-absent")) {
1990 if (e)
1991 e->required_absent = req;
1992 if (o)
1993 o->required_absent = req;
1994 if (j)
1995 j->required_absent = req;
1996 }
1997 else if (pa_streq(lvalue, "required-any")) {
1998 if (e) {
1999 e->required_any = req;
2000 e->path->has_req_any = TRUE;
2001 }
2002 if (o) {
2003 o->required_any = req;
2004 o->element->path->has_req_any = TRUE;
2005 }
2006 if (j) {
2007 j->required_any = req;
2008 j->path->has_req_any = TRUE;
2009 }
2010
2011 }
2012 else {
2013 if (e)
2014 e->required = req;
2015 if (o)
2016 o->required = req;
2017 if (j)
2018 j->required = req;
2019 }
2020
2021 return 0;
2022 }
2023
2024 static int element_parse_direction(
2025 const char *filename,
2026 unsigned line,
2027 const char *section,
2028 const char *lvalue,
2029 const char *rvalue,
2030 void *data,
2031 void *userdata) {
2032
2033 pa_alsa_path *p = userdata;
2034 pa_alsa_element *e;
2035
2036 pa_assert(p);
2037
2038 if (!(e = element_get(p, section, TRUE))) {
2039 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
2040 return -1;
2041 }
2042
2043 if (pa_streq(rvalue, "playback"))
2044 e->direction = PA_ALSA_DIRECTION_OUTPUT;
2045 else if (pa_streq(rvalue, "capture"))
2046 e->direction = PA_ALSA_DIRECTION_INPUT;
2047 else {
2048 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
2049 return -1;
2050 }
2051
2052 return 0;
2053 }
2054
2055 static int element_parse_direction_try_other(
2056 const char *filename,
2057 unsigned line,
2058 const char *section,
2059 const char *lvalue,
2060 const char *rvalue,
2061 void *data,
2062 void *userdata) {
2063
2064 pa_alsa_path *p = userdata;
2065 pa_alsa_element *e;
2066 int yes;
2067
2068 if (!(e = element_get(p, section, TRUE))) {
2069 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
2070 return -1;
2071 }
2072
2073 if ((yes = pa_parse_boolean(rvalue)) < 0) {
2074 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
2075 return -1;
2076 }
2077
2078 e->direction_try_other = !!yes;
2079 return 0;
2080 }
2081
2082 static int element_parse_volume_limit(
2083 const char *filename,
2084 unsigned line,
2085 const char *section,
2086 const char *lvalue,
2087 const char *rvalue,
2088 void *data,
2089 void *userdata) {
2090
2091 pa_alsa_path *p = userdata;
2092 pa_alsa_element *e;
2093 long volume_limit;
2094
2095 if (!(e = element_get(p, section, TRUE))) {
2096 pa_log("[%s:%u] volume-limit makes no sense in '%s'", filename, line, section);
2097 return -1;
2098 }
2099
2100 if (pa_atol(rvalue, &volume_limit) < 0 || volume_limit < 0) {
2101 pa_log("[%s:%u] Invalid value for volume-limit", filename, line);
2102 return -1;
2103 }
2104
2105 e->volume_limit = volume_limit;
2106 return 0;
2107 }
2108
2109 static pa_channel_position_mask_t parse_mask(const char *m) {
2110 pa_channel_position_mask_t v;
2111
2112 if (pa_streq(m, "all-left"))
2113 v = PA_CHANNEL_POSITION_MASK_LEFT;
2114 else if (pa_streq(m, "all-right"))
2115 v = PA_CHANNEL_POSITION_MASK_RIGHT;
2116 else if (pa_streq(m, "all-center"))
2117 v = PA_CHANNEL_POSITION_MASK_CENTER;
2118 else if (pa_streq(m, "all-front"))
2119 v = PA_CHANNEL_POSITION_MASK_FRONT;
2120 else if (pa_streq(m, "all-rear"))
2121 v = PA_CHANNEL_POSITION_MASK_REAR;
2122 else if (pa_streq(m, "all-side"))
2123 v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER;
2124 else if (pa_streq(m, "all-top"))
2125 v = PA_CHANNEL_POSITION_MASK_TOP;
2126 else if (pa_streq(m, "all-no-lfe"))
2127 v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE);
2128 else if (pa_streq(m, "all"))
2129 v = PA_CHANNEL_POSITION_MASK_ALL;
2130 else {
2131 pa_channel_position_t p;
2132
2133 if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
2134 return 0;
2135
2136 v = PA_CHANNEL_POSITION_MASK(p);
2137 }
2138
2139 return v;
2140 }
2141
2142 static int element_parse_override_map(
2143 const char *filename,
2144 unsigned line,
2145 const char *section,
2146 const char *lvalue,
2147 const char *rvalue,
2148 void *data,
2149 void *userdata) {
2150
2151 pa_alsa_path *p = userdata;
2152 pa_alsa_element *e;
2153 const char *state = NULL;
2154 unsigned i = 0;
2155 char *n;
2156
2157 if (!(e = element_get(p, section, TRUE))) {
2158 pa_log("[%s:%u] Override map makes no sense in '%s'", filename, line, section);
2159 return -1;
2160 }
2161
2162 while ((n = pa_split(rvalue, ",", &state))) {
2163 pa_channel_position_mask_t m;
2164
2165 if (!*n)
2166 m = 0;
2167 else {
2168 if ((m = parse_mask(n)) == 0) {
2169 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename, line, n, section);
2170 pa_xfree(n);
2171 return -1;
2172 }
2173 }
2174
2175 if (pa_streq(lvalue, "override-map.1"))
2176 e->masks[i++][0] = m;
2177 else
2178 e->masks[i++][1] = m;
2179
2180 /* Later on we might add override-map.3 and so on here ... */
2181
2182 pa_xfree(n);
2183 }
2184
2185 e->override_map = TRUE;
2186
2187 return 0;
2188 }
2189
2190 static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
2191 snd_mixer_selem_id_t *sid;
2192 snd_mixer_elem_t *me;
2193 int r;
2194
2195 pa_assert(e);
2196 pa_assert(m);
2197
2198 SELEM_INIT(sid, e->alsa_name);
2199 if (!(me = snd_mixer_find_selem(m, sid))) {
2200 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2201 return -1;
2202 }
2203
2204 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
2205
2206 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
2207 r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
2208 else
2209 r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
2210
2211 if (r < 0)
2212 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
2213
2214 } else {
2215 pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
2216
2217 if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0)
2218 pa_log_warn("Failed to set enumeration of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
2219 }
2220
2221 return r;
2222 }
2223
2224 int pa_alsa_setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
2225 pa_alsa_option *o;
2226 uint32_t idx;
2227
2228 pa_assert(s);
2229 pa_assert(m);
2230
2231 PA_IDXSET_FOREACH(o, s->options, idx)
2232 element_set_option(o->element, m, o->alsa_idx);
2233
2234 return 0;
2235 }
2236
2237 static int option_verify(pa_alsa_option *o) {
2238 static const struct description_map well_known_descriptions[] = {
2239 { "input", N_("Input") },
2240 { "input-docking", N_("Docking Station Input") },
2241 { "input-docking-microphone", N_("Docking Station Microphone") },
2242 { "input-docking-linein", N_("Docking Station Line In") },
2243 { "input-linein", N_("Line In") },
2244 { "input-microphone", N_("Microphone") },
2245 { "input-microphone-front", N_("Front Microphone") },
2246 { "input-microphone-rear", N_("Rear Microphone") },
2247 { "input-microphone-external", N_("External Microphone") },
2248 { "input-microphone-internal", N_("Internal Microphone") },
2249 { "input-radio", N_("Radio") },
2250 { "input-video", N_("Video") },
2251 { "input-agc-on", N_("Automatic Gain Control") },
2252 { "input-agc-off", N_("No Automatic Gain Control") },
2253 { "input-boost-on", N_("Boost") },
2254 { "input-boost-off", N_("No Boost") },
2255 { "output-amplifier-on", N_("Amplifier") },
2256 { "output-amplifier-off", N_("No Amplifier") },
2257 { "output-bass-boost-on", N_("Bass Boost") },
2258 { "output-bass-boost-off", N_("No Bass Boost") },
2259 { "output-speaker", N_("Speaker") },
2260 { "output-headphones", N_("Headphones") }
2261 };
2262
2263 pa_assert(o);
2264
2265 if (!o->name) {
2266 pa_log("No name set for option %s", o->alsa_name);
2267 return -1;
2268 }
2269
2270 if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
2271 o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
2272 pa_log("Element %s of option %s not set for select.", o->element->alsa_name, o->name);
2273 return -1;
2274 }
2275
2276 if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
2277 !pa_streq(o->alsa_name, "on") &&
2278 !pa_streq(o->alsa_name, "off")) {
2279 pa_log("Switch %s options need be named off or on ", o->element->alsa_name);
2280 return -1;
2281 }
2282
2283 if (!o->description)
2284 o->description = pa_xstrdup(lookup_description(o->name,
2285 well_known_descriptions,
2286 PA_ELEMENTSOF(well_known_descriptions)));
2287 if (!o->description)
2288 o->description = pa_xstrdup(o->name);
2289
2290 return 0;
2291 }
2292
2293 static int element_verify(pa_alsa_element *e) {
2294 pa_alsa_option *o;
2295
2296 pa_assert(e);
2297
2298 // 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);
2299 if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
2300 (e->required_any != PA_ALSA_REQUIRED_IGNORE && e->required_any == e->required_absent) ||
2301 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required_any != PA_ALSA_REQUIRED_IGNORE) ||
2302 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
2303 pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name);
2304 return -1;
2305 }
2306
2307 if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
2308 pa_log("Element %s cannot set select for both switch and enumeration.", e->alsa_name);
2309 return -1;
2310 }
2311
2312 PA_LLIST_FOREACH(o, e->options)
2313 if (option_verify(o) < 0)
2314 return -1;
2315
2316 return 0;
2317 }
2318
2319 static int path_verify(pa_alsa_path *p) {
2320 static const struct description_map well_known_descriptions[] = {
2321 { "analog-input", N_("Analog Input") },
2322 { "analog-input-microphone", N_("Microphone") },
2323 { "analog-input-microphone-front", N_("Front Microphone") },
2324 { "analog-input-microphone-rear", N_("Rear Microphone") },
2325 { "analog-input-microphone-dock", N_("Dock Microphone") },
2326 { "analog-input-microphone-internal", N_("Internal Microphone") },
2327 { "analog-input-linein", N_("Line In") },
2328 { "analog-input-radio", N_("Radio") },
2329 { "analog-input-video", N_("Video") },
2330 { "analog-output", N_("Analog Output") },
2331 { "analog-output-headphones", N_("Headphones") },
2332 { "analog-output-lfe-on-mono", N_("LFE on Separate Mono Output") },
2333 { "analog-output-lineout", N_("Line Out") },
2334 { "analog-output-mono", N_("Analog Mono Output") },
2335 { "analog-output-speaker", N_("Speakers") },
2336 { "hdmi-output", N_("HDMI / DisplayPort") },
2337 { "iec958-stereo-output", N_("Digital Output (S/PDIF)") },
2338 { "iec958-passthrough-output", N_("Digital Passthrough (S/PDIF)") }
2339 };
2340
2341 pa_alsa_element *e;
2342
2343 pa_assert(p);
2344
2345 PA_LLIST_FOREACH(e, p->elements)
2346 if (element_verify(e) < 0)
2347 return -1;
2348
2349 if (!p->description)
2350 p->description = pa_xstrdup(lookup_description(p->name,
2351 well_known_descriptions,
2352 PA_ELEMENTSOF(well_known_descriptions)));
2353
2354 if (!p->description)
2355 p->description = pa_xstrdup(p->name);
2356
2357 return 0;
2358 }
2359
2360 static const char *get_default_paths_dir(void) {
2361 if (pa_run_from_build_tree())
2362 return PA_BUILDDIR "/modules/alsa/mixer/paths/";
2363 else
2364 return PA_ALSA_PATHS_DIR;
2365 }
2366
2367 pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa_direction_t direction) {
2368 pa_alsa_path *p;
2369 char *fn;
2370 int r;
2371 const char *n;
2372
2373 pa_config_item items[] = {
2374 /* [General] */
2375 { "priority", pa_config_parse_unsigned, NULL, "General" },
2376 { "description", pa_config_parse_string, NULL, "General" },
2377 { "name", pa_config_parse_string, NULL, "General" },
2378
2379 /* [Option ...] */
2380 { "priority", option_parse_priority, NULL, NULL },
2381 { "name", option_parse_name, NULL, NULL },
2382
2383 /* [Element ...] */
2384 { "switch", element_parse_switch, NULL, NULL },
2385 { "volume", element_parse_volume, NULL, NULL },
2386 { "enumeration", element_parse_enumeration, NULL, NULL },
2387 { "override-map.1", element_parse_override_map, NULL, NULL },
2388 { "override-map.2", element_parse_override_map, NULL, NULL },
2389 /* ... later on we might add override-map.3 and so on here ... */
2390 { "required", element_parse_required, NULL, NULL },
2391 { "required-any", element_parse_required, NULL, NULL },
2392 { "required-absent", element_parse_required, NULL, NULL },
2393 { "direction", element_parse_direction, NULL, NULL },
2394 { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
2395 { "volume-limit", element_parse_volume_limit, NULL, NULL },
2396 { NULL, NULL, NULL, NULL }
2397 };
2398
2399 pa_assert(fname);
2400
2401 p = pa_xnew0(pa_alsa_path, 1);
2402 n = pa_path_get_filename(fname);
2403 p->name = pa_xstrndup(n, strcspn(n, "."));
2404 p->direction = direction;
2405
2406 items[0].data = &p->priority;
2407 items[1].data = &p->description;
2408 items[2].data = &p->name;
2409
2410 if (!paths_dir)
2411 paths_dir = get_default_paths_dir();
2412
2413 fn = pa_maybe_prefix_path(fname, paths_dir);
2414
2415 r = pa_config_parse(fn, NULL, items, p);
2416 pa_xfree(fn);
2417
2418 if (r < 0)
2419 goto fail;
2420
2421 if (path_verify(p) < 0)
2422 goto fail;
2423
2424 return p;
2425
2426 fail:
2427 pa_alsa_path_free(p);
2428 return NULL;
2429 }
2430
2431 pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction) {
2432 pa_alsa_path *p;
2433 pa_alsa_element *e;
2434
2435 pa_assert(element);
2436
2437 p = pa_xnew0(pa_alsa_path, 1);
2438 p->name = pa_xstrdup(element);
2439 p->direction = direction;
2440
2441 e = pa_xnew0(pa_alsa_element, 1);
2442 e->path = p;
2443 e->alsa_name = pa_xstrdup(element);
2444 e->direction = direction;
2445 e->volume_limit = -1;
2446
2447 e->switch_use = PA_ALSA_SWITCH_MUTE;
2448 e->volume_use = PA_ALSA_VOLUME_MERGE;
2449
2450 PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
2451 p->last_element = e;
2452 return p;
2453 }
2454
2455 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
2456 pa_alsa_option *o, *n;
2457
2458 pa_assert(e);
2459
2460 for (o = e->options; o; o = n) {
2461 n = o->next;
2462
2463 if (o->alsa_idx < 0) {
2464 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
2465 option_free(o);
2466 }
2467 }
2468
2469 return
2470 e->switch_use != PA_ALSA_SWITCH_IGNORE ||
2471 e->volume_use != PA_ALSA_VOLUME_IGNORE ||
2472 e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
2473 }
2474
2475 static void path_drop_unsupported(pa_alsa_path *p) {
2476 pa_alsa_element *e, *n;
2477
2478 pa_assert(p);
2479
2480 for (e = p->elements; e; e = n) {
2481 n = e->next;
2482
2483 if (!element_drop_unsupported(e)) {
2484 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
2485 element_free(e);
2486 }
2487 }
2488 }
2489
2490 static void path_make_options_unique(pa_alsa_path *p) {
2491 pa_alsa_element *e;
2492 pa_alsa_option *o, *u;
2493
2494 PA_LLIST_FOREACH(e, p->elements) {
2495 PA_LLIST_FOREACH(o, e->options) {
2496 unsigned i;
2497 char *m;
2498
2499 for (u = o->next; u; u = u->next)
2500 if (pa_streq(u->name, o->name))
2501 break;
2502
2503 if (!u)
2504 continue;
2505
2506 m = pa_xstrdup(o->name);
2507
2508 /* OK, this name is not unique, hence let's rename */
2509 for (i = 1, u = o; u; u = u->next) {
2510 char *nn, *nd;
2511
2512 if (!pa_streq(u->name, m))
2513 continue;
2514
2515 nn = pa_sprintf_malloc("%s-%u", m, i);
2516 pa_xfree(u->name);
2517 u->name = nn;
2518
2519 nd = pa_sprintf_malloc("%s %u", u->description, i);
2520 pa_xfree(u->description);
2521 u->description = nd;
2522
2523 i++;
2524 }
2525
2526 pa_xfree(m);
2527 }
2528 }
2529 }
2530
2531 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
2532 pa_alsa_option *o;
2533
2534 for (; e; e = e->next)
2535 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
2536 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
2537 break;
2538
2539 if (!e)
2540 return FALSE;
2541
2542 for (o = e->options; o; o = o->next) {
2543 pa_alsa_setting *s;
2544
2545 if (template) {
2546 s = pa_xnewdup(pa_alsa_setting, template, 1);
2547 s->options = pa_idxset_copy(template->options);
2548 s->name = pa_sprintf_malloc("%s+%s", template->name, o->name);
2549 s->description =
2550 (template->description[0] && o->description[0])
2551 ? pa_sprintf_malloc("%s / %s", template->description, o->description)
2552 : (template->description[0]
2553 ? pa_xstrdup(template->description)
2554 : pa_xstrdup(o->description));
2555
2556 s->priority = PA_MAX(template->priority, o->priority);
2557 } else {
2558 s = pa_xnew0(pa_alsa_setting, 1);
2559 s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2560 s->name = pa_xstrdup(o->name);
2561 s->description = pa_xstrdup(o->description);
2562 s->priority = o->priority;
2563 }
2564
2565 pa_idxset_put(s->options, o, NULL);
2566
2567 if (element_create_settings(e->next, s))
2568 /* This is not a leaf, so let's get rid of it */
2569 setting_free(s);
2570 else {
2571 /* This is a leaf, so let's add it */
2572 PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2573
2574 e->path->last_setting = s;
2575 }
2576 }
2577
2578 return TRUE;
2579 }
2580
2581 static void path_create_settings(pa_alsa_path *p) {
2582 pa_assert(p);
2583
2584 element_create_settings(p->elements, NULL);
2585 }
2586
2587 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, snd_hctl_t *hctl, pa_bool_t ignore_dB) {
2588 pa_alsa_element *e;
2589 pa_alsa_jack *j;
2590 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2591 pa_channel_position_t t;
2592 pa_channel_position_mask_t path_volume_channels = 0;
2593
2594 pa_assert(p);
2595 pa_assert(m);
2596
2597 if (p->probed)
2598 return p->supported ? 0 : -1;
2599 p->probed = TRUE;
2600
2601 pa_zero(min_dB);
2602 pa_zero(max_dB);
2603
2604 pa_log_debug("Probing path '%s'", p->name);
2605
2606 PA_LLIST_FOREACH(j, p->jacks) {
2607 if (jack_probe(j, hctl) < 0) {
2608 p->supported = FALSE;
2609 pa_log_debug("Probe of jack '%s' failed.", j->alsa_name);
2610 return -1;
2611 }
2612 pa_log_debug("Probe of jack '%s' succeeded (%s)", j->alsa_name, j->has_control ? "found!" : "not found");
2613 }
2614
2615 PA_LLIST_FOREACH(e, p->elements) {
2616 if (element_probe(e, m) < 0) {
2617 p->supported = FALSE;
2618 pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2619 return -1;
2620 }
2621 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);
2622
2623 if (ignore_dB)
2624 e->has_dB = FALSE;
2625
2626 if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2627
2628 if (!p->has_volume) {
2629 p->min_volume = e->min_volume;
2630 p->max_volume = e->max_volume;
2631 }
2632
2633 if (e->has_dB) {
2634 if (!p->has_volume) {
2635 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2636 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2637 min_dB[t] = e->min_dB;
2638 max_dB[t] = e->max_dB;
2639 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2640 }
2641
2642 p->has_dB = TRUE;
2643 } else {
2644
2645 if (p->has_dB) {
2646 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2647 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2648 min_dB[t] += e->min_dB;
2649 max_dB[t] += e->max_dB;
2650 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2651 }
2652 } else {
2653 /* Hmm, there's another element before us
2654 * which cannot do dB volumes, so we we need
2655 * to 'neutralize' this slider */
2656 e->volume_use = PA_ALSA_VOLUME_ZERO;
2657 pa_log_info("Zeroing volume of '%s' on path '%s'", e->alsa_name, p->name);
2658 }
2659 }
2660 } else if (p->has_volume) {
2661 /* We can't use this volume, so let's ignore it */
2662 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2663 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e->alsa_name, p->name);
2664 }
2665 p->has_volume = TRUE;
2666 }
2667
2668 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2669 p->has_mute = TRUE;
2670 }
2671
2672 if (p->has_req_any && !p->req_any_present) {
2673 p->supported = FALSE;
2674 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name);
2675 return -1;
2676 }
2677
2678 path_drop_unsupported(p);
2679 path_make_options_unique(p);
2680 path_create_settings(p);
2681
2682 p->supported = TRUE;
2683
2684 p->min_dB = INFINITY;
2685 p->max_dB = -INFINITY;
2686
2687 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2688 if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) {
2689 if (p->min_dB > min_dB[t])
2690 p->min_dB = min_dB[t];
2691
2692 if (p->max_dB < max_dB[t])
2693 p->max_dB = max_dB[t];
2694 }
2695 }
2696
2697 return 0;
2698 }
2699
2700 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2701 pa_assert(s);
2702
2703 pa_log_debug("Setting %s (%s) priority=%u",
2704 s->name,
2705 pa_strnull(s->description),
2706 s->priority);
2707 }
2708
2709 void pa_alsa_jack_dump(pa_alsa_jack *j) {
2710 pa_assert(j);
2711
2712 pa_log_debug("Jack %s, alsa_name='%s', detection %s", j->name, j->alsa_name, j->has_control ? "possible" : "unavailable");
2713 }
2714
2715 void pa_alsa_option_dump(pa_alsa_option *o) {
2716 pa_assert(o);
2717
2718 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2719 o->alsa_name,
2720 pa_strnull(o->name),
2721 pa_strnull(o->description),
2722 o->alsa_idx,
2723 o->priority);
2724 }
2725
2726 void pa_alsa_element_dump(pa_alsa_element *e) {
2727 pa_alsa_option *o;
2728 pa_assert(e);
2729
2730 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",
2731 e->alsa_name,
2732 e->direction,
2733 e->switch_use,
2734 e->volume_use,
2735 e->volume_limit,
2736 e->enumeration_use,
2737 e->required,
2738 e->required_any,
2739 e->required_absent,
2740 (long long unsigned) e->merged_mask,
2741 e->n_channels,
2742 pa_yes_no(e->override_map));
2743
2744 PA_LLIST_FOREACH(o, e->options)
2745 pa_alsa_option_dump(o);
2746 }
2747
2748 void pa_alsa_path_dump(pa_alsa_path *p) {
2749 pa_alsa_element *e;
2750 pa_alsa_jack *j;
2751 pa_alsa_setting *s;
2752 pa_assert(p);
2753
2754 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2755 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2756 p->name,
2757 pa_strnull(p->description),
2758 p->direction,
2759 p->priority,
2760 pa_yes_no(p->probed),
2761 pa_yes_no(p->supported),
2762 pa_yes_no(p->has_mute),
2763 pa_yes_no(p->has_volume),
2764 pa_yes_no(p->has_dB),
2765 p->min_volume, p->max_volume,
2766 p->min_dB, p->max_dB);
2767
2768 PA_LLIST_FOREACH(e, p->elements)
2769 pa_alsa_element_dump(e);
2770
2771 PA_LLIST_FOREACH(j, p->jacks)
2772 pa_alsa_jack_dump(j);
2773
2774 PA_LLIST_FOREACH(s, p->settings)
2775 pa_alsa_setting_dump(s);
2776 }
2777
2778 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2779 snd_mixer_selem_id_t *sid;
2780 snd_mixer_elem_t *me;
2781
2782 pa_assert(e);
2783 pa_assert(m);
2784 pa_assert(cb);
2785
2786 SELEM_INIT(sid, e->alsa_name);
2787 if (!(me = snd_mixer_find_selem(m, sid))) {
2788 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2789 return;
2790 }
2791
2792 snd_mixer_elem_set_callback(me, cb);
2793 snd_mixer_elem_set_callback_private(me, userdata);
2794 }
2795
2796 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2797 pa_alsa_element *e;
2798
2799 pa_assert(p);
2800 pa_assert(m);
2801 pa_assert(cb);
2802
2803 PA_LLIST_FOREACH(e, p->elements)
2804 element_set_callback(e, m, cb, userdata);
2805 }
2806
2807 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2808 pa_alsa_path *p;
2809 void *state;
2810
2811 pa_assert(ps);
2812 pa_assert(m);
2813 pa_assert(cb);
2814
2815 PA_HASHMAP_FOREACH(p, ps->paths, state)
2816 pa_alsa_path_set_callback(p, m, cb, userdata);
2817 }
2818
2819 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction, const char *paths_dir) {
2820 pa_alsa_path_set *ps;
2821 char **pn = NULL, **en = NULL, **ie;
2822 pa_alsa_decibel_fix *db_fix;
2823 void *state, *state2;
2824 pa_hashmap *cache;
2825
2826 pa_assert(m);
2827 pa_assert(m->profile_set);
2828 pa_assert(m->profile_set->decibel_fixes);
2829 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2830
2831 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2832 return NULL;
2833
2834 ps = pa_xnew0(pa_alsa_path_set, 1);
2835 ps->direction = direction;
2836 ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2837
2838 if (direction == PA_ALSA_DIRECTION_OUTPUT) {
2839 pn = m->output_path_names;
2840 cache = m->profile_set->output_paths;
2841 }
2842 else if (direction == PA_ALSA_DIRECTION_INPUT) {
2843 pn = m->input_path_names;
2844 cache = m->profile_set->input_paths;
2845 }
2846
2847 if (pn) {
2848 char **in;
2849
2850 for (in = pn; *in; in++) {
2851 pa_alsa_path *p = NULL;
2852 pa_bool_t duplicate = FALSE;
2853 char **kn;
2854
2855 for (kn = pn; kn < in; kn++)
2856 if (pa_streq(*kn, *in)) {
2857 duplicate = TRUE;
2858 break;
2859 }
2860
2861 if (duplicate)
2862 continue;
2863
2864 p = pa_hashmap_get(cache, *in);
2865 if (!p) {
2866 char *fn = pa_sprintf_malloc("%s.conf", *in);
2867 p = pa_alsa_path_new(paths_dir, fn, direction);
2868 pa_xfree(fn);
2869 if (p)
2870 pa_hashmap_put(cache, *in, p);
2871 }
2872 pa_assert(pa_hashmap_get(cache, *in) == p);
2873 if (p)
2874 pa_hashmap_put(ps->paths, p, p);
2875
2876 }
2877
2878 goto finish;
2879 }
2880
2881 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2882 en = m->output_element;
2883 else if (direction == PA_ALSA_DIRECTION_INPUT)
2884 en = m->input_element;
2885
2886 if (!en) {
2887 pa_alsa_path_set_free(ps);
2888 return NULL;
2889 }
2890
2891 for (ie = en; *ie; ie++) {
2892 char **je;
2893 pa_alsa_path *p;
2894
2895 p = pa_alsa_path_synthesize(*ie, direction);
2896
2897 /* Mark all other passed elements for require-absent */
2898 for (je = en; *je; je++) {
2899 pa_alsa_element *e;
2900
2901 if (je == ie)
2902 continue;
2903
2904 e = pa_xnew0(pa_alsa_element, 1);
2905 e->path = p;
2906 e->alsa_name = pa_xstrdup(*je);
2907 e->direction = direction;
2908 e->required_absent = PA_ALSA_REQUIRED_ANY;
2909 e->volume_limit = -1;
2910
2911 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2912 p->last_element = e;
2913 }
2914
2915 pa_hashmap_put(ps->paths, *ie, p);
2916 }
2917
2918 finish:
2919 /* Assign decibel fixes to elements. */
2920 PA_HASHMAP_FOREACH(db_fix, m->profile_set->decibel_fixes, state) {
2921 pa_alsa_path *p;
2922
2923 PA_HASHMAP_FOREACH(p, ps->paths, state2) {
2924 pa_alsa_element *e;
2925
2926 PA_LLIST_FOREACH(e, p->elements) {
2927 if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_name)) {
2928 /* The profile set that contains the dB fix may be freed
2929 * before the element, so we have to copy the dB fix
2930 * object. */
2931 e->db_fix = pa_xnewdup(pa_alsa_decibel_fix, db_fix, 1);
2932 e->db_fix->profile_set = NULL;
2933 e->db_fix->name = pa_xstrdup(db_fix->name);
2934 e->db_fix->db_values = pa_xmemdup(db_fix->db_values, (db_fix->max_step - db_fix->min_step + 1) * sizeof(long));
2935 }
2936 }
2937 }
2938 }
2939
2940 return ps;
2941 }
2942
2943 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2944 pa_alsa_path *p;
2945 void *state;
2946 pa_assert(ps);
2947
2948 pa_log_debug("Path Set %p, direction=%i",
2949 (void*) ps,
2950 ps->direction);
2951
2952 PA_HASHMAP_FOREACH(p, ps->paths, state)
2953 pa_alsa_path_dump(p);
2954 }
2955
2956
2957 static pa_bool_t options_have_option(pa_alsa_option *options, const char *alsa_name) {
2958 pa_alsa_option *o;
2959
2960 pa_assert(options);
2961 pa_assert(alsa_name);
2962
2963 PA_LLIST_FOREACH(o, options) {
2964 if (pa_streq(o->alsa_name, alsa_name))
2965 return TRUE;
2966 }
2967 return FALSE;
2968 }
2969
2970 static pa_bool_t enumeration_is_subset(pa_alsa_option *a_options, pa_alsa_option *b_options) {
2971 pa_alsa_option *oa, *ob;
2972
2973 if (!a_options) return TRUE;
2974 if (!b_options) return FALSE;
2975
2976 /* If there is an option A offers that B does not, then A is not a subset of B. */
2977 PA_LLIST_FOREACH(oa, a_options) {
2978 pa_bool_t found = FALSE;
2979 PA_LLIST_FOREACH(ob, b_options) {
2980 if (pa_streq(oa->alsa_name, ob->alsa_name)) {
2981 found = TRUE;
2982 break;
2983 }
2984 }
2985 if (!found)
2986 return FALSE;
2987 }
2988 return TRUE;
2989 }
2990
2991 /**
2992 * Compares two elements to see if a is a subset of b
2993 */
2994 static pa_bool_t element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_t *m) {
2995 pa_assert(a);
2996 pa_assert(b);
2997 pa_assert(m);
2998
2999 /* General rules:
3000 * Every state is a subset of itself (with caveats for volume_limits and options)
3001 * IGNORE is a subset of every other state */
3002
3003 /* Check the volume_use */
3004 if (a->volume_use != PA_ALSA_VOLUME_IGNORE) {
3005
3006 /* "Constant" is subset of "Constant" only when their constant values are equal */
3007 if (a->volume_use == PA_ALSA_VOLUME_CONSTANT && b->volume_use == PA_ALSA_VOLUME_CONSTANT && a->constant_volume != b->constant_volume)
3008 return FALSE;
3009
3010 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
3011 if (a->volume_use != b->volume_use && b->volume_use != PA_ALSA_VOLUME_MERGE)
3012 return FALSE;
3013
3014 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
3015 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
3016 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
3017 if (b->volume_use == PA_ALSA_VOLUME_MERGE && b->volume_limit >= 0) {
3018 long a_limit;
3019
3020 if (a->volume_use == PA_ALSA_VOLUME_CONSTANT)
3021 a_limit = a->constant_volume;
3022 else if (a->volume_use == PA_ALSA_VOLUME_ZERO) {
3023 long dB = 0;
3024
3025 if (a->db_fix) {
3026 int rounding = (a->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1);
3027 a_limit = decibel_fix_get_step(a->db_fix, &dB, rounding);
3028 } else {
3029 snd_mixer_selem_id_t *sid;
3030 snd_mixer_elem_t *me;
3031
3032 SELEM_INIT(sid, a->alsa_name);
3033 if (!(me = snd_mixer_find_selem(m, sid))) {
3034 pa_log_warn("Element %s seems to have disappeared.", a->alsa_name);
3035 return FALSE;
3036 }
3037
3038 if (a->direction == PA_ALSA_DIRECTION_OUTPUT) {
3039 if (snd_mixer_selem_ask_playback_dB_vol(me, dB, +1, &a_limit) < 0)
3040 return FALSE;
3041 } else {
3042 if (snd_mixer_selem_ask_capture_dB_vol(me, dB, -1, &a_limit) < 0)
3043 return FALSE;
3044 }
3045 }
3046 } else if (a->volume_use == PA_ALSA_VOLUME_OFF)
3047 a_limit = a->min_volume;
3048 else if (a->volume_use == PA_ALSA_VOLUME_MERGE)
3049 a_limit = a->volume_limit;
3050 else
3051 /* This should never be reached */
3052 pa_assert(FALSE);
3053
3054 if (a_limit > b->volume_limit)
3055 return FALSE;
3056 }
3057
3058 if (a->volume_use == PA_ALSA_VOLUME_MERGE) {
3059 int s;
3060 /* If override-maps are different, they're not subsets */
3061 if (a->n_channels != b->n_channels)
3062 return FALSE;
3063 for (s = 0; s < SND_MIXER_SCHN_LAST; s++)
3064 if (a->masks[s][a->n_channels-1] != b->masks[s][b->n_channels-1]) {
3065 pa_log_debug("Element %s is not a subset - mask a: 0x%lx, mask b: 0x%lx, at channel %d",
3066 a->alsa_name, a->masks[s][a->n_channels-1], b->masks[s][b->n_channels-1], s);
3067 return FALSE;
3068 }
3069 }
3070 }
3071
3072 if (a->switch_use != PA_ALSA_SWITCH_IGNORE) {
3073 /* "On" is a subset of "Mute".
3074 * "Off" is a subset of "Mute".
3075 * "On" is a subset of "Select", if there is an "Option:On" in B.
3076 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3077 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3078
3079 if (a->switch_use != b->switch_use) {
3080
3081 if (a->switch_use == PA_ALSA_SWITCH_SELECT || a->switch_use == PA_ALSA_SWITCH_MUTE
3082 || b->switch_use == PA_ALSA_SWITCH_OFF || b->switch_use == PA_ALSA_SWITCH_ON)
3083 return FALSE;
3084
3085 if (b->switch_use == PA_ALSA_SWITCH_SELECT) {
3086 if (a->switch_use == PA_ALSA_SWITCH_ON) {
3087 if (!options_have_option(b->options, "on"))
3088 return FALSE;
3089 } else if (a->switch_use == PA_ALSA_SWITCH_OFF) {
3090 if (!options_have_option(b->options, "off"))
3091 return FALSE;
3092 }
3093 }
3094 } else if (a->switch_use == PA_ALSA_SWITCH_SELECT) {
3095 if (!enumeration_is_subset(a->options, b->options))
3096 return FALSE;
3097 }
3098 }
3099
3100 if (a->enumeration_use != PA_ALSA_ENUMERATION_IGNORE) {
3101 if (b->enumeration_use == PA_ALSA_ENUMERATION_IGNORE)
3102 return FALSE;
3103 if (!enumeration_is_subset(a->options, b->options))
3104 return FALSE;
3105 }
3106
3107 return TRUE;
3108 }
3109
3110 static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
3111 pa_alsa_path *p;
3112 void *state;
3113
3114 pa_assert(ps);
3115 pa_assert(m);
3116
3117 /* If we only have one path, then don't bother */
3118 if (pa_hashmap_size(ps->paths) < 2)
3119 return;
3120
3121 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3122 pa_alsa_path *p2;
3123 void *state2;
3124
3125 PA_HASHMAP_FOREACH(p2, ps->paths, state2) {
3126 pa_alsa_element *ea, *eb;
3127 pa_alsa_jack *ja, *jb;
3128 pa_bool_t is_subset = TRUE;
3129
3130 if (p == p2)
3131 continue;
3132
3133 /* If a has a jack that b does not have, a is not a subset */
3134 PA_LLIST_FOREACH(ja, p->jacks) {
3135 pa_bool_t exists = FALSE;
3136
3137 if (!ja->has_control)
3138 continue;
3139
3140 PA_LLIST_FOREACH(jb, p2->jacks) {
3141 if (jb->has_control && !strcmp(jb->alsa_name, ja->alsa_name)) {
3142 exists = TRUE;
3143 break;
3144 }
3145 }
3146
3147 if (!exists) {
3148 is_subset = FALSE;
3149 break;
3150 }
3151 }
3152
3153 /* Compare the elements of each set... */
3154 pa_assert_se(ea = p->elements);
3155 pa_assert_se(eb = p2->elements);
3156
3157 while (is_subset) {
3158 if (pa_streq(ea->alsa_name, eb->alsa_name)) {
3159 if (element_is_subset(ea, eb, m)) {
3160 ea = ea->next;
3161 eb = eb->next;
3162 if ((ea && !eb) || (!ea && eb))
3163 is_subset = FALSE;
3164 else if (!ea && !eb)
3165 break;
3166 } else
3167 is_subset = FALSE;
3168
3169 } else
3170 is_subset = FALSE;
3171 }
3172
3173 if (is_subset) {
3174 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p->name, p2->name);
3175 pa_hashmap_remove(ps->paths, p);
3176 break;
3177 }
3178 }
3179 }
3180 }
3181
3182 static pa_alsa_path* path_set_find_path_by_name(pa_alsa_path_set *ps, const char* name, pa_alsa_path *ignore)
3183 {
3184 pa_alsa_path* p;
3185 void *state;
3186
3187 PA_HASHMAP_FOREACH(p, ps->paths, state)
3188 if (p != ignore && pa_streq(p->name, name))
3189 return p;
3190 return NULL;
3191 }
3192
3193 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
3194 pa_alsa_path *p, *q;
3195 void *state, *state2;
3196
3197 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3198 unsigned i;
3199 char *m;
3200
3201 q = path_set_find_path_by_name(ps, p->name, p);
3202
3203 if (!q)
3204 continue;
3205
3206 m = pa_xstrdup(p->name);
3207
3208 /* OK, this name is not unique, hence let's rename */
3209 i = 1;
3210 PA_HASHMAP_FOREACH(q, ps->paths, state2) {
3211 char *nn, *nd;
3212
3213 if (!pa_streq(q->name, m))
3214 continue;
3215
3216 nn = pa_sprintf_malloc("%s-%u", m, i);
3217 pa_xfree(q->name);
3218 q->name = nn;
3219
3220 nd = pa_sprintf_malloc("%s %u", q->description, i);
3221 pa_xfree(q->description);
3222 q->description = nd;
3223
3224 i++;
3225 }
3226
3227 pa_xfree(m);
3228 }
3229 }
3230
3231 static void mapping_free(pa_alsa_mapping *m) {
3232 pa_assert(m);
3233
3234 pa_xfree(m->name);
3235 pa_xfree(m->description);
3236
3237 pa_xstrfreev(m->device_strings);
3238 pa_xstrfreev(m->input_path_names);
3239 pa_xstrfreev(m->output_path_names);
3240 pa_xstrfreev(m->input_element);
3241 pa_xstrfreev(m->output_element);
3242 if (m->input_path_set)
3243 pa_alsa_path_set_free(m->input_path_set);
3244 if (m->output_path_set)
3245 pa_alsa_path_set_free(m->output_path_set);
3246
3247 pa_assert(!m->input_pcm);
3248 pa_assert(!m->output_pcm);
3249
3250 pa_xfree(m);
3251 }
3252
3253 static void profile_free(pa_alsa_profile *p) {
3254 pa_assert(p);
3255
3256 pa_xfree(p->name);
3257 pa_xfree(p->description);
3258
3259 pa_xstrfreev(p->input_mapping_names);
3260 pa_xstrfreev(p->output_mapping_names);
3261
3262 if (p->input_mappings)
3263 pa_idxset_free(p->input_mappings, NULL, NULL);
3264
3265 if (p->output_mappings)
3266 pa_idxset_free(p->output_mappings, NULL, NULL);
3267
3268 pa_xfree(p);
3269 }
3270
3271 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
3272 pa_assert(ps);
3273
3274 if (ps->input_paths) {
3275 pa_alsa_path *p;
3276
3277 while ((p = pa_hashmap_steal_first(ps->input_paths)))
3278 pa_alsa_path_free(p);
3279
3280 pa_hashmap_free(ps->input_paths, NULL, NULL);
3281 }
3282
3283 if (ps->output_paths) {
3284 pa_alsa_path *p;
3285
3286 while ((p = pa_hashmap_steal_first(ps->output_paths)))
3287 pa_alsa_path_free(p);
3288
3289 pa_hashmap_free(ps->output_paths, NULL, NULL);
3290 }
3291
3292 if (ps->profiles) {
3293 pa_alsa_profile *p;
3294
3295 while ((p = pa_hashmap_steal_first(ps->profiles)))
3296 profile_free(p);
3297
3298 pa_hashmap_free(ps->profiles, NULL, NULL);
3299 }
3300
3301 if (ps->mappings) {
3302 pa_alsa_mapping *m;
3303
3304 while ((m = pa_hashmap_steal_first(ps->mappings)))
3305 mapping_free(m);
3306
3307 pa_hashmap_free(ps->mappings, NULL, NULL);
3308 }
3309
3310 if (ps->decibel_fixes) {
3311 pa_alsa_decibel_fix *db_fix;
3312
3313 while ((db_fix = pa_hashmap_steal_first(ps->decibel_fixes)))
3314 decibel_fix_free(db_fix);
3315
3316 pa_hashmap_free(ps->decibel_fixes, NULL, NULL);
3317 }
3318
3319 pa_xfree(ps);
3320 }
3321
3322 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
3323 pa_alsa_mapping *m;
3324
3325 if (!pa_startswith(name, "Mapping "))
3326 return NULL;
3327
3328 name += 8;
3329
3330 if ((m = pa_hashmap_get(ps->mappings, name)))
3331 return m;
3332
3333 m = pa_xnew0(pa_alsa_mapping, 1);
3334 m->profile_set = ps;
3335 m->name = pa_xstrdup(name);
3336 pa_channel_map_init(&m->channel_map);
3337
3338 pa_hashmap_put(ps->mappings, m->name, m);
3339
3340 return m;
3341 }
3342
3343 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
3344 pa_alsa_profile *p;
3345
3346 if (!pa_startswith(name, "Profile "))
3347 return NULL;
3348
3349 name += 8;
3350
3351 if ((p = pa_hashmap_get(ps->profiles, name)))
3352 return p;
3353
3354 p = pa_xnew0(pa_alsa_profile, 1);
3355 p->profile_set = ps;
3356 p->name = pa_xstrdup(name);
3357
3358 pa_hashmap_put(ps->profiles, p->name, p);
3359
3360 return p;
3361 }
3362
3363 static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) {
3364 pa_alsa_decibel_fix *db_fix;
3365
3366 if (!pa_startswith(name, "DecibelFix "))
3367 return NULL;
3368
3369 name += 11;
3370
3371 if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name)))
3372 return db_fix;
3373
3374 db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
3375 db_fix->profile_set = ps;
3376 db_fix->name = pa_xstrdup(name);
3377
3378 pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix);
3379
3380 return db_fix;
3381 }
3382
3383 static int mapping_parse_device_strings(
3384 const char *filename,
3385 unsigned line,
3386 const char *section,
3387 const char *lvalue,
3388 const char *rvalue,
3389 void *data,
3390 void *userdata) {
3391
3392 pa_alsa_profile_set *ps = userdata;
3393 pa_alsa_mapping *m;
3394
3395 pa_assert(ps);
3396
3397 if (!(m = mapping_get(ps, section))) {
3398 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3399 return -1;
3400 }
3401
3402 pa_xstrfreev(m->device_strings);
3403 if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
3404 pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
3405 return -1;
3406 }
3407
3408 return 0;
3409 }
3410
3411 static int mapping_parse_channel_map(
3412 const char *filename,
3413 unsigned line,
3414 const char *section,
3415 const char *lvalue,
3416 const char *rvalue,
3417 void *data,
3418 void *userdata) {
3419
3420 pa_alsa_profile_set *ps = userdata;
3421 pa_alsa_mapping *m;
3422
3423 pa_assert(ps);
3424
3425 if (!(m = mapping_get(ps, section))) {
3426 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3427 return -1;
3428 }
3429
3430 if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
3431 pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
3432 return -1;
3433 }
3434
3435 return 0;
3436 }
3437
3438 static int mapping_parse_paths(
3439 const char *filename,
3440 unsigned line,
3441 const char *section,
3442 const char *lvalue,
3443 const char *rvalue,
3444 void *data,
3445 void *userdata) {
3446
3447 pa_alsa_profile_set *ps = userdata;
3448 pa_alsa_mapping *m;
3449
3450 pa_assert(ps);
3451
3452 if (!(m = mapping_get(ps, section))) {
3453 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3454 return -1;
3455 }
3456
3457 if (pa_streq(lvalue, "paths-input")) {
3458 pa_xstrfreev(m->input_path_names);
3459 m->input_path_names = pa_split_spaces_strv(rvalue);
3460 } else {
3461 pa_xstrfreev(m->output_path_names);
3462 m->output_path_names = pa_split_spaces_strv(rvalue);
3463 }
3464
3465 return 0;
3466 }
3467
3468 static int mapping_parse_element(
3469 const char *filename,
3470 unsigned line,
3471 const char *section,
3472 const char *lvalue,
3473 const char *rvalue,
3474 void *data,
3475 void *userdata) {
3476
3477 pa_alsa_profile_set *ps = userdata;
3478 pa_alsa_mapping *m;
3479
3480 pa_assert(ps);
3481
3482 if (!(m = mapping_get(ps, section))) {
3483 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3484 return -1;
3485 }
3486
3487 if (pa_streq(lvalue, "element-input")) {
3488 pa_xstrfreev(m->input_element);
3489 m->input_element = pa_split_spaces_strv(rvalue);
3490 } else {
3491 pa_xstrfreev(m->output_element);
3492 m->output_element = pa_split_spaces_strv(rvalue);
3493 }
3494
3495 return 0;
3496 }
3497
3498 static int mapping_parse_direction(
3499 const char *filename,
3500 unsigned line,
3501 const char *section,
3502 const char *lvalue,
3503 const char *rvalue,
3504 void *data,
3505 void *userdata) {
3506
3507 pa_alsa_profile_set *ps = userdata;
3508 pa_alsa_mapping *m;
3509
3510 pa_assert(ps);
3511
3512 if (!(m = mapping_get(ps, section))) {
3513 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3514 return -1;
3515 }
3516
3517 if (pa_streq(rvalue, "input"))
3518 m->direction = PA_ALSA_DIRECTION_INPUT;
3519 else if (pa_streq(rvalue, "output"))
3520 m->direction = PA_ALSA_DIRECTION_OUTPUT;
3521 else if (pa_streq(rvalue, "any"))
3522 m->direction = PA_ALSA_DIRECTION_ANY;
3523 else {
3524 pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
3525 return -1;
3526 }
3527
3528 return 0;
3529 }
3530
3531 static int mapping_parse_description(
3532 const char *filename,
3533 unsigned line,
3534 const char *section,
3535 const char *lvalue,
3536 const char *rvalue,
3537 void *data,
3538 void *userdata) {
3539
3540 pa_alsa_profile_set *ps = userdata;
3541 pa_alsa_profile *p;
3542 pa_alsa_mapping *m;
3543
3544 pa_assert(ps);
3545
3546 if ((m = mapping_get(ps, section))) {
3547 pa_xfree(m->description);
3548 m->description = pa_xstrdup(rvalue);
3549 } else if ((p = profile_get(ps, section))) {
3550 pa_xfree(p->description);
3551 p->description = pa_xstrdup(rvalue);
3552 } else {
3553 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3554 return -1;
3555 }
3556
3557 return 0;
3558 }
3559
3560 static int mapping_parse_priority(
3561 const char *filename,
3562 unsigned line,
3563 const char *section,
3564 const char *lvalue,
3565 const char *rvalue,
3566 void *data,
3567 void *userdata) {
3568
3569 pa_alsa_profile_set *ps = userdata;
3570 pa_alsa_profile *p;
3571 pa_alsa_mapping *m;
3572 uint32_t prio;
3573
3574 pa_assert(ps);
3575
3576 if (pa_atou(rvalue, &prio) < 0) {
3577 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
3578 return -1;
3579 }
3580
3581 if ((m = mapping_get(ps, section)))
3582 m->priority = prio;
3583 else if ((p = profile_get(ps, section)))
3584 p->priority = prio;
3585 else {
3586 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3587 return -1;
3588 }
3589
3590 return 0;
3591 }
3592
3593 static int profile_parse_mappings(
3594 const char *filename,
3595 unsigned line,
3596 const char *section,
3597 const char *lvalue,
3598 const char *rvalue,
3599 void *data,
3600 void *userdata) {
3601
3602 pa_alsa_profile_set *ps = userdata;
3603 pa_alsa_profile *p;
3604
3605 pa_assert(ps);
3606
3607 if (!(p = profile_get(ps, section))) {
3608 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3609 return -1;
3610 }
3611
3612 if (pa_streq(lvalue, "input-mappings")) {
3613 pa_xstrfreev(p->input_mapping_names);
3614 p->input_mapping_names = pa_split_spaces_strv(rvalue);
3615 } else {
3616 pa_xstrfreev(p->output_mapping_names);
3617 p->output_mapping_names = pa_split_spaces_strv(rvalue);
3618 }
3619
3620 return 0;
3621 }
3622
3623 static int profile_parse_skip_probe(
3624 const char *filename,
3625 unsigned line,
3626 const char *section,
3627 const char *lvalue,
3628 const char *rvalue,
3629 void *data,
3630 void *userdata) {
3631
3632 pa_alsa_profile_set *ps = userdata;
3633 pa_alsa_profile *p;
3634 int b;
3635
3636 pa_assert(ps);
3637
3638 if (!(p = profile_get(ps, section))) {
3639 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3640 return -1;
3641 }
3642
3643 if ((b = pa_parse_boolean(rvalue)) < 0) {
3644 pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
3645 return -1;
3646 }
3647
3648 p->supported = b;
3649
3650 return 0;
3651 }
3652
3653 static int decibel_fix_parse_db_values(
3654 const char *filename,
3655 unsigned line,
3656 const char *section,
3657 const char *lvalue,
3658 const char *rvalue,
3659 void *data,
3660 void *userdata) {
3661
3662 pa_alsa_profile_set *ps = userdata;
3663 pa_alsa_decibel_fix *db_fix;
3664 char **items;
3665 char *item;
3666 long *db_values;
3667 unsigned n = 8; /* Current size of the db_values table. */
3668 unsigned min_step = 0;
3669 unsigned max_step = 0;
3670 unsigned i = 0; /* Index to the items table. */
3671 unsigned prev_step = 0;
3672 double prev_db = 0;
3673
3674 pa_assert(filename);
3675 pa_assert(section);
3676 pa_assert(lvalue);
3677 pa_assert(rvalue);
3678 pa_assert(ps);
3679
3680 if (!(db_fix = decibel_fix_get(ps, section))) {
3681 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3682 return -1;
3683 }
3684
3685 if (!(items = pa_split_spaces_strv(rvalue))) {
3686 pa_log("[%s:%u] Value missing", pa_strnull(filename), line);
3687 return -1;
3688 }
3689
3690 db_values = pa_xnew(long, n);
3691
3692 while ((item = items[i++])) {
3693 char *s = item; /* Step value string. */
3694 char *d = item; /* dB value string. */
3695 uint32_t step;
3696 double db;
3697
3698 /* Move d forward until it points to a colon or to the end of the item. */
3699 for (; *d && *d != ':'; ++d);
3700
3701 if (d == s) {
3702 /* item started with colon. */
3703 pa_log("[%s:%u] No step value found in %s", filename, line, item);
3704 goto fail;
3705 }
3706
3707 if (!*d || !*(d + 1)) {
3708 /* No colon found, or it was the last character in item. */
3709 pa_log("[%s:%u] No dB value found in %s", filename, line, item);
3710 goto fail;
3711 }
3712
3713 /* pa_atou() needs a null-terminating string. Let's replace the colon
3714 * with a zero byte. */
3715 *d++ = '\0';
3716
3717 if (pa_atou(s, &step) < 0) {
3718 pa_log("[%s:%u] Invalid step value: %s", filename, line, s);
3719 goto fail;
3720 }
3721
3722 if (pa_atod(d, &db) < 0) {
3723 pa_log("[%s:%u] Invalid dB value: %s", filename, line, d);
3724 goto fail;
3725 }
3726
3727 if (step <= prev_step && i != 1) {
3728 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename, line, step, prev_step);
3729 goto fail;
3730 }
3731
3732 if (db < prev_db && i != 1) {
3733 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename, line, db, prev_db);
3734 goto fail;
3735 }
3736
3737 if (i == 1) {
3738 min_step = step;
3739 db_values[0] = (long) (db * 100.0);
3740 prev_step = step;
3741 prev_db = db;
3742 } else {
3743 /* Interpolate linearly. */
3744 double db_increment = (db - prev_db) / (step - prev_step);
3745
3746 for (; prev_step < step; ++prev_step, prev_db += db_increment) {
3747
3748 /* Reallocate the db_values table if it's about to overflow. */
3749 if (prev_step + 1 - min_step == n) {
3750 n *= 2;
3751 db_values = pa_xrenew(long, db_values, n);
3752 }
3753
3754 db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
3755 }
3756 }
3757
3758 max_step = step;
3759 }
3760
3761 db_fix->min_step = min_step;
3762 db_fix->max_step = max_step;
3763 pa_xfree(db_fix->db_values);
3764 db_fix->db_values = db_values;
3765
3766 pa_xstrfreev(items);
3767
3768 return 0;
3769
3770 fail:
3771 pa_xstrfreev(items);
3772 pa_xfree(db_values);
3773
3774 return -1;
3775 }
3776
3777 static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
3778 pa_alsa_direction_t direction) {
3779
3780 pa_alsa_path *p;
3781 void *state;
3782 snd_pcm_t *pcm_handle;
3783 pa_alsa_path_set *ps;
3784 snd_mixer_t *mixer_handle;
3785 snd_hctl_t *hctl_handle;
3786
3787 if (direction == PA_ALSA_DIRECTION_OUTPUT) {
3788 if (m->output_path_set)
3789 return; /* Already probed */
3790 m->output_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3791 pcm_handle = m->output_pcm;
3792 } else {
3793 if (m->input_path_set)
3794 return; /* Already probed */
3795 m->input_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3796 pcm_handle = m->input_pcm;
3797 }
3798
3799 if (!ps)
3800 return; /* No paths */
3801
3802 pa_assert(pcm_handle);
3803
3804 mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL, &hctl_handle);
3805 if (!mixer_handle || !hctl_handle) {
3806 /* Cannot open mixer, remove all entries */
3807 while (pa_hashmap_steal_first(ps->paths));
3808 return;
3809 }
3810
3811
3812 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3813 if (pa_alsa_path_probe(p, mixer_handle, hctl_handle, m->profile_set->ignore_dB) < 0) {
3814 pa_hashmap_remove(ps->paths, p);
3815 }
3816 }
3817
3818 path_set_condense(ps, mixer_handle);
3819 path_set_make_paths_unique(ps);
3820
3821 if (mixer_handle)
3822 snd_mixer_close(mixer_handle);
3823
3824 pa_log_debug("Available mixer paths (after tidying):");
3825 pa_alsa_path_set_dump(ps);
3826 }
3827
3828 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
3829
3830 static const struct description_map well_known_descriptions[] = {
3831 { "analog-mono", N_("Analog Mono") },
3832 { "analog-stereo", N_("Analog Stereo") },
3833 { "analog-surround-21", N_("Analog Surround 2.1") },
3834 { "analog-surround-30", N_("Analog Surround 3.0") },
3835 { "analog-surround-31", N_("Analog Surround 3.1") },
3836 { "analog-surround-40", N_("Analog Surround 4.0") },
3837 { "analog-surround-41", N_("Analog Surround 4.1") },
3838 { "analog-surround-50", N_("Analog Surround 5.0") },
3839 { "analog-surround-51", N_("Analog Surround 5.1") },
3840 { "analog-surround-61", N_("Analog Surround 6.0") },
3841 { "analog-surround-61", N_("Analog Surround 6.1") },
3842 { "analog-surround-70", N_("Analog Surround 7.0") },
3843 { "analog-surround-71", N_("Analog Surround 7.1") },
3844 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3845 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3846 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3847 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3848 { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
3849 { "hdmi-stereo", N_("Digital Stereo (HDMI)") },
3850 { "hdmi-surround-51", N_("Digital Surround 5.1 (HDMI)") }
3851 };
3852
3853 pa_assert(m);
3854
3855 if (!pa_channel_map_valid(&m->channel_map)) {
3856 pa_log("Mapping %s is missing channel map.", m->name);
3857 return -1;
3858 }
3859
3860 if (!m->device_strings) {
3861 pa_log("Mapping %s is missing device strings.", m->name);
3862 return -1;
3863 }
3864
3865 if ((m->input_path_names && m->input_element) ||
3866 (m->output_path_names && m->output_element)) {
3867 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
3868 return -1;
3869 }
3870
3871 if (!m->description)
3872 m->description = pa_xstrdup(lookup_description(m->name,
3873 well_known_descriptions,
3874 PA_ELEMENTSOF(well_known_descriptions)));
3875
3876 if (!m->description)
3877 m->description = pa_xstrdup(m->name);
3878
3879 if (bonus) {
3880 if (pa_channel_map_equal(&m->channel_map, bonus))
3881 m->priority += 50;
3882 else if (m->channel_map.channels == bonus->channels)
3883 m->priority += 30;
3884 }
3885
3886 return 0;
3887 }
3888
3889 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
3890 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
3891
3892 pa_assert(m);
3893
3894 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3895 m->name,
3896 pa_strnull(m->description),
3897 m->priority,
3898 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
3899 pa_yes_no(m->supported),
3900 m->direction);
3901 }
3902
3903 static void profile_set_add_auto_pair(
3904 pa_alsa_profile_set *ps,
3905 pa_alsa_mapping *m, /* output */
3906 pa_alsa_mapping *n /* input */) {
3907
3908 char *name;
3909 pa_alsa_profile *p;
3910
3911 pa_assert(ps);
3912 pa_assert(m || n);
3913
3914 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
3915 return;
3916
3917 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
3918 return;
3919
3920 if (m && n)
3921 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
3922 else if (m)
3923 name = pa_sprintf_malloc("output:%s", m->name);
3924 else
3925 name = pa_sprintf_malloc("input:%s", n->name);
3926
3927 if (pa_hashmap_get(ps->profiles, name)) {
3928 pa_xfree(name);
3929 return;
3930 }
3931
3932 p = pa_xnew0(pa_alsa_profile, 1);
3933 p->profile_set = ps;
3934 p->name = name;
3935
3936 if (m) {
3937 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3938 pa_idxset_put(p->output_mappings, m, NULL);
3939 p->priority += m->priority * 100;
3940 }
3941
3942 if (n) {
3943 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3944 pa_idxset_put(p->input_mappings, n, NULL);
3945 p->priority += n->priority;
3946 }
3947
3948 pa_hashmap_put(ps->profiles, p->name, p);
3949 }
3950
3951 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
3952 pa_alsa_mapping *m, *n;
3953 void *m_state, *n_state;
3954
3955 pa_assert(ps);
3956
3957 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
3958 profile_set_add_auto_pair(ps, m, NULL);
3959
3960 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3961 profile_set_add_auto_pair(ps, m, n);
3962 }
3963
3964 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3965 profile_set_add_auto_pair(ps, NULL, n);
3966 }
3967
3968 static int profile_verify(pa_alsa_profile *p) {
3969
3970 static const struct description_map well_known_descriptions[] = {
3971 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3972 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3973 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3974 { "off", N_("Off") }
3975 };
3976
3977 pa_assert(p);
3978
3979 /* Replace the output mapping names by the actual mappings */
3980 if (p->output_mapping_names) {
3981 char **name;
3982
3983 pa_assert(!p->output_mappings);
3984 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3985
3986 for (name = p->output_mapping_names; *name; name++) {
3987 pa_alsa_mapping *m;
3988 char **in;
3989 pa_bool_t duplicate = FALSE;
3990
3991 for (in = name + 1; *in; in++)
3992 if (pa_streq(*name, *in)) {
3993 duplicate = TRUE;
3994 break;
3995 }
3996
3997 if (duplicate)
3998 continue;
3999
4000 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
4001 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
4002 return -1;
4003 }
4004
4005 pa_idxset_put(p->output_mappings, m, NULL);
4006
4007 if (p->supported)
4008 m->supported++;
4009 }
4010
4011 pa_xstrfreev(p->output_mapping_names);
4012 p->output_mapping_names = NULL;
4013 }
4014
4015 /* Replace the input mapping names by the actual mappings */
4016 if (p->input_mapping_names) {
4017 char **name;
4018
4019 pa_assert(!p->input_mappings);
4020 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4021
4022 for (name = p->input_mapping_names; *name; name++) {
4023 pa_alsa_mapping *m;
4024 char **in;
4025 pa_bool_t duplicate = FALSE;
4026
4027 for (in = name + 1; *in; in++)
4028 if (pa_streq(*name, *in)) {
4029 duplicate = TRUE;
4030 break;
4031 }
4032
4033 if (duplicate)
4034 continue;
4035
4036 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
4037 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
4038 return -1;
4039 }
4040
4041 pa_idxset_put(p->input_mappings, m, NULL);
4042
4043 if (p->supported)
4044 m->supported++;
4045 }
4046
4047 pa_xstrfreev(p->input_mapping_names);
4048 p->input_mapping_names = NULL;
4049 }
4050
4051 if (!p->input_mappings && !p->output_mappings) {
4052 pa_log("Profile '%s' lacks mappings.", p->name);
4053 return -1;
4054 }
4055
4056 if (!p->description)
4057 p->description = pa_xstrdup(lookup_description(p->name,
4058 well_known_descriptions,
4059 PA_ELEMENTSOF(well_known_descriptions)));
4060
4061 if (!p->description) {
4062 pa_strbuf *sb;
4063 uint32_t idx;
4064 pa_alsa_mapping *m;
4065
4066 sb = pa_strbuf_new();
4067
4068 if (p->output_mappings)
4069 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4070 if (!pa_strbuf_isempty(sb))
4071 pa_strbuf_puts(sb, " + ");
4072
4073 pa_strbuf_printf(sb, _("%s Output"), m->description);
4074 }
4075
4076 if (p->input_mappings)
4077 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4078 if (!pa_strbuf_isempty(sb))
4079 pa_strbuf_puts(sb, " + ");
4080
4081 pa_strbuf_printf(sb, _("%s Input"), m->description);
4082 }
4083
4084 p->description = pa_strbuf_tostring_free(sb);
4085 }
4086
4087 return 0;
4088 }
4089
4090 void pa_alsa_profile_dump(pa_alsa_profile *p) {
4091 uint32_t idx;
4092 pa_alsa_mapping *m;
4093 pa_assert(p);
4094
4095 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4096 p->name,
4097 pa_strnull(p->description),
4098 p->priority,
4099 pa_yes_no(p->supported),
4100 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
4101 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
4102
4103 if (p->input_mappings)
4104 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4105 pa_log_debug("Input %s", m->name);
4106
4107 if (p->output_mappings)
4108 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4109 pa_log_debug("Output %s", m->name);
4110 }
4111
4112 static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
4113 pa_assert(db_fix);
4114
4115 /* Check that the dB mapping has been configured. Since "db-values" is
4116 * currently the only option in the DecibelFix section, and decibel fix
4117 * objects don't get created if a DecibelFix section is empty, this is
4118 * actually a redundant check. Having this may prevent future bugs,
4119 * however. */
4120 if (!db_fix->db_values) {
4121 pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
4122 return -1;
4123 }
4124
4125 return 0;
4126 }
4127
4128 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
4129 char *db_values = NULL;
4130
4131 pa_assert(db_fix);
4132
4133 if (db_fix->db_values) {
4134 pa_strbuf *buf;
4135 unsigned long i, nsteps;
4136
4137 pa_assert(db_fix->min_step <= db_fix->max_step);
4138 nsteps = db_fix->max_step - db_fix->min_step + 1;
4139
4140 buf = pa_strbuf_new();
4141 for (i = 0; i < nsteps; ++i)
4142 pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
4143
4144 db_values = pa_strbuf_tostring_free(buf);
4145 }
4146
4147 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4148 db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
4149
4150 pa_xfree(db_values);
4151 }
4152
4153 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
4154 pa_alsa_profile_set *ps;
4155 pa_alsa_profile *p;
4156 pa_alsa_mapping *m;
4157 pa_alsa_decibel_fix *db_fix;
4158 char *fn;
4159 int r;
4160 void *state;
4161
4162 static pa_config_item items[] = {
4163 /* [General] */
4164 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
4165
4166 /* [Mapping ...] */
4167 { "device-strings", mapping_parse_device_strings, NULL, NULL },
4168 { "channel-map", mapping_parse_channel_map, NULL, NULL },
4169 { "paths-input", mapping_parse_paths, NULL, NULL },
4170 { "paths-output", mapping_parse_paths, NULL, NULL },
4171 { "element-input", mapping_parse_element, NULL, NULL },
4172 { "element-output", mapping_parse_element, NULL, NULL },
4173 { "direction", mapping_parse_direction, NULL, NULL },
4174
4175 /* Shared by [Mapping ...] and [Profile ...] */
4176 { "description", mapping_parse_description, NULL, NULL },
4177 { "priority", mapping_parse_priority, NULL, NULL },
4178
4179 /* [Profile ...] */
4180 { "input-mappings", profile_parse_mappings, NULL, NULL },
4181 { "output-mappings", profile_parse_mappings, NULL, NULL },
4182 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
4183
4184 /* [DecibelFix ...] */
4185 { "db-values", decibel_fix_parse_db_values, NULL, NULL },
4186 { NULL, NULL, NULL, NULL }
4187 };
4188
4189 ps = pa_xnew0(pa_alsa_profile_set, 1);
4190 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4191 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4192 ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4193 ps->input_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4194 ps->output_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4195
4196 items[0].data = &ps->auto_profiles;
4197
4198 if (!fname)
4199 fname = "default.conf";
4200
4201 fn = pa_maybe_prefix_path(fname,
4202 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
4203 PA_ALSA_PROFILE_SETS_DIR);
4204
4205 r = pa_config_parse(fn, NULL, items, ps);
4206 pa_xfree(fn);
4207
4208 if (r < 0)
4209 goto fail;
4210
4211 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4212 if (mapping_verify(m, bonus) < 0)
4213 goto fail;
4214
4215 if (ps->auto_profiles)
4216 profile_set_add_auto(ps);
4217
4218 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4219 if (profile_verify(p) < 0)
4220 goto fail;
4221
4222 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4223 if (decibel_fix_verify(db_fix) < 0)
4224 goto fail;
4225
4226 return ps;
4227
4228 fail:
4229 pa_alsa_profile_set_free(ps);
4230 return NULL;
4231 }
4232
4233 static void profile_finalize_probing(pa_alsa_profile *to_be_finalized, pa_alsa_profile *next) {
4234 pa_alsa_mapping *m;
4235 uint32_t idx;
4236
4237 if (!to_be_finalized)
4238 return;
4239
4240 if (to_be_finalized->output_mappings)
4241 PA_IDXSET_FOREACH(m, to_be_finalized->output_mappings, idx) {
4242
4243 if (!m->output_pcm)
4244 continue;
4245
4246 if (to_be_finalized->supported)
4247 m->supported++;
4248
4249 /* If this mapping is also in the next profile, we won't close the
4250 * pcm handle here, because it would get immediately reopened
4251 * anyway. */
4252 if (next && next->output_mappings && pa_idxset_get_by_data(next->output_mappings, m, NULL))
4253 continue;
4254
4255 snd_pcm_close(m->output_pcm);
4256 m->output_pcm = NULL;
4257 }
4258
4259 if (to_be_finalized->input_mappings)
4260 PA_IDXSET_FOREACH(m, to_be_finalized->input_mappings, idx) {
4261
4262 if (!m->input_pcm)
4263 continue;
4264
4265 if (to_be_finalized->supported)
4266 m->supported++;
4267
4268 /* If this mapping is also in the next profile, we won't close the
4269 * pcm handle here, because it would get immediately reopened
4270 * anyway. */
4271 if (next && next->input_mappings && pa_idxset_get_by_data(next->input_mappings, m, NULL))
4272 continue;
4273
4274 snd_pcm_close(m->input_pcm);
4275 m->input_pcm = NULL;
4276 }
4277 }
4278
4279 static snd_pcm_t* mapping_open_pcm(pa_alsa_mapping *m,
4280 const pa_sample_spec *ss,
4281 const char *dev_id,
4282 int mode,
4283 unsigned default_n_fragments,
4284 unsigned default_fragment_size_msec) {
4285
4286 pa_sample_spec try_ss = *ss;
4287 pa_channel_map try_map = m->channel_map;
4288 snd_pcm_uframes_t try_period_size, try_buffer_size;
4289
4290 try_ss.channels = try_map.channels;
4291
4292 try_period_size =
4293 pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
4294 pa_frame_size(&try_ss);
4295 try_buffer_size = default_n_fragments * try_period_size;
4296
4297 return pa_alsa_open_by_template(
4298 m->device_strings, dev_id, NULL, &try_ss,
4299 &try_map, mode, &try_period_size,
4300 &try_buffer_size, 0, NULL, NULL, TRUE);
4301 }
4302
4303 static void paths_drop_unsupported(pa_hashmap* h) {
4304
4305 void* state = NULL;
4306 const void* key;
4307 pa_alsa_path* p;
4308
4309 pa_assert(h);
4310 p = pa_hashmap_iterate(h, &state, &key);
4311 while (p) {
4312 if (p->supported <= 0) {
4313 pa_hashmap_remove(h, key);
4314 pa_alsa_path_free(p);
4315 }
4316 p = pa_hashmap_iterate(h, &state, &key);
4317 }
4318 }
4319
4320 void pa_alsa_profile_set_probe(
4321 pa_alsa_profile_set *ps,
4322 const char *dev_id,
4323 const pa_sample_spec *ss,
4324 unsigned default_n_fragments,
4325 unsigned default_fragment_size_msec) {
4326
4327 void *state;
4328 pa_alsa_profile *p, *last = NULL;
4329 pa_alsa_mapping *m;
4330
4331 pa_assert(ps);
4332 pa_assert(dev_id);
4333 pa_assert(ss);
4334
4335 if (ps->probed)
4336 return;
4337
4338 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
4339 uint32_t idx;
4340
4341 /* Skip if this is already marked that it is supported (i.e. from the config file) */
4342 if (!p->supported) {
4343
4344 pa_log_debug("Looking at profile %s", p->name);
4345 profile_finalize_probing(last, p);
4346 p->supported = TRUE;
4347
4348 /* Check if we can open all new ones */
4349 if (p->output_mappings)
4350 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4351
4352 if (m->output_pcm)
4353 continue;
4354
4355 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
4356 if (!(m->output_pcm = mapping_open_pcm(m, ss, dev_id,
4357 SND_PCM_STREAM_PLAYBACK,
4358 default_n_fragments,
4359 default_fragment_size_msec))) {
4360 p->supported = FALSE;
4361 break;
4362 }
4363 }
4364
4365 if (p->input_mappings && p->supported)
4366 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4367
4368 if (m->input_pcm)
4369 continue;
4370
4371 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
4372 if (!(m->input_pcm = mapping_open_pcm(m, ss, dev_id,
4373 SND_PCM_STREAM_CAPTURE,
4374 default_n_fragments,
4375 default_fragment_size_msec))) {
4376 p->supported = FALSE;
4377 break;
4378 }
4379 }
4380
4381 last = p;
4382
4383 if (!p->supported)
4384 continue;
4385 }
4386
4387 pa_log_debug("Profile %s supported.", p->name);
4388
4389 if (p->output_mappings)
4390 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4391 if (m->output_pcm)
4392 mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT);
4393
4394 if (p->input_mappings)
4395 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4396 if (m->input_pcm)
4397 mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT);
4398 }
4399
4400 /* Clean up */
4401 profile_finalize_probing(last, NULL);
4402
4403 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4404 if (!p->supported) {
4405 pa_hashmap_remove(ps->profiles, p->name);
4406 profile_free(p);
4407 }
4408
4409 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4410 if (m->supported <= 0) {
4411 pa_hashmap_remove(ps->mappings, m->name);
4412 mapping_free(m);
4413 }
4414
4415 paths_drop_unsupported(ps->input_paths);
4416 paths_drop_unsupported(ps->output_paths);
4417
4418 ps->probed = TRUE;
4419 }
4420
4421 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
4422 pa_alsa_profile *p;
4423 pa_alsa_mapping *m;
4424 pa_alsa_decibel_fix *db_fix;
4425 void *state;
4426
4427 pa_assert(ps);
4428
4429 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4430 (void*)
4431 ps,
4432 pa_yes_no(ps->auto_profiles),
4433 pa_yes_no(ps->probed),
4434 pa_hashmap_size(ps->mappings),
4435 pa_hashmap_size(ps->profiles),
4436 pa_hashmap_size(ps->decibel_fixes));
4437
4438 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4439 pa_alsa_mapping_dump(m);
4440
4441 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4442 pa_alsa_profile_dump(p);
4443
4444 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4445 pa_alsa_decibel_fix_dump(db_fix);
4446 }
4447
4448 static pa_device_port* device_port_alsa_init(pa_hashmap *ports,
4449 const char* name,
4450 const char* description,
4451 pa_alsa_path *path,
4452 pa_alsa_setting *setting,
4453 pa_card_profile *cp,
4454 pa_hashmap *extra,
4455 pa_core *core) {
4456
4457 pa_device_port * p = pa_hashmap_get(ports, name);
4458 if (!p) {
4459 pa_alsa_port_data *data;
4460
4461 p = pa_device_port_new(core, name, description, sizeof(pa_alsa_port_data));
4462 pa_assert(p);
4463 pa_hashmap_put(ports, p->name, p);
4464 p->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4465
4466 data = PA_DEVICE_PORT_DATA(p);
4467 data->path = path;
4468 data->setting = setting;
4469 path->port = p;
4470 }
4471
4472 p->is_input |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_INPUT;
4473 p->is_output |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_OUTPUT;
4474
4475 if (cp)
4476 pa_hashmap_put(p->profiles, cp->name, cp);
4477
4478 if (extra) {
4479 pa_hashmap_put(extra, p->name, p);
4480 pa_device_port_ref(p);
4481 }
4482
4483 return p;
4484 }
4485
4486 void pa_alsa_path_set_add_ports(
4487 pa_alsa_path_set *ps,
4488 pa_card_profile *cp,
4489 pa_hashmap *ports,
4490 pa_hashmap *extra,
4491 pa_core *core) {
4492
4493 pa_alsa_path *path;
4494 void *state;
4495
4496 pa_assert(ports);
4497
4498 if (!ps)
4499 return;
4500
4501 PA_HASHMAP_FOREACH(path, ps->paths, state) {
4502 if (!path->settings || !path->settings->next) {
4503 /* If there is no or just one setting we only need a
4504 * single entry */
4505 pa_device_port *port = device_port_alsa_init(ports, path->name,
4506 path->description, path, path->settings, cp, extra, core);
4507 port->priority = path->priority * 100;
4508
4509 } else {
4510 pa_alsa_setting *s;
4511 PA_LLIST_FOREACH(s, path->settings) {
4512 pa_device_port *port;
4513 char *n, *d;
4514
4515 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
4516
4517 if (s->description[0])
4518 d = pa_sprintf_malloc("%s / %s", path->description, s->description);
4519 else
4520 d = pa_xstrdup(path->description);
4521
4522 port = device_port_alsa_init(ports, n, d, path, s, cp, extra, core);
4523 port->priority = path->priority * 100 + s->priority;
4524
4525 pa_xfree(n);
4526 pa_xfree(d);
4527 }
4528 }
4529 }
4530 }
4531
4532 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps, pa_card *card) {
4533
4534 pa_assert(p);
4535 pa_assert(!*p);
4536 pa_assert(ps);
4537
4538 if (ps->paths && pa_hashmap_size(ps->paths) > 0) {
4539 pa_assert(card);
4540 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4541 pa_alsa_path_set_add_ports(ps, NULL, card->ports, *p, card->core);
4542 }
4543
4544 pa_log_debug("Added %u ports", *p ? pa_hashmap_size(*p) : 0);
4545 }