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