]> code.delx.au - pulseaudio/blob - src/modules/alsa/alsa-mixer.c
Move i18n.[ch] to src/pulsecore
[pulseaudio] / src / modules / alsa / alsa-mixer.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2004-2009 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <sys/types.h>
28 #include <asoundlib.h>
29 #include <math.h>
30
31 #ifdef HAVE_VALGRIND_MEMCHECK_H
32 #include <valgrind/memcheck.h>
33 #endif
34
35 #include <pulse/mainloop-api.h>
36 #include <pulse/sample.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/volume.h>
40 #include <pulse/xmalloc.h>
41 #include <pulse/utf8.h>
42
43 #include <pulsecore/i18n.h>
44 #include <pulsecore/log.h>
45 #include <pulsecore/macro.h>
46 #include <pulsecore/core-util.h>
47 #include <pulsecore/conf-parser.h>
48 #include <pulsecore/strbuf.h>
49
50 #include "alsa-mixer.h"
51 #include "alsa-util.h"
52
53 struct description_map {
54 const char *name;
55 const char *description;
56 };
57
58 static const char *lookup_description(const char *name, const struct description_map dm[], unsigned n) {
59 unsigned i;
60
61 for (i = 0; i < n; i++)
62 if (pa_streq(dm[i].name, name))
63 return _(dm[i].description);
64
65 return NULL;
66 }
67
68 struct pa_alsa_fdlist {
69 unsigned num_fds;
70 struct pollfd *fds;
71 /* This is a temporary buffer used to avoid lots of mallocs */
72 struct pollfd *work_fds;
73
74 snd_mixer_t *mixer;
75
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, (e->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -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
2877 static pa_bool_t options_have_option(pa_alsa_option *options, const char *alsa_name) {
2878 pa_alsa_option *o;
2879
2880 pa_assert(options);
2881 pa_assert(alsa_name);
2882
2883 PA_LLIST_FOREACH(o, options) {
2884 if (pa_streq(o->alsa_name, alsa_name))
2885 return TRUE;
2886 }
2887 return FALSE;
2888 }
2889
2890 static pa_bool_t enumeration_is_subset(pa_alsa_option *a_options, pa_alsa_option *b_options) {
2891 pa_alsa_option *oa, *ob;
2892
2893 if (!a_options) return TRUE;
2894 if (!b_options) return FALSE;
2895
2896 /* If there is an option A offers that B does not, then A is not a subset of B. */
2897 PA_LLIST_FOREACH(oa, a_options) {
2898 pa_bool_t found = FALSE;
2899 PA_LLIST_FOREACH(ob, b_options) {
2900 if (pa_streq(oa->alsa_name, ob->alsa_name)) {
2901 found = TRUE;
2902 break;
2903 }
2904 }
2905 if (!found)
2906 return FALSE;
2907 }
2908 return TRUE;
2909 }
2910
2911 /**
2912 * Compares two elements to see if a is a subset of b
2913 */
2914 static pa_bool_t element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_t *m) {
2915 pa_assert(a);
2916 pa_assert(b);
2917 pa_assert(m);
2918
2919 /* General rules:
2920 * Every state is a subset of itself (with caveats for volume_limits and options)
2921 * IGNORE is a subset of every other state */
2922
2923 /* Check the volume_use */
2924 if (a->volume_use != PA_ALSA_VOLUME_IGNORE) {
2925
2926 /* "Constant" is subset of "Constant" only when their constant values are equal */
2927 if (a->volume_use == PA_ALSA_VOLUME_CONSTANT && b->volume_use == PA_ALSA_VOLUME_CONSTANT && a->constant_volume != b->constant_volume)
2928 return FALSE;
2929
2930 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
2931 if (a->volume_use != b->volume_use && b->volume_use != PA_ALSA_VOLUME_MERGE)
2932 return FALSE;
2933
2934 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
2935 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
2936 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
2937 if (b->volume_use == PA_ALSA_VOLUME_MERGE && b->volume_limit >= 0) {
2938 long a_limit;
2939
2940 if (a->volume_use == PA_ALSA_VOLUME_CONSTANT)
2941 a_limit = a->constant_volume;
2942 else if (a->volume_use == PA_ALSA_VOLUME_ZERO) {
2943 long dB = 0;
2944
2945 if (a->db_fix) {
2946 int rounding = (a->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1);
2947 a_limit = decibel_fix_get_step(a->db_fix, &dB, rounding);
2948 } else {
2949 snd_mixer_selem_id_t *sid;
2950 snd_mixer_elem_t *me;
2951
2952 SELEM_INIT(sid, a->alsa_name);
2953 if (!(me = snd_mixer_find_selem(m, sid))) {
2954 pa_log_warn("Element %s seems to have disappeared.", a->alsa_name);
2955 return FALSE;
2956 }
2957
2958 if (a->direction == PA_ALSA_DIRECTION_OUTPUT) {
2959 if (snd_mixer_selem_ask_playback_dB_vol(me, dB, +1, &a_limit) < 0)
2960 return FALSE;
2961 } else {
2962 if (snd_mixer_selem_ask_capture_dB_vol(me, dB, -1, &a_limit) < 0)
2963 return FALSE;
2964 }
2965 }
2966 } else if (a->volume_use == PA_ALSA_VOLUME_OFF)
2967 a_limit = a->min_volume;
2968 else if (a->volume_use == PA_ALSA_VOLUME_MERGE)
2969 a_limit = a->volume_limit;
2970 else
2971 /* This should never be reached */
2972 pa_assert(FALSE);
2973
2974 if (a_limit > b->volume_limit)
2975 return FALSE;
2976 }
2977 }
2978
2979 if (a->switch_use != PA_ALSA_SWITCH_IGNORE) {
2980 /* "On" is a subset of "Mute".
2981 * "Off" is a subset of "Mute".
2982 * "On" is a subset of "Select", if there is an "Option:On" in B.
2983 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
2984 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
2985
2986 if (a->switch_use != b->switch_use) {
2987
2988 if (a->switch_use == PA_ALSA_SWITCH_SELECT || a->switch_use == PA_ALSA_SWITCH_MUTE
2989 || b->switch_use == PA_ALSA_SWITCH_OFF || b->switch_use == PA_ALSA_SWITCH_ON)
2990 return FALSE;
2991
2992 if (b->switch_use == PA_ALSA_SWITCH_SELECT) {
2993 if (a->switch_use == PA_ALSA_SWITCH_ON) {
2994 if (!options_have_option(b->options, "on"))
2995 return FALSE;
2996 } else if (a->switch_use == PA_ALSA_SWITCH_OFF) {
2997 if (!options_have_option(b->options, "off"))
2998 return FALSE;
2999 }
3000 }
3001 } else if (a->switch_use == PA_ALSA_SWITCH_SELECT) {
3002 if (!enumeration_is_subset(a->options, b->options))
3003 return FALSE;
3004 }
3005 }
3006
3007 if (a->enumeration_use != PA_ALSA_ENUMERATION_IGNORE) {
3008 if (b->enumeration_use == PA_ALSA_ENUMERATION_IGNORE)
3009 return FALSE;
3010 if (!enumeration_is_subset(a->options, b->options))
3011 return FALSE;
3012 }
3013
3014 return TRUE;
3015 }
3016
3017 static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
3018 pa_alsa_path *p, *np;
3019
3020 pa_assert(ps);
3021 pa_assert(m);
3022
3023 /* If we only have one path, then don't bother */
3024 if (!ps->paths || !ps->paths->next)
3025 return;
3026
3027 for (p = ps->paths; p; p = np) {
3028 pa_alsa_path *p2;
3029 np = p->next;
3030
3031 PA_LLIST_FOREACH(p2, ps->paths) {
3032 pa_alsa_element *ea, *eb;
3033 pa_bool_t is_subset = TRUE;
3034
3035 if (p == p2)
3036 continue;
3037
3038 /* Compare the elements of each set... */
3039 pa_assert_se(ea = p->elements);
3040 pa_assert_se(eb = p2->elements);
3041
3042 while (is_subset) {
3043 if (pa_streq(ea->alsa_name, eb->alsa_name)) {
3044 if (element_is_subset(ea, eb, m)) {
3045 ea = ea->next;
3046 eb = eb->next;
3047 if ((ea && !eb) || (!ea && eb))
3048 is_subset = FALSE;
3049 else if (!ea && !eb)
3050 break;
3051 } else
3052 is_subset = FALSE;
3053
3054 } else
3055 is_subset = FALSE;
3056 }
3057
3058 if (is_subset) {
3059 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p->name, p2->name);
3060 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
3061 pa_alsa_path_free(p);
3062 break;
3063 }
3064 }
3065 }
3066 }
3067
3068 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
3069 pa_alsa_path *p, *q;
3070
3071 PA_LLIST_FOREACH(p, ps->paths) {
3072 unsigned i;
3073 char *m;
3074
3075 for (q = p->next; q; q = q->next)
3076 if (pa_streq(q->name, p->name))
3077 break;
3078
3079 if (!q)
3080 continue;
3081
3082 m = pa_xstrdup(p->name);
3083
3084 /* OK, this name is not unique, hence let's rename */
3085 for (i = 1, q = p; q; q = q->next) {
3086 char *nn, *nd;
3087
3088 if (!pa_streq(q->name, m))
3089 continue;
3090
3091 nn = pa_sprintf_malloc("%s-%u", m, i);
3092 pa_xfree(q->name);
3093 q->name = nn;
3094
3095 nd = pa_sprintf_malloc("%s %u", q->description, i);
3096 pa_xfree(q->description);
3097 q->description = nd;
3098
3099 i++;
3100 }
3101
3102 pa_xfree(m);
3103 }
3104 }
3105
3106 void pa_alsa_path_set_probe(pa_alsa_path_set *ps, snd_mixer_t *m, pa_bool_t ignore_dB) {
3107 pa_alsa_path *p, *n;
3108
3109 pa_assert(ps);
3110
3111 if (ps->probed)
3112 return;
3113
3114 for (p = ps->paths; p; p = n) {
3115 n = p->next;
3116
3117 if (pa_alsa_path_probe(p, m, ignore_dB) < 0) {
3118 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
3119 pa_alsa_path_free(p);
3120 }
3121 }
3122
3123 pa_log_debug("Found mixer paths (before tidying):");
3124 pa_alsa_path_set_dump(ps);
3125
3126 path_set_condense(ps, m);
3127 path_set_make_paths_unique(ps);
3128 ps->probed = TRUE;
3129
3130 pa_log_debug("Available mixer paths (after tidying):");
3131 pa_alsa_path_set_dump(ps);
3132 }
3133
3134 static void mapping_free(pa_alsa_mapping *m) {
3135 pa_assert(m);
3136
3137 pa_xfree(m->name);
3138 pa_xfree(m->description);
3139
3140 pa_xstrfreev(m->device_strings);
3141 pa_xstrfreev(m->input_path_names);
3142 pa_xstrfreev(m->output_path_names);
3143 pa_xstrfreev(m->input_element);
3144 pa_xstrfreev(m->output_element);
3145
3146 pa_assert(!m->input_pcm);
3147 pa_assert(!m->output_pcm);
3148
3149 pa_xfree(m);
3150 }
3151
3152 static void profile_free(pa_alsa_profile *p) {
3153 pa_assert(p);
3154
3155 pa_xfree(p->name);
3156 pa_xfree(p->description);
3157
3158 pa_xstrfreev(p->input_mapping_names);
3159 pa_xstrfreev(p->output_mapping_names);
3160
3161 if (p->input_mappings)
3162 pa_idxset_free(p->input_mappings, NULL, NULL);
3163
3164 if (p->output_mappings)
3165 pa_idxset_free(p->output_mappings, NULL, NULL);
3166
3167 pa_xfree(p);
3168 }
3169
3170 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
3171 pa_assert(ps);
3172
3173 if (ps->profiles) {
3174 pa_alsa_profile *p;
3175
3176 while ((p = pa_hashmap_steal_first(ps->profiles)))
3177 profile_free(p);
3178
3179 pa_hashmap_free(ps->profiles, NULL, NULL);
3180 }
3181
3182 if (ps->mappings) {
3183 pa_alsa_mapping *m;
3184
3185 while ((m = pa_hashmap_steal_first(ps->mappings)))
3186 mapping_free(m);
3187
3188 pa_hashmap_free(ps->mappings, NULL, NULL);
3189 }
3190
3191 if (ps->decibel_fixes) {
3192 pa_alsa_decibel_fix *db_fix;
3193
3194 while ((db_fix = pa_hashmap_steal_first(ps->decibel_fixes)))
3195 decibel_fix_free(db_fix);
3196
3197 pa_hashmap_free(ps->decibel_fixes, NULL, NULL);
3198 }
3199
3200 pa_xfree(ps);
3201 }
3202
3203 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
3204 pa_alsa_mapping *m;
3205
3206 if (!pa_startswith(name, "Mapping "))
3207 return NULL;
3208
3209 name += 8;
3210
3211 if ((m = pa_hashmap_get(ps->mappings, name)))
3212 return m;
3213
3214 m = pa_xnew0(pa_alsa_mapping, 1);
3215 m->profile_set = ps;
3216 m->name = pa_xstrdup(name);
3217 pa_channel_map_init(&m->channel_map);
3218
3219 pa_hashmap_put(ps->mappings, m->name, m);
3220
3221 return m;
3222 }
3223
3224 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
3225 pa_alsa_profile *p;
3226
3227 if (!pa_startswith(name, "Profile "))
3228 return NULL;
3229
3230 name += 8;
3231
3232 if ((p = pa_hashmap_get(ps->profiles, name)))
3233 return p;
3234
3235 p = pa_xnew0(pa_alsa_profile, 1);
3236 p->profile_set = ps;
3237 p->name = pa_xstrdup(name);
3238
3239 pa_hashmap_put(ps->profiles, p->name, p);
3240
3241 return p;
3242 }
3243
3244 static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) {
3245 pa_alsa_decibel_fix *db_fix;
3246
3247 if (!pa_startswith(name, "DecibelFix "))
3248 return NULL;
3249
3250 name += 11;
3251
3252 if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name)))
3253 return db_fix;
3254
3255 db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
3256 db_fix->profile_set = ps;
3257 db_fix->name = pa_xstrdup(name);
3258
3259 pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix);
3260
3261 return db_fix;
3262 }
3263
3264 static int mapping_parse_device_strings(
3265 const char *filename,
3266 unsigned line,
3267 const char *section,
3268 const char *lvalue,
3269 const char *rvalue,
3270 void *data,
3271 void *userdata) {
3272
3273 pa_alsa_profile_set *ps = userdata;
3274 pa_alsa_mapping *m;
3275
3276 pa_assert(ps);
3277
3278 if (!(m = mapping_get(ps, section))) {
3279 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3280 return -1;
3281 }
3282
3283 pa_xstrfreev(m->device_strings);
3284 if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
3285 pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
3286 return -1;
3287 }
3288
3289 return 0;
3290 }
3291
3292 static int mapping_parse_channel_map(
3293 const char *filename,
3294 unsigned line,
3295 const char *section,
3296 const char *lvalue,
3297 const char *rvalue,
3298 void *data,
3299 void *userdata) {
3300
3301 pa_alsa_profile_set *ps = userdata;
3302 pa_alsa_mapping *m;
3303
3304 pa_assert(ps);
3305
3306 if (!(m = mapping_get(ps, section))) {
3307 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3308 return -1;
3309 }
3310
3311 if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
3312 pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
3313 return -1;
3314 }
3315
3316 return 0;
3317 }
3318
3319 static int mapping_parse_paths(
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_mapping *m;
3330
3331 pa_assert(ps);
3332
3333 if (!(m = mapping_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, "paths-input")) {
3339 pa_xstrfreev(m->input_path_names);
3340 m->input_path_names = pa_split_spaces_strv(rvalue);
3341 } else {
3342 pa_xstrfreev(m->output_path_names);
3343 m->output_path_names = pa_split_spaces_strv(rvalue);
3344 }
3345
3346 return 0;
3347 }
3348
3349 static int mapping_parse_element(
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_mapping *m;
3360
3361 pa_assert(ps);
3362
3363 if (!(m = mapping_get(ps, section))) {
3364 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3365 return -1;
3366 }
3367
3368 if (pa_streq(lvalue, "element-input")) {
3369 pa_xstrfreev(m->input_element);
3370 m->input_element = pa_split_spaces_strv(rvalue);
3371 } else {
3372 pa_xstrfreev(m->output_element);
3373 m->output_element = pa_split_spaces_strv(rvalue);
3374 }
3375
3376 return 0;
3377 }
3378
3379 static int mapping_parse_direction(
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_mapping *m;
3390
3391 pa_assert(ps);
3392
3393 if (!(m = mapping_get(ps, section))) {
3394 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3395 return -1;
3396 }
3397
3398 if (pa_streq(rvalue, "input"))
3399 m->direction = PA_ALSA_DIRECTION_INPUT;
3400 else if (pa_streq(rvalue, "output"))
3401 m->direction = PA_ALSA_DIRECTION_OUTPUT;
3402 else if (pa_streq(rvalue, "any"))
3403 m->direction = PA_ALSA_DIRECTION_ANY;
3404 else {
3405 pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
3406 return -1;
3407 }
3408
3409 return 0;
3410 }
3411
3412 static int mapping_parse_description(
3413 const char *filename,
3414 unsigned line,
3415 const char *section,
3416 const char *lvalue,
3417 const char *rvalue,
3418 void *data,
3419 void *userdata) {
3420
3421 pa_alsa_profile_set *ps = userdata;
3422 pa_alsa_profile *p;
3423 pa_alsa_mapping *m;
3424
3425 pa_assert(ps);
3426
3427 if ((m = mapping_get(ps, section))) {
3428 pa_xfree(m->description);
3429 m->description = pa_xstrdup(rvalue);
3430 } else if ((p = profile_get(ps, section))) {
3431 pa_xfree(p->description);
3432 p->description = pa_xstrdup(rvalue);
3433 } else {
3434 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3435 return -1;
3436 }
3437
3438 return 0;
3439 }
3440
3441 static int mapping_parse_priority(
3442 const char *filename,
3443 unsigned line,
3444 const char *section,
3445 const char *lvalue,
3446 const char *rvalue,
3447 void *data,
3448 void *userdata) {
3449
3450 pa_alsa_profile_set *ps = userdata;
3451 pa_alsa_profile *p;
3452 pa_alsa_mapping *m;
3453 uint32_t prio;
3454
3455 pa_assert(ps);
3456
3457 if (pa_atou(rvalue, &prio) < 0) {
3458 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
3459 return -1;
3460 }
3461
3462 if ((m = mapping_get(ps, section)))
3463 m->priority = prio;
3464 else if ((p = profile_get(ps, section)))
3465 p->priority = prio;
3466 else {
3467 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
3468 return -1;
3469 }
3470
3471 return 0;
3472 }
3473
3474 static int profile_parse_mappings(
3475 const char *filename,
3476 unsigned line,
3477 const char *section,
3478 const char *lvalue,
3479 const char *rvalue,
3480 void *data,
3481 void *userdata) {
3482
3483 pa_alsa_profile_set *ps = userdata;
3484 pa_alsa_profile *p;
3485
3486 pa_assert(ps);
3487
3488 if (!(p = profile_get(ps, section))) {
3489 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3490 return -1;
3491 }
3492
3493 if (pa_streq(lvalue, "input-mappings")) {
3494 pa_xstrfreev(p->input_mapping_names);
3495 p->input_mapping_names = pa_split_spaces_strv(rvalue);
3496 } else {
3497 pa_xstrfreev(p->output_mapping_names);
3498 p->output_mapping_names = pa_split_spaces_strv(rvalue);
3499 }
3500
3501 return 0;
3502 }
3503
3504 static int profile_parse_skip_probe(
3505 const char *filename,
3506 unsigned line,
3507 const char *section,
3508 const char *lvalue,
3509 const char *rvalue,
3510 void *data,
3511 void *userdata) {
3512
3513 pa_alsa_profile_set *ps = userdata;
3514 pa_alsa_profile *p;
3515 int b;
3516
3517 pa_assert(ps);
3518
3519 if (!(p = profile_get(ps, section))) {
3520 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3521 return -1;
3522 }
3523
3524 if ((b = pa_parse_boolean(rvalue)) < 0) {
3525 pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
3526 return -1;
3527 }
3528
3529 p->supported = b;
3530
3531 return 0;
3532 }
3533
3534 static int decibel_fix_parse_db_values(
3535 const char *filename,
3536 unsigned line,
3537 const char *section,
3538 const char *lvalue,
3539 const char *rvalue,
3540 void *data,
3541 void *userdata) {
3542
3543 pa_alsa_profile_set *ps = userdata;
3544 pa_alsa_decibel_fix *db_fix;
3545 char **items;
3546 char *item;
3547 long *db_values;
3548 unsigned n = 8; /* Current size of the db_values table. */
3549 unsigned min_step = 0;
3550 unsigned max_step = 0;
3551 unsigned i = 0; /* Index to the items table. */
3552 unsigned prev_step = 0;
3553 double prev_db = 0;
3554
3555 pa_assert(filename);
3556 pa_assert(section);
3557 pa_assert(lvalue);
3558 pa_assert(rvalue);
3559 pa_assert(ps);
3560
3561 if (!(db_fix = decibel_fix_get(ps, section))) {
3562 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
3563 return -1;
3564 }
3565
3566 if (!(items = pa_split_spaces_strv(rvalue))) {
3567 pa_log("[%s:%u] Value missing", pa_strnull(filename), line);
3568 return -1;
3569 }
3570
3571 db_values = pa_xnew(long, n);
3572
3573 while ((item = items[i++])) {
3574 char *s = item; /* Step value string. */
3575 char *d = item; /* dB value string. */
3576 uint32_t step;
3577 double db;
3578
3579 /* Move d forward until it points to a colon or to the end of the item. */
3580 for (; *d && *d != ':'; ++d);
3581
3582 if (d == s) {
3583 /* item started with colon. */
3584 pa_log("[%s:%u] No step value found in %s", filename, line, item);
3585 goto fail;
3586 }
3587
3588 if (!*d || !*(d + 1)) {
3589 /* No colon found, or it was the last character in item. */
3590 pa_log("[%s:%u] No dB value found in %s", filename, line, item);
3591 goto fail;
3592 }
3593
3594 /* pa_atou() needs a null-terminating string. Let's replace the colon
3595 * with a zero byte. */
3596 *d++ = '\0';
3597
3598 if (pa_atou(s, &step) < 0) {
3599 pa_log("[%s:%u] Invalid step value: %s", filename, line, s);
3600 goto fail;
3601 }
3602
3603 if (pa_atod(d, &db) < 0) {
3604 pa_log("[%s:%u] Invalid dB value: %s", filename, line, d);
3605 goto fail;
3606 }
3607
3608 if (step <= prev_step && i != 1) {
3609 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename, line, step, prev_step);
3610 goto fail;
3611 }
3612
3613 if (db < prev_db && i != 1) {
3614 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename, line, db, prev_db);
3615 goto fail;
3616 }
3617
3618 if (i == 1) {
3619 min_step = step;
3620 db_values[0] = (long) (db * 100.0);
3621 prev_step = step;
3622 prev_db = db;
3623 } else {
3624 /* Interpolate linearly. */
3625 double db_increment = (db - prev_db) / (step - prev_step);
3626
3627 for (; prev_step < step; ++prev_step, prev_db += db_increment) {
3628
3629 /* Reallocate the db_values table if it's about to overflow. */
3630 if (prev_step + 1 - min_step == n) {
3631 n *= 2;
3632 db_values = pa_xrenew(long, db_values, n);
3633 }
3634
3635 db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
3636 }
3637 }
3638
3639 max_step = step;
3640 }
3641
3642 db_fix->min_step = min_step;
3643 db_fix->max_step = max_step;
3644 pa_xfree(db_fix->db_values);
3645 db_fix->db_values = db_values;
3646
3647 pa_xstrfreev(items);
3648
3649 return 0;
3650
3651 fail:
3652 pa_xstrfreev(items);
3653 pa_xfree(db_values);
3654
3655 return -1;
3656 }
3657
3658 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
3659
3660 static const struct description_map well_known_descriptions[] = {
3661 { "analog-mono", N_("Analog Mono") },
3662 { "analog-stereo", N_("Analog Stereo") },
3663 { "analog-surround-21", N_("Analog Surround 2.1") },
3664 { "analog-surround-30", N_("Analog Surround 3.0") },
3665 { "analog-surround-31", N_("Analog Surround 3.1") },
3666 { "analog-surround-40", N_("Analog Surround 4.0") },
3667 { "analog-surround-41", N_("Analog Surround 4.1") },
3668 { "analog-surround-50", N_("Analog Surround 5.0") },
3669 { "analog-surround-51", N_("Analog Surround 5.1") },
3670 { "analog-surround-61", N_("Analog Surround 6.0") },
3671 { "analog-surround-61", N_("Analog Surround 6.1") },
3672 { "analog-surround-70", N_("Analog Surround 7.0") },
3673 { "analog-surround-71", N_("Analog Surround 7.1") },
3674 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3675 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3676 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3677 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3678 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3679 };
3680
3681 pa_assert(m);
3682
3683 if (!pa_channel_map_valid(&m->channel_map)) {
3684 pa_log("Mapping %s is missing channel map.", m->name);
3685 return -1;
3686 }
3687
3688 if (!m->device_strings) {
3689 pa_log("Mapping %s is missing device strings.", m->name);
3690 return -1;
3691 }
3692
3693 if ((m->input_path_names && m->input_element) ||
3694 (m->output_path_names && m->output_element)) {
3695 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
3696 return -1;
3697 }
3698
3699 if (!m->description)
3700 m->description = pa_xstrdup(lookup_description(m->name,
3701 well_known_descriptions,
3702 PA_ELEMENTSOF(well_known_descriptions)));
3703
3704 if (!m->description)
3705 m->description = pa_xstrdup(m->name);
3706
3707 if (bonus) {
3708 if (pa_channel_map_equal(&m->channel_map, bonus))
3709 m->priority += 50;
3710 else if (m->channel_map.channels == bonus->channels)
3711 m->priority += 30;
3712 }
3713
3714 return 0;
3715 }
3716
3717 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
3718 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
3719
3720 pa_assert(m);
3721
3722 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3723 m->name,
3724 pa_strnull(m->description),
3725 m->priority,
3726 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
3727 pa_yes_no(m->supported),
3728 m->direction);
3729 }
3730
3731 static void profile_set_add_auto_pair(
3732 pa_alsa_profile_set *ps,
3733 pa_alsa_mapping *m, /* output */
3734 pa_alsa_mapping *n /* input */) {
3735
3736 char *name;
3737 pa_alsa_profile *p;
3738
3739 pa_assert(ps);
3740 pa_assert(m || n);
3741
3742 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
3743 return;
3744
3745 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
3746 return;
3747
3748 if (m && n)
3749 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
3750 else if (m)
3751 name = pa_sprintf_malloc("output:%s", m->name);
3752 else
3753 name = pa_sprintf_malloc("input:%s", n->name);
3754
3755 if (pa_hashmap_get(ps->profiles, name)) {
3756 pa_xfree(name);
3757 return;
3758 }
3759
3760 p = pa_xnew0(pa_alsa_profile, 1);
3761 p->profile_set = ps;
3762 p->name = name;
3763
3764 if (m) {
3765 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3766 pa_idxset_put(p->output_mappings, m, NULL);
3767 p->priority += m->priority * 100;
3768 }
3769
3770 if (n) {
3771 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3772 pa_idxset_put(p->input_mappings, n, NULL);
3773 p->priority += n->priority;
3774 }
3775
3776 pa_hashmap_put(ps->profiles, p->name, p);
3777 }
3778
3779 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
3780 pa_alsa_mapping *m, *n;
3781 void *m_state, *n_state;
3782
3783 pa_assert(ps);
3784
3785 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
3786 profile_set_add_auto_pair(ps, m, NULL);
3787
3788 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3789 profile_set_add_auto_pair(ps, m, n);
3790 }
3791
3792 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3793 profile_set_add_auto_pair(ps, NULL, n);
3794 }
3795
3796 static int profile_verify(pa_alsa_profile *p) {
3797
3798 static const struct description_map well_known_descriptions[] = {
3799 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3800 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3801 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3802 { "off", N_("Off") }
3803 };
3804
3805 pa_assert(p);
3806
3807 /* Replace the output mapping names by the actual mappings */
3808 if (p->output_mapping_names) {
3809 char **name;
3810
3811 pa_assert(!p->output_mappings);
3812 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3813
3814 for (name = p->output_mapping_names; *name; name++) {
3815 pa_alsa_mapping *m;
3816 char **in;
3817 pa_bool_t duplicate = FALSE;
3818
3819 for (in = name + 1; *in; in++)
3820 if (pa_streq(*name, *in)) {
3821 duplicate = TRUE;
3822 break;
3823 }
3824
3825 if (duplicate)
3826 continue;
3827
3828 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
3829 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
3830 return -1;
3831 }
3832
3833 pa_idxset_put(p->output_mappings, m, NULL);
3834
3835 if (p->supported)
3836 m->supported++;
3837 }
3838
3839 pa_xstrfreev(p->output_mapping_names);
3840 p->output_mapping_names = NULL;
3841 }
3842
3843 /* Replace the input mapping names by the actual mappings */
3844 if (p->input_mapping_names) {
3845 char **name;
3846
3847 pa_assert(!p->input_mappings);
3848 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3849
3850 for (name = p->input_mapping_names; *name; name++) {
3851 pa_alsa_mapping *m;
3852 char **in;
3853 pa_bool_t duplicate = FALSE;
3854
3855 for (in = name + 1; *in; in++)
3856 if (pa_streq(*name, *in)) {
3857 duplicate = TRUE;
3858 break;
3859 }
3860
3861 if (duplicate)
3862 continue;
3863
3864 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
3865 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
3866 return -1;
3867 }
3868
3869 pa_idxset_put(p->input_mappings, m, NULL);
3870
3871 if (p->supported)
3872 m->supported++;
3873 }
3874
3875 pa_xstrfreev(p->input_mapping_names);
3876 p->input_mapping_names = NULL;
3877 }
3878
3879 if (!p->input_mappings && !p->output_mappings) {
3880 pa_log("Profile '%s' lacks mappings.", p->name);
3881 return -1;
3882 }
3883
3884 if (!p->description)
3885 p->description = pa_xstrdup(lookup_description(p->name,
3886 well_known_descriptions,
3887 PA_ELEMENTSOF(well_known_descriptions)));
3888
3889 if (!p->description) {
3890 pa_strbuf *sb;
3891 uint32_t idx;
3892 pa_alsa_mapping *m;
3893
3894 sb = pa_strbuf_new();
3895
3896 if (p->output_mappings)
3897 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3898 if (!pa_strbuf_isempty(sb))
3899 pa_strbuf_puts(sb, " + ");
3900
3901 pa_strbuf_printf(sb, _("%s Output"), m->description);
3902 }
3903
3904 if (p->input_mappings)
3905 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3906 if (!pa_strbuf_isempty(sb))
3907 pa_strbuf_puts(sb, " + ");
3908
3909 pa_strbuf_printf(sb, _("%s Input"), m->description);
3910 }
3911
3912 p->description = pa_strbuf_tostring_free(sb);
3913 }
3914
3915 return 0;
3916 }
3917
3918 void pa_alsa_profile_dump(pa_alsa_profile *p) {
3919 uint32_t idx;
3920 pa_alsa_mapping *m;
3921 pa_assert(p);
3922
3923 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3924 p->name,
3925 pa_strnull(p->description),
3926 p->priority,
3927 pa_yes_no(p->supported),
3928 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
3929 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
3930
3931 if (p->input_mappings)
3932 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
3933 pa_log_debug("Input %s", m->name);
3934
3935 if (p->output_mappings)
3936 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
3937 pa_log_debug("Output %s", m->name);
3938 }
3939
3940 static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
3941 pa_assert(db_fix);
3942
3943 /* Check that the dB mapping has been configured. Since "db-values" is
3944 * currently the only option in the DecibelFix section, and decibel fix
3945 * objects don't get created if a DecibelFix section is empty, this is
3946 * actually a redundant check. Having this may prevent future bugs,
3947 * however. */
3948 if (!db_fix->db_values) {
3949 pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
3950 return -1;
3951 }
3952
3953 return 0;
3954 }
3955
3956 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
3957 char *db_values = NULL;
3958
3959 pa_assert(db_fix);
3960
3961 if (db_fix->db_values) {
3962 pa_strbuf *buf;
3963 unsigned long i, nsteps;
3964
3965 pa_assert(db_fix->min_step <= db_fix->max_step);
3966 nsteps = db_fix->max_step - db_fix->min_step + 1;
3967
3968 buf = pa_strbuf_new();
3969 for (i = 0; i < nsteps; ++i)
3970 pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
3971
3972 db_values = pa_strbuf_tostring_free(buf);
3973 }
3974
3975 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
3976 db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
3977
3978 pa_xfree(db_values);
3979 }
3980
3981 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
3982 pa_alsa_profile_set *ps;
3983 pa_alsa_profile *p;
3984 pa_alsa_mapping *m;
3985 pa_alsa_decibel_fix *db_fix;
3986 char *fn;
3987 int r;
3988 void *state;
3989
3990 static pa_config_item items[] = {
3991 /* [General] */
3992 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
3993
3994 /* [Mapping ...] */
3995 { "device-strings", mapping_parse_device_strings, NULL, NULL },
3996 { "channel-map", mapping_parse_channel_map, NULL, NULL },
3997 { "paths-input", mapping_parse_paths, NULL, NULL },
3998 { "paths-output", mapping_parse_paths, NULL, NULL },
3999 { "element-input", mapping_parse_element, NULL, NULL },
4000 { "element-output", mapping_parse_element, NULL, NULL },
4001 { "direction", mapping_parse_direction, NULL, NULL },
4002
4003 /* Shared by [Mapping ...] and [Profile ...] */
4004 { "description", mapping_parse_description, NULL, NULL },
4005 { "priority", mapping_parse_priority, NULL, NULL },
4006
4007 /* [Profile ...] */
4008 { "input-mappings", profile_parse_mappings, NULL, NULL },
4009 { "output-mappings", profile_parse_mappings, NULL, NULL },
4010 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
4011
4012 /* [DecibelFix ...] */
4013 { "db-values", decibel_fix_parse_db_values, NULL, NULL },
4014 { NULL, NULL, NULL, NULL }
4015 };
4016
4017 ps = pa_xnew0(pa_alsa_profile_set, 1);
4018 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4019 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4020 ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4021
4022 items[0].data = &ps->auto_profiles;
4023
4024 if (!fname)
4025 fname = "default.conf";
4026
4027 fn = pa_maybe_prefix_path(fname,
4028 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
4029 PA_ALSA_PROFILE_SETS_DIR);
4030
4031 r = pa_config_parse(fn, NULL, items, ps);
4032 pa_xfree(fn);
4033
4034 if (r < 0)
4035 goto fail;
4036
4037 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4038 if (mapping_verify(m, bonus) < 0)
4039 goto fail;
4040
4041 if (ps->auto_profiles)
4042 profile_set_add_auto(ps);
4043
4044 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4045 if (profile_verify(p) < 0)
4046 goto fail;
4047
4048 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4049 if (decibel_fix_verify(db_fix) < 0)
4050 goto fail;
4051
4052 return ps;
4053
4054 fail:
4055 pa_alsa_profile_set_free(ps);
4056 return NULL;
4057 }
4058
4059 void pa_alsa_profile_set_probe(
4060 pa_alsa_profile_set *ps,
4061 const char *dev_id,
4062 const pa_sample_spec *ss,
4063 unsigned default_n_fragments,
4064 unsigned default_fragment_size_msec) {
4065
4066 void *state;
4067 pa_alsa_profile *p, *last = NULL;
4068 pa_alsa_mapping *m;
4069
4070 pa_assert(ps);
4071 pa_assert(dev_id);
4072 pa_assert(ss);
4073
4074 if (ps->probed)
4075 return;
4076
4077 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
4078 pa_sample_spec try_ss;
4079 pa_channel_map try_map;
4080 snd_pcm_uframes_t try_period_size, try_buffer_size;
4081 uint32_t idx;
4082
4083 /* Is this already marked that it is supported? (i.e. from the config file) */
4084 if (p->supported)
4085 continue;
4086
4087 pa_log_debug("Looking at profile %s", p->name);
4088
4089 /* Close PCMs from the last iteration we don't need anymore */
4090 if (last && last->output_mappings)
4091 PA_IDXSET_FOREACH(m, last->output_mappings, idx) {
4092
4093 if (!m->output_pcm)
4094 break;
4095
4096 if (last->supported)
4097 m->supported++;
4098
4099 if (!p->output_mappings || !pa_idxset_get_by_data(p->output_mappings, m, NULL)) {
4100 snd_pcm_close(m->output_pcm);
4101 m->output_pcm = NULL;
4102 }
4103 }
4104
4105 if (last && last->input_mappings)
4106 PA_IDXSET_FOREACH(m, last->input_mappings, idx) {
4107
4108 if (!m->input_pcm)
4109 break;
4110
4111 if (last->supported)
4112 m->supported++;
4113
4114 if (!p->input_mappings || !pa_idxset_get_by_data(p->input_mappings, m, NULL)) {
4115 snd_pcm_close(m->input_pcm);
4116 m->input_pcm = NULL;
4117 }
4118 }
4119
4120 p->supported = TRUE;
4121
4122 /* Check if we can open all new ones */
4123 if (p->output_mappings)
4124 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4125
4126 if (m->output_pcm)
4127 continue;
4128
4129 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
4130 try_map = m->channel_map;
4131 try_ss = *ss;
4132 try_ss.channels = try_map.channels;
4133
4134 try_period_size =
4135 pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
4136 pa_frame_size(&try_ss);
4137 try_buffer_size = default_n_fragments * try_period_size;
4138
4139 if (!(m ->output_pcm = pa_alsa_open_by_template(
4140 m->device_strings,
4141 dev_id,
4142 NULL,
4143 &try_ss, &try_map,
4144 SND_PCM_STREAM_PLAYBACK,
4145 &try_period_size, &try_buffer_size, 0, NULL, NULL,
4146 TRUE))) {
4147 p->supported = FALSE;
4148 break;
4149 }
4150 }
4151
4152 if (p->input_mappings && p->supported)
4153 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4154
4155 if (m->input_pcm)
4156 continue;
4157
4158 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
4159 try_map = m->channel_map;
4160 try_ss = *ss;
4161 try_ss.channels = try_map.channels;
4162
4163 try_period_size =
4164 pa_usec_to_bytes(default_fragment_size_msec*PA_USEC_PER_MSEC, &try_ss) /
4165 pa_frame_size(&try_ss);
4166 try_buffer_size = default_n_fragments * try_period_size;
4167
4168 if (!(m ->input_pcm = pa_alsa_open_by_template(
4169 m->device_strings,
4170 dev_id,
4171 NULL,
4172 &try_ss, &try_map,
4173 SND_PCM_STREAM_CAPTURE,
4174 &try_period_size, &try_buffer_size, 0, NULL, NULL,
4175 TRUE))) {
4176 p->supported = FALSE;
4177 break;
4178 }
4179 }
4180
4181 last = p;
4182
4183 if (p->supported)
4184 pa_log_debug("Profile %s supported.", p->name);
4185 }
4186
4187 /* Clean up */
4188 if (last) {
4189 uint32_t idx;
4190
4191 if (last->output_mappings)
4192 PA_IDXSET_FOREACH(m, last->output_mappings, idx)
4193 if (m->output_pcm) {
4194
4195 if (last->supported)
4196 m->supported++;
4197
4198 snd_pcm_close(m->output_pcm);
4199 m->output_pcm = NULL;
4200 }
4201
4202 if (last->input_mappings)
4203 PA_IDXSET_FOREACH(m, last->input_mappings, idx)
4204 if (m->input_pcm) {
4205
4206 if (last->supported)
4207 m->supported++;
4208
4209 snd_pcm_close(m->input_pcm);
4210 m->input_pcm = NULL;
4211 }
4212 }
4213
4214 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4215 if (!p->supported) {
4216 pa_hashmap_remove(ps->profiles, p->name);
4217 profile_free(p);
4218 }
4219
4220 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4221 if (m->supported <= 0) {
4222 pa_hashmap_remove(ps->mappings, m->name);
4223 mapping_free(m);
4224 }
4225
4226 ps->probed = TRUE;
4227 }
4228
4229 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
4230 pa_alsa_profile *p;
4231 pa_alsa_mapping *m;
4232 pa_alsa_decibel_fix *db_fix;
4233 void *state;
4234
4235 pa_assert(ps);
4236
4237 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4238 (void*)
4239 ps,
4240 pa_yes_no(ps->auto_profiles),
4241 pa_yes_no(ps->probed),
4242 pa_hashmap_size(ps->mappings),
4243 pa_hashmap_size(ps->profiles),
4244 pa_hashmap_size(ps->decibel_fixes));
4245
4246 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4247 pa_alsa_mapping_dump(m);
4248
4249 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4250 pa_alsa_profile_dump(p);
4251
4252 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4253 pa_alsa_decibel_fix_dump(db_fix);
4254 }
4255
4256 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps) {
4257 pa_alsa_path *path;
4258
4259 pa_assert(p);
4260 pa_assert(!*p);
4261 pa_assert(ps);
4262
4263 /* if there is no path, we don't want a port list */
4264 if (!ps->paths)
4265 return;
4266
4267 if (!ps->paths->next){
4268 pa_alsa_setting *s;
4269
4270 /* If there is only one path, but no or only one setting, then
4271 * we want a port list either */
4272 if (!ps->paths->settings || !ps->paths->settings->next)
4273 return;
4274
4275 /* Ok, there is only one path, however with multiple settings,
4276 * so let's create a port for each setting */
4277 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4278
4279 PA_LLIST_FOREACH(s, ps->paths->settings) {
4280 pa_device_port *port;
4281 pa_alsa_port_data *data;
4282
4283 port = pa_device_port_new(s->name, s->description, sizeof(pa_alsa_port_data));
4284 port->priority = s->priority;
4285
4286 data = PA_DEVICE_PORT_DATA(port);
4287 data->path = ps->paths;
4288 data->setting = s;
4289
4290 pa_hashmap_put(*p, port->name, port);
4291 }
4292
4293 } else {
4294
4295 /* We have multiple paths, so let's create a port for each
4296 * one, and each of each settings */
4297 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4298
4299 PA_LLIST_FOREACH(path, ps->paths) {
4300
4301 if (!path->settings || !path->settings->next) {
4302 pa_device_port *port;
4303 pa_alsa_port_data *data;
4304
4305 /* If there is no or just one setting we only need a
4306 * single entry */
4307
4308 port = pa_device_port_new(path->name, path->description, sizeof(pa_alsa_port_data));
4309 port->priority = path->priority * 100;
4310
4311
4312 data = PA_DEVICE_PORT_DATA(port);
4313 data->path = path;
4314 data->setting = path->settings;
4315
4316 pa_hashmap_put(*p, port->name, port);
4317 } else {
4318 pa_alsa_setting *s;
4319
4320 PA_LLIST_FOREACH(s, path->settings) {
4321 pa_device_port *port;
4322 pa_alsa_port_data *data;
4323 char *n, *d;
4324
4325 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
4326
4327 if (s->description[0])
4328 d = pa_sprintf_malloc(_("%s / %s"), path->description, s->description);
4329 else
4330 d = pa_xstrdup(path->description);
4331
4332 port = pa_device_port_new(n, d, sizeof(pa_alsa_port_data));
4333 port->priority = path->priority * 100 + s->priority;
4334
4335 pa_xfree(n);
4336 pa_xfree(d);
4337
4338 data = PA_DEVICE_PORT_DATA(port);
4339 data->path = path;
4340 data->setting = s;
4341
4342 pa_hashmap_put(*p, port->name, port);
4343 }
4344 }
4345 }
4346 }
4347
4348 pa_log_debug("Added %u ports", pa_hashmap_size(*p));
4349 }