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