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