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