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