]> code.delx.au - pulseaudio/blob - src/modules/alsa/alsa-mixer.c
Remove unnecessary #includes
[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_unify(pa_alsa_path_set *ps) {
2877 pa_alsa_path *p;
2878 pa_bool_t has_dB = TRUE, has_volume = TRUE, has_mute = TRUE;
2879 pa_assert(ps);
2880
2881 /* We have issues dealing with paths that vary too wildly. That
2882 * means for now we have to have all paths support volume/mute/dB
2883 * or none. */
2884
2885 PA_LLIST_FOREACH(p, ps->paths) {
2886 pa_assert(p->probed);
2887
2888 if (!p->has_volume)
2889 has_volume = FALSE;
2890 else if (!p->has_dB)
2891 has_dB = FALSE;
2892
2893 if (!p->has_mute)
2894 has_mute = FALSE;
2895 }
2896
2897 if (!has_volume || !has_dB || !has_mute) {
2898
2899 if (!has_volume)
2900 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2901 else if (!has_dB)
2902 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2903
2904 if (!has_mute)
2905 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2906
2907 PA_LLIST_FOREACH(p, ps->paths) {
2908 if (!has_volume)
2909 p->has_volume = FALSE;
2910 else if (!has_dB)
2911 p->has_dB = FALSE;
2912
2913 if (!has_mute)
2914 p->has_mute = FALSE;
2915 }
2916 }
2917 }
2918
2919 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
2920 pa_alsa_path *p, *q;
2921
2922 PA_LLIST_FOREACH(p, ps->paths) {
2923 unsigned i;
2924 char *m;
2925
2926 for (q = p->next; q; q = q->next)
2927 if (pa_streq(q->name, p->name))
2928 break;
2929
2930 if (!q)
2931 continue;
2932
2933 m = pa_xstrdup(p->name);
2934
2935 /* OK, this name is not unique, hence let's rename */
2936 for (i = 1, q = p; q; q = q->next) {
2937 char *nn, *nd;
2938
2939 if (!pa_streq(q->name, m))
2940 continue;
2941
2942 nn = pa_sprintf_malloc("%s-%u", m, i);
2943 pa_xfree(q->name);
2944 q->name = nn;
2945
2946 nd = pa_sprintf_malloc("%s %u", q->description, i);
2947 pa_xfree(q->description);
2948 q->description = nd;
2949
2950 i++;
2951 }
2952
2953 pa_xfree(m);
2954 }
2955 }
2956
2957 void pa_alsa_path_set_probe(pa_alsa_path_set *ps, snd_mixer_t *m, pa_bool_t ignore_dB) {
2958 pa_alsa_path *p, *n;
2959
2960 pa_assert(ps);
2961
2962 if (ps->probed)
2963 return;
2964
2965 for (p = ps->paths; p; p = n) {
2966 n = p->next;
2967
2968 if (pa_alsa_path_probe(p, m, ignore_dB) < 0) {
2969 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
2970 pa_alsa_path_free(p);
2971 }
2972 }
2973
2974 path_set_unify(ps);
2975 path_set_make_paths_unique(ps);
2976 ps->probed = TRUE;
2977 }
2978
2979 static void mapping_free(pa_alsa_mapping *m) {
2980 pa_assert(m);
2981
2982 pa_xfree(m->name);
2983 pa_xfree(m->description);
2984
2985 pa_xstrfreev(m->device_strings);
2986 pa_xstrfreev(m->input_path_names);
2987 pa_xstrfreev(m->output_path_names);
2988 pa_xstrfreev(m->input_element);
2989 pa_xstrfreev(m->output_element);
2990
2991 pa_assert(!m->input_pcm);
2992 pa_assert(!m->output_pcm);
2993
2994 pa_xfree(m);
2995 }
2996
2997 static void profile_free(pa_alsa_profile *p) {
2998 pa_assert(p);
2999
3000 pa_xfree(p->name);
3001 pa_xfree(p->description);
3002
3003 pa_xstrfreev(p->input_mapping_names);
3004 pa_xstrfreev(p->output_mapping_names);
3005
3006 if (p->input_mappings)
3007 pa_idxset_free(p->input_mappings, NULL, NULL);
3008
3009 if (p->output_mappings)
3010 pa_idxset_free(p->output_mappings, NULL, NULL);
3011
3012 pa_xfree(p);
3013 }
3014
3015 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
3016 pa_assert(ps);
3017
3018 if (ps->profiles) {
3019 pa_alsa_profile *p;
3020
3021 while ((p = pa_hashmap_steal_first(ps->profiles)))
3022 profile_free(p);
3023
3024 pa_hashmap_free(ps->profiles, NULL, NULL);
3025 }
3026
3027 if (ps->mappings) {
3028 pa_alsa_mapping *m;
3029
3030 while ((m = pa_hashmap_steal_first(ps->mappings)))
3031 mapping_free(m);
3032
3033 pa_hashmap_free(ps->mappings, NULL, NULL);
3034 }
3035
3036 if (ps->decibel_fixes) {
3037 pa_alsa_decibel_fix *db_fix;
3038
3039 while ((db_fix = pa_hashmap_steal_first(ps->decibel_fixes)))
3040 decibel_fix_free(db_fix);
3041
3042 pa_hashmap_free(ps->decibel_fixes, NULL, NULL);
3043 }
3044
3045 pa_xfree(ps);
3046 }
3047
3048 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
3049 pa_alsa_mapping *m;
3050
3051 if (!pa_startswith(name, "Mapping "))
3052 return NULL;
3053
3054 name += 8;
3055
3056 if ((m = pa_hashmap_get(ps->mappings, name)))
3057 return m;
3058
3059 m = pa_xnew0(pa_alsa_mapping, 1);
3060 m->profile_set = ps;
3061 m->name = pa_xstrdup(name);
3062 pa_channel_map_init(&m->channel_map);
3063
3064 pa_hashmap_put(ps->mappings, m->name, m);
3065
3066 return m;
3067 }
3068
3069 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
3070 pa_alsa_profile *p;
3071
3072 if (!pa_startswith(name, "Profile "))
3073 return NULL;
3074
3075 name += 8;
3076
3077 if ((p = pa_hashmap_get(ps->profiles, name)))
3078 return p;
3079
3080 p = pa_xnew0(pa_alsa_profile, 1);
3081 p->profile_set = ps;
3082 p->name = pa_xstrdup(name);
3083
3084 pa_hashmap_put(ps->profiles, p->name, p);
3085
3086 return p;
3087 }
3088
3089 static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) {
3090 pa_alsa_decibel_fix *db_fix;
3091
3092 if (!pa_startswith(name, "DecibelFix "))
3093 return NULL;
3094
3095 name += 11;
3096
3097 if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name)))
3098 return db_fix;
3099
3100 db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
3101 db_fix->profile_set = ps;
3102 db_fix->name = pa_xstrdup(name);
3103
3104 pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix);
3105
3106 return db_fix;
3107 }
3108
3109 static int mapping_parse_device_strings(
3110 const char *filename,
3111 unsigned line,
3112 const char *section,
3113 const char *lvalue,
3114 const char *rvalue,
3115 void *data,
3116 void *userdata) {
3117
3118 pa_alsa_profile_set *ps = userdata;
3119 pa_alsa_mapping *m;
3120
3121 pa_assert(ps);
3122
3123 if (!(m = mapping_get(ps, section))) {
3124 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3125 return -1;
3126 }
3127
3128 pa_xstrfreev(m->device_strings);
3129 if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
3130 pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
3131 return -1;
3132 }
3133
3134 return 0;
3135 }
3136
3137 static int mapping_parse_channel_map(
3138 const char *filename,
3139 unsigned line,
3140 const char *section,
3141 const char *lvalue,
3142 const char *rvalue,
3143 void *data,
3144 void *userdata) {
3145
3146 pa_alsa_profile_set *ps = userdata;
3147 pa_alsa_mapping *m;
3148
3149 pa_assert(ps);
3150
3151 if (!(m = mapping_get(ps, section))) {
3152 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3153 return -1;
3154 }
3155
3156 if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
3157 pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
3158 return -1;
3159 }
3160
3161 return 0;
3162 }
3163
3164 static int mapping_parse_paths(
3165 const char *filename,
3166 unsigned line,
3167 const char *section,
3168 const char *lvalue,
3169 const char *rvalue,
3170 void *data,
3171 void *userdata) {
3172
3173 pa_alsa_profile_set *ps = userdata;
3174 pa_alsa_mapping *m;
3175
3176 pa_assert(ps);
3177
3178 if (!(m = mapping_get(ps, section))) {
3179 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3180 return -1;
3181 }
3182
3183 if (pa_streq(lvalue, "paths-input")) {
3184 pa_xstrfreev(m->input_path_names);
3185 m->input_path_names = pa_split_spaces_strv(rvalue);
3186 } else {
3187 pa_xstrfreev(m->output_path_names);
3188 m->output_path_names = pa_split_spaces_strv(rvalue);
3189 }
3190
3191 return 0;
3192 }
3193
3194 static int mapping_parse_element(
3195 const char *filename,
3196 unsigned line,
3197 const char *section,
3198 const char *lvalue,
3199 const char *rvalue,
3200 void *data,
3201 void *userdata) {
3202
3203 pa_alsa_profile_set *ps = userdata;
3204 pa_alsa_mapping *m;
3205
3206 pa_assert(ps);
3207
3208 if (!(m = mapping_get(ps, section))) {
3209 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3210 return -1;
3211 }
3212
3213 if (pa_streq(lvalue, "element-input")) {
3214 pa_xstrfreev(m->input_element);
3215 m->input_element = pa_split_spaces_strv(rvalue);
3216 } else {
3217 pa_xstrfreev(m->output_element);
3218 m->output_element = pa_split_spaces_strv(rvalue);
3219 }
3220
3221 return 0;
3222 }
3223
3224 static int mapping_parse_direction(
3225 const char *filename,
3226 unsigned line,
3227 const char *section,
3228 const char *lvalue,
3229 const char *rvalue,
3230 void *data,
3231 void *userdata) {
3232
3233 pa_alsa_profile_set *ps = userdata;
3234 pa_alsa_mapping *m;
3235
3236 pa_assert(ps);
3237
3238 if (!(m = mapping_get(ps, section))) {
3239 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3240 return -1;
3241 }
3242
3243 if (pa_streq(rvalue, "input"))
3244 m->direction = PA_ALSA_DIRECTION_INPUT;
3245 else if (pa_streq(rvalue, "output"))
3246 m->direction = PA_ALSA_DIRECTION_OUTPUT;
3247 else if (pa_streq(rvalue, "any"))
3248 m->direction = PA_ALSA_DIRECTION_ANY;
3249 else {
3250 pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
3251 return -1;
3252 }
3253
3254 return 0;
3255 }
3256
3257 static int mapping_parse_description(
3258 const char *filename,
3259 unsigned line,
3260 const char *section,
3261 const char *lvalue,
3262 const char *rvalue,
3263 void *data,
3264 void *userdata) {
3265
3266 pa_alsa_profile_set *ps = userdata;
3267 pa_alsa_profile *p;
3268 pa_alsa_mapping *m;
3269
3270 pa_assert(ps);
3271
3272 if ((m = mapping_get(ps, section))) {
3273 pa_xfree(m->description);
3274 m->description = pa_xstrdup(rvalue);
3275 } else if ((p = profile_get(ps, section))) {
3276 pa_xfree(p->description);
3277 p->description = pa_xstrdup(rvalue);
3278 } else {
3279 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3280 return -1;
3281 }
3282
3283 return 0;
3284 }
3285
3286 static int mapping_parse_priority(
3287 const char *filename,
3288 unsigned line,
3289 const char *section,
3290 const char *lvalue,
3291 const char *rvalue,
3292 void *data,
3293 void *userdata) {
3294
3295 pa_alsa_profile_set *ps = userdata;
3296 pa_alsa_profile *p;
3297 pa_alsa_mapping *m;
3298 uint32_t prio;
3299
3300 pa_assert(ps);
3301
3302 if (pa_atou(rvalue, &prio) < 0) {
3303 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
3304 return -1;
3305 }
3306
3307 if ((m = mapping_get(ps, section)))
3308 m->priority = prio;
3309 else if ((p = profile_get(ps, section)))
3310 p->priority = prio;
3311 else {
3312 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3313 return -1;
3314 }
3315
3316 return 0;
3317 }
3318
3319 static int profile_parse_mappings(
3320 const char *filename,
3321 unsigned line,
3322 const char *section,
3323 const char *lvalue,
3324 const char *rvalue,
3325 void *data,
3326 void *userdata) {
3327
3328 pa_alsa_profile_set *ps = userdata;
3329 pa_alsa_profile *p;
3330
3331 pa_assert(ps);
3332
3333 if (!(p = profile_get(ps, section))) {
3334 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3335 return -1;
3336 }
3337
3338 if (pa_streq(lvalue, "input-mappings")) {
3339 pa_xstrfreev(p->input_mapping_names);
3340 p->input_mapping_names = pa_split_spaces_strv(rvalue);
3341 } else {
3342 pa_xstrfreev(p->output_mapping_names);
3343 p->output_mapping_names = pa_split_spaces_strv(rvalue);
3344 }
3345
3346 return 0;
3347 }
3348
3349 static int profile_parse_skip_probe(
3350 const char *filename,
3351 unsigned line,
3352 const char *section,
3353 const char *lvalue,
3354 const char *rvalue,
3355 void *data,
3356 void *userdata) {
3357
3358 pa_alsa_profile_set *ps = userdata;
3359 pa_alsa_profile *p;
3360 int b;
3361
3362 pa_assert(ps);
3363
3364 if (!(p = profile_get(ps, section))) {
3365 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3366 return -1;
3367 }
3368
3369 if ((b = pa_parse_boolean(rvalue)) < 0) {
3370 pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
3371 return -1;
3372 }
3373
3374 p->supported = b;
3375
3376 return 0;
3377 }
3378
3379 static int decibel_fix_parse_db_values(
3380 const char *filename,
3381 unsigned line,
3382 const char *section,
3383 const char *lvalue,
3384 const char *rvalue,
3385 void *data,
3386 void *userdata) {
3387
3388 pa_alsa_profile_set *ps = userdata;
3389 pa_alsa_decibel_fix *db_fix;
3390 char **items;
3391 char *item;
3392 long *db_values;
3393 unsigned n = 8; /* Current size of the db_values table. */
3394 unsigned min_step = 0;
3395 unsigned max_step = 0;
3396 unsigned i = 0; /* Index to the items table. */
3397 unsigned prev_step = 0;
3398 double prev_db = 0;
3399
3400 pa_assert(filename);
3401 pa_assert(section);
3402 pa_assert(lvalue);
3403 pa_assert(rvalue);
3404 pa_assert(ps);
3405
3406 if (!(db_fix = decibel_fix_get(ps, section))) {
3407 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3408 return -1;
3409 }
3410
3411 if (!(items = pa_split_spaces_strv(rvalue))) {
3412 pa_log("[%s:%u] Value missing", pa_strnull(filename), line);
3413 return -1;
3414 }
3415
3416 db_values = pa_xnew(long, n);
3417
3418 while ((item = items[i++])) {
3419 char *s = item; /* Step value string. */
3420 char *d = item; /* dB value string. */
3421 uint32_t step;
3422 double db;
3423
3424 /* Move d forward until it points to a colon or to the end of the item. */
3425 for (; *d && *d != ':'; ++d);
3426
3427 if (d == s) {
3428 /* item started with colon. */
3429 pa_log("[%s:%u] No step value found in %s", filename, line, item);
3430 goto fail;
3431 }
3432
3433 if (!*d || !*(d + 1)) {
3434 /* No colon found, or it was the last character in item. */
3435 pa_log("[%s:%u] No dB value found in %s", filename, line, item);
3436 goto fail;
3437 }
3438
3439 /* pa_atou() needs a null-terminating string. Let's replace the colon
3440 * with a zero byte. */
3441 *d++ = '\0';
3442
3443 if (pa_atou(s, &step) < 0) {
3444 pa_log("[%s:%u] Invalid step value: %s", filename, line, s);
3445 goto fail;
3446 }
3447
3448 if (pa_atod(d, &db) < 0) {
3449 pa_log("[%s:%u] Invalid dB value: %s", filename, line, d);
3450 goto fail;
3451 }
3452
3453 if (step <= prev_step && i != 1) {
3454 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename, line, step, prev_step);
3455 goto fail;
3456 }
3457
3458 if (db < prev_db && i != 1) {
3459 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename, line, db, prev_db);
3460 goto fail;
3461 }
3462
3463 if (i == 1) {
3464 min_step = step;
3465 db_values[0] = (long) (db * 100.0);
3466 prev_step = step;
3467 prev_db = db;
3468 } else {
3469 /* Interpolate linearly. */
3470 double db_increment = (db - prev_db) / (step - prev_step);
3471
3472 for (; prev_step < step; ++prev_step, prev_db += db_increment) {
3473
3474 /* Reallocate the db_values table if it's about to overflow. */
3475 if (prev_step + 1 - min_step == n) {
3476 n *= 2;
3477 db_values = pa_xrenew(long, db_values, n);
3478 }
3479
3480 db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
3481 }
3482 }
3483
3484 max_step = step;
3485 }
3486
3487 db_fix->min_step = min_step;
3488 db_fix->max_step = max_step;
3489 pa_xfree(db_fix->db_values);
3490 db_fix->db_values = db_values;
3491
3492 pa_xstrfreev(items);
3493
3494 return 0;
3495
3496 fail:
3497 pa_xstrfreev(items);
3498 pa_xfree(db_values);
3499
3500 return -1;
3501 }
3502
3503 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
3504
3505 static const struct description_map well_known_descriptions[] = {
3506 { "analog-mono", N_("Analog Mono") },
3507 { "analog-stereo", N_("Analog Stereo") },
3508 { "analog-surround-21", N_("Analog Surround 2.1") },
3509 { "analog-surround-30", N_("Analog Surround 3.0") },
3510 { "analog-surround-31", N_("Analog Surround 3.1") },
3511 { "analog-surround-40", N_("Analog Surround 4.0") },
3512 { "analog-surround-41", N_("Analog Surround 4.1") },
3513 { "analog-surround-50", N_("Analog Surround 5.0") },
3514 { "analog-surround-51", N_("Analog Surround 5.1") },
3515 { "analog-surround-61", N_("Analog Surround 6.0") },
3516 { "analog-surround-61", N_("Analog Surround 6.1") },
3517 { "analog-surround-70", N_("Analog Surround 7.0") },
3518 { "analog-surround-71", N_("Analog Surround 7.1") },
3519 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3520 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3521 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3522 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3523 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3524 };
3525
3526 pa_assert(m);
3527
3528 if (!pa_channel_map_valid(&m->channel_map)) {
3529 pa_log("Mapping %s is missing channel map.", m->name);
3530 return -1;
3531 }
3532
3533 if (!m->device_strings) {
3534 pa_log("Mapping %s is missing device strings.", m->name);
3535 return -1;
3536 }
3537
3538 if ((m->input_path_names && m->input_element) ||
3539 (m->output_path_names && m->output_element)) {
3540 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
3541 return -1;
3542 }
3543
3544 if (!m->description)
3545 m->description = pa_xstrdup(lookup_description(m->name,
3546 well_known_descriptions,
3547 PA_ELEMENTSOF(well_known_descriptions)));
3548
3549 if (!m->description)
3550 m->description = pa_xstrdup(m->name);
3551
3552 if (bonus) {
3553 if (pa_channel_map_equal(&m->channel_map, bonus))
3554 m->priority += 50;
3555 else if (m->channel_map.channels == bonus->channels)
3556 m->priority += 30;
3557 }
3558
3559 return 0;
3560 }
3561
3562 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
3563 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
3564
3565 pa_assert(m);
3566
3567 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3568 m->name,
3569 pa_strnull(m->description),
3570 m->priority,
3571 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
3572 pa_yes_no(m->supported),
3573 m->direction);
3574 }
3575
3576 static void profile_set_add_auto_pair(
3577 pa_alsa_profile_set *ps,
3578 pa_alsa_mapping *m, /* output */
3579 pa_alsa_mapping *n /* input */) {
3580
3581 char *name;
3582 pa_alsa_profile *p;
3583
3584 pa_assert(ps);
3585 pa_assert(m || n);
3586
3587 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
3588 return;
3589
3590 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
3591 return;
3592
3593 if (m && n)
3594 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
3595 else if (m)
3596 name = pa_sprintf_malloc("output:%s", m->name);
3597 else
3598 name = pa_sprintf_malloc("input:%s", n->name);
3599
3600 if (pa_hashmap_get(ps->profiles, name)) {
3601 pa_xfree(name);
3602 return;
3603 }
3604
3605 p = pa_xnew0(pa_alsa_profile, 1);
3606 p->profile_set = ps;
3607 p->name = name;
3608
3609 if (m) {
3610 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3611 pa_idxset_put(p->output_mappings, m, NULL);
3612 p->priority += m->priority * 100;
3613 }
3614
3615 if (n) {
3616 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3617 pa_idxset_put(p->input_mappings, n, NULL);
3618 p->priority += n->priority;
3619 }
3620
3621 pa_hashmap_put(ps->profiles, p->name, p);
3622 }
3623
3624 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
3625 pa_alsa_mapping *m, *n;
3626 void *m_state, *n_state;
3627
3628 pa_assert(ps);
3629
3630 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
3631 profile_set_add_auto_pair(ps, m, NULL);
3632
3633 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3634 profile_set_add_auto_pair(ps, m, n);
3635 }
3636
3637 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3638 profile_set_add_auto_pair(ps, NULL, n);
3639 }
3640
3641 static int profile_verify(pa_alsa_profile *p) {
3642
3643 static const struct description_map well_known_descriptions[] = {
3644 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3645 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3646 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3647 { "off", N_("Off") }
3648 };
3649
3650 pa_assert(p);
3651
3652 /* Replace the output mapping names by the actual mappings */
3653 if (p->output_mapping_names) {
3654 char **name;
3655
3656 pa_assert(!p->output_mappings);
3657 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3658
3659 for (name = p->output_mapping_names; *name; name++) {
3660 pa_alsa_mapping *m;
3661 char **in;
3662 pa_bool_t duplicate = FALSE;
3663
3664 for (in = name + 1; *in; in++)
3665 if (pa_streq(*name, *in)) {
3666 duplicate = TRUE;
3667 break;
3668 }
3669
3670 if (duplicate)
3671 continue;
3672
3673 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
3674 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
3675 return -1;
3676 }
3677
3678 pa_idxset_put(p->output_mappings, m, NULL);
3679
3680 if (p->supported)
3681 m->supported++;
3682 }
3683
3684 pa_xstrfreev(p->output_mapping_names);
3685 p->output_mapping_names = NULL;
3686 }
3687
3688 /* Replace the input mapping names by the actual mappings */
3689 if (p->input_mapping_names) {
3690 char **name;
3691
3692 pa_assert(!p->input_mappings);
3693 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3694
3695 for (name = p->input_mapping_names; *name; name++) {
3696 pa_alsa_mapping *m;
3697 char **in;
3698 pa_bool_t duplicate = FALSE;
3699
3700 for (in = name + 1; *in; in++)
3701 if (pa_streq(*name, *in)) {
3702 duplicate = TRUE;
3703 break;
3704 }
3705
3706 if (duplicate)
3707 continue;
3708
3709 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
3710 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
3711 return -1;
3712 }
3713
3714 pa_idxset_put(p->input_mappings, m, NULL);
3715
3716 if (p->supported)
3717 m->supported++;
3718 }
3719
3720 pa_xstrfreev(p->input_mapping_names);
3721 p->input_mapping_names = NULL;
3722 }
3723
3724 if (!p->input_mappings && !p->output_mappings) {
3725 pa_log("Profile '%s' lacks mappings.", p->name);
3726 return -1;
3727 }
3728
3729 if (!p->description)
3730 p->description = pa_xstrdup(lookup_description(p->name,
3731 well_known_descriptions,
3732 PA_ELEMENTSOF(well_known_descriptions)));
3733
3734 if (!p->description) {
3735 pa_strbuf *sb;
3736 uint32_t idx;
3737 pa_alsa_mapping *m;
3738
3739 sb = pa_strbuf_new();
3740
3741 if (p->output_mappings)
3742 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3743 if (!pa_strbuf_isempty(sb))
3744 pa_strbuf_puts(sb, " + ");
3745
3746 pa_strbuf_printf(sb, _("%s Output"), m->description);
3747 }
3748
3749 if (p->input_mappings)
3750 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3751 if (!pa_strbuf_isempty(sb))
3752 pa_strbuf_puts(sb, " + ");
3753
3754 pa_strbuf_printf(sb, _("%s Input"), m->description);
3755 }
3756
3757 p->description = pa_strbuf_tostring_free(sb);
3758 }
3759
3760 return 0;
3761 }
3762
3763 void pa_alsa_profile_dump(pa_alsa_profile *p) {
3764 uint32_t idx;
3765 pa_alsa_mapping *m;
3766 pa_assert(p);
3767
3768 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3769 p->name,
3770 pa_strnull(p->description),
3771 p->priority,
3772 pa_yes_no(p->supported),
3773 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
3774 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
3775
3776 if (p->input_mappings)
3777 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
3778 pa_log_debug("Input %s", m->name);
3779
3780 if (p->output_mappings)
3781 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
3782 pa_log_debug("Output %s", m->name);
3783 }
3784
3785 static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
3786 pa_assert(db_fix);
3787
3788 /* Check that the dB mapping has been configured. Since "db-values" is
3789 * currently the only option in the DecibelFix section, and decibel fix
3790 * objects don't get created if a DecibelFix section is empty, this is
3791 * actually a redundant check. Having this may prevent future bugs,
3792 * however. */
3793 if (!db_fix->db_values) {
3794 pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
3795 return -1;
3796 }
3797
3798 return 0;
3799 }
3800
3801 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
3802 char *db_values = NULL;
3803
3804 pa_assert(db_fix);
3805
3806 if (db_fix->db_values) {
3807 pa_strbuf *buf;
3808 unsigned long i, nsteps;
3809
3810 pa_assert(db_fix->min_step <= db_fix->max_step);
3811 nsteps = db_fix->max_step - db_fix->min_step + 1;
3812
3813 buf = pa_strbuf_new();
3814 for (i = 0; i < nsteps; ++i)
3815 pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
3816
3817 db_values = pa_strbuf_tostring_free(buf);
3818 }
3819
3820 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
3821 db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
3822
3823 pa_xfree(db_values);
3824 }
3825
3826 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
3827 pa_alsa_profile_set *ps;
3828 pa_alsa_profile *p;
3829 pa_alsa_mapping *m;
3830 pa_alsa_decibel_fix *db_fix;
3831 char *fn;
3832 int r;
3833 void *state;
3834
3835 static pa_config_item items[] = {
3836 /* [General] */
3837 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
3838
3839 /* [Mapping ...] */
3840 { "device-strings", mapping_parse_device_strings, NULL, NULL },
3841 { "channel-map", mapping_parse_channel_map, NULL, NULL },
3842 { "paths-input", mapping_parse_paths, NULL, NULL },
3843 { "paths-output", mapping_parse_paths, NULL, NULL },
3844 { "element-input", mapping_parse_element, NULL, NULL },
3845 { "element-output", mapping_parse_element, NULL, NULL },
3846 { "direction", mapping_parse_direction, NULL, NULL },
3847
3848 /* Shared by [Mapping ...] and [Profile ...] */
3849 { "description", mapping_parse_description, NULL, NULL },
3850 { "priority", mapping_parse_priority, NULL, NULL },
3851
3852 /* [Profile ...] */
3853 { "input-mappings", profile_parse_mappings, NULL, NULL },
3854 { "output-mappings", profile_parse_mappings, NULL, NULL },
3855 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
3856
3857 /* [DecibelFix ...] */
3858 { "db-values", decibel_fix_parse_db_values, NULL, NULL },
3859 { NULL, NULL, NULL, NULL }
3860 };
3861
3862 ps = pa_xnew0(pa_alsa_profile_set, 1);
3863 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3864 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3865 ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3866
3867 items[0].data = &ps->auto_profiles;
3868
3869 if (!fname)
3870 fname = "default.conf";
3871
3872 fn = pa_maybe_prefix_path(fname,
3873 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
3874 PA_ALSA_PROFILE_SETS_DIR);
3875
3876 r = pa_config_parse(fn, NULL, items, ps);
3877 pa_xfree(fn);
3878
3879 if (r < 0)
3880 goto fail;
3881
3882 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3883 if (mapping_verify(m, bonus) < 0)
3884 goto fail;
3885
3886 if (ps->auto_profiles)
3887 profile_set_add_auto(ps);
3888
3889 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3890 if (profile_verify(p) < 0)
3891 goto fail;
3892
3893 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
3894 if (decibel_fix_verify(db_fix) < 0)
3895 goto fail;
3896
3897 return ps;
3898
3899 fail:
3900 pa_alsa_profile_set_free(ps);
3901 return NULL;
3902 }
3903
3904 void pa_alsa_profile_set_probe(
3905 pa_alsa_profile_set *ps,
3906 const char *dev_id,
3907 const pa_sample_spec *ss,
3908 unsigned default_n_fragments,
3909 unsigned default_fragment_size_msec) {
3910
3911 void *state;
3912 pa_alsa_profile *p, *last = NULL;
3913 pa_alsa_mapping *m;
3914
3915 pa_assert(ps);
3916 pa_assert(dev_id);
3917 pa_assert(ss);
3918
3919 if (ps->probed)
3920 return;
3921
3922 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
3923 pa_sample_spec try_ss;
3924 pa_channel_map try_map;
3925 snd_pcm_uframes_t try_period_size, try_buffer_size;
3926 uint32_t idx;
3927
3928 /* Is this already marked that it is supported? (i.e. from the config file) */
3929 if (p->supported)
3930 continue;
3931
3932 pa_log_debug("Looking at profile %s", p->name);
3933
3934 /* Close PCMs from the last iteration we don't need anymore */
3935 if (last && last->output_mappings)
3936 PA_IDXSET_FOREACH(m, last->output_mappings, idx) {
3937
3938 if (!m->output_pcm)
3939 break;
3940
3941 if (last->supported)
3942 m->supported++;
3943
3944 if (!p->output_mappings || !pa_idxset_get_by_data(p->output_mappings, m, NULL)) {
3945 snd_pcm_close(m->output_pcm);
3946 m->output_pcm = NULL;
3947 }
3948 }
3949
3950 if (last && last->input_mappings)
3951 PA_IDXSET_FOREACH(m, last->input_mappings, idx) {
3952
3953 if (!m->input_pcm)
3954 break;
3955
3956 if (last->supported)
3957 m->supported++;
3958
3959 if (!p->input_mappings || !pa_idxset_get_by_data(p->input_mappings, m, NULL)) {
3960 snd_pcm_close(m->input_pcm);
3961 m->input_pcm = NULL;
3962 }
3963 }
3964
3965 p->supported = TRUE;
3966
3967 /* Check if we can open all new ones */
3968 if (p->output_mappings)
3969 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3970
3971 if (m->output_pcm)
3972 continue;
3973
3974 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
3975 try_map = m->channel_map;
3976 try_ss = *ss;
3977 try_ss.channels = try_map.channels;
3978
3979 try_period_size =
3980 pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
3981 pa_frame_size(&try_ss);
3982 try_buffer_size = default_n_fragments * try_period_size;
3983
3984 if (!(m ->output_pcm = pa_alsa_open_by_template(
3985 m->device_strings,
3986 dev_id,
3987 NULL,
3988 &try_ss, &try_map,
3989 SND_PCM_STREAM_PLAYBACK,
3990 &try_period_size, &try_buffer_size, 0, NULL, NULL,
3991 TRUE))) {
3992 p->supported = FALSE;
3993 break;
3994 }
3995 }
3996
3997 if (p->input_mappings && p->supported)
3998 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3999
4000 if (m->input_pcm)
4001 continue;
4002
4003 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
4004 try_map = m->channel_map;
4005 try_ss = *ss;
4006 try_ss.channels = try_map.channels;
4007
4008 try_period_size =
4009 pa_usec_to_bytes(default_fragment_size_msec*PA_USEC_PER_MSEC, &try_ss) /
4010 pa_frame_size(&try_ss);
4011 try_buffer_size = default_n_fragments * try_period_size;
4012
4013 if (!(m ->input_pcm = pa_alsa_open_by_template(
4014 m->device_strings,
4015 dev_id,
4016 NULL,
4017 &try_ss, &try_map,
4018 SND_PCM_STREAM_CAPTURE,
4019 &try_period_size, &try_buffer_size, 0, NULL, NULL,
4020 TRUE))) {
4021 p->supported = FALSE;
4022 break;
4023 }
4024 }
4025
4026 last = p;
4027
4028 if (p->supported)
4029 pa_log_debug("Profile %s supported.", p->name);
4030 }
4031
4032 /* Clean up */
4033 if (last) {
4034 uint32_t idx;
4035
4036 if (last->output_mappings)
4037 PA_IDXSET_FOREACH(m, last->output_mappings, idx)
4038 if (m->output_pcm) {
4039
4040 if (last->supported)
4041 m->supported++;
4042
4043 snd_pcm_close(m->output_pcm);
4044 m->output_pcm = NULL;
4045 }
4046
4047 if (last->input_mappings)
4048 PA_IDXSET_FOREACH(m, last->input_mappings, idx)
4049 if (m->input_pcm) {
4050
4051 if (last->supported)
4052 m->supported++;
4053
4054 snd_pcm_close(m->input_pcm);
4055 m->input_pcm = NULL;
4056 }
4057 }
4058
4059 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4060 if (!p->supported) {
4061 pa_hashmap_remove(ps->profiles, p->name);
4062 profile_free(p);
4063 }
4064
4065 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4066 if (m->supported <= 0) {
4067 pa_hashmap_remove(ps->mappings, m->name);
4068 mapping_free(m);
4069 }
4070
4071 ps->probed = TRUE;
4072 }
4073
4074 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
4075 pa_alsa_profile *p;
4076 pa_alsa_mapping *m;
4077 pa_alsa_decibel_fix *db_fix;
4078 void *state;
4079
4080 pa_assert(ps);
4081
4082 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4083 (void*)
4084 ps,
4085 pa_yes_no(ps->auto_profiles),
4086 pa_yes_no(ps->probed),
4087 pa_hashmap_size(ps->mappings),
4088 pa_hashmap_size(ps->profiles),
4089 pa_hashmap_size(ps->decibel_fixes));
4090
4091 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4092 pa_alsa_mapping_dump(m);
4093
4094 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4095 pa_alsa_profile_dump(p);
4096
4097 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4098 pa_alsa_decibel_fix_dump(db_fix);
4099 }
4100
4101 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps) {
4102 pa_alsa_path *path;
4103
4104 pa_assert(p);
4105 pa_assert(!*p);
4106 pa_assert(ps);
4107
4108 /* if there is no path, we don't want a port list */
4109 if (!ps->paths)
4110 return;
4111
4112 if (!ps->paths->next){
4113 pa_alsa_setting *s;
4114
4115 /* If there is only one path, but no or only one setting, then
4116 * we want a port list either */
4117 if (!ps->paths->settings || !ps->paths->settings->next)
4118 return;
4119
4120 /* Ok, there is only one path, however with multiple settings,
4121 * so let's create a port for each setting */
4122 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4123
4124 PA_LLIST_FOREACH(s, ps->paths->settings) {
4125 pa_device_port *port;
4126 pa_alsa_port_data *data;
4127
4128 port = pa_device_port_new(s->name, s->description, sizeof(pa_alsa_port_data));
4129 port->priority = s->priority;
4130
4131 data = PA_DEVICE_PORT_DATA(port);
4132 data->path = ps->paths;
4133 data->setting = s;
4134
4135 pa_hashmap_put(*p, port->name, port);
4136 }
4137
4138 } else {
4139
4140 /* We have multiple paths, so let's create a port for each
4141 * one, and each of each settings */
4142 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4143
4144 PA_LLIST_FOREACH(path, ps->paths) {
4145
4146 if (!path->settings || !path->settings->next) {
4147 pa_device_port *port;
4148 pa_alsa_port_data *data;
4149
4150 /* If there is no or just one setting we only need a
4151 * single entry */
4152
4153 port = pa_device_port_new(path->name, path->description, sizeof(pa_alsa_port_data));
4154 port->priority = path->priority * 100;
4155
4156
4157 data = PA_DEVICE_PORT_DATA(port);
4158 data->path = path;
4159 data->setting = path->settings;
4160
4161 pa_hashmap_put(*p, port->name, port);
4162 } else {
4163 pa_alsa_setting *s;
4164
4165 PA_LLIST_FOREACH(s, path->settings) {
4166 pa_device_port *port;
4167 pa_alsa_port_data *data;
4168 char *n, *d;
4169
4170 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
4171
4172 if (s->description[0])
4173 d = pa_sprintf_malloc(_("%s / %s"), path->description, s->description);
4174 else
4175 d = pa_xstrdup(path->description);
4176
4177 port = pa_device_port_new(n, d, sizeof(pa_alsa_port_data));
4178 port->priority = path->priority * 100 + s->priority;
4179
4180 pa_xfree(n);
4181 pa_xfree(d);
4182
4183 data = PA_DEVICE_PORT_DATA(port);
4184 data->path = path;
4185 data->setting = s;
4186
4187 pa_hashmap_put(*p, port->name, port);
4188 }
4189 }
4190 }
4191 }
4192
4193 pa_log_debug("Added %u ports", pa_hashmap_size(*p));
4194 }