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