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