]> code.delx.au - pulseaudio/blob - src/modules/alsa/alsa-mixer.c
Fix typos
[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 element_free(pa_alsa_element *e) {
495 pa_alsa_option *o;
496 pa_assert(e);
497
498 while ((o = e->options)) {
499 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
500 option_free(o);
501 }
502
503 pa_xfree(e->alsa_name);
504 pa_xfree(e);
505 }
506
507 void pa_alsa_path_free(pa_alsa_path *p) {
508 pa_alsa_element *e;
509 pa_alsa_setting *s;
510
511 pa_assert(p);
512
513 while ((e = p->elements)) {
514 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
515 element_free(e);
516 }
517
518 while ((s = p->settings)) {
519 PA_LLIST_REMOVE(pa_alsa_setting, p->settings, s);
520 setting_free(s);
521 }
522
523 pa_xfree(p->name);
524 pa_xfree(p->description);
525 pa_xfree(p);
526 }
527
528 void pa_alsa_path_set_free(pa_alsa_path_set *ps) {
529 pa_alsa_path *p;
530 pa_assert(ps);
531
532 while ((p = ps->paths)) {
533 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
534 pa_alsa_path_free(p);
535 }
536
537 pa_xfree(ps);
538 }
539
540 static long to_alsa_dB(pa_volume_t v) {
541 return (long) (pa_sw_volume_to_dB(v) * 100.0);
542 }
543
544 static pa_volume_t from_alsa_dB(long v) {
545 return pa_sw_volume_from_dB((double) v / 100.0);
546 }
547
548 static long to_alsa_volume(pa_volume_t v, long min, long max) {
549 long w;
550
551 w = (long) round(((double) v * (double) (max - min)) / PA_VOLUME_NORM) + min;
552 return PA_CLAMP_UNLIKELY(w, min, max);
553 }
554
555 static pa_volume_t from_alsa_volume(long v, long min, long max) {
556 return (pa_volume_t) round(((double) (v - min) * PA_VOLUME_NORM) / (double) (max - min));
557 }
558
559 #define SELEM_INIT(sid, name) \
560 do { \
561 snd_mixer_selem_id_alloca(&(sid)); \
562 snd_mixer_selem_id_set_name((sid), (name)); \
563 snd_mixer_selem_id_set_index((sid), 0); \
564 } while(FALSE)
565
566 static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
567 snd_mixer_selem_id_t *sid;
568 snd_mixer_elem_t *me;
569 snd_mixer_selem_channel_id_t c;
570 pa_channel_position_mask_t mask = 0;
571 unsigned k;
572
573 pa_assert(m);
574 pa_assert(e);
575 pa_assert(cm);
576 pa_assert(v);
577
578 SELEM_INIT(sid, e->alsa_name);
579 if (!(me = snd_mixer_find_selem(m, sid))) {
580 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
581 return -1;
582 }
583
584 pa_cvolume_mute(v, cm->channels);
585
586 /* We take the highest volume of all channels that match */
587
588 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
589 int r;
590 pa_volume_t f;
591
592 if (e->has_dB) {
593 long value = 0;
594
595 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
596 if (snd_mixer_selem_has_playback_channel(me, c))
597 r = snd_mixer_selem_get_playback_dB(me, c, &value);
598 else
599 r = -1;
600 } else {
601 if (snd_mixer_selem_has_capture_channel(me, c))
602 r = snd_mixer_selem_get_capture_dB(me, c, &value);
603 else
604 r = -1;
605 }
606
607 if (r < 0)
608 continue;
609
610 #ifdef HAVE_VALGRIND_MEMCHECK_H
611 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
612 #endif
613
614 f = from_alsa_dB(value);
615
616 } else {
617 long value = 0;
618
619 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
620 if (snd_mixer_selem_has_playback_channel(me, c))
621 r = snd_mixer_selem_get_playback_volume(me, c, &value);
622 else
623 r = -1;
624 } else {
625 if (snd_mixer_selem_has_capture_channel(me, c))
626 r = snd_mixer_selem_get_capture_volume(me, c, &value);
627 else
628 r = -1;
629 }
630
631 if (r < 0)
632 continue;
633
634 f = from_alsa_volume(value, e->min_volume, e->max_volume);
635 }
636
637 for (k = 0; k < cm->channels; k++)
638 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
639 if (v->values[k] < f)
640 v->values[k] = f;
641
642 mask |= e->masks[c][e->n_channels-1];
643 }
644
645 for (k = 0; k < cm->channels; k++)
646 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
647 v->values[k] = PA_VOLUME_NORM;
648
649 return 0;
650 }
651
652 int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
653 pa_alsa_element *e;
654
655 pa_assert(m);
656 pa_assert(p);
657 pa_assert(cm);
658 pa_assert(v);
659
660 if (!p->has_volume)
661 return -1;
662
663 pa_cvolume_reset(v, cm->channels);
664
665 PA_LLIST_FOREACH(e, p->elements) {
666 pa_cvolume ev;
667
668 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
669 continue;
670
671 pa_assert(!p->has_dB || e->has_dB);
672
673 if (element_get_volume(e, m, cm, &ev) < 0)
674 return -1;
675
676 /* If we have no dB information all we can do is take the first element and leave */
677 if (!p->has_dB) {
678 *v = ev;
679 return 0;
680 }
681
682 pa_sw_cvolume_multiply(v, v, &ev);
683 }
684
685 return 0;
686 }
687
688 static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t *b) {
689 snd_mixer_selem_id_t *sid;
690 snd_mixer_elem_t *me;
691 snd_mixer_selem_channel_id_t c;
692
693 pa_assert(m);
694 pa_assert(e);
695 pa_assert(b);
696
697 SELEM_INIT(sid, e->alsa_name);
698 if (!(me = snd_mixer_find_selem(m, sid))) {
699 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
700 return -1;
701 }
702
703 /* We return muted if at least one channel is muted */
704
705 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
706 int r;
707 int value = 0;
708
709 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
710 if (snd_mixer_selem_has_playback_channel(me, c))
711 r = snd_mixer_selem_get_playback_switch(me, c, &value);
712 else
713 r = -1;
714 } else {
715 if (snd_mixer_selem_has_capture_channel(me, c))
716 r = snd_mixer_selem_get_capture_switch(me, c, &value);
717 else
718 r = -1;
719 }
720
721 if (r < 0)
722 continue;
723
724 if (!value) {
725 *b = FALSE;
726 return 0;
727 }
728 }
729
730 *b = TRUE;
731 return 0;
732 }
733
734 int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t *muted) {
735 pa_alsa_element *e;
736
737 pa_assert(m);
738 pa_assert(p);
739 pa_assert(muted);
740
741 if (!p->has_mute)
742 return -1;
743
744 PA_LLIST_FOREACH(e, p->elements) {
745 pa_bool_t b;
746
747 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
748 continue;
749
750 if (element_get_switch(e, m, &b) < 0)
751 return -1;
752
753 if (!b) {
754 *muted = TRUE;
755 return 0;
756 }
757 }
758
759 *muted = FALSE;
760 return 0;
761 }
762
763 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) {
764
765 snd_mixer_selem_id_t *sid;
766 pa_cvolume rv;
767 snd_mixer_elem_t *me;
768 snd_mixer_selem_channel_id_t c;
769 pa_channel_position_mask_t mask = 0;
770 unsigned k;
771
772 pa_assert(m);
773 pa_assert(e);
774 pa_assert(cm);
775 pa_assert(v);
776 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
777
778 SELEM_INIT(sid, e->alsa_name);
779 if (!(me = snd_mixer_find_selem(m, sid))) {
780 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
781 return -1;
782 }
783
784 pa_cvolume_mute(&rv, cm->channels);
785
786 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
787 int r;
788 pa_volume_t f = PA_VOLUME_MUTED;
789 pa_bool_t found = FALSE;
790
791 for (k = 0; k < cm->channels; k++)
792 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) {
793 found = TRUE;
794 if (v->values[k] > f)
795 f = v->values[k];
796 }
797
798 if (!found) {
799 /* Hmm, so this channel does not exist in the volume
800 * struct, so let's bind it to the overall max of the
801 * volume. */
802 f = pa_cvolume_max(v);
803 }
804
805 if (e->has_dB) {
806 long value = to_alsa_dB(f);
807
808 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
809 /* If we call set_play_volume() without checking first
810 * if the channel is available, ALSA behaves ver
811 * strangely and doesn't fail the call */
812 if (snd_mixer_selem_has_playback_channel(me, c)) {
813 if (write_to_hw) {
814 if ((r = snd_mixer_selem_set_playback_dB(me, c, value, +1)) >= 0)
815 r = snd_mixer_selem_get_playback_dB(me, c, &value);
816 } else {
817 long alsa_val;
818 if ((r = snd_mixer_selem_ask_playback_dB_vol(me, value, +1, &alsa_val)) >= 0)
819 r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value);
820 }
821 } else
822 r = -1;
823 } else {
824 if (snd_mixer_selem_has_capture_channel(me, c)) {
825 if (write_to_hw) {
826 if ((r = snd_mixer_selem_set_capture_dB(me, c, value, +1)) >= 0)
827 r = snd_mixer_selem_get_capture_dB(me, c, &value);
828 } else {
829 long alsa_val;
830 if ((r = snd_mixer_selem_ask_capture_dB_vol(me, value, +1, &alsa_val)) >= 0)
831 r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value);
832 }
833 } else
834 r = -1;
835 }
836
837 if (r < 0)
838 continue;
839
840 #ifdef HAVE_VALGRIND_MEMCHECK_H
841 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
842 #endif
843
844 f = from_alsa_dB(value);
845
846 } else {
847 long value;
848
849 value = to_alsa_volume(f, e->min_volume, e->max_volume);
850
851 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
852 if (snd_mixer_selem_has_playback_channel(me, c)) {
853 if ((r = snd_mixer_selem_set_playback_volume(me, c, value)) >= 0)
854 r = snd_mixer_selem_get_playback_volume(me, c, &value);
855 } else
856 r = -1;
857 } else {
858 if (snd_mixer_selem_has_capture_channel(me, c)) {
859 if ((r = snd_mixer_selem_set_capture_volume(me, c, value)) >= 0)
860 r = snd_mixer_selem_get_capture_volume(me, c, &value);
861 } else
862 r = -1;
863 }
864
865 if (r < 0)
866 continue;
867
868 f = from_alsa_volume(value, e->min_volume, e->max_volume);
869 }
870
871 for (k = 0; k < cm->channels; k++)
872 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
873 if (rv.values[k] < f)
874 rv.values[k] = f;
875
876 mask |= e->masks[c][e->n_channels-1];
877 }
878
879 for (k = 0; k < cm->channels; k++)
880 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
881 rv.values[k] = PA_VOLUME_NORM;
882
883 *v = rv;
884 return 0;
885 }
886
887 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) {
888
889 pa_alsa_element *e;
890 pa_cvolume rv;
891
892 pa_assert(m);
893 pa_assert(p);
894 pa_assert(cm);
895 pa_assert(v);
896 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
897
898 if (!p->has_volume)
899 return -1;
900
901 rv = *v; /* Remaining adjustment */
902 pa_cvolume_reset(v, cm->channels); /* Adjustment done */
903
904 PA_LLIST_FOREACH(e, p->elements) {
905 pa_cvolume ev;
906
907 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
908 continue;
909
910 pa_assert(!p->has_dB || e->has_dB);
911
912 ev = rv;
913 if (element_set_volume(e, m, cm, &ev, write_to_hw) < 0)
914 return -1;
915
916 if (!p->has_dB) {
917 *v = ev;
918 return 0;
919 }
920
921 pa_sw_cvolume_multiply(v, v, &ev);
922 pa_sw_cvolume_divide(&rv, &rv, &ev);
923 }
924
925 return 0;
926 }
927
928 static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t b) {
929 snd_mixer_elem_t *me;
930 snd_mixer_selem_id_t *sid;
931 int r;
932
933 pa_assert(m);
934 pa_assert(e);
935
936 SELEM_INIT(sid, e->alsa_name);
937 if (!(me = snd_mixer_find_selem(m, sid))) {
938 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
939 return -1;
940 }
941
942 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
943 r = snd_mixer_selem_set_playback_switch_all(me, b);
944 else
945 r = snd_mixer_selem_set_capture_switch_all(me, b);
946
947 if (r < 0)
948 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
949
950 return r;
951 }
952
953 int pa_alsa_path_set_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t muted) {
954 pa_alsa_element *e;
955
956 pa_assert(m);
957 pa_assert(p);
958
959 if (!p->has_mute)
960 return -1;
961
962 PA_LLIST_FOREACH(e, p->elements) {
963
964 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
965 continue;
966
967 if (element_set_switch(e, m, !muted) < 0)
968 return -1;
969 }
970
971 return 0;
972 }
973
974 static int element_mute_volume(pa_alsa_element *e, snd_mixer_t *m) {
975 snd_mixer_elem_t *me;
976 snd_mixer_selem_id_t *sid;
977 int r;
978
979 pa_assert(m);
980 pa_assert(e);
981
982 SELEM_INIT(sid, e->alsa_name);
983 if (!(me = snd_mixer_find_selem(m, sid))) {
984 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
985 return -1;
986 }
987
988 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
989 r = snd_mixer_selem_set_playback_volume_all(me, e->min_volume);
990 else
991 r = snd_mixer_selem_set_capture_volume_all(me, e->min_volume);
992
993 if (r < 0)
994 pa_log_warn("Failed to set volume to muted of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
995
996 return r;
997 }
998
999 /* The volume to 0dB */
1000 static int element_zero_volume(pa_alsa_element *e, snd_mixer_t *m) {
1001 snd_mixer_elem_t *me;
1002 snd_mixer_selem_id_t *sid;
1003 int r;
1004
1005 pa_assert(m);
1006 pa_assert(e);
1007
1008 SELEM_INIT(sid, e->alsa_name);
1009 if (!(me = snd_mixer_find_selem(m, sid))) {
1010 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1011 return -1;
1012 }
1013
1014 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1015 r = snd_mixer_selem_set_playback_dB_all(me, 0, +1);
1016 else
1017 r = snd_mixer_selem_set_capture_dB_all(me, 0, +1);
1018
1019 if (r < 0)
1020 pa_log_warn("Failed to set volume to 0dB of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1021
1022 return r;
1023 }
1024
1025 int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
1026 pa_alsa_element *e;
1027 int r = 0;
1028
1029 pa_assert(m);
1030 pa_assert(p);
1031
1032 pa_log_debug("Activating path %s", p->name);
1033 pa_alsa_path_dump(p);
1034
1035 PA_LLIST_FOREACH(e, p->elements) {
1036
1037 switch (e->switch_use) {
1038 case PA_ALSA_SWITCH_OFF:
1039 r = element_set_switch(e, m, FALSE);
1040 break;
1041
1042 case PA_ALSA_SWITCH_ON:
1043 r = element_set_switch(e, m, TRUE);
1044 break;
1045
1046 case PA_ALSA_SWITCH_MUTE:
1047 case PA_ALSA_SWITCH_IGNORE:
1048 case PA_ALSA_SWITCH_SELECT:
1049 r = 0;
1050 break;
1051 }
1052
1053 if (r < 0)
1054 return -1;
1055
1056 switch (e->volume_use) {
1057 case PA_ALSA_VOLUME_OFF:
1058 r = element_mute_volume(e, m);
1059 break;
1060
1061 case PA_ALSA_VOLUME_ZERO:
1062 r = element_zero_volume(e, m);
1063 break;
1064
1065 case PA_ALSA_VOLUME_MERGE:
1066 case PA_ALSA_VOLUME_IGNORE:
1067 r = 0;
1068 break;
1069 }
1070
1071 if (r < 0)
1072 return -1;
1073 }
1074
1075 return 0;
1076 }
1077
1078 static int check_required(pa_alsa_element *e, snd_mixer_elem_t *me) {
1079 pa_bool_t has_switch;
1080 pa_bool_t has_enumeration;
1081 pa_bool_t has_volume;
1082
1083 pa_assert(e);
1084 pa_assert(me);
1085
1086 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1087 has_switch =
1088 snd_mixer_selem_has_playback_switch(me) ||
1089 (e->direction_try_other && snd_mixer_selem_has_capture_switch(me));
1090 } else {
1091 has_switch =
1092 snd_mixer_selem_has_capture_switch(me) ||
1093 (e->direction_try_other && snd_mixer_selem_has_playback_switch(me));
1094 }
1095
1096 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1097 has_volume =
1098 snd_mixer_selem_has_playback_volume(me) ||
1099 (e->direction_try_other && snd_mixer_selem_has_capture_volume(me));
1100 } else {
1101 has_volume =
1102 snd_mixer_selem_has_capture_volume(me) ||
1103 (e->direction_try_other && snd_mixer_selem_has_playback_volume(me));
1104 }
1105
1106 has_enumeration = snd_mixer_selem_is_enumerated(me);
1107
1108 if ((e->required == PA_ALSA_REQUIRED_SWITCH && !has_switch) ||
1109 (e->required == PA_ALSA_REQUIRED_VOLUME && !has_volume) ||
1110 (e->required == PA_ALSA_REQUIRED_ENUMERATION && !has_enumeration))
1111 return -1;
1112
1113 if (e->required == PA_ALSA_REQUIRED_ANY && !(has_switch || has_volume || has_enumeration))
1114 return -1;
1115
1116 if ((e->required_absent == PA_ALSA_REQUIRED_SWITCH && has_switch) ||
1117 (e->required_absent == PA_ALSA_REQUIRED_VOLUME && has_volume) ||
1118 (e->required_absent == PA_ALSA_REQUIRED_ENUMERATION && has_enumeration))
1119 return -1;
1120
1121 if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration))
1122 return -1;
1123
1124 return 0;
1125 }
1126
1127 static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
1128 snd_mixer_selem_id_t *sid;
1129 snd_mixer_elem_t *me;
1130
1131 pa_assert(m);
1132 pa_assert(e);
1133
1134 SELEM_INIT(sid, e->alsa_name);
1135
1136 if (!(me = snd_mixer_find_selem(m, sid))) {
1137
1138 if (e->required != PA_ALSA_REQUIRED_IGNORE)
1139 return -1;
1140
1141 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1142 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1143 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1144
1145 return 0;
1146 }
1147
1148 if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
1149 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1150
1151 if (!snd_mixer_selem_has_playback_switch(me)) {
1152 if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
1153 e->direction = PA_ALSA_DIRECTION_INPUT;
1154 else
1155 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1156 }
1157
1158 } else {
1159
1160 if (!snd_mixer_selem_has_capture_switch(me)) {
1161 if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
1162 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1163 else
1164 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1165 }
1166 }
1167
1168 if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
1169 e->direction_try_other = FALSE;
1170 }
1171
1172 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1173
1174 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1175
1176 if (!snd_mixer_selem_has_playback_volume(me)) {
1177 if (e->direction_try_other && snd_mixer_selem_has_capture_volume(me))
1178 e->direction = PA_ALSA_DIRECTION_INPUT;
1179 else
1180 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1181 }
1182
1183 } else {
1184
1185 if (!snd_mixer_selem_has_capture_volume(me)) {
1186 if (e->direction_try_other && snd_mixer_selem_has_playback_volume(me))
1187 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1188 else
1189 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1190 }
1191 }
1192
1193 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1194 long min_dB = 0, max_dB = 0;
1195 int r;
1196
1197 e->direction_try_other = FALSE;
1198
1199 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1200 e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0;
1201 else
1202 e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0;
1203
1204 if (e->has_dB) {
1205 #ifdef HAVE_VALGRIND_MEMCHECK_H
1206 VALGRIND_MAKE_MEM_DEFINED(&min_dB, sizeof(min_dB));
1207 VALGRIND_MAKE_MEM_DEFINED(&max_dB, sizeof(max_dB));
1208 #endif
1209
1210 e->min_dB = ((double) min_dB) / 100.0;
1211 e->max_dB = ((double) max_dB) / 100.0;
1212
1213 if (min_dB >= max_dB) {
1214 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);
1215 e->has_dB = FALSE;
1216 }
1217 }
1218
1219 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1220 r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume);
1221 else
1222 r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
1223
1224 if (r < 0) {
1225 pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1226 return -1;
1227 }
1228
1229
1230 if (e->min_volume >= e->max_volume) {
1231 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);
1232 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1233
1234 } else {
1235 pa_bool_t is_mono;
1236 pa_channel_position_t p;
1237
1238 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1239 is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
1240 else
1241 is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
1242
1243 if (is_mono) {
1244 e->n_channels = 1;
1245
1246 if (!e->override_map) {
1247 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++)
1248 e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
1249 e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
1250 }
1251
1252 e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
1253 } else {
1254 e->n_channels = 0;
1255 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1256
1257 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1258 continue;
1259
1260 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1261 e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1262 else
1263 e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1264 }
1265
1266 if (e->n_channels <= 0) {
1267 pa_log_warn("Volume element %s with no channels?", e->alsa_name);
1268 return -1;
1269 }
1270
1271 if (!e->override_map) {
1272 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1273 pa_bool_t has_channel;
1274
1275 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1276 continue;
1277
1278 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1279 has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1280 else
1281 has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1282
1283 e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
1284 }
1285 }
1286
1287 e->merged_mask = 0;
1288 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++)
1289 e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
1290 }
1291 }
1292 }
1293
1294 }
1295
1296 if (check_required(e, me) < 0)
1297 return -1;
1298
1299 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1300 pa_alsa_option *o;
1301
1302 PA_LLIST_FOREACH(o, e->options)
1303 o->alsa_idx = pa_streq(o->alsa_name, "on") ? 1 : 0;
1304 } else if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1305 int n;
1306 pa_alsa_option *o;
1307
1308 if ((n = snd_mixer_selem_get_enum_items(me)) < 0) {
1309 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n));
1310 return -1;
1311 }
1312
1313 PA_LLIST_FOREACH(o, e->options) {
1314 int i;
1315
1316 for (i = 0; i < n; i++) {
1317 char buf[128];
1318
1319 if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
1320 continue;
1321
1322 if (!pa_streq(buf, o->alsa_name))
1323 continue;
1324
1325 o->alsa_idx = i;
1326 }
1327 }
1328 }
1329
1330 return 0;
1331 }
1332
1333 static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, pa_bool_t prefixed) {
1334 pa_alsa_element *e;
1335
1336 pa_assert(p);
1337 pa_assert(section);
1338
1339 if (prefixed) {
1340 if (!pa_startswith(section, "Element "))
1341 return NULL;
1342
1343 section += 8;
1344 }
1345
1346 /* This is not an element section, but an enum section? */
1347 if (strchr(section, ':'))
1348 return NULL;
1349
1350 if (p->last_element && pa_streq(p->last_element->alsa_name, section))
1351 return p->last_element;
1352
1353 PA_LLIST_FOREACH(e, p->elements)
1354 if (pa_streq(e->alsa_name, section))
1355 goto finish;
1356
1357 e = pa_xnew0(pa_alsa_element, 1);
1358 e->path = p;
1359 e->alsa_name = pa_xstrdup(section);
1360 e->direction = p->direction;
1361
1362 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
1363
1364 finish:
1365 p->last_element = e;
1366 return e;
1367 }
1368
1369 static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
1370 char *en;
1371 const char *on;
1372 pa_alsa_option *o;
1373 pa_alsa_element *e;
1374
1375 if (!pa_startswith(section, "Option "))
1376 return NULL;
1377
1378 section += 7;
1379
1380 /* This is not an enum section, but an element section? */
1381 if (!(on = strchr(section, ':')))
1382 return NULL;
1383
1384 en = pa_xstrndup(section, on - section);
1385 on++;
1386
1387 if (p->last_option &&
1388 pa_streq(p->last_option->element->alsa_name, en) &&
1389 pa_streq(p->last_option->alsa_name, on)) {
1390 pa_xfree(en);
1391 return p->last_option;
1392 }
1393
1394 pa_assert_se(e = element_get(p, en, FALSE));
1395 pa_xfree(en);
1396
1397 PA_LLIST_FOREACH(o, e->options)
1398 if (pa_streq(o->alsa_name, on))
1399 goto finish;
1400
1401 o = pa_xnew0(pa_alsa_option, 1);
1402 o->element = e;
1403 o->alsa_name = pa_xstrdup(on);
1404 o->alsa_idx = -1;
1405
1406 if (p->last_option && p->last_option->element == e)
1407 PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
1408 else
1409 PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
1410
1411 finish:
1412 p->last_option = o;
1413 return o;
1414 }
1415
1416 static int element_parse_switch(
1417 const char *filename,
1418 unsigned line,
1419 const char *section,
1420 const char *lvalue,
1421 const char *rvalue,
1422 void *data,
1423 void *userdata) {
1424
1425 pa_alsa_path *p = userdata;
1426 pa_alsa_element *e;
1427
1428 pa_assert(p);
1429
1430 if (!(e = element_get(p, section, TRUE))) {
1431 pa_log("[%s:%u] Switch makes no sense in '%s'", filename, line, section);
1432 return -1;
1433 }
1434
1435 if (pa_streq(rvalue, "ignore"))
1436 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1437 else if (pa_streq(rvalue, "mute"))
1438 e->switch_use = PA_ALSA_SWITCH_MUTE;
1439 else if (pa_streq(rvalue, "off"))
1440 e->switch_use = PA_ALSA_SWITCH_OFF;
1441 else if (pa_streq(rvalue, "on"))
1442 e->switch_use = PA_ALSA_SWITCH_ON;
1443 else if (pa_streq(rvalue, "select"))
1444 e->switch_use = PA_ALSA_SWITCH_SELECT;
1445 else {
1446 pa_log("[%s:%u] Switch invalid of '%s'", filename, line, section);
1447 return -1;
1448 }
1449
1450 return 0;
1451 }
1452
1453 static int element_parse_volume(
1454 const char *filename,
1455 unsigned line,
1456 const char *section,
1457 const char *lvalue,
1458 const char *rvalue,
1459 void *data,
1460 void *userdata) {
1461
1462 pa_alsa_path *p = userdata;
1463 pa_alsa_element *e;
1464
1465 pa_assert(p);
1466
1467 if (!(e = element_get(p, section, TRUE))) {
1468 pa_log("[%s:%u] Volume makes no sense in '%s'", filename, line, section);
1469 return -1;
1470 }
1471
1472 if (pa_streq(rvalue, "ignore"))
1473 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1474 else if (pa_streq(rvalue, "merge"))
1475 e->volume_use = PA_ALSA_VOLUME_MERGE;
1476 else if (pa_streq(rvalue, "off"))
1477 e->volume_use = PA_ALSA_VOLUME_OFF;
1478 else if (pa_streq(rvalue, "zero"))
1479 e->volume_use = PA_ALSA_VOLUME_ZERO;
1480 else {
1481 pa_log("[%s:%u] Volume invalid of '%s'", filename, line, section);
1482 return -1;
1483 }
1484
1485 return 0;
1486 }
1487
1488 static int element_parse_enumeration(
1489 const char *filename,
1490 unsigned line,
1491 const char *section,
1492 const char *lvalue,
1493 const char *rvalue,
1494 void *data,
1495 void *userdata) {
1496
1497 pa_alsa_path *p = userdata;
1498 pa_alsa_element *e;
1499
1500 pa_assert(p);
1501
1502 if (!(e = element_get(p, section, TRUE))) {
1503 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename, line, section);
1504 return -1;
1505 }
1506
1507 if (pa_streq(rvalue, "ignore"))
1508 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1509 else if (pa_streq(rvalue, "select"))
1510 e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
1511 else {
1512 pa_log("[%s:%u] Enumeration invalid of '%s'", filename, line, section);
1513 return -1;
1514 }
1515
1516 return 0;
1517 }
1518
1519 static int option_parse_priority(
1520 const char *filename,
1521 unsigned line,
1522 const char *section,
1523 const char *lvalue,
1524 const char *rvalue,
1525 void *data,
1526 void *userdata) {
1527
1528 pa_alsa_path *p = userdata;
1529 pa_alsa_option *o;
1530 uint32_t prio;
1531
1532 pa_assert(p);
1533
1534 if (!(o = option_get(p, section))) {
1535 pa_log("[%s:%u] Priority makes no sense in '%s'", filename, line, section);
1536 return -1;
1537 }
1538
1539 if (pa_atou(rvalue, &prio) < 0) {
1540 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
1541 return -1;
1542 }
1543
1544 o->priority = prio;
1545 return 0;
1546 }
1547
1548 static int option_parse_name(
1549 const char *filename,
1550 unsigned line,
1551 const char *section,
1552 const char *lvalue,
1553 const char *rvalue,
1554 void *data,
1555 void *userdata) {
1556
1557 pa_alsa_path *p = userdata;
1558 pa_alsa_option *o;
1559
1560 pa_assert(p);
1561
1562 if (!(o = option_get(p, section))) {
1563 pa_log("[%s:%u] Name makes no sense in '%s'", filename, line, section);
1564 return -1;
1565 }
1566
1567 pa_xfree(o->name);
1568 o->name = pa_xstrdup(rvalue);
1569
1570 return 0;
1571 }
1572
1573 static int element_parse_required(
1574 const char *filename,
1575 unsigned line,
1576 const char *section,
1577 const char *lvalue,
1578 const char *rvalue,
1579 void *data,
1580 void *userdata) {
1581
1582 pa_alsa_path *p = userdata;
1583 pa_alsa_element *e;
1584 pa_alsa_required_t req;
1585
1586 pa_assert(p);
1587
1588 if (!(e = element_get(p, section, TRUE))) {
1589 pa_log("[%s:%u] Required makes no sense in '%s'", filename, line, section);
1590 return -1;
1591 }
1592
1593 if (pa_streq(rvalue, "ignore"))
1594 req = PA_ALSA_REQUIRED_IGNORE;
1595 else if (pa_streq(rvalue, "switch"))
1596 req = PA_ALSA_REQUIRED_SWITCH;
1597 else if (pa_streq(rvalue, "volume"))
1598 req = PA_ALSA_REQUIRED_VOLUME;
1599 else if (pa_streq(rvalue, "enumeration"))
1600 req = PA_ALSA_REQUIRED_ENUMERATION;
1601 else if (pa_streq(rvalue, "any"))
1602 req = PA_ALSA_REQUIRED_ANY;
1603 else {
1604 pa_log("[%s:%u] Required invalid of '%s'", filename, line, section);
1605 return -1;
1606 }
1607
1608 if (pa_streq(lvalue, "required-absent"))
1609 e->required_absent = req;
1610 else
1611 e->required = req;
1612
1613 return 0;
1614 }
1615
1616 static int element_parse_direction(
1617 const char *filename,
1618 unsigned line,
1619 const char *section,
1620 const char *lvalue,
1621 const char *rvalue,
1622 void *data,
1623 void *userdata) {
1624
1625 pa_alsa_path *p = userdata;
1626 pa_alsa_element *e;
1627
1628 pa_assert(p);
1629
1630 if (!(e = element_get(p, section, TRUE))) {
1631 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1632 return -1;
1633 }
1634
1635 if (pa_streq(rvalue, "playback"))
1636 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1637 else if (pa_streq(rvalue, "capture"))
1638 e->direction = PA_ALSA_DIRECTION_INPUT;
1639 else {
1640 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1641 return -1;
1642 }
1643
1644 return 0;
1645 }
1646
1647 static int element_parse_direction_try_other(
1648 const char *filename,
1649 unsigned line,
1650 const char *section,
1651 const char *lvalue,
1652 const char *rvalue,
1653 void *data,
1654 void *userdata) {
1655
1656 pa_alsa_path *p = userdata;
1657 pa_alsa_element *e;
1658 int yes;
1659
1660 if (!(e = element_get(p, section, TRUE))) {
1661 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1662 return -1;
1663 }
1664
1665 if ((yes = pa_parse_boolean(rvalue)) < 0) {
1666 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1667 return -1;
1668 }
1669
1670 e->direction_try_other = !!yes;
1671 return 0;
1672 }
1673
1674 static pa_channel_position_mask_t parse_mask(const char *m) {
1675 pa_channel_position_mask_t v;
1676
1677 if (pa_streq(m, "all-left"))
1678 v = PA_CHANNEL_POSITION_MASK_LEFT;
1679 else if (pa_streq(m, "all-right"))
1680 v = PA_CHANNEL_POSITION_MASK_RIGHT;
1681 else if (pa_streq(m, "all-center"))
1682 v = PA_CHANNEL_POSITION_MASK_CENTER;
1683 else if (pa_streq(m, "all-front"))
1684 v = PA_CHANNEL_POSITION_MASK_FRONT;
1685 else if (pa_streq(m, "all-rear"))
1686 v = PA_CHANNEL_POSITION_MASK_REAR;
1687 else if (pa_streq(m, "all-side"))
1688 v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER;
1689 else if (pa_streq(m, "all-top"))
1690 v = PA_CHANNEL_POSITION_MASK_TOP;
1691 else if (pa_streq(m, "all-no-lfe"))
1692 v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE);
1693 else if (pa_streq(m, "all"))
1694 v = PA_CHANNEL_POSITION_MASK_ALL;
1695 else {
1696 pa_channel_position_t p;
1697
1698 if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
1699 return 0;
1700
1701 v = PA_CHANNEL_POSITION_MASK(p);
1702 }
1703
1704 return v;
1705 }
1706
1707 static int element_parse_override_map(
1708 const char *filename,
1709 unsigned line,
1710 const char *section,
1711 const char *lvalue,
1712 const char *rvalue,
1713 void *data,
1714 void *userdata) {
1715
1716 pa_alsa_path *p = userdata;
1717 pa_alsa_element *e;
1718 const char *state = NULL;
1719 unsigned i = 0;
1720 char *n;
1721
1722 if (!(e = element_get(p, section, TRUE))) {
1723 pa_log("[%s:%u] Override map makes no sense in '%s'", filename, line, section);
1724 return -1;
1725 }
1726
1727 while ((n = pa_split(rvalue, ",", &state))) {
1728 pa_channel_position_mask_t m;
1729
1730 if (!*n)
1731 m = 0;
1732 else {
1733 if ((m = parse_mask(n)) == 0) {
1734 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename, line, n, section);
1735 pa_xfree(n);
1736 return -1;
1737 }
1738 }
1739
1740 if (pa_streq(lvalue, "override-map.1"))
1741 e->masks[i++][0] = m;
1742 else
1743 e->masks[i++][1] = m;
1744
1745 /* Later on we might add override-map.3 and so on here ... */
1746
1747 pa_xfree(n);
1748 }
1749
1750 e->override_map = TRUE;
1751
1752 return 0;
1753 }
1754
1755 static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
1756 snd_mixer_selem_id_t *sid;
1757 snd_mixer_elem_t *me;
1758 int r;
1759
1760 pa_assert(e);
1761 pa_assert(m);
1762
1763 SELEM_INIT(sid, e->alsa_name);
1764 if (!(me = snd_mixer_find_selem(m, sid))) {
1765 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1766 return -1;
1767 }
1768
1769 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1770
1771 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1772 r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
1773 else
1774 r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
1775
1776 if (r < 0)
1777 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1778
1779 } else {
1780 pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
1781
1782 if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0)
1783 pa_log_warn("Failed to set enumeration of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1784 }
1785
1786 return r;
1787 }
1788
1789 int pa_alsa_setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
1790 pa_alsa_option *o;
1791 uint32_t idx;
1792
1793 pa_assert(s);
1794 pa_assert(m);
1795
1796 PA_IDXSET_FOREACH(o, s->options, idx)
1797 element_set_option(o->element, m, o->alsa_idx);
1798
1799 return 0;
1800 }
1801
1802 static int option_verify(pa_alsa_option *o) {
1803 static const struct description_map well_known_descriptions[] = {
1804 { "input", N_("Input") },
1805 { "input-docking", N_("Docking Station Input") },
1806 { "input-docking-microphone", N_("Docking Station Microphone") },
1807 { "input-linein", N_("Line-In") },
1808 { "input-microphone", N_("Microphone") },
1809 { "input-microphone-external", N_("External Microphone") },
1810 { "input-microphone-internal", N_("Internal Microphone") },
1811 { "input-radio", N_("Radio") },
1812 { "input-video", N_("Video") },
1813 { "input-agc-on", N_("Automatic Gain Control") },
1814 { "input-agc-off", N_("No Automatic Gain Control") },
1815 { "input-boost-on", N_("Boost") },
1816 { "input-boost-off", N_("No Boost") },
1817 { "output-amplifier-on", N_("Amplifier") },
1818 { "output-amplifier-off", N_("No Amplifier") },
1819 { "output-bass-boost-on", N_("Bass Boost") },
1820 { "output-bass-boost-off", N_("No Bass Boost") },
1821 { "output-speaker", N_("Speaker") },
1822 { "output-headphones", N_("Headphones") }
1823 };
1824
1825 pa_assert(o);
1826
1827 if (!o->name) {
1828 pa_log("No name set for option %s", o->alsa_name);
1829 return -1;
1830 }
1831
1832 if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
1833 o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
1834 pa_log("Element %s of option %s not set for select.", o->element->alsa_name, o->name);
1835 return -1;
1836 }
1837
1838 if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
1839 !pa_streq(o->alsa_name, "on") &&
1840 !pa_streq(o->alsa_name, "off")) {
1841 pa_log("Switch %s options need be named off or on ", o->element->alsa_name);
1842 return -1;
1843 }
1844
1845 if (!o->description)
1846 o->description = pa_xstrdup(lookup_description(o->name,
1847 well_known_descriptions,
1848 PA_ELEMENTSOF(well_known_descriptions)));
1849 if (!o->description)
1850 o->description = pa_xstrdup(o->name);
1851
1852 return 0;
1853 }
1854
1855 static int element_verify(pa_alsa_element *e) {
1856 pa_alsa_option *o;
1857
1858 pa_assert(e);
1859
1860 if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
1861 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
1862 pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name);
1863 return -1;
1864 }
1865
1866 if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1867 pa_log("Element %s cannot set select for both switch and enumeration.", e->alsa_name);
1868 return -1;
1869 }
1870
1871 PA_LLIST_FOREACH(o, e->options)
1872 if (option_verify(o) < 0)
1873 return -1;
1874
1875 return 0;
1876 }
1877
1878 static int path_verify(pa_alsa_path *p) {
1879 static const struct description_map well_known_descriptions[] = {
1880 { "analog-input", N_("Analog Input") },
1881 { "analog-input-microphone", N_("Analog Microphone") },
1882 { "analog-input-linein", N_("Analog Line-In") },
1883 { "analog-input-radio", N_("Analog Radio") },
1884 { "analog-input-video", N_("Analog Video") },
1885 { "analog-output", N_("Analog Output") },
1886 { "analog-output-headphones", N_("Analog Headphones") },
1887 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
1888 { "analog-output-mono", N_("Analog Mono Output") },
1889 { "analog-output-speaker", N_("Analog Speakers") },
1890 { "iec958-stereo-output", N_("Digital Output (IEC958)") },
1891 { "iec958-passthrough-output", N_("Digital Passthrough (IEC958)") }
1892 };
1893
1894 pa_alsa_element *e;
1895
1896 pa_assert(p);
1897
1898 PA_LLIST_FOREACH(e, p->elements)
1899 if (element_verify(e) < 0)
1900 return -1;
1901
1902 if (!p->description)
1903 p->description = pa_xstrdup(lookup_description(p->name,
1904 well_known_descriptions,
1905 PA_ELEMENTSOF(well_known_descriptions)));
1906
1907 if (!p->description)
1908 p->description = pa_xstrdup(p->name);
1909
1910 return 0;
1911 }
1912
1913 pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction) {
1914 pa_alsa_path *p;
1915 char *fn;
1916 int r;
1917 const char *n;
1918
1919 pa_config_item items[] = {
1920 /* [General] */
1921 { "priority", pa_config_parse_unsigned, NULL, "General" },
1922 { "description", pa_config_parse_string, NULL, "General" },
1923 { "name", pa_config_parse_string, NULL, "General" },
1924
1925 /* [Option ...] */
1926 { "priority", option_parse_priority, NULL, NULL },
1927 { "name", option_parse_name, NULL, NULL },
1928
1929 /* [Element ...] */
1930 { "switch", element_parse_switch, NULL, NULL },
1931 { "volume", element_parse_volume, NULL, NULL },
1932 { "enumeration", element_parse_enumeration, NULL, NULL },
1933 { "override-map.1", element_parse_override_map, NULL, NULL },
1934 { "override-map.2", element_parse_override_map, NULL, NULL },
1935 /* ... later on we might add override-map.3 and so on here ... */
1936 { "required", element_parse_required, NULL, NULL },
1937 { "required-absent", element_parse_required, NULL, NULL },
1938 { "direction", element_parse_direction, NULL, NULL },
1939 { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
1940 { NULL, NULL, NULL, NULL }
1941 };
1942
1943 pa_assert(fname);
1944
1945 p = pa_xnew0(pa_alsa_path, 1);
1946 n = pa_path_get_filename(fname);
1947 p->name = pa_xstrndup(n, strcspn(n, "."));
1948 p->direction = direction;
1949
1950 items[0].data = &p->priority;
1951 items[1].data = &p->description;
1952 items[2].data = &p->name;
1953
1954 fn = pa_maybe_prefix_path(fname,
1955 #if defined(__linux__) && !defined(__OPTIMIZE__)
1956 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/paths/" :
1957 #endif
1958 PA_ALSA_PATHS_DIR);
1959
1960 r = pa_config_parse(fn, NULL, items, p);
1961 pa_xfree(fn);
1962
1963 if (r < 0)
1964 goto fail;
1965
1966 if (path_verify(p) < 0)
1967 goto fail;
1968
1969 return p;
1970
1971 fail:
1972 pa_alsa_path_free(p);
1973 return NULL;
1974 }
1975
1976 pa_alsa_path* pa_alsa_path_synthesize(const char*element, pa_alsa_direction_t direction) {
1977 pa_alsa_path *p;
1978 pa_alsa_element *e;
1979
1980 pa_assert(element);
1981
1982 p = pa_xnew0(pa_alsa_path, 1);
1983 p->name = pa_xstrdup(element);
1984 p->direction = direction;
1985
1986 e = pa_xnew0(pa_alsa_element, 1);
1987 e->path = p;
1988 e->alsa_name = pa_xstrdup(element);
1989 e->direction = direction;
1990
1991 e->switch_use = PA_ALSA_SWITCH_MUTE;
1992 e->volume_use = PA_ALSA_VOLUME_MERGE;
1993
1994 PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
1995 return p;
1996 }
1997
1998 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
1999 pa_alsa_option *o, *n;
2000
2001 pa_assert(e);
2002
2003 for (o = e->options; o; o = n) {
2004 n = o->next;
2005
2006 if (o->alsa_idx < 0) {
2007 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
2008 option_free(o);
2009 }
2010 }
2011
2012 return
2013 e->switch_use != PA_ALSA_SWITCH_IGNORE ||
2014 e->volume_use != PA_ALSA_VOLUME_IGNORE ||
2015 e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
2016 }
2017
2018 static void path_drop_unsupported(pa_alsa_path *p) {
2019 pa_alsa_element *e, *n;
2020
2021 pa_assert(p);
2022
2023 for (e = p->elements; e; e = n) {
2024 n = e->next;
2025
2026 if (!element_drop_unsupported(e)) {
2027 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
2028 element_free(e);
2029 }
2030 }
2031 }
2032
2033 static void path_make_options_unique(pa_alsa_path *p) {
2034 pa_alsa_element *e;
2035 pa_alsa_option *o, *u;
2036
2037 PA_LLIST_FOREACH(e, p->elements) {
2038 PA_LLIST_FOREACH(o, e->options) {
2039 unsigned i;
2040 char *m;
2041
2042 for (u = o->next; u; u = u->next)
2043 if (pa_streq(u->name, o->name))
2044 break;
2045
2046 if (!u)
2047 continue;
2048
2049 m = pa_xstrdup(o->name);
2050
2051 /* OK, this name is not unique, hence let's rename */
2052 for (i = 1, u = o; u; u = u->next) {
2053 char *nn, *nd;
2054
2055 if (!pa_streq(u->name, m))
2056 continue;
2057
2058 nn = pa_sprintf_malloc("%s-%u", m, i);
2059 pa_xfree(u->name);
2060 u->name = nn;
2061
2062 nd = pa_sprintf_malloc("%s %u", u->description, i);
2063 pa_xfree(u->description);
2064 u->description = nd;
2065
2066 i++;
2067 }
2068
2069 pa_xfree(m);
2070 }
2071 }
2072 }
2073
2074 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
2075 pa_alsa_option *o;
2076
2077 for (; e; e = e->next)
2078 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
2079 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
2080 break;
2081
2082 if (!e)
2083 return FALSE;
2084
2085 for (o = e->options; o; o = o->next) {
2086 pa_alsa_setting *s;
2087
2088 if (template) {
2089 s = pa_xnewdup(pa_alsa_setting, template, 1);
2090 s->options = pa_idxset_copy(template->options);
2091 s->name = pa_sprintf_malloc(_("%s+%s"), template->name, o->name);
2092 s->description =
2093 (template->description[0] && o->description[0])
2094 ? pa_sprintf_malloc(_("%s / %s"), template->description, o->description)
2095 : (template->description[0]
2096 ? pa_xstrdup(template->description)
2097 : pa_xstrdup(o->description));
2098
2099 s->priority = PA_MAX(template->priority, o->priority);
2100 } else {
2101 s = pa_xnew0(pa_alsa_setting, 1);
2102 s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2103 s->name = pa_xstrdup(o->name);
2104 s->description = pa_xstrdup(o->description);
2105 s->priority = o->priority;
2106 }
2107
2108 pa_idxset_put(s->options, o, NULL);
2109
2110 if (element_create_settings(e->next, s))
2111 /* This is not a leaf, so let's get rid of it */
2112 setting_free(s);
2113 else {
2114 /* This is a leaf, so let's add it */
2115 PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2116
2117 e->path->last_setting = s;
2118 }
2119 }
2120
2121 return TRUE;
2122 }
2123
2124 static void path_create_settings(pa_alsa_path *p) {
2125 pa_assert(p);
2126
2127 element_create_settings(p->elements, NULL);
2128 }
2129
2130 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) {
2131 pa_alsa_element *e;
2132 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2133 pa_channel_position_t t;
2134
2135 pa_assert(p);
2136 pa_assert(m);
2137
2138 if (p->probed)
2139 return 0;
2140
2141 pa_zero(min_dB);
2142 pa_zero(max_dB);
2143
2144 pa_log_debug("Probing path '%s'", p->name);
2145
2146 PA_LLIST_FOREACH(e, p->elements) {
2147 if (element_probe(e, m) < 0) {
2148 p->supported = FALSE;
2149 pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2150 return -1;
2151 }
2152
2153 if (ignore_dB)
2154 e->has_dB = FALSE;
2155
2156 if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2157
2158 if (!p->has_volume) {
2159 p->min_volume = e->min_volume;
2160 p->max_volume = e->max_volume;
2161 }
2162
2163 if (e->has_dB) {
2164 if (!p->has_volume) {
2165 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2166 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2167 min_dB[t] = e->min_dB;
2168 max_dB[t] = e->max_dB;
2169 }
2170
2171 p->has_dB = TRUE;
2172 } else {
2173
2174 if (p->has_dB) {
2175 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2176 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2177 min_dB[t] += e->min_dB;
2178 max_dB[t] += e->max_dB;
2179 }
2180 } else
2181 /* Hmm, there's another element before us
2182 * which cannot do dB volumes, so we we need
2183 * to 'neutralize' this slider */
2184 e->volume_use = PA_ALSA_VOLUME_ZERO;
2185 }
2186 } else if (p->has_volume)
2187 /* We can't use this volume, so let's ignore it */
2188 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2189
2190 p->has_volume = TRUE;
2191 }
2192
2193 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2194 p->has_mute = TRUE;
2195 }
2196
2197 path_drop_unsupported(p);
2198 path_make_options_unique(p);
2199 path_create_settings(p);
2200
2201 p->supported = TRUE;
2202 p->probed = TRUE;
2203
2204 p->min_dB = INFINITY;
2205 p->max_dB = -INFINITY;
2206
2207 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2208 if (p->min_dB > min_dB[t])
2209 p->min_dB = min_dB[t];
2210
2211 if (p->max_dB < max_dB[t])
2212 p->max_dB = max_dB[t];
2213 }
2214
2215 return 0;
2216 }
2217
2218 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2219 pa_assert(s);
2220
2221 pa_log_debug("Setting %s (%s) priority=%u",
2222 s->name,
2223 pa_strnull(s->description),
2224 s->priority);
2225 }
2226
2227 void pa_alsa_option_dump(pa_alsa_option *o) {
2228 pa_assert(o);
2229
2230 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2231 o->alsa_name,
2232 pa_strnull(o->name),
2233 pa_strnull(o->description),
2234 o->alsa_idx,
2235 o->priority);
2236 }
2237
2238 void pa_alsa_element_dump(pa_alsa_element *e) {
2239 pa_alsa_option *o;
2240 pa_assert(e);
2241
2242 pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, enumeration=%i, required=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s",
2243 e->alsa_name,
2244 e->direction,
2245 e->switch_use,
2246 e->volume_use,
2247 e->enumeration_use,
2248 e->required,
2249 e->required_absent,
2250 (long long unsigned) e->merged_mask,
2251 e->n_channels,
2252 pa_yes_no(e->override_map));
2253
2254 PA_LLIST_FOREACH(o, e->options)
2255 pa_alsa_option_dump(o);
2256 }
2257
2258 void pa_alsa_path_dump(pa_alsa_path *p) {
2259 pa_alsa_element *e;
2260 pa_alsa_setting *s;
2261 pa_assert(p);
2262
2263 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2264 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2265 p->name,
2266 pa_strnull(p->description),
2267 p->direction,
2268 p->priority,
2269 pa_yes_no(p->probed),
2270 pa_yes_no(p->supported),
2271 pa_yes_no(p->has_mute),
2272 pa_yes_no(p->has_volume),
2273 pa_yes_no(p->has_dB),
2274 p->min_volume, p->max_volume,
2275 p->min_dB, p->max_dB);
2276
2277 PA_LLIST_FOREACH(e, p->elements)
2278 pa_alsa_element_dump(e);
2279
2280 PA_LLIST_FOREACH(s, p->settings)
2281 pa_alsa_setting_dump(s);
2282 }
2283
2284 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2285 snd_mixer_selem_id_t *sid;
2286 snd_mixer_elem_t *me;
2287
2288 pa_assert(e);
2289 pa_assert(m);
2290 pa_assert(cb);
2291
2292 SELEM_INIT(sid, e->alsa_name);
2293 if (!(me = snd_mixer_find_selem(m, sid))) {
2294 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2295 return;
2296 }
2297
2298 snd_mixer_elem_set_callback(me, cb);
2299 snd_mixer_elem_set_callback_private(me, userdata);
2300 }
2301
2302 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2303 pa_alsa_element *e;
2304
2305 pa_assert(p);
2306 pa_assert(m);
2307 pa_assert(cb);
2308
2309 PA_LLIST_FOREACH(e, p->elements)
2310 element_set_callback(e, m, cb, userdata);
2311 }
2312
2313 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2314 pa_alsa_path *p;
2315
2316 pa_assert(ps);
2317 pa_assert(m);
2318 pa_assert(cb);
2319
2320 PA_LLIST_FOREACH(p, ps->paths)
2321 pa_alsa_path_set_callback(p, m, cb, userdata);
2322 }
2323
2324 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction) {
2325 pa_alsa_path_set *ps;
2326 char **pn = NULL, **en = NULL, **ie;
2327
2328 pa_assert(m);
2329 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2330
2331 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2332 return NULL;
2333
2334 ps = pa_xnew0(pa_alsa_path_set, 1);
2335 ps->direction = direction;
2336
2337 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2338 pn = m->output_path_names;
2339 else if (direction == PA_ALSA_DIRECTION_INPUT)
2340 pn = m->input_path_names;
2341
2342 if (pn) {
2343 char **in;
2344
2345 for (in = pn; *in; in++) {
2346 pa_alsa_path *p;
2347 pa_bool_t duplicate = FALSE;
2348 char **kn, *fn;
2349
2350 for (kn = pn; kn != in; kn++)
2351 if (pa_streq(*kn, *in)) {
2352 duplicate = TRUE;
2353 break;
2354 }
2355
2356 if (duplicate)
2357 continue;
2358
2359 fn = pa_sprintf_malloc("%s.conf", *in);
2360
2361 if ((p = pa_alsa_path_new(fn, direction))) {
2362 p->path_set = ps;
2363 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2364 ps->last_path = p;
2365 }
2366
2367 pa_xfree(fn);
2368 }
2369
2370 return ps;
2371 }
2372
2373 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2374 en = m->output_element;
2375 else if (direction == PA_ALSA_DIRECTION_INPUT)
2376 en = m->input_element;
2377
2378 if (!en) {
2379 pa_alsa_path_set_free(ps);
2380 return NULL;
2381 }
2382
2383 for (ie = en; *ie; ie++) {
2384 char **je;
2385 pa_alsa_path *p;
2386
2387 p = pa_alsa_path_synthesize(*ie, direction);
2388 p->path_set = ps;
2389
2390 /* Mark all other passed elements for require-absent */
2391 for (je = en; *je; je++) {
2392 pa_alsa_element *e;
2393 e = pa_xnew0(pa_alsa_element, 1);
2394 e->path = p;
2395 e->alsa_name = pa_xstrdup(*je);
2396 e->direction = direction;
2397 e->required_absent = PA_ALSA_REQUIRED_ANY;
2398
2399 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2400 p->last_element = e;
2401 }
2402
2403 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2404 ps->last_path = p;
2405 }
2406
2407 return ps;
2408 }
2409
2410 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2411 pa_alsa_path *p;
2412 pa_assert(ps);
2413
2414 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2415 (void*) ps,
2416 ps->direction,
2417 pa_yes_no(ps->probed));
2418
2419 PA_LLIST_FOREACH(p, ps->paths)
2420 pa_alsa_path_dump(p);
2421 }
2422
2423 static void path_set_unify(pa_alsa_path_set *ps) {
2424 pa_alsa_path *p;
2425 pa_bool_t has_dB = TRUE, has_volume = TRUE, has_mute = TRUE;
2426 pa_assert(ps);
2427
2428 /* We have issues dealing with paths that vary too wildly. That
2429 * means for now we have to have all paths support volume/mute/dB
2430 * or none. */
2431
2432 PA_LLIST_FOREACH(p, ps->paths) {
2433 pa_assert(p->probed);
2434
2435 if (!p->has_volume)
2436 has_volume = FALSE;
2437 else if (!p->has_dB)
2438 has_dB = FALSE;
2439
2440 if (!p->has_mute)
2441 has_mute = FALSE;
2442 }
2443
2444 if (!has_volume || !has_dB || !has_mute) {
2445
2446 if (!has_volume)
2447 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2448 else if (!has_dB)
2449 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2450
2451 if (!has_mute)
2452 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2453
2454 PA_LLIST_FOREACH(p, ps->paths) {
2455 if (!has_volume)
2456 p->has_volume = FALSE;
2457 else if (!has_dB)
2458 p->has_dB = FALSE;
2459
2460 if (!has_mute)
2461 p->has_mute = FALSE;
2462 }
2463 }
2464 }
2465
2466 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
2467 pa_alsa_path *p, *q;
2468
2469 PA_LLIST_FOREACH(p, ps->paths) {
2470 unsigned i;
2471 char *m;
2472
2473 for (q = p->next; q; q = q->next)
2474 if (pa_streq(q->name, p->name))
2475 break;
2476
2477 if (!q)
2478 continue;
2479
2480 m = pa_xstrdup(p->name);
2481
2482 /* OK, this name is not unique, hence let's rename */
2483 for (i = 1, q = p; q; q = q->next) {
2484 char *nn, *nd;
2485
2486 if (!pa_streq(q->name, m))
2487 continue;
2488
2489 nn = pa_sprintf_malloc("%s-%u", m, i);
2490 pa_xfree(q->name);
2491 q->name = nn;
2492
2493 nd = pa_sprintf_malloc("%s %u", q->description, i);
2494 pa_xfree(q->description);
2495 q->description = nd;
2496
2497 i++;
2498 }
2499
2500 pa_xfree(m);
2501 }
2502 }
2503
2504 void pa_alsa_path_set_probe(pa_alsa_path_set *ps, snd_mixer_t *m, pa_bool_t ignore_dB) {
2505 pa_alsa_path *p, *n;
2506
2507 pa_assert(ps);
2508
2509 if (ps->probed)
2510 return;
2511
2512 for (p = ps->paths; p; p = n) {
2513 n = p->next;
2514
2515 if (pa_alsa_path_probe(p, m, ignore_dB) < 0) {
2516 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
2517 pa_alsa_path_free(p);
2518 }
2519 }
2520
2521 path_set_unify(ps);
2522 path_set_make_paths_unique(ps);
2523 ps->probed = TRUE;
2524 }
2525
2526 static void mapping_free(pa_alsa_mapping *m) {
2527 pa_assert(m);
2528
2529 pa_xfree(m->name);
2530 pa_xfree(m->description);
2531
2532 pa_xstrfreev(m->device_strings);
2533 pa_xstrfreev(m->input_path_names);
2534 pa_xstrfreev(m->output_path_names);
2535 pa_xstrfreev(m->input_element);
2536 pa_xstrfreev(m->output_element);
2537
2538 pa_assert(!m->input_pcm);
2539 pa_assert(!m->output_pcm);
2540
2541 pa_xfree(m);
2542 }
2543
2544 static void profile_free(pa_alsa_profile *p) {
2545 pa_assert(p);
2546
2547 pa_xfree(p->name);
2548 pa_xfree(p->description);
2549
2550 pa_xstrfreev(p->input_mapping_names);
2551 pa_xstrfreev(p->output_mapping_names);
2552
2553 if (p->input_mappings)
2554 pa_idxset_free(p->input_mappings, NULL, NULL);
2555
2556 if (p->output_mappings)
2557 pa_idxset_free(p->output_mappings, NULL, NULL);
2558
2559 pa_xfree(p);
2560 }
2561
2562 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
2563 pa_assert(ps);
2564
2565 if (ps->profiles) {
2566 pa_alsa_profile *p;
2567
2568 while ((p = pa_hashmap_steal_first(ps->profiles)))
2569 profile_free(p);
2570
2571 pa_hashmap_free(ps->profiles, NULL, NULL);
2572 }
2573
2574 if (ps->mappings) {
2575 pa_alsa_mapping *m;
2576
2577 while ((m = pa_hashmap_steal_first(ps->mappings)))
2578 mapping_free(m);
2579
2580 pa_hashmap_free(ps->mappings, NULL, NULL);
2581 }
2582
2583 pa_xfree(ps);
2584 }
2585
2586 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
2587 pa_alsa_mapping *m;
2588
2589 if (!pa_startswith(name, "Mapping "))
2590 return NULL;
2591
2592 name += 8;
2593
2594 if ((m = pa_hashmap_get(ps->mappings, name)))
2595 return m;
2596
2597 m = pa_xnew0(pa_alsa_mapping, 1);
2598 m->profile_set = ps;
2599 m->name = pa_xstrdup(name);
2600 pa_channel_map_init(&m->channel_map);
2601
2602 pa_hashmap_put(ps->mappings, m->name, m);
2603
2604 return m;
2605 }
2606
2607 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
2608 pa_alsa_profile *p;
2609
2610 if (!pa_startswith(name, "Profile "))
2611 return NULL;
2612
2613 name += 8;
2614
2615 if ((p = pa_hashmap_get(ps->profiles, name)))
2616 return p;
2617
2618 p = pa_xnew0(pa_alsa_profile, 1);
2619 p->profile_set = ps;
2620 p->name = pa_xstrdup(name);
2621
2622 pa_hashmap_put(ps->profiles, p->name, p);
2623
2624 return p;
2625 }
2626
2627 static int mapping_parse_device_strings(
2628 const char *filename,
2629 unsigned line,
2630 const char *section,
2631 const char *lvalue,
2632 const char *rvalue,
2633 void *data,
2634 void *userdata) {
2635
2636 pa_alsa_profile_set *ps = userdata;
2637 pa_alsa_mapping *m;
2638
2639 pa_assert(ps);
2640
2641 if (!(m = mapping_get(ps, section))) {
2642 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2643 return -1;
2644 }
2645
2646 pa_xstrfreev(m->device_strings);
2647 if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
2648 pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
2649 return -1;
2650 }
2651
2652 return 0;
2653 }
2654
2655 static int mapping_parse_channel_map(
2656 const char *filename,
2657 unsigned line,
2658 const char *section,
2659 const char *lvalue,
2660 const char *rvalue,
2661 void *data,
2662 void *userdata) {
2663
2664 pa_alsa_profile_set *ps = userdata;
2665 pa_alsa_mapping *m;
2666
2667 pa_assert(ps);
2668
2669 if (!(m = mapping_get(ps, section))) {
2670 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2671 return -1;
2672 }
2673
2674 if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
2675 pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
2676 return -1;
2677 }
2678
2679 return 0;
2680 }
2681
2682 static int mapping_parse_paths(
2683 const char *filename,
2684 unsigned line,
2685 const char *section,
2686 const char *lvalue,
2687 const char *rvalue,
2688 void *data,
2689 void *userdata) {
2690
2691 pa_alsa_profile_set *ps = userdata;
2692 pa_alsa_mapping *m;
2693
2694 pa_assert(ps);
2695
2696 if (!(m = mapping_get(ps, section))) {
2697 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2698 return -1;
2699 }
2700
2701 if (pa_streq(lvalue, "paths-input")) {
2702 pa_xstrfreev(m->input_path_names);
2703 m->input_path_names = pa_split_spaces_strv(rvalue);
2704 } else {
2705 pa_xstrfreev(m->output_path_names);
2706 m->output_path_names = pa_split_spaces_strv(rvalue);
2707 }
2708
2709 return 0;
2710 }
2711
2712 static int mapping_parse_element(
2713 const char *filename,
2714 unsigned line,
2715 const char *section,
2716 const char *lvalue,
2717 const char *rvalue,
2718 void *data,
2719 void *userdata) {
2720
2721 pa_alsa_profile_set *ps = userdata;
2722 pa_alsa_mapping *m;
2723
2724 pa_assert(ps);
2725
2726 if (!(m = mapping_get(ps, section))) {
2727 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2728 return -1;
2729 }
2730
2731 if (pa_streq(lvalue, "element-input")) {
2732 pa_xstrfreev(m->input_element);
2733 m->input_element = pa_split_spaces_strv(rvalue);
2734 } else {
2735 pa_xstrfreev(m->output_element);
2736 m->output_element = pa_split_spaces_strv(rvalue);
2737 }
2738
2739 return 0;
2740 }
2741
2742 static int mapping_parse_direction(
2743 const char *filename,
2744 unsigned line,
2745 const char *section,
2746 const char *lvalue,
2747 const char *rvalue,
2748 void *data,
2749 void *userdata) {
2750
2751 pa_alsa_profile_set *ps = userdata;
2752 pa_alsa_mapping *m;
2753
2754 pa_assert(ps);
2755
2756 if (!(m = mapping_get(ps, section))) {
2757 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2758 return -1;
2759 }
2760
2761 if (pa_streq(rvalue, "input"))
2762 m->direction = PA_ALSA_DIRECTION_INPUT;
2763 else if (pa_streq(rvalue, "output"))
2764 m->direction = PA_ALSA_DIRECTION_OUTPUT;
2765 else if (pa_streq(rvalue, "any"))
2766 m->direction = PA_ALSA_DIRECTION_ANY;
2767 else {
2768 pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
2769 return -1;
2770 }
2771
2772 return 0;
2773 }
2774
2775 static int mapping_parse_description(
2776 const char *filename,
2777 unsigned line,
2778 const char *section,
2779 const char *lvalue,
2780 const char *rvalue,
2781 void *data,
2782 void *userdata) {
2783
2784 pa_alsa_profile_set *ps = userdata;
2785 pa_alsa_profile *p;
2786 pa_alsa_mapping *m;
2787
2788 pa_assert(ps);
2789
2790 if ((m = mapping_get(ps, section))) {
2791 pa_xfree(m->description);
2792 m->description = pa_xstrdup(rvalue);
2793 } else if ((p = profile_get(ps, section))) {
2794 pa_xfree(p->description);
2795 p->description = pa_xstrdup(rvalue);
2796 } else {
2797 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2798 return -1;
2799 }
2800
2801 return 0;
2802 }
2803
2804 static int mapping_parse_priority(
2805 const char *filename,
2806 unsigned line,
2807 const char *section,
2808 const char *lvalue,
2809 const char *rvalue,
2810 void *data,
2811 void *userdata) {
2812
2813 pa_alsa_profile_set *ps = userdata;
2814 pa_alsa_profile *p;
2815 pa_alsa_mapping *m;
2816 uint32_t prio;
2817
2818 pa_assert(ps);
2819
2820 if (pa_atou(rvalue, &prio) < 0) {
2821 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
2822 return -1;
2823 }
2824
2825 if ((m = mapping_get(ps, section)))
2826 m->priority = prio;
2827 else if ((p = profile_get(ps, section)))
2828 p->priority = prio;
2829 else {
2830 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2831 return -1;
2832 }
2833
2834 return 0;
2835 }
2836
2837 static int profile_parse_mappings(
2838 const char *filename,
2839 unsigned line,
2840 const char *section,
2841 const char *lvalue,
2842 const char *rvalue,
2843 void *data,
2844 void *userdata) {
2845
2846 pa_alsa_profile_set *ps = userdata;
2847 pa_alsa_profile *p;
2848
2849 pa_assert(ps);
2850
2851 if (!(p = profile_get(ps, section))) {
2852 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2853 return -1;
2854 }
2855
2856 if (pa_streq(lvalue, "input-mappings")) {
2857 pa_xstrfreev(p->input_mapping_names);
2858 p->input_mapping_names = pa_split_spaces_strv(rvalue);
2859 } else {
2860 pa_xstrfreev(p->output_mapping_names);
2861 p->output_mapping_names = pa_split_spaces_strv(rvalue);
2862 }
2863
2864 return 0;
2865 }
2866
2867 static int profile_parse_skip_probe(
2868 const char *filename,
2869 unsigned line,
2870 const char *section,
2871 const char *lvalue,
2872 const char *rvalue,
2873 void *data,
2874 void *userdata) {
2875
2876 pa_alsa_profile_set *ps = userdata;
2877 pa_alsa_profile *p;
2878 int b;
2879
2880 pa_assert(ps);
2881
2882 if (!(p = profile_get(ps, section))) {
2883 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2884 return -1;
2885 }
2886
2887 if ((b = pa_parse_boolean(rvalue)) < 0) {
2888 pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
2889 return -1;
2890 }
2891
2892 p->supported = b;
2893
2894 return 0;
2895 }
2896
2897 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
2898
2899 static const struct description_map well_known_descriptions[] = {
2900 { "analog-mono", N_("Analog Mono") },
2901 { "analog-stereo", N_("Analog Stereo") },
2902 { "analog-surround-21", N_("Analog Surround 2.1") },
2903 { "analog-surround-30", N_("Analog Surround 3.0") },
2904 { "analog-surround-31", N_("Analog Surround 3.1") },
2905 { "analog-surround-40", N_("Analog Surround 4.0") },
2906 { "analog-surround-41", N_("Analog Surround 4.1") },
2907 { "analog-surround-50", N_("Analog Surround 5.0") },
2908 { "analog-surround-51", N_("Analog Surround 5.1") },
2909 { "analog-surround-61", N_("Analog Surround 6.0") },
2910 { "analog-surround-61", N_("Analog Surround 6.1") },
2911 { "analog-surround-70", N_("Analog Surround 7.0") },
2912 { "analog-surround-71", N_("Analog Surround 7.1") },
2913 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
2914 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
2915 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
2916 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
2917 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
2918 };
2919
2920 pa_assert(m);
2921
2922 if (!pa_channel_map_valid(&m->channel_map)) {
2923 pa_log("Mapping %s is missing channel map.", m->name);
2924 return -1;
2925 }
2926
2927 if (!m->device_strings) {
2928 pa_log("Mapping %s is missing device strings.", m->name);
2929 return -1;
2930 }
2931
2932 if ((m->input_path_names && m->input_element) ||
2933 (m->output_path_names && m->output_element)) {
2934 pa_log("Mapping %s must have either mixer path or mixer elment, not both.", m->name);
2935 return -1;
2936 }
2937
2938 if (!m->description)
2939 m->description = pa_xstrdup(lookup_description(m->name,
2940 well_known_descriptions,
2941 PA_ELEMENTSOF(well_known_descriptions)));
2942
2943 if (!m->description)
2944 m->description = pa_xstrdup(m->name);
2945
2946 if (bonus) {
2947 if (pa_channel_map_equal(&m->channel_map, bonus))
2948 m->priority += 50;
2949 else if (m->channel_map.channels == bonus->channels)
2950 m->priority += 30;
2951 }
2952
2953 return 0;
2954 }
2955
2956 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
2957 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
2958
2959 pa_assert(m);
2960
2961 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
2962 m->name,
2963 pa_strnull(m->description),
2964 m->priority,
2965 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
2966 pa_yes_no(m->supported),
2967 m->direction);
2968 }
2969
2970 static void profile_set_add_auto_pair(
2971 pa_alsa_profile_set *ps,
2972 pa_alsa_mapping *m, /* output */
2973 pa_alsa_mapping *n /* input */) {
2974
2975 char *name;
2976 pa_alsa_profile *p;
2977
2978 pa_assert(ps);
2979 pa_assert(m || n);
2980
2981 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
2982 return;
2983
2984 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
2985 return;
2986
2987 if (m && n)
2988 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
2989 else if (m)
2990 name = pa_sprintf_malloc("output:%s", m->name);
2991 else
2992 name = pa_sprintf_malloc("input:%s", n->name);
2993
2994 if (pa_hashmap_get(ps->profiles, name)) {
2995 pa_xfree(name);
2996 return;
2997 }
2998
2999 p = pa_xnew0(pa_alsa_profile, 1);
3000 p->profile_set = ps;
3001 p->name = name;
3002
3003 if (m) {
3004 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3005 pa_idxset_put(p->output_mappings, m, NULL);
3006 p->priority += m->priority * 100;
3007 }
3008
3009 if (n) {
3010 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3011 pa_idxset_put(p->input_mappings, n, NULL);
3012 p->priority += n->priority;
3013 }
3014
3015 pa_hashmap_put(ps->profiles, p->name, p);
3016 }
3017
3018 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
3019 pa_alsa_mapping *m, *n;
3020 void *m_state, *n_state;
3021
3022 pa_assert(ps);
3023
3024 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
3025 profile_set_add_auto_pair(ps, m, NULL);
3026
3027 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3028 profile_set_add_auto_pair(ps, m, n);
3029 }
3030
3031 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3032 profile_set_add_auto_pair(ps, NULL, n);
3033 }
3034
3035 static int profile_verify(pa_alsa_profile *p) {
3036
3037 static const struct description_map well_known_descriptions[] = {
3038 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3039 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3040 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3041 { "off", N_("Off") }
3042 };
3043
3044 pa_assert(p);
3045
3046 /* Replace the output mapping names by the actual mappings */
3047 if (p->output_mapping_names) {
3048 char **name;
3049
3050 pa_assert(!p->output_mappings);
3051 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3052
3053 for (name = p->output_mapping_names; *name; name++) {
3054 pa_alsa_mapping *m;
3055 char **in;
3056 pa_bool_t duplicate = FALSE;
3057
3058 for (in = name + 1; *in; in++)
3059 if (pa_streq(*name, *in)) {
3060 duplicate = TRUE;
3061 break;
3062 }
3063
3064 if (duplicate)
3065 continue;
3066
3067 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
3068 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
3069 return -1;
3070 }
3071
3072 pa_idxset_put(p->output_mappings, m, NULL);
3073
3074 if (p->supported)
3075 m->supported++;
3076 }
3077
3078 pa_xstrfreev(p->output_mapping_names);
3079 p->output_mapping_names = NULL;
3080 }
3081
3082 /* Replace the input mapping names by the actual mappings */
3083 if (p->input_mapping_names) {
3084 char **name;
3085
3086 pa_assert(!p->input_mappings);
3087 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3088
3089 for (name = p->input_mapping_names; *name; name++) {
3090 pa_alsa_mapping *m;
3091 char **in;
3092 pa_bool_t duplicate = FALSE;
3093
3094 for (in = name + 1; *in; in++)
3095 if (pa_streq(*name, *in)) {
3096 duplicate = TRUE;
3097 break;
3098 }
3099
3100 if (duplicate)
3101 continue;
3102
3103 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
3104 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
3105 return -1;
3106 }
3107
3108 pa_idxset_put(p->input_mappings, m, NULL);
3109
3110 if (p->supported)
3111 m->supported++;
3112 }
3113
3114 pa_xstrfreev(p->input_mapping_names);
3115 p->input_mapping_names = NULL;
3116 }
3117
3118 if (!p->input_mappings && !p->output_mappings) {
3119 pa_log("Profile '%s' lacks mappings.", p->name);
3120 return -1;
3121 }
3122
3123 if (!p->description)
3124 p->description = pa_xstrdup(lookup_description(p->name,
3125 well_known_descriptions,
3126 PA_ELEMENTSOF(well_known_descriptions)));
3127
3128 if (!p->description) {
3129 pa_strbuf *sb;
3130 uint32_t idx;
3131 pa_alsa_mapping *m;
3132
3133 sb = pa_strbuf_new();
3134
3135 if (p->output_mappings)
3136 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3137 if (!pa_strbuf_isempty(sb))
3138 pa_strbuf_puts(sb, " + ");
3139
3140 pa_strbuf_printf(sb, _("%s Output"), m->description);
3141 }
3142
3143 if (p->input_mappings)
3144 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3145 if (!pa_strbuf_isempty(sb))
3146 pa_strbuf_puts(sb, " + ");
3147
3148 pa_strbuf_printf(sb, _("%s Input"), m->description);
3149 }
3150
3151 p->description = pa_strbuf_tostring_free(sb);
3152 }
3153
3154 return 0;
3155 }
3156
3157 void pa_alsa_profile_dump(pa_alsa_profile *p) {
3158 uint32_t idx;
3159 pa_alsa_mapping *m;
3160 pa_assert(p);
3161
3162 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3163 p->name,
3164 pa_strnull(p->description),
3165 p->priority,
3166 pa_yes_no(p->supported),
3167 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
3168 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
3169
3170 if (p->input_mappings)
3171 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
3172 pa_log_debug("Input %s", m->name);
3173
3174 if (p->output_mappings)
3175 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
3176 pa_log_debug("Output %s", m->name);
3177 }
3178
3179 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
3180 pa_alsa_profile_set *ps;
3181 pa_alsa_profile *p;
3182 pa_alsa_mapping *m;
3183 char *fn;
3184 int r;
3185 void *state;
3186
3187 static pa_config_item items[] = {
3188 /* [General] */
3189 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
3190
3191 /* [Mapping ...] */
3192 { "device-strings", mapping_parse_device_strings, NULL, NULL },
3193 { "channel-map", mapping_parse_channel_map, NULL, NULL },
3194 { "paths-input", mapping_parse_paths, NULL, NULL },
3195 { "paths-output", mapping_parse_paths, NULL, NULL },
3196 { "element-input", mapping_parse_element, NULL, NULL },
3197 { "element-output", mapping_parse_element, NULL, NULL },
3198 { "direction", mapping_parse_direction, NULL, NULL },
3199
3200 /* Shared by [Mapping ...] and [Profile ...] */
3201 { "description", mapping_parse_description, NULL, NULL },
3202 { "priority", mapping_parse_priority, NULL, NULL },
3203
3204 /* [Profile ...] */
3205 { "input-mappings", profile_parse_mappings, NULL, NULL },
3206 { "output-mappings", profile_parse_mappings, NULL, NULL },
3207 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
3208 { NULL, NULL, NULL, NULL }
3209 };
3210
3211 ps = pa_xnew0(pa_alsa_profile_set, 1);
3212 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3213 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3214
3215 items[0].data = &ps->auto_profiles;
3216
3217 if (!fname)
3218 fname = "default.conf";
3219
3220 fn = pa_maybe_prefix_path(fname,
3221 #if defined(__linux__) && !defined(__OPTIMIZE__)
3222 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
3223 #endif
3224 PA_ALSA_PROFILE_SETS_DIR);
3225
3226 r = pa_config_parse(fn, NULL, items, ps);
3227 pa_xfree(fn);
3228
3229 if (r < 0)
3230 goto fail;
3231
3232 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3233 if (mapping_verify(m, bonus) < 0)
3234 goto fail;
3235
3236 if (ps->auto_profiles)
3237 profile_set_add_auto(ps);
3238
3239 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3240 if (profile_verify(p) < 0)
3241 goto fail;
3242
3243 return ps;
3244
3245 fail:
3246 pa_alsa_profile_set_free(ps);
3247 return NULL;
3248 }
3249
3250 void pa_alsa_profile_set_probe(
3251 pa_alsa_profile_set *ps,
3252 const char *dev_id,
3253 const pa_sample_spec *ss,
3254 unsigned default_n_fragments,
3255 unsigned default_fragment_size_msec) {
3256
3257 void *state;
3258 pa_alsa_profile *p, *last = NULL;
3259 pa_alsa_mapping *m;
3260
3261 pa_assert(ps);
3262 pa_assert(dev_id);
3263 pa_assert(ss);
3264
3265 if (ps->probed)
3266 return;
3267
3268 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
3269 pa_sample_spec try_ss;
3270 pa_channel_map try_map;
3271 snd_pcm_uframes_t try_period_size, try_buffer_size;
3272 uint32_t idx;
3273
3274 /* Is this already marked that it is supported? (i.e. from the config file) */
3275 if (p->supported)
3276 continue;
3277
3278 pa_log_debug("Looking at profile %s", p->name);
3279
3280 /* Close PCMs from the last iteration we don't need anymore */
3281 if (last && last->output_mappings)
3282 PA_IDXSET_FOREACH(m, last->output_mappings, idx) {
3283
3284 if (!m->output_pcm)
3285 break;
3286
3287 if (last->supported)
3288 m->supported++;
3289
3290 if (!p->output_mappings || !pa_idxset_get_by_data(p->output_mappings, m, NULL)) {
3291 snd_pcm_close(m->output_pcm);
3292 m->output_pcm = NULL;
3293 }
3294 }
3295
3296 if (last && last->input_mappings)
3297 PA_IDXSET_FOREACH(m, last->input_mappings, idx) {
3298
3299 if (!m->input_pcm)
3300 break;
3301
3302 if (last->supported)
3303 m->supported++;
3304
3305 if (!p->input_mappings || !pa_idxset_get_by_data(p->input_mappings, m, NULL)) {
3306 snd_pcm_close(m->input_pcm);
3307 m->input_pcm = NULL;
3308 }
3309 }
3310
3311 p->supported = TRUE;
3312
3313 /* Check if we can open all new ones */
3314 if (p->output_mappings)
3315 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3316
3317 if (m->output_pcm)
3318 continue;
3319
3320 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
3321 try_map = m->channel_map;
3322 try_ss = *ss;
3323 try_ss.channels = try_map.channels;
3324
3325 try_period_size =
3326 pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
3327 pa_frame_size(&try_ss);
3328 try_buffer_size = default_n_fragments * try_period_size;
3329
3330 if (!(m ->output_pcm = pa_alsa_open_by_template(
3331 m->device_strings,
3332 dev_id,
3333 NULL,
3334 &try_ss, &try_map,
3335 SND_PCM_STREAM_PLAYBACK,
3336 &try_period_size, &try_buffer_size, 0, NULL, NULL,
3337 TRUE))) {
3338 p->supported = FALSE;
3339 break;
3340 }
3341 }
3342
3343 if (p->input_mappings && p->supported)
3344 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3345
3346 if (m->input_pcm)
3347 continue;
3348
3349 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
3350 try_map = m->channel_map;
3351 try_ss = *ss;
3352 try_ss.channels = try_map.channels;
3353
3354 try_period_size =
3355 pa_usec_to_bytes(default_fragment_size_msec*PA_USEC_PER_MSEC, &try_ss) /
3356 pa_frame_size(&try_ss);
3357 try_buffer_size = default_n_fragments * try_period_size;
3358
3359 if (!(m ->input_pcm = pa_alsa_open_by_template(
3360 m->device_strings,
3361 dev_id,
3362 NULL,
3363 &try_ss, &try_map,
3364 SND_PCM_STREAM_CAPTURE,
3365 &try_period_size, &try_buffer_size, 0, NULL, NULL,
3366 TRUE))) {
3367 p->supported = FALSE;
3368 break;
3369 }
3370 }
3371
3372 last = p;
3373
3374 if (p->supported)
3375 pa_log_debug("Profile %s supported.", p->name);
3376 }
3377
3378 /* Clean up */
3379 if (last) {
3380 uint32_t idx;
3381
3382 if (last->output_mappings)
3383 PA_IDXSET_FOREACH(m, last->output_mappings, idx)
3384 if (m->output_pcm) {
3385
3386 if (last->supported)
3387 m->supported++;
3388
3389 snd_pcm_close(m->output_pcm);
3390 m->output_pcm = NULL;
3391 }
3392
3393 if (last->input_mappings)
3394 PA_IDXSET_FOREACH(m, last->input_mappings, idx)
3395 if (m->input_pcm) {
3396
3397 if (last->supported)
3398 m->supported++;
3399
3400 snd_pcm_close(m->input_pcm);
3401 m->input_pcm = NULL;
3402 }
3403 }
3404
3405 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3406 if (!p->supported) {
3407 pa_hashmap_remove(ps->profiles, p->name);
3408 profile_free(p);
3409 }
3410
3411 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3412 if (m->supported <= 0) {
3413 pa_hashmap_remove(ps->mappings, m->name);
3414 mapping_free(m);
3415 }
3416
3417 ps->probed = TRUE;
3418 }
3419
3420 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
3421 pa_alsa_profile *p;
3422 pa_alsa_mapping *m;
3423 void *state;
3424
3425 pa_assert(ps);
3426
3427 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u",
3428 (void*)
3429 ps,
3430 pa_yes_no(ps->auto_profiles),
3431 pa_yes_no(ps->probed),
3432 pa_hashmap_size(ps->mappings),
3433 pa_hashmap_size(ps->profiles));
3434
3435 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3436 pa_alsa_mapping_dump(m);
3437
3438 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3439 pa_alsa_profile_dump(p);
3440 }
3441
3442 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps) {
3443 pa_alsa_path *path;
3444
3445 pa_assert(p);
3446 pa_assert(!*p);
3447 pa_assert(ps);
3448
3449 /* if there is no path, we don't want a port list */
3450 if (!ps->paths)
3451 return;
3452
3453 if (!ps->paths->next){
3454 pa_alsa_setting *s;
3455
3456 /* If there is only one path, but no or only one setting, then
3457 * we want a port list either */
3458 if (!ps->paths->settings || !ps->paths->settings->next)
3459 return;
3460
3461 /* Ok, there is only one path, however with multiple settings,
3462 * so let's create a port for each setting */
3463 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3464
3465 PA_LLIST_FOREACH(s, ps->paths->settings) {
3466 pa_device_port *port;
3467 pa_alsa_port_data *data;
3468
3469 port = pa_device_port_new(s->name, s->description, sizeof(pa_alsa_port_data));
3470 port->priority = s->priority;
3471
3472 data = PA_DEVICE_PORT_DATA(port);
3473 data->path = ps->paths;
3474 data->setting = s;
3475
3476 pa_hashmap_put(*p, port->name, port);
3477 }
3478
3479 } else {
3480
3481 /* We have multiple paths, so let's create a port for each
3482 * one, and each of each settings */
3483 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3484
3485 PA_LLIST_FOREACH(path, ps->paths) {
3486
3487 if (!path->settings || !path->settings->next) {
3488 pa_device_port *port;
3489 pa_alsa_port_data *data;
3490
3491 /* If there is no or just one setting we only need a
3492 * single entry */
3493
3494 port = pa_device_port_new(path->name, path->description, sizeof(pa_alsa_port_data));
3495 port->priority = path->priority * 100;
3496
3497
3498 data = PA_DEVICE_PORT_DATA(port);
3499 data->path = path;
3500 data->setting = path->settings;
3501
3502 pa_hashmap_put(*p, port->name, port);
3503 } else {
3504 pa_alsa_setting *s;
3505
3506 PA_LLIST_FOREACH(s, path->settings) {
3507 pa_device_port *port;
3508 pa_alsa_port_data *data;
3509 char *n, *d;
3510
3511 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
3512
3513 if (s->description[0])
3514 d = pa_sprintf_malloc(_("%s / %s"), path->description, s->description);
3515 else
3516 d = pa_xstrdup(path->description);
3517
3518 port = pa_device_port_new(n, d, sizeof(pa_alsa_port_data));
3519 port->priority = path->priority * 100 + s->priority;
3520
3521 pa_xfree(n);
3522 pa_xfree(d);
3523
3524 data = PA_DEVICE_PORT_DATA(port);
3525 data->path = path;
3526 data->setting = s;
3527
3528 pa_hashmap_put(*p, port->name, port);
3529 }
3530 }
3531 }
3532 }
3533
3534 pa_log_debug("Added %u ports", pa_hashmap_size(*p));
3535 }