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 /* Check that the kernel driver returns consistent limits with
1715 * both _get_*_dB_range() and _ask_*_vol_dB(). */
1716 if (e->has_dB && !e->db_fix) {
1717 long min_dB_checked = 0;
1718 long max_dB_checked = 0;
1719
1720 if (element_ask_vol_dB(me, e->direction, e->min_volume, &min_dB_checked) < 0) {
1721 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
1722 pa_log_warn("Failed to query the dB value for %s at volume level %li", buf, e->min_volume);
1723 return false;
1724 }
1725
1726 if (element_ask_vol_dB(me, e->direction, e->max_volume, &max_dB_checked) < 0) {
1727 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
1728 pa_log_warn("Failed to query the dB value for %s at volume level %li", buf, e->max_volume);
1729 return false;
1730 }
1731
1732 if (min_dB != min_dB_checked || max_dB != max_dB_checked) {
1733 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
1734 pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
1735 "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
1736 "%0.2f dB at level %li.", buf, min_dB / 100.0, max_dB / 100.0,
1737 min_dB_checked / 100.0, e->min_volume, max_dB_checked / 100.0, e->max_volume);
1738 return false;
1739 }
1740 }
1741
1742 if (e->has_dB) {
1743 e->min_dB = ((double) min_dB) / 100.0;
1744 e->max_dB = ((double) max_dB) / 100.0;
1745
1746 if (min_dB >= max_dB) {
1747 pa_assert(!e->db_fix);
1748 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.",
1749 e->min_dB, e->max_dB);
1750 e->has_dB = false;
1751 }
1752 }
1753
1754 if (e->volume_limit >= 0) {
1755 if (e->volume_limit <= e->min_volume || e->volume_limit > e->max_volume) {
1756 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
1757 pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1758 "%li-%li. The volume limit is ignored.",
1759 buf, e->path->name, e->volume_limit, e->min_volume + 1, e->max_volume);
1760 } else {
1761 e->max_volume = e->volume_limit;
1762
1763 if (e->has_dB) {
1764 if (e->db_fix) {
1765 e->db_fix->max_step = e->max_volume;
1766 e->max_dB = ((double) e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step]) / 100.0;
1767 } else if (element_ask_vol_dB(me, e->direction, e->max_volume, &max_dB) < 0) {
1768 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
1769 pa_log_warn("Failed to get dB value of %s: %s", buf, pa_alsa_strerror(r));
1770 e->has_dB = false;
1771 } else
1772 e->max_dB = ((double) max_dB) / 100.0;
1773 }
1774 }
1775 }
1776
1777 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1778 is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
1779 else
1780 is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
1781
1782 if (is_mono) {
1783 e->n_channels = 1;
1784
1785 if ((e->override_map & (1 << (e->n_channels-1))) && e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] == 0) {
1786 pa_log_warn("Override map for mono element %s is invalid, ignoring override map", e->path->name);
1787 e->override_map &= ~(1 << (e->n_channels-1));
1788 }
1789 if (!(e->override_map & (1 << (e->n_channels-1)))) {
1790 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1791 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1792 continue;
1793 e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
1794 }
1795 e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
1796 }
1797 e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
1798 return true;
1799 }
1800
1801 e->n_channels = 0;
1802 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1803 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1804 continue;
1805
1806 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1807 e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1808 else
1809 e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1810 }
1811
1812 if (e->n_channels <= 0) {
1813 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
1814 pa_log_warn("Volume element %s with no channels?", buf);
1815 return false;
1816 } else if (e->n_channels > POSITION_MASK_CHANNELS) {
1817 /* FIXME: In some places code like this is used:
1818 *
1819 * e->masks[alsa_channel_ids[p]][e->n_channels-1]
1820 *
1821 * The definition of e->masks is
1822 *
1823 * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][POSITION_MASK_CHANNELS];
1824 *
1825 * Since the array size is fixed at POSITION_MASK_CHANNELS, we obviously
1826 * don't support elements with more than POSITION_MASK_CHANNELS
1827 * channels... */
1828 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
1829 pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", buf, e->n_channels);
1830 return false;
1831 }
1832
1833 retry:
1834 if (!(e->override_map & (1 << (e->n_channels-1)))) {
1835 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1836 bool has_channel;
1837
1838 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1839 continue;
1840
1841 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1842 has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1843 else
1844 has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1845
1846 e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
1847 }
1848 }
1849
1850 e->merged_mask = 0;
1851 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1852 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1853 continue;
1854
1855 e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
1856 }
1857
1858 if (e->merged_mask == 0) {
1859 if (!(e->override_map & (1 << (e->n_channels-1)))) {
1860 pa_log_warn("Channel map for element %s is invalid", e->path->name);
1861 return false;
1862 }
1863 pa_log_warn("Override map for element %s has empty result, ignoring override map", e->path->name);
1864 e->override_map &= ~(1 << (e->n_channels-1));
1865 goto retry;
1866 }
1867
1868 return true;
1869 }
1870
element_probe(pa_alsa_element * e,snd_mixer_t * m)1871 static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
1872 snd_mixer_selem_id_t *sid;
1873 snd_mixer_elem_t *me;
1874
1875 pa_assert(m);
1876 pa_assert(e);
1877 pa_assert(e->path);
1878
1879 SELEM_INIT(sid, &e->alsa_id);
1880
1881 if (!(me = snd_mixer_find_selem(m, sid))) {
1882
1883 if (e->required != PA_ALSA_REQUIRED_IGNORE)
1884 return -1;
1885
1886 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1887 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1888 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1889
1890 return 0;
1891 }
1892
1893 if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
1894 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1895
1896 if (!snd_mixer_selem_has_playback_switch(me)) {
1897 if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
1898 e->direction = PA_ALSA_DIRECTION_INPUT;
1899 else
1900 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1901 }
1902
1903 } else {
1904
1905 if (!snd_mixer_selem_has_capture_switch(me)) {
1906 if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
1907 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1908 else
1909 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1910 }
1911 }
1912
1913 if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
1914 e->direction_try_other = false;
1915 }
1916
1917 if (!element_probe_volume(e, me))
1918 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1919
1920 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1921 pa_alsa_option *o;
1922
1923 PA_LLIST_FOREACH(o, e->options)
1924 o->alsa_idx = pa_streq(o->alsa_name, "on") ? 1 : 0;
1925 } else if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1926 int n;
1927 pa_alsa_option *o;
1928
1929 if ((n = snd_mixer_selem_get_enum_items(me)) < 0) {
1930 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n));
1931 return -1;
1932 }
1933
1934 PA_LLIST_FOREACH(o, e->options) {
1935 int i;
1936
1937 for (i = 0; i < n; i++) {
1938 char buf[128];
1939
1940 if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
1941 continue;
1942
1943 if (!pa_streq(buf, o->alsa_name))
1944 continue;
1945
1946 o->alsa_idx = i;
1947 }
1948 }
1949 }
1950
1951 if (check_required(e, me) < 0)
1952 return -1;
1953
1954 return 0;
1955 }
1956
jack_probe(pa_alsa_jack * j,pa_alsa_mapping * mapping,snd_mixer_t * m)1957 static int jack_probe(pa_alsa_jack *j, pa_alsa_mapping *mapping, snd_mixer_t *m) {
1958 bool has_control;
1959
1960 pa_assert(j);
1961 pa_assert(j->path);
1962
1963 if (j->append_pcm_to_name) {
1964 char *new_name;
1965
1966 if (!mapping) {
1967 /* This could also be an assertion, because this should never
1968 * happen. At the time of writing, mapping can only be NULL when
1969 * module-alsa-sink/source synthesizes a path, and those
1970 * synthesized paths never have any jacks, so jack_probe() should
1971 * never be called with a NULL mapping. */
1972 pa_log("Jack %s: append_pcm_to_name is set, but mapping is NULL. Can't use this jack.", j->name);
1973 return -1;
1974 }
1975
1976 new_name = pa_sprintf_malloc("%s,pcm=%i Jack", j->name, mapping->hw_device_index);
1977 pa_xfree(j->alsa_id.name);
1978 j->alsa_id.name = new_name;
1979 j->append_pcm_to_name = false;
1980 }
1981
1982 has_control = pa_alsa_mixer_find_card(m, &j->alsa_id, 0) != NULL;
1983 pa_alsa_jack_set_has_control(j, has_control);
1984
1985 if (j->has_control) {
1986 if (j->required_absent != PA_ALSA_REQUIRED_IGNORE)
1987 return -1;
1988 if (j->required_any != PA_ALSA_REQUIRED_IGNORE)
1989 j->path->req_any_present = true;
1990 } else {
1991 if (j->required != PA_ALSA_REQUIRED_IGNORE)
1992 return -1;
1993 }
1994
1995 return 0;
1996 }
1997
pa_alsa_element_get(pa_alsa_path * p,const char * section,bool prefixed)1998 pa_alsa_element * pa_alsa_element_get(pa_alsa_path *p, const char *section, bool prefixed) {
1999 pa_alsa_element *e;
2000 char *name;
2001 int index;
2002
2003 pa_assert(p);
2004 pa_assert(section);
2005
2006 if (prefixed) {
2007 if (!pa_startswith(section, "Element "))
2008 return NULL;
2009
2010 section += 8;
2011 }
2012
2013 /* This is not an element section, but an enum section? */
2014 if (strchr(section, ':'))
2015 return NULL;
2016
2017 name = alloca(strlen(section) + 1);
2018 if (alsa_id_decode(section, name, &index))
2019 return NULL;
2020
2021 if (p->last_element && pa_streq(p->last_element->alsa_id.name, name) &&
2022 p->last_element->alsa_id.index == index)
2023 return p->last_element;
2024
2025 PA_LLIST_FOREACH(e, p->elements)
2026 if (pa_streq(e->alsa_id.name, name) && e->alsa_id.index == index)
2027 goto finish;
2028
2029 e = pa_xnew0(pa_alsa_element, 1);
2030 e->path = p;
2031 e->alsa_id.name = pa_xstrdup(name);
2032 e->alsa_id.index = index;
2033 e->direction = p->direction;
2034 e->volume_limit = -1;
2035
2036 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2037
2038 finish:
2039 p->last_element = e;
2040 return e;
2041 }
2042
jack_get(pa_alsa_path * p,const char * section)2043 static pa_alsa_jack* jack_get(pa_alsa_path *p, const char *section) {
2044 pa_alsa_jack *j;
2045 char *name;
2046 int index;
2047
2048 if (!pa_startswith(section, "Jack "))
2049 return NULL;
2050 section += 5;
2051
2052 name = alloca(strlen(section) + 1);
2053 if (alsa_id_decode(section, name, &index))
2054 return NULL;
2055
2056 if (p->last_jack && pa_streq(p->last_jack->name, name) &&
2057 p->last_jack->alsa_id.index == index)
2058 return p->last_jack;
2059
2060 PA_LLIST_FOREACH(j, p->jacks)
2061 if (pa_streq(j->name, name) && j->alsa_id.index == index)
2062 goto finish;
2063
2064 j = pa_alsa_jack_new(p, NULL, name, index);
2065 PA_LLIST_INSERT_AFTER(pa_alsa_jack, p->jacks, p->last_jack, j);
2066
2067 finish:
2068 p->last_jack = j;
2069 return j;
2070 }
2071
option_get(pa_alsa_path * p,const char * section)2072 static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
2073 char *en, *name;
2074 const char *on;
2075 pa_alsa_option *o;
2076 pa_alsa_element *e;
2077 size_t len;
2078 int index;
2079
2080 if (!pa_startswith(section, "Option "))
2081 return NULL;
2082
2083 section += 7;
2084
2085 /* This is not an enum section, but an element section? */
2086 if (!(on = strchr(section, ':')))
2087 return NULL;
2088
2089 len = on - section;
2090 en = alloca(len + 1);
2091 strncpy(en, section, len);
2092 en[len] = '\0';
2093
2094 name = alloca(strlen(en) + 1);
2095 if (alsa_id_decode(en, name, &index))
2096 return NULL;
2097
2098 on++;
2099
2100 if (p->last_option &&
2101 pa_streq(p->last_option->element->alsa_id.name, name) &&
2102 p->last_option->element->alsa_id.index == index &&
2103 pa_streq(p->last_option->alsa_name, on)) {
2104 return p->last_option;
2105 }
2106
2107 pa_assert_se(e = pa_alsa_element_get(p, en, false));
2108
2109 PA_LLIST_FOREACH(o, e->options)
2110 if (pa_streq(o->alsa_name, on))
2111 goto finish;
2112
2113 o = pa_xnew0(pa_alsa_option, 1);
2114 o->element = e;
2115 o->alsa_name = pa_xstrdup(on);
2116 o->alsa_idx = -1;
2117
2118 if (p->last_option && p->last_option->element == e)
2119 PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
2120 else
2121 PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
2122
2123 finish:
2124 p->last_option = o;
2125 return o;
2126 }
2127
element_parse_switch(pa_config_parser_state * state)2128 static int element_parse_switch(pa_config_parser_state *state) {
2129 pa_alsa_path *p;
2130 pa_alsa_element *e;
2131
2132 pa_assert(state);
2133
2134 p = state->userdata;
2135
2136 if (!(e = pa_alsa_element_get(p, state->section, true))) {
2137 pa_log("[%s:%u] Switch makes no sense in '%s'", state->filename, state->lineno, state->section);
2138 return -1;
2139 }
2140
2141 if (pa_streq(state->rvalue, "ignore"))
2142 e->switch_use = PA_ALSA_SWITCH_IGNORE;
2143 else if (pa_streq(state->rvalue, "mute"))
2144 e->switch_use = PA_ALSA_SWITCH_MUTE;
2145 else if (pa_streq(state->rvalue, "off"))
2146 e->switch_use = PA_ALSA_SWITCH_OFF;
2147 else if (pa_streq(state->rvalue, "on"))
2148 e->switch_use = PA_ALSA_SWITCH_ON;
2149 else if (pa_streq(state->rvalue, "select"))
2150 e->switch_use = PA_ALSA_SWITCH_SELECT;
2151 else {
2152 pa_log("[%s:%u] Switch invalid of '%s'", state->filename, state->lineno, state->section);
2153 return -1;
2154 }
2155
2156 return 0;
2157 }
2158
element_parse_volume(pa_config_parser_state * state)2159 static int element_parse_volume(pa_config_parser_state *state) {
2160 pa_alsa_path *p;
2161 pa_alsa_element *e;
2162
2163 pa_assert(state);
2164
2165 p = state->userdata;
2166
2167 if (!(e = pa_alsa_element_get(p, state->section, true))) {
2168 pa_log("[%s:%u] Volume makes no sense in '%s'", state->filename, state->lineno, state->section);
2169 return -1;
2170 }
2171
2172 if (pa_streq(state->rvalue, "ignore"))
2173 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2174 else if (pa_streq(state->rvalue, "merge"))
2175 e->volume_use = PA_ALSA_VOLUME_MERGE;
2176 else if (pa_streq(state->rvalue, "off"))
2177 e->volume_use = PA_ALSA_VOLUME_OFF;
2178 else if (pa_streq(state->rvalue, "zero"))
2179 e->volume_use = PA_ALSA_VOLUME_ZERO;
2180 else {
2181 uint32_t constant;
2182
2183 if (pa_atou(state->rvalue, &constant) >= 0) {
2184 e->volume_use = PA_ALSA_VOLUME_CONSTANT;
2185 e->constant_volume = constant;
2186 } else {
2187 pa_log("[%s:%u] Volume invalid of '%s'", state->filename, state->lineno, state->section);
2188 return -1;
2189 }
2190 }
2191
2192 return 0;
2193 }
2194
element_parse_enumeration(pa_config_parser_state * state)2195 static int element_parse_enumeration(pa_config_parser_state *state) {
2196 pa_alsa_path *p;
2197 pa_alsa_element *e;
2198
2199 pa_assert(state);
2200
2201 p = state->userdata;
2202
2203 if (!(e = pa_alsa_element_get(p, state->section, true))) {
2204 pa_log("[%s:%u] Enumeration makes no sense in '%s'", state->filename, state->lineno, state->section);
2205 return -1;
2206 }
2207
2208 if (pa_streq(state->rvalue, "ignore"))
2209 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
2210 else if (pa_streq(state->rvalue, "select"))
2211 e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
2212 else {
2213 pa_log("[%s:%u] Enumeration invalid of '%s'", state->filename, state->lineno, state->section);
2214 return -1;
2215 }
2216
2217 return 0;
2218 }
2219
parse_type(pa_config_parser_state * state)2220 static int parse_type(pa_config_parser_state *state) {
2221 struct device_port_types {
2222 const char *name;
2223 pa_device_port_type_t type;
2224 } device_port_types[] = {
2225 { "unknown", PA_DEVICE_PORT_TYPE_UNKNOWN },
2226 { "aux", PA_DEVICE_PORT_TYPE_AUX },
2227 { "speaker", PA_DEVICE_PORT_TYPE_SPEAKER },
2228 { "headphones", PA_DEVICE_PORT_TYPE_HEADPHONES },
2229 { "line", PA_DEVICE_PORT_TYPE_LINE },
2230 { "mic", PA_DEVICE_PORT_TYPE_MIC },
2231 { "headset", PA_DEVICE_PORT_TYPE_HEADSET },
2232 { "handset", PA_DEVICE_PORT_TYPE_HANDSET },
2233 { "earpiece", PA_DEVICE_PORT_TYPE_EARPIECE },
2234 { "spdif", PA_DEVICE_PORT_TYPE_SPDIF },
2235 { "hdmi", PA_DEVICE_PORT_TYPE_HDMI },
2236 { "tv", PA_DEVICE_PORT_TYPE_TV },
2237 { "radio", PA_DEVICE_PORT_TYPE_RADIO },
2238 { "video", PA_DEVICE_PORT_TYPE_VIDEO },
2239 { "usb", PA_DEVICE_PORT_TYPE_USB },
2240 { "bluetooth", PA_DEVICE_PORT_TYPE_BLUETOOTH },
2241 { "portable", PA_DEVICE_PORT_TYPE_PORTABLE },
2242 { "handsfree", PA_DEVICE_PORT_TYPE_HANDSFREE },
2243 { "car", PA_DEVICE_PORT_TYPE_CAR },
2244 { "hifi", PA_DEVICE_PORT_TYPE_HIFI },
2245 { "phone", PA_DEVICE_PORT_TYPE_PHONE },
2246 { "network", PA_DEVICE_PORT_TYPE_NETWORK },
2247 { "analog", PA_DEVICE_PORT_TYPE_ANALOG },
2248 };
2249 pa_alsa_path *path;
2250 unsigned int idx;
2251
2252 path = state->userdata;
2253
2254 for (idx = 0; idx < PA_ELEMENTSOF(device_port_types); idx++)
2255 if (pa_streq(state->rvalue, device_port_types[idx].name)) {
2256 path->device_port_type = device_port_types[idx].type;
2257 return 0;
2258 }
2259
2260 pa_log("[%s:%u] Invalid value for option 'type': %s", state->filename, state->lineno, state->rvalue);
2261 return -1;
2262 }
2263
parse_eld_device(pa_config_parser_state * state)2264 static int parse_eld_device(pa_config_parser_state *state) {
2265 pa_alsa_path *path;
2266 uint32_t eld_device;
2267
2268 path = state->userdata;
2269
2270 if (pa_atou(state->rvalue, &eld_device) >= 0) {
2271 path->autodetect_eld_device = false;
2272 path->eld_device = eld_device;
2273 return 0;
2274 }
2275
2276 if (pa_streq(state->rvalue, "auto")) {
2277 path->autodetect_eld_device = true;
2278 path->eld_device = -1;
2279 return 0;
2280 }
2281
2282 pa_log("[%s:%u] Invalid value for option 'eld-device': %s", state->filename, state->lineno, state->rvalue);
2283 return -1;
2284 }
2285
option_parse_priority(pa_config_parser_state * state)2286 static int option_parse_priority(pa_config_parser_state *state) {
2287 pa_alsa_path *p;
2288 pa_alsa_option *o;
2289 uint32_t prio;
2290
2291 pa_assert(state);
2292
2293 p = state->userdata;
2294
2295 if (!(o = option_get(p, state->section))) {
2296 pa_log("[%s:%u] Priority makes no sense in '%s'", state->filename, state->lineno, state->section);
2297 return -1;
2298 }
2299
2300 if (pa_atou(state->rvalue, &prio) < 0) {
2301 pa_log("[%s:%u] Priority invalid of '%s'", state->filename, state->lineno, state->section);
2302 return -1;
2303 }
2304
2305 o->priority = prio;
2306 return 0;
2307 }
2308
option_parse_name(pa_config_parser_state * state)2309 static int option_parse_name(pa_config_parser_state *state) {
2310 pa_alsa_path *p;
2311 pa_alsa_option *o;
2312
2313 pa_assert(state);
2314
2315 p = state->userdata;
2316
2317 if (!(o = option_get(p, state->section))) {
2318 pa_log("[%s:%u] Name makes no sense in '%s'", state->filename, state->lineno, state->section);
2319 return -1;
2320 }
2321
2322 pa_xfree(o->name);
2323 o->name = pa_xstrdup(state->rvalue);
2324
2325 return 0;
2326 }
2327
element_parse_required(pa_config_parser_state * state)2328 static int element_parse_required(pa_config_parser_state *state) {
2329 pa_alsa_path *p;
2330 pa_alsa_element *e;
2331 pa_alsa_option *o;
2332 pa_alsa_jack *j;
2333 pa_alsa_required_t req;
2334
2335 pa_assert(state);
2336
2337 p = state->userdata;
2338
2339 e = pa_alsa_element_get(p, state->section, true);
2340 o = option_get(p, state->section);
2341 j = jack_get(p, state->section);
2342 if (!e && !o && !j) {
2343 pa_log("[%s:%u] Required makes no sense in '%s'", state->filename, state->lineno, state->section);
2344 return -1;
2345 }
2346
2347 if (pa_streq(state->rvalue, "ignore"))
2348 req = PA_ALSA_REQUIRED_IGNORE;
2349 else if (pa_streq(state->rvalue, "switch") && e)
2350 req = PA_ALSA_REQUIRED_SWITCH;
2351 else if (pa_streq(state->rvalue, "volume") && e)
2352 req = PA_ALSA_REQUIRED_VOLUME;
2353 else if (pa_streq(state->rvalue, "enumeration"))
2354 req = PA_ALSA_REQUIRED_ENUMERATION;
2355 else if (pa_streq(state->rvalue, "any"))
2356 req = PA_ALSA_REQUIRED_ANY;
2357 else {
2358 pa_log("[%s:%u] Required invalid of '%s'", state->filename, state->lineno, state->section);
2359 return -1;
2360 }
2361
2362 if (pa_streq(state->lvalue, "required-absent")) {
2363 if (e)
2364 e->required_absent = req;
2365 if (o)
2366 o->required_absent = req;
2367 if (j)
2368 j->required_absent = req;
2369 }
2370 else if (pa_streq(state->lvalue, "required-any")) {
2371 if (e) {
2372 e->required_any = req;
2373 e->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
2374 }
2375 if (o) {
2376 o->required_any = req;
2377 o->element->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
2378 }
2379 if (j) {
2380 j->required_any = req;
2381 j->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
2382 }
2383
2384 }
2385 else {
2386 if (e)
2387 e->required = req;
2388 if (o)
2389 o->required = req;
2390 if (j)
2391 j->required = req;
2392 }
2393
2394 return 0;
2395 }
2396
element_parse_direction(pa_config_parser_state * state)2397 static int element_parse_direction(pa_config_parser_state *state) {
2398 pa_alsa_path *p;
2399 pa_alsa_element *e;
2400
2401 pa_assert(state);
2402
2403 p = state->userdata;
2404
2405 if (!(e = pa_alsa_element_get(p, state->section, true))) {
2406 pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section);
2407 return -1;
2408 }
2409
2410 if (pa_streq(state->rvalue, "playback"))
2411 e->direction = PA_ALSA_DIRECTION_OUTPUT;
2412 else if (pa_streq(state->rvalue, "capture"))
2413 e->direction = PA_ALSA_DIRECTION_INPUT;
2414 else {
2415 pa_log("[%s:%u] Direction invalid of '%s'", state->filename, state->lineno, state->section);
2416 return -1;
2417 }
2418
2419 return 0;
2420 }
2421
element_parse_direction_try_other(pa_config_parser_state * state)2422 static int element_parse_direction_try_other(pa_config_parser_state *state) {
2423 pa_alsa_path *p;
2424 pa_alsa_element *e;
2425 int yes;
2426
2427 pa_assert(state);
2428
2429 p = state->userdata;
2430
2431 if (!(e = pa_alsa_element_get(p, state->section, true))) {
2432 pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section);
2433 return -1;
2434 }
2435
2436 if ((yes = pa_parse_boolean(state->rvalue)) < 0) {
2437 pa_log("[%s:%u] Direction invalid of '%s'", state->filename, state->lineno, state->section);
2438 return -1;
2439 }
2440
2441 e->direction_try_other = !!yes;
2442 return 0;
2443 }
2444
element_parse_volume_limit(pa_config_parser_state * state)2445 static int element_parse_volume_limit(pa_config_parser_state *state) {
2446 pa_alsa_path *p;
2447 pa_alsa_element *e;
2448 long volume_limit;
2449
2450 pa_assert(state);
2451
2452 p = state->userdata;
2453
2454 if (!(e = pa_alsa_element_get(p, state->section, true))) {
2455 pa_log("[%s:%u] volume-limit makes no sense in '%s'", state->filename, state->lineno, state->section);
2456 return -1;
2457 }
2458
2459 if (pa_atol(state->rvalue, &volume_limit) < 0 || volume_limit < 0) {
2460 pa_log("[%s:%u] Invalid value for volume-limit", state->filename, state->lineno);
2461 return -1;
2462 }
2463
2464 e->volume_limit = volume_limit;
2465 return 0;
2466 }
2467
parse_channel_position(const char * m)2468 static unsigned int parse_channel_position(const char *m)
2469 {
2470 pa_channel_position_t p;
2471
2472 if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
2473 return SND_MIXER_SCHN_UNKNOWN;
2474
2475 return alsa_channel_ids[p];
2476 }
2477
parse_mask(const char * m)2478 static pa_channel_position_mask_t parse_mask(const char *m) {
2479 pa_channel_position_mask_t v;
2480
2481 if (pa_streq(m, "all-left"))
2482 v = PA_CHANNEL_POSITION_MASK_LEFT;
2483 else if (pa_streq(m, "all-right"))
2484 v = PA_CHANNEL_POSITION_MASK_RIGHT;
2485 else if (pa_streq(m, "all-center"))
2486 v = PA_CHANNEL_POSITION_MASK_CENTER;
2487 else if (pa_streq(m, "all-front"))
2488 v = PA_CHANNEL_POSITION_MASK_FRONT;
2489 else if (pa_streq(m, "all-rear"))
2490 v = PA_CHANNEL_POSITION_MASK_REAR;
2491 else if (pa_streq(m, "all-side"))
2492 v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER;
2493 else if (pa_streq(m, "all-top"))
2494 v = PA_CHANNEL_POSITION_MASK_TOP;
2495 else if (pa_streq(m, "all-no-lfe"))
2496 v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE);
2497 else if (pa_streq(m, "all"))
2498 v = PA_CHANNEL_POSITION_MASK_ALL;
2499 else {
2500 pa_channel_position_t p;
2501
2502 if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
2503 return 0;
2504
2505 v = PA_CHANNEL_POSITION_MASK(p);
2506 }
2507
2508 return v;
2509 }
2510
element_parse_override_map(pa_config_parser_state * state)2511 static int element_parse_override_map(pa_config_parser_state *state) {
2512 pa_alsa_path *p;
2513 pa_alsa_element *e;
2514 const char *split_state = NULL;
2515 char *s;
2516 unsigned i = 0;
2517 int channel_count = 0;
2518 char *n;
2519
2520 pa_assert(state);
2521
2522 p = state->userdata;
2523
2524 if (!(e = pa_alsa_element_get(p, state->section, true))) {
2525 pa_log("[%s:%u] Override map makes no sense in '%s'", state->filename, state->lineno, state->section);
2526 return -1;
2527 }
2528
2529 s = strstr(state->lvalue, ".");
2530 if (s) {
2531 pa_atoi(s + 1, &channel_count);
2532 if (channel_count < 1 || channel_count > POSITION_MASK_CHANNELS) {
2533 pa_log("[%s:%u] Override map index '%s' invalid in '%s'", state->filename, state->lineno, state->lvalue, state->section);
2534 return 0;
2535 }
2536 } else {
2537 pa_log("[%s:%u] Invalid override map syntax '%s' in '%s'", state->filename, state->lineno, state->lvalue, state->section);
2538 return -1;
2539 }
2540
2541 while ((n = pa_split(state->rvalue, ",", &split_state))) {
2542 pa_channel_position_mask_t m;
2543 snd_mixer_selem_channel_id_t channel_position;
2544
2545 if (i >= (unsigned)channel_count) {
2546 pa_log("[%s:%u] Invalid override map size (>%d) in '%s'", state->filename, state->lineno, channel_count, state->section);
2547 return -1;
2548 }
2549 channel_position = alsa_channel_positions[i];
2550
2551 if (!*n)
2552 m = 0;
2553 else {
2554 s = strstr(n, ":");
2555 if (s) {
2556 *s = '\0';
2557 s++;
2558 channel_position = parse_channel_position(n);
2559 if (channel_position == SND_MIXER_SCHN_UNKNOWN) {
2560 pa_log("[%s:%u] Override map position '%s' invalid in '%s'", state->filename, state->lineno, n, state->section);
2561 pa_xfree(n);
2562 return -1;
2563 }
2564 }
2565 if ((m = parse_mask(s ? s : n)) == 0) {
2566 pa_log("[%s:%u] Override map '%s' invalid in '%s'", state->filename, state->lineno, s ? s : n, state->section);
2567 pa_xfree(n);
2568 return -1;
2569 }
2570 }
2571
2572 if (e->masks[channel_position][channel_count-1]) {
2573 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);
2574 pa_xfree(n);
2575 return -1;
2576 }
2577 e->override_map |= (1 << (channel_count - 1));
2578 e->masks[channel_position][channel_count-1] = m;
2579 pa_xfree(n);
2580 i++;
2581 }
2582
2583 return 0;
2584 }
2585
jack_parse_state(pa_config_parser_state * state)2586 static int jack_parse_state(pa_config_parser_state *state) {
2587 pa_alsa_path *p;
2588 pa_alsa_jack *j;
2589 pa_available_t pa;
2590
2591 pa_assert(state);
2592
2593 p = state->userdata;
2594
2595 if (!(j = jack_get(p, state->section))) {
2596 pa_log("[%s:%u] state makes no sense in '%s'", state->filename, state->lineno, state->section);
2597 return -1;
2598 }
2599
2600 if (pa_streq(state->rvalue, "yes"))
2601 pa = PA_AVAILABLE_YES;
2602 else if (pa_streq(state->rvalue, "no"))
2603 pa = PA_AVAILABLE_NO;
2604 else if (pa_streq(state->rvalue, "unknown"))
2605 pa = PA_AVAILABLE_UNKNOWN;
2606 else {
2607 pa_log("[%s:%u] state must be 'yes', 'no' or 'unknown' in '%s'", state->filename, state->lineno, state->section);
2608 return -1;
2609 }
2610
2611 if (pa_streq(state->lvalue, "state.unplugged"))
2612 j->state_unplugged = pa;
2613 else {
2614 j->state_plugged = pa;
2615 pa_assert(pa_streq(state->lvalue, "state.plugged"));
2616 }
2617
2618 return 0;
2619 }
2620
jack_parse_append_pcm_to_name(pa_config_parser_state * state)2621 static int jack_parse_append_pcm_to_name(pa_config_parser_state *state) {
2622 pa_alsa_path *path;
2623 pa_alsa_jack *jack;
2624 int b;
2625
2626 pa_assert(state);
2627
2628 path = state->userdata;
2629 if (!(jack = jack_get(path, state->section))) {
2630 pa_log("[%s:%u] Option 'append_pcm_to_name' not expected in section '%s'",
2631 state->filename, state->lineno, state->section);
2632 return -1;
2633 }
2634
2635 b = pa_parse_boolean(state->rvalue);
2636 if (b < 0) {
2637 pa_log("[%s:%u] Invalid value for 'append_pcm_to_name': %s", state->filename, state->lineno, state->rvalue);
2638 return -1;
2639 }
2640
2641 jack->append_pcm_to_name = b;
2642 return 0;
2643 }
2644
element_set_option(pa_alsa_element * e,snd_mixer_t * m,int alsa_idx)2645 static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
2646 snd_mixer_selem_id_t *sid;
2647 snd_mixer_elem_t *me;
2648 char buf[64];
2649 int r;
2650
2651 pa_assert(e);
2652 pa_assert(m);
2653
2654 SELEM_INIT(sid, &e->alsa_id);
2655 if (!(me = snd_mixer_find_selem(m, sid))) {
2656 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
2657 pa_log_warn("Element %s seems to have disappeared.", buf);
2658 return -1;
2659 }
2660
2661 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
2662
2663 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
2664 r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
2665 else
2666 r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
2667
2668 if (r < 0) {
2669 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
2670 pa_log_warn("Failed to set switch of %s: %s", buf, pa_alsa_strerror(errno));
2671 }
2672
2673 } else {
2674 pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
2675
2676 if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0) {
2677 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
2678 pa_log_warn("Failed to set enumeration of %s: %s", buf, pa_alsa_strerror(errno));
2679 }
2680 }
2681
2682 return r;
2683 }
2684
setting_select(pa_alsa_setting * s,snd_mixer_t * m)2685 static int setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
2686 pa_alsa_option *o;
2687 uint32_t idx;
2688
2689 pa_assert(s);
2690 pa_assert(m);
2691
2692 PA_IDXSET_FOREACH(o, s->options, idx)
2693 element_set_option(o->element, m, o->alsa_idx);
2694
2695 return 0;
2696 }
2697
option_verify(pa_alsa_option * o)2698 static int option_verify(pa_alsa_option *o) {
2699 static const struct description_map well_known_descriptions[] = {
2700 { "input", N_("Input") },
2701 { "input-docking", N_("Docking Station Input") },
2702 { "input-docking-microphone", N_("Docking Station Microphone") },
2703 { "input-docking-linein", N_("Docking Station Line In") },
2704 { "input-linein", N_("Line In") },
2705 { "input-microphone", N_("Microphone") },
2706 { "input-microphone-front", N_("Front Microphone") },
2707 { "input-microphone-rear", N_("Rear Microphone") },
2708 { "input-microphone-external", N_("External Microphone") },
2709 { "input-microphone-internal", N_("Internal Microphone") },
2710 { "input-radio", N_("Radio") },
2711 { "input-video", N_("Video") },
2712 { "input-agc-on", N_("Automatic Gain Control") },
2713 { "input-agc-off", N_("No Automatic Gain Control") },
2714 { "input-boost-on", N_("Boost") },
2715 { "input-boost-off", N_("No Boost") },
2716 { "output-amplifier-on", N_("Amplifier") },
2717 { "output-amplifier-off", N_("No Amplifier") },
2718 { "output-bass-boost-on", N_("Bass Boost") },
2719 { "output-bass-boost-off", N_("No Bass Boost") },
2720 { "output-speaker", N_("Speaker") },
2721 { "output-headphones", N_("Headphones") }
2722 };
2723 char buf[64];
2724
2725 pa_assert(o);
2726
2727 if (!o->name) {
2728 pa_log("No name set for option %s", o->alsa_name);
2729 return -1;
2730 }
2731
2732 if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
2733 o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
2734 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &o->element->alsa_id);
2735 pa_log("Element %s of option %s not set for select.", buf, o->name);
2736 return -1;
2737 }
2738
2739 if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
2740 !pa_streq(o->alsa_name, "on") &&
2741 !pa_streq(o->alsa_name, "off")) {
2742 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &o->element->alsa_id);
2743 pa_log("Switch %s options need be named off or on ", buf);
2744 return -1;
2745 }
2746
2747 if (!o->description)
2748 o->description = pa_xstrdup(lookup_description(o->name,
2749 well_known_descriptions,
2750 PA_ELEMENTSOF(well_known_descriptions)));
2751 if (!o->description)
2752 o->description = pa_xstrdup(o->name);
2753
2754 return 0;
2755 }
2756
element_verify(pa_alsa_element * e)2757 static int element_verify(pa_alsa_element *e) {
2758 pa_alsa_option *o;
2759 char buf[64];
2760
2761 pa_assert(e);
2762
2763 // 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);
2764 if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
2765 (e->required_any != PA_ALSA_REQUIRED_IGNORE && e->required_any == e->required_absent) ||
2766 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required_any != PA_ALSA_REQUIRED_IGNORE) ||
2767 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
2768 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
2769 pa_log("Element %s cannot be required and absent at the same time.", buf);
2770 return -1;
2771 }
2772
2773 if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
2774 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
2775 pa_log("Element %s cannot set select for both switch and enumeration.", buf);
2776 return -1;
2777 }
2778
2779 PA_LLIST_FOREACH(o, e->options)
2780 if (option_verify(o) < 0)
2781 return -1;
2782
2783 return 0;
2784 }
2785
path_verify(pa_alsa_path * p)2786 static int path_verify(pa_alsa_path *p) {
2787 static const struct description2_map well_known_descriptions[] = {
2788 { "analog-input", N_("Analog Input"), PA_DEVICE_PORT_TYPE_ANALOG },
2789 { "analog-input-microphone", N_("Microphone"), PA_DEVICE_PORT_TYPE_MIC },
2790 { "analog-input-microphone-front", N_("Front Microphone"), PA_DEVICE_PORT_TYPE_MIC },
2791 { "analog-input-microphone-rear", N_("Rear Microphone"), PA_DEVICE_PORT_TYPE_MIC },
2792 { "analog-input-microphone-dock", N_("Dock Microphone"), PA_DEVICE_PORT_TYPE_MIC },
2793 { "analog-input-microphone-internal", N_("Internal Microphone"), PA_DEVICE_PORT_TYPE_MIC },
2794 { "analog-input-microphone-headset", N_("Headset Microphone"), PA_DEVICE_PORT_TYPE_HEADSET },
2795 { "analog-input-linein", N_("Line In"), PA_DEVICE_PORT_TYPE_LINE },
2796 { "analog-input-radio", N_("Radio"), PA_DEVICE_PORT_TYPE_RADIO },
2797 { "analog-input-video", N_("Video"), PA_DEVICE_PORT_TYPE_VIDEO },
2798 { "analog-output", N_("Analog Output"), PA_DEVICE_PORT_TYPE_ANALOG },
2799 { "analog-output-headphones", N_("Headphones"), PA_DEVICE_PORT_TYPE_HEADPHONES },
2800 { "analog-output-headphones-2", N_("Headphones 2"), PA_DEVICE_PORT_TYPE_HEADPHONES },
2801 { "analog-output-headphones-mono", N_("Headphones Mono Output"), PA_DEVICE_PORT_TYPE_HEADPHONES },
2802 { "analog-output-lineout", N_("Line Out"), PA_DEVICE_PORT_TYPE_LINE },
2803 { "analog-output-mono", N_("Analog Mono Output"), PA_DEVICE_PORT_TYPE_ANALOG },
2804 { "analog-output-speaker", N_("Speakers"), PA_DEVICE_PORT_TYPE_SPEAKER },
2805 { "hdmi-output", N_("HDMI / DisplayPort"), PA_DEVICE_PORT_TYPE_HDMI },
2806 { "iec958-stereo-output", N_("Digital Output (S/PDIF)"), PA_DEVICE_PORT_TYPE_SPDIF },
2807 { "iec958-stereo-input", N_("Digital Input (S/PDIF)"), PA_DEVICE_PORT_TYPE_SPDIF },
2808 { "multichannel-input", N_("Multichannel Input"), PA_DEVICE_PORT_TYPE_LINE },
2809 { "multichannel-output", N_("Multichannel Output"), PA_DEVICE_PORT_TYPE_LINE },
2810 { "steelseries-arctis-output-game-common", N_("Game Output"), PA_DEVICE_PORT_TYPE_HEADSET },
2811 { "steelseries-arctis-output-chat-common", N_("Chat Output"), PA_DEVICE_PORT_TYPE_HEADSET },
2812 };
2813
2814 pa_alsa_element *e;
2815 const char *key = p->description_key ? p->description_key : p->name;
2816 const struct description2_map *map = lookup_description2(key,
2817 well_known_descriptions,
2818 PA_ELEMENTSOF(well_known_descriptions));
2819
2820 pa_assert(p);
2821
2822 PA_LLIST_FOREACH(e, p->elements)
2823 if (element_verify(e) < 0)
2824 return -1;
2825
2826 if (map) {
2827 if (p->device_port_type == PA_DEVICE_PORT_TYPE_UNKNOWN)
2828 p->device_port_type = map->type;
2829 if (!p->description)
2830 p->description = pa_xstrdup(map->description);
2831 }
2832
2833 if (!p->description) {
2834 if (p->description_key)
2835 pa_log_warn("Path %s: Unrecognized description key: %s", p->name, p->description_key);
2836
2837 p->description = pa_xstrdup(p->name);
2838 }
2839
2840 return 0;
2841 }
2842
get_default_paths_dir(void)2843 static const char *get_default_paths_dir(void) {
2844 #ifdef HAVE_RUNNING_FROM_BUILD_TREE
2845 if (pa_run_from_build_tree())
2846 return PA_SRCDIR "/modules/alsa/mixer/paths/";
2847 else
2848 #endif
2849 return PA_ALSA_PATHS_DIR;
2850 }
2851
pa_alsa_path_new(const char * paths_dir,const char * fname,pa_alsa_direction_t direction)2852 pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa_direction_t direction) {
2853 pa_alsa_path *p;
2854 char *fn;
2855 int r;
2856 const char *n;
2857 bool mute_during_activation = false;
2858
2859 pa_config_item items[] = {
2860 /* [General] */
2861 { "priority", pa_config_parse_unsigned, NULL, "General" },
2862 { "description-key", pa_config_parse_string, NULL, "General" },
2863 { "description", pa_config_parse_string, NULL, "General" },
2864 { "mute-during-activation", pa_config_parse_bool, NULL, "General" },
2865 { "type", parse_type, NULL, "General" },
2866 { "eld-device", parse_eld_device, NULL, "General" },
2867
2868 /* [Option ...] */
2869 { "priority", option_parse_priority, NULL, NULL },
2870 { "name", option_parse_name, NULL, NULL },
2871
2872 /* [Jack ...] */
2873 { "state.plugged", jack_parse_state, NULL, NULL },
2874 { "state.unplugged", jack_parse_state, NULL, NULL },
2875 { "append-pcm-to-name", jack_parse_append_pcm_to_name, NULL, NULL },
2876
2877 /* [Element ...] */
2878 { "switch", element_parse_switch, NULL, NULL },
2879 { "volume", element_parse_volume, NULL, NULL },
2880 { "enumeration", element_parse_enumeration, NULL, NULL },
2881 { "override-map.1", element_parse_override_map, NULL, NULL },
2882 { "override-map.2", element_parse_override_map, NULL, NULL },
2883 { "override-map.3", element_parse_override_map, NULL, NULL },
2884 { "override-map.4", element_parse_override_map, NULL, NULL },
2885 { "override-map.5", element_parse_override_map, NULL, NULL },
2886 { "override-map.6", element_parse_override_map, NULL, NULL },
2887 { "override-map.7", element_parse_override_map, NULL, NULL },
2888 { "override-map.8", element_parse_override_map, NULL, NULL },
2889 #if POSITION_MASK_CHANNELS > 8
2890 #error "Add override-map.9+ definitions"
2891 #endif
2892 /* ... later on we might add override-map.3 and so on here ... */
2893 { "required", element_parse_required, NULL, NULL },
2894 { "required-any", element_parse_required, NULL, NULL },
2895 { "required-absent", element_parse_required, NULL, NULL },
2896 { "direction", element_parse_direction, NULL, NULL },
2897 { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
2898 { "volume-limit", element_parse_volume_limit, NULL, NULL },
2899 { NULL, NULL, NULL, NULL }
2900 };
2901
2902 pa_assert(fname);
2903
2904 p = pa_xnew0(pa_alsa_path, 1);
2905 n = pa_path_get_filename(fname);
2906 p->name = pa_xstrndup(n, strcspn(n, "."));
2907 p->proplist = pa_proplist_new();
2908 p->direction = direction;
2909 p->eld_device = -1;
2910
2911 items[0].data = &p->priority;
2912 items[1].data = &p->description_key;
2913 items[2].data = &p->description;
2914 items[3].data = &mute_during_activation;
2915
2916 if (!paths_dir)
2917 paths_dir = get_default_paths_dir();
2918
2919 fn = pa_maybe_prefix_path(fname, paths_dir);
2920
2921 r = pa_config_parse(fn, NULL, items, p->proplist, false, p);
2922 pa_xfree(fn);
2923
2924 if (r < 0)
2925 goto fail;
2926
2927 p->mute_during_activation = mute_during_activation;
2928
2929 if (path_verify(p) < 0)
2930 goto fail;
2931
2932 return p;
2933
2934 fail:
2935 pa_alsa_path_free(p);
2936 return NULL;
2937 }
2938
pa_alsa_path_synthesize(const char * element,pa_alsa_direction_t direction)2939 pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction) {
2940 pa_alsa_path *p;
2941 pa_alsa_element *e;
2942 char *name;
2943 int index;
2944
2945 pa_assert(element);
2946
2947 name = alloca(strlen(element) + 1);
2948 if (alsa_id_decode(element, name, &index))
2949 return NULL;
2950
2951 p = pa_xnew0(pa_alsa_path, 1);
2952 p->name = pa_xstrdup(element);
2953 p->direction = direction;
2954 p->proplist = pa_proplist_new();
2955
2956 e = pa_xnew0(pa_alsa_element, 1);
2957 e->path = p;
2958 e->alsa_id.name = pa_xstrdup(name);
2959 e->alsa_id.index = index;
2960 e->direction = direction;
2961 e->volume_limit = -1;
2962
2963 e->switch_use = PA_ALSA_SWITCH_MUTE;
2964 e->volume_use = PA_ALSA_VOLUME_MERGE;
2965
2966 PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
2967 p->last_element = e;
2968 return p;
2969 }
2970
element_drop_unsupported(pa_alsa_element * e)2971 static bool element_drop_unsupported(pa_alsa_element *e) {
2972 pa_alsa_option *o, *n;
2973
2974 pa_assert(e);
2975
2976 for (o = e->options; o; o = n) {
2977 n = o->next;
2978
2979 if (o->alsa_idx < 0) {
2980 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
2981 option_free(o);
2982 }
2983 }
2984
2985 return
2986 e->switch_use != PA_ALSA_SWITCH_IGNORE ||
2987 e->volume_use != PA_ALSA_VOLUME_IGNORE ||
2988 e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
2989 }
2990
path_drop_unsupported(pa_alsa_path * p)2991 static void path_drop_unsupported(pa_alsa_path *p) {
2992 pa_alsa_element *e, *n;
2993
2994 pa_assert(p);
2995
2996 for (e = p->elements; e; e = n) {
2997 n = e->next;
2998
2999 if (!element_drop_unsupported(e)) {
3000 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
3001 element_free(e);
3002 }
3003 }
3004 }
3005
path_make_options_unique(pa_alsa_path * p)3006 static void path_make_options_unique(pa_alsa_path *p) {
3007 pa_alsa_element *e;
3008 pa_alsa_option *o, *u;
3009
3010 PA_LLIST_FOREACH(e, p->elements) {
3011 PA_LLIST_FOREACH(o, e->options) {
3012 unsigned i;
3013 char *m;
3014
3015 for (u = o->next; u; u = u->next)
3016 if (pa_streq(u->name, o->name))
3017 break;
3018
3019 if (!u)
3020 continue;
3021
3022 m = pa_xstrdup(o->name);
3023
3024 /* OK, this name is not unique, hence let's rename */
3025 for (i = 1, u = o; u; u = u->next) {
3026 char *nn, *nd;
3027
3028 if (!pa_streq(u->name, m))
3029 continue;
3030
3031 nn = pa_sprintf_malloc("%s-%u", m, i);
3032 pa_xfree(u->name);
3033 u->name = nn;
3034
3035 nd = pa_sprintf_malloc("%s %u", u->description, i);
3036 pa_xfree(u->description);
3037 u->description = nd;
3038
3039 i++;
3040 }
3041
3042 pa_xfree(m);
3043 }
3044 }
3045 }
3046
element_create_settings(pa_alsa_element * e,pa_alsa_setting * template)3047 static bool element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
3048 pa_alsa_option *o;
3049
3050 for (; e; e = e->next)
3051 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
3052 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
3053 break;
3054
3055 if (!e)
3056 return false;
3057
3058 for (o = e->options; o; o = o->next) {
3059 pa_alsa_setting *s;
3060
3061 if (template) {
3062 s = pa_xnewdup(pa_alsa_setting, template, 1);
3063 s->options = pa_idxset_copy(template->options, NULL);
3064 s->name = pa_sprintf_malloc("%s+%s", template->name, o->name);
3065 s->description =
3066 (template->description[0] && o->description[0])
3067 ? pa_sprintf_malloc("%s / %s", template->description, o->description)
3068 : (template->description[0]
3069 ? pa_xstrdup(template->description)
3070 : pa_xstrdup(o->description));
3071
3072 s->priority = PA_MAX(template->priority, o->priority);
3073 } else {
3074 s = pa_xnew0(pa_alsa_setting, 1);
3075 s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3076 s->name = pa_xstrdup(o->name);
3077 s->description = pa_xstrdup(o->description);
3078 s->priority = o->priority;
3079 }
3080
3081 pa_idxset_put(s->options, o, NULL);
3082
3083 if (element_create_settings(e->next, s))
3084 /* This is not a leaf, so let's get rid of it */
3085 setting_free(s);
3086 else {
3087 /* This is a leaf, so let's add it */
3088 PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
3089
3090 e->path->last_setting = s;
3091 }
3092 }
3093
3094 return true;
3095 }
3096
path_create_settings(pa_alsa_path * p)3097 static void path_create_settings(pa_alsa_path *p) {
3098 pa_assert(p);
3099
3100 element_create_settings(p->elements, NULL);
3101 }
3102
pa_alsa_path_probe(pa_alsa_path * p,pa_alsa_mapping * mapping,snd_mixer_t * m,bool ignore_dB)3103 int pa_alsa_path_probe(pa_alsa_path *p, pa_alsa_mapping *mapping, snd_mixer_t *m, bool ignore_dB) {
3104 pa_alsa_element *e;
3105 pa_alsa_jack *j;
3106 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
3107 pa_channel_position_t t;
3108 pa_channel_position_mask_t path_volume_channels = 0;
3109 bool min_dB_set, max_dB_set;
3110 char buf[64];
3111
3112 pa_assert(p);
3113 pa_assert(m);
3114
3115 if (p->probed)
3116 return p->supported ? 0 : -1;
3117 p->probed = true;
3118
3119 pa_zero(min_dB);
3120 pa_zero(max_dB);
3121
3122 pa_log_debug("Probing path '%s'", p->name);
3123
3124 PA_LLIST_FOREACH(j, p->jacks) {
3125 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &j->alsa_id);
3126 if (jack_probe(j, mapping, m) < 0) {
3127 p->supported = false;
3128 pa_log_debug("Probe of jack %s failed.", buf);
3129 return -1;
3130 }
3131 pa_log_debug("Probe of jack %s succeeded (%s)", buf, j->has_control ? "found!" : "not found");
3132 }
3133
3134 PA_LLIST_FOREACH(e, p->elements) {
3135 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
3136 if (element_probe(e, m) < 0) {
3137 p->supported = false;
3138 pa_log_debug("Probe of element %s failed.", buf);
3139 return -1;
3140 }
3141 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);
3142
3143 if (ignore_dB)
3144 e->has_dB = false;
3145
3146 if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
3147
3148 if (!p->has_volume) {
3149 p->min_volume = e->min_volume;
3150 p->max_volume = e->max_volume;
3151 }
3152
3153 if (e->has_dB) {
3154 if (!p->has_volume) {
3155 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
3156 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
3157 min_dB[t] = e->min_dB;
3158 max_dB[t] = e->max_dB;
3159 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
3160 }
3161
3162 p->has_dB = true;
3163 } else {
3164
3165 if (p->has_dB) {
3166 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
3167 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
3168 min_dB[t] += e->min_dB;
3169 max_dB[t] += e->max_dB;
3170 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
3171 }
3172 } else {
3173 /* Hmm, there's another element before us
3174 * which cannot do dB volumes, so we we need
3175 * to 'neutralize' this slider */
3176 e->volume_use = PA_ALSA_VOLUME_ZERO;
3177 pa_log_info("Zeroing volume of %s on path '%s'", buf, p->name);
3178 }
3179 }
3180 } else if (p->has_volume) {
3181 /* We can't use this volume, so let's ignore it */
3182 e->volume_use = PA_ALSA_VOLUME_IGNORE;
3183 pa_log_info("Ignoring volume of %s on path '%s' (missing dB info)", buf, p->name);
3184 }
3185 p->has_volume = true;
3186 }
3187
3188 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
3189 p->has_mute = true;
3190 }
3191
3192 if (p->has_req_any && !p->req_any_present) {
3193 p->supported = false;
3194 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name);
3195 return -1;
3196 }
3197
3198 path_drop_unsupported(p);
3199 path_make_options_unique(p);
3200 path_create_settings(p);
3201
3202 p->supported = true;
3203
3204 p->min_dB = INFINITY;
3205 min_dB_set = false;
3206 p->max_dB = -INFINITY;
3207 max_dB_set = false;
3208
3209 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
3210 if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) {
3211 if (p->min_dB > min_dB[t]) {
3212 p->min_dB = min_dB[t];
3213 min_dB_set = true;
3214 }
3215
3216 if (p->max_dB < max_dB[t]) {
3217 p->max_dB = max_dB[t];
3218 max_dB_set = true;
3219 }
3220 }
3221 }
3222
3223 /* this is probably a wrong prediction, but it should be safe */
3224 if (!min_dB_set)
3225 p->min_dB = -INFINITY;
3226 if (!max_dB_set)
3227 p->max_dB = 0;
3228
3229 return 0;
3230 }
3231
pa_alsa_setting_dump(pa_alsa_setting * s)3232 void pa_alsa_setting_dump(pa_alsa_setting *s) {
3233 pa_assert(s);
3234
3235 pa_log_debug("Setting %s (%s) priority=%u",
3236 s->name,
3237 pa_strnull(s->description),
3238 s->priority);
3239 }
3240
pa_alsa_jack_dump(pa_alsa_jack * j)3241 void pa_alsa_jack_dump(pa_alsa_jack *j) {
3242 pa_assert(j);
3243
3244 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");
3245 }
3246
pa_alsa_option_dump(pa_alsa_option * o)3247 void pa_alsa_option_dump(pa_alsa_option *o) {
3248 pa_assert(o);
3249
3250 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
3251 o->alsa_name,
3252 pa_strnull(o->name),
3253 pa_strnull(o->description),
3254 o->alsa_idx,
3255 o->priority);
3256 }
3257
pa_alsa_element_dump(pa_alsa_element * e)3258 void pa_alsa_element_dump(pa_alsa_element *e) {
3259 char buf[64];
3260
3261 pa_alsa_option *o;
3262 pa_assert(e);
3263
3264 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
3265 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",
3266 buf,
3267 e->direction,
3268 e->switch_use,
3269 e->volume_use,
3270 e->volume_limit,
3271 e->enumeration_use,
3272 e->required,
3273 e->required_any,
3274 e->required_absent,
3275 (long long unsigned) e->merged_mask,
3276 e->n_channels,
3277 e->override_map);
3278
3279 PA_LLIST_FOREACH(o, e->options)
3280 pa_alsa_option_dump(o);
3281 }
3282
pa_alsa_path_dump(pa_alsa_path * p)3283 void pa_alsa_path_dump(pa_alsa_path *p) {
3284 pa_alsa_element *e;
3285 pa_alsa_jack *j;
3286 pa_alsa_setting *s;
3287 pa_assert(p);
3288
3289 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
3290 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
3291 p->name,
3292 pa_strnull(p->description),
3293 p->direction,
3294 p->priority,
3295 pa_yes_no(p->probed),
3296 pa_yes_no(p->supported),
3297 pa_yes_no(p->has_mute),
3298 pa_yes_no(p->has_volume),
3299 pa_yes_no(p->has_dB),
3300 p->min_volume, p->max_volume,
3301 p->min_dB, p->max_dB);
3302
3303 PA_LLIST_FOREACH(e, p->elements)
3304 pa_alsa_element_dump(e);
3305
3306 PA_LLIST_FOREACH(j, p->jacks)
3307 pa_alsa_jack_dump(j);
3308
3309 PA_LLIST_FOREACH(s, p->settings)
3310 pa_alsa_setting_dump(s);
3311 }
3312
element_set_callback(pa_alsa_element * e,snd_mixer_t * m,snd_mixer_elem_callback_t cb,void * userdata)3313 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
3314 snd_mixer_selem_id_t *sid;
3315 snd_mixer_elem_t *me;
3316 char buf[64];
3317
3318 pa_assert(e);
3319 pa_assert(m);
3320 pa_assert(cb);
3321
3322 SELEM_INIT(sid, &e->alsa_id);
3323 if (!(me = snd_mixer_find_selem(m, sid))) {
3324 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
3325 pa_log_warn("Element %s seems to have disappeared.", buf);
3326 return;
3327 }
3328
3329 snd_mixer_elem_set_callback(me, cb);
3330 snd_mixer_elem_set_callback_private(me, userdata);
3331 }
3332
pa_alsa_path_set_callback(pa_alsa_path * p,snd_mixer_t * m,snd_mixer_elem_callback_t cb,void * userdata)3333 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
3334 pa_alsa_element *e;
3335
3336 pa_assert(p);
3337 pa_assert(m);
3338 pa_assert(cb);
3339
3340 PA_LLIST_FOREACH(e, p->elements)
3341 element_set_callback(e, m, cb, userdata);
3342 }
3343
pa_alsa_path_set_set_callback(pa_alsa_path_set * ps,snd_mixer_t * m,snd_mixer_elem_callback_t cb,void * userdata)3344 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
3345 pa_alsa_path *p;
3346 void *state;
3347
3348 pa_assert(ps);
3349 pa_assert(m);
3350 pa_assert(cb);
3351
3352 PA_HASHMAP_FOREACH(p, ps->paths, state)
3353 pa_alsa_path_set_callback(p, m, cb, userdata);
3354 }
3355
profile_set_get_path(pa_alsa_profile_set * ps,const char * path_name)3356 static pa_alsa_path *profile_set_get_path(pa_alsa_profile_set *ps, const char *path_name) {
3357 pa_alsa_path *path;
3358
3359 pa_assert(ps);
3360 pa_assert(path_name);
3361
3362 if ((path = pa_hashmap_get(ps->output_paths, path_name)))
3363 return path;
3364
3365 return pa_hashmap_get(ps->input_paths, path_name);
3366 }
3367
profile_set_add_path(pa_alsa_profile_set * ps,pa_alsa_path * path)3368 static void profile_set_add_path(pa_alsa_profile_set *ps, pa_alsa_path *path) {
3369 pa_assert(ps);
3370 pa_assert(path);
3371
3372 switch (path->direction) {
3373 case PA_ALSA_DIRECTION_OUTPUT:
3374 pa_assert_se(pa_hashmap_put(ps->output_paths, path->name, path) >= 0);
3375 break;
3376
3377 case PA_ALSA_DIRECTION_INPUT:
3378 pa_assert_se(pa_hashmap_put(ps->input_paths, path->name, path) >= 0);
3379 break;
3380
3381 default:
3382 pa_assert_not_reached();
3383 }
3384 }
3385
pa_alsa_path_set_new(pa_alsa_mapping * m,pa_alsa_direction_t direction,const char * paths_dir)3386 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction, const char *paths_dir) {
3387 pa_alsa_path_set *ps;
3388 char **pn = NULL, **en = NULL, **ie;
3389 pa_alsa_decibel_fix *db_fix;
3390 void *state, *state2;
3391 char name[64];
3392 int index;
3393
3394 pa_assert(m);
3395 pa_assert(m->profile_set);
3396 pa_assert(m->profile_set->decibel_fixes);
3397 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
3398
3399 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
3400 return NULL;
3401
3402 ps = pa_xnew0(pa_alsa_path_set, 1);
3403 ps->direction = direction;
3404 ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3405
3406 if (direction == PA_ALSA_DIRECTION_OUTPUT)
3407 pn = m->output_path_names;
3408 else
3409 pn = m->input_path_names;
3410
3411 if (pn) {
3412 char **in;
3413
3414 for (in = pn; *in; in++) {
3415 pa_alsa_path *p = NULL;
3416 bool duplicate = false;
3417 char **kn;
3418
3419 for (kn = pn; kn < in; kn++)
3420 if (pa_streq(*kn, *in)) {
3421 duplicate = true;
3422 break;
3423 }
3424
3425 if (duplicate)
3426 continue;
3427
3428 p = profile_set_get_path(m->profile_set, *in);
3429
3430 if (p && p->direction != direction) {
3431 pa_log("Configuration error: Path %s is used both as an input and as an output path.", p->name);
3432 goto fail;
3433 }
3434
3435 if (!p) {
3436 char *fn = pa_sprintf_malloc("%s.conf", *in);
3437 p = pa_alsa_path_new(paths_dir, fn, direction);
3438 pa_xfree(fn);
3439 if (p)
3440 profile_set_add_path(m->profile_set, p);
3441 }
3442
3443 if (p)
3444 pa_hashmap_put(ps->paths, p, p);
3445
3446 }
3447
3448 goto finish;
3449 }
3450
3451 if (direction == PA_ALSA_DIRECTION_OUTPUT)
3452 en = m->output_element;
3453 else
3454 en = m->input_element;
3455
3456 if (!en)
3457 goto fail;
3458
3459 for (ie = en; *ie; ie++) {
3460 char **je;
3461 pa_alsa_path *p;
3462
3463 p = pa_alsa_path_synthesize(*ie, direction);
3464
3465 /* Mark all other passed elements for require-absent */
3466 for (je = en; *je; je++) {
3467 pa_alsa_element *e;
3468
3469 if (je == ie)
3470 continue;
3471
3472 if (strlen(*je) + 1 >= sizeof(name)) {
3473 pa_log("Element identifier %s is too long!", *je);
3474 continue;
3475 }
3476
3477 if (alsa_id_decode(*je, name, &index))
3478 continue;
3479
3480 e = pa_xnew0(pa_alsa_element, 1);
3481 e->path = p;
3482 e->alsa_id.name = pa_xstrdup(name);
3483 e->alsa_id.index = index;
3484 e->direction = direction;
3485 e->required_absent = PA_ALSA_REQUIRED_ANY;
3486 e->volume_limit = -1;
3487
3488 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
3489 p->last_element = e;
3490 }
3491
3492 pa_hashmap_put(ps->paths, *ie, p);
3493 }
3494
3495 finish:
3496 /* Assign decibel fixes to elements. */
3497 PA_HASHMAP_FOREACH(db_fix, m->profile_set->decibel_fixes, state) {
3498 pa_alsa_path *p;
3499
3500 PA_HASHMAP_FOREACH(p, ps->paths, state2) {
3501 pa_alsa_element *e;
3502
3503 PA_LLIST_FOREACH(e, p->elements) {
3504 if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_id.name) &&
3505 db_fix->index == e->alsa_id.index) {
3506 /* The profile set that contains the dB fix may be freed
3507 * before the element, so we have to copy the dB fix
3508 * object. */
3509 e->db_fix = pa_xnewdup(pa_alsa_decibel_fix, db_fix, 1);
3510 e->db_fix->profile_set = NULL;
3511 e->db_fix->name = pa_xstrdup(db_fix->name);
3512 e->db_fix->db_values = pa_xmemdup(db_fix->db_values, (db_fix->max_step - db_fix->min_step + 1) * sizeof(long));
3513 }
3514 }
3515 }
3516 }
3517
3518 return ps;
3519
3520 fail:
3521 if (ps)
3522 pa_alsa_path_set_free(ps);
3523
3524 return NULL;
3525 }
3526
pa_alsa_path_set_dump(pa_alsa_path_set * ps)3527 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
3528 pa_alsa_path *p;
3529 void *state;
3530 pa_assert(ps);
3531
3532 pa_log_debug("Path Set %p, direction=%i",
3533 (void*) ps,
3534 ps->direction);
3535
3536 PA_HASHMAP_FOREACH(p, ps->paths, state)
3537 pa_alsa_path_dump(p);
3538 }
3539
options_have_option(pa_alsa_option * options,const char * alsa_name)3540 static bool options_have_option(pa_alsa_option *options, const char *alsa_name) {
3541 pa_alsa_option *o;
3542
3543 pa_assert(options);
3544 pa_assert(alsa_name);
3545
3546 PA_LLIST_FOREACH(o, options) {
3547 if (pa_streq(o->alsa_name, alsa_name))
3548 return true;
3549 }
3550 return false;
3551 }
3552
enumeration_is_subset(pa_alsa_option * a_options,pa_alsa_option * b_options)3553 static bool enumeration_is_subset(pa_alsa_option *a_options, pa_alsa_option *b_options) {
3554 pa_alsa_option *oa, *ob;
3555
3556 if (!a_options) return true;
3557 if (!b_options) return false;
3558
3559 /* If there is an option A offers that B does not, then A is not a subset of B. */
3560 PA_LLIST_FOREACH(oa, a_options) {
3561 bool found = false;
3562 PA_LLIST_FOREACH(ob, b_options) {
3563 if (pa_streq(oa->alsa_name, ob->alsa_name)) {
3564 found = true;
3565 break;
3566 }
3567 }
3568 if (!found)
3569 return false;
3570 }
3571 return true;
3572 }
3573
3574 /**
3575 * Compares two elements to see if a is a subset of b
3576 */
element_is_subset(pa_alsa_element * a,pa_alsa_element * b,snd_mixer_t * m)3577 static bool element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_t *m) {
3578 char buf[64];
3579
3580 pa_assert(a);
3581 pa_assert(b);
3582 pa_assert(m);
3583
3584 /* General rules:
3585 * Every state is a subset of itself (with caveats for volume_limits and options)
3586 * IGNORE is a subset of every other state */
3587
3588 /* Check the volume_use */
3589 if (a->volume_use != PA_ALSA_VOLUME_IGNORE) {
3590
3591 /* "Constant" is subset of "Constant" only when their constant values are equal */
3592 if (a->volume_use == PA_ALSA_VOLUME_CONSTANT && b->volume_use == PA_ALSA_VOLUME_CONSTANT && a->constant_volume != b->constant_volume)
3593 return false;
3594
3595 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
3596 if (a->volume_use != b->volume_use && b->volume_use != PA_ALSA_VOLUME_MERGE)
3597 return false;
3598
3599 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
3600 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
3601 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
3602 if (b->volume_use == PA_ALSA_VOLUME_MERGE && b->volume_limit >= 0) {
3603 long a_limit;
3604
3605 if (a->volume_use == PA_ALSA_VOLUME_CONSTANT)
3606 a_limit = a->constant_volume;
3607 else if (a->volume_use == PA_ALSA_VOLUME_ZERO) {
3608 long dB = 0;
3609
3610 if (a->db_fix) {
3611 int rounding = (a->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1);
3612 a_limit = decibel_fix_get_step(a->db_fix, &dB, rounding);
3613 } else {
3614 snd_mixer_selem_id_t *sid;
3615 snd_mixer_elem_t *me;
3616
3617 SELEM_INIT(sid, &a->alsa_id);
3618 if (!(me = snd_mixer_find_selem(m, sid))) {
3619 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &a->alsa_id);
3620 pa_log_warn("Element %s seems to have disappeared.", buf);
3621 return false;
3622 }
3623
3624 if (a->direction == PA_ALSA_DIRECTION_OUTPUT) {
3625 if (snd_mixer_selem_ask_playback_dB_vol(me, dB, +1, &a_limit) < 0)
3626 return false;
3627 } else {
3628 if (snd_mixer_selem_ask_capture_dB_vol(me, dB, -1, &a_limit) < 0)
3629 return false;
3630 }
3631 }
3632 } else if (a->volume_use == PA_ALSA_VOLUME_OFF)
3633 a_limit = a->min_volume;
3634 else if (a->volume_use == PA_ALSA_VOLUME_MERGE)
3635 a_limit = a->volume_limit;
3636 else
3637 pa_assert_not_reached();
3638
3639 if (a_limit > b->volume_limit)
3640 return false;
3641 }
3642
3643 if (a->volume_use == PA_ALSA_VOLUME_MERGE) {
3644 int s;
3645 /* If override-maps are different, they're not subsets */
3646 if (a->n_channels != b->n_channels)
3647 return false;
3648 for (s = 0; s <= SND_MIXER_SCHN_LAST; s++)
3649 if (a->masks[s][a->n_channels-1] != b->masks[s][b->n_channels-1]) {
3650 pa_alsa_mixer_id_to_string(buf, sizeof(buf), &a->alsa_id);
3651 pa_log_debug("Element %s is not a subset - mask a: 0x%" PRIx64 ", mask b: 0x%" PRIx64 ", at channel %d",
3652 buf, a->masks[s][a->n_channels-1], b->masks[s][b->n_channels-1], s);
3653 return false;
3654 }
3655 }
3656 }
3657
3658 if (a->switch_use != PA_ALSA_SWITCH_IGNORE) {
3659 /* "On" is a subset of "Mute".
3660 * "Off" is a subset of "Mute".
3661 * "On" is a subset of "Select", if there is an "Option:On" in B.
3662 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3663 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3664
3665 if (a->switch_use != b->switch_use) {
3666
3667 if (a->switch_use == PA_ALSA_SWITCH_SELECT || a->switch_use == PA_ALSA_SWITCH_MUTE
3668 || b->switch_use == PA_ALSA_SWITCH_OFF || b->switch_use == PA_ALSA_SWITCH_ON)
3669 return false;
3670
3671 if (b->switch_use == PA_ALSA_SWITCH_SELECT) {
3672 if (a->switch_use == PA_ALSA_SWITCH_ON) {
3673 if (!options_have_option(b->options, "on"))
3674 return false;
3675 } else if (a->switch_use == PA_ALSA_SWITCH_OFF) {
3676 if (!options_have_option(b->options, "off"))
3677 return false;
3678 }
3679 }
3680 } else if (a->switch_use == PA_ALSA_SWITCH_SELECT) {
3681 if (!enumeration_is_subset(a->options, b->options))
3682 return false;
3683 }
3684 }
3685
3686 if (a->enumeration_use != PA_ALSA_ENUMERATION_IGNORE) {
3687 if (b->enumeration_use == PA_ALSA_ENUMERATION_IGNORE)
3688 return false;
3689 if (!enumeration_is_subset(a->options, b->options))
3690 return false;
3691 }
3692
3693 return true;
3694 }
3695
path_set_condense(pa_alsa_path_set * ps,snd_mixer_t * m)3696 static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
3697 pa_alsa_path *p;
3698 void *state;
3699
3700 pa_assert(ps);
3701 pa_assert(m);
3702
3703 /* If we only have one path, then don't bother */
3704 if (pa_hashmap_size(ps->paths) < 2)
3705 return;
3706
3707 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3708 pa_alsa_path *p2;
3709 void *state2;
3710
3711 PA_HASHMAP_FOREACH(p2, ps->paths, state2) {
3712 pa_alsa_element *ea, *eb;
3713 pa_alsa_jack *ja, *jb;
3714 bool is_subset = true;
3715
3716 if (p == p2)
3717 continue;
3718
3719 /* If a has a jack that b does not have, a is not a subset */
3720 PA_LLIST_FOREACH(ja, p->jacks) {
3721 bool exists = false;
3722
3723 if (!ja->has_control)
3724 continue;
3725
3726 PA_LLIST_FOREACH(jb, p2->jacks) {
3727 if (jb->has_control && pa_streq(ja->alsa_id.name, jb->alsa_id.name) &&
3728 (ja->alsa_id.index == jb->alsa_id.index) &&
3729 (ja->state_plugged == jb->state_plugged) &&
3730 (ja->state_unplugged == jb->state_unplugged)) {
3731 exists = true;
3732 break;
3733 }
3734 }
3735
3736 if (!exists) {
3737 is_subset = false;
3738 break;
3739 }
3740 }
3741
3742 /* Compare the elements of each set... */
3743 PA_LLIST_FOREACH(ea, p->elements) {
3744 bool found_matching_element = false;
3745
3746 if (!is_subset)
3747 break;
3748
3749 PA_LLIST_FOREACH(eb, p2->elements) {
3750 if (pa_streq(ea->alsa_id.name, eb->alsa_id.name) &&
3751 ea->alsa_id.index == eb->alsa_id.index) {
3752 found_matching_element = true;
3753 is_subset = element_is_subset(ea, eb, m);
3754 break;
3755 }
3756 }
3757
3758 if (!found_matching_element)
3759 is_subset = false;
3760 }
3761
3762 if (is_subset) {
3763 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p->name, p2->name);
3764 pa_hashmap_remove(ps->paths, p);
3765 break;
3766 }
3767 }
3768 }
3769 }
3770
path_set_find_path_by_description(pa_alsa_path_set * ps,const char * description,pa_alsa_path * ignore)3771 static pa_alsa_path* path_set_find_path_by_description(pa_alsa_path_set *ps, const char* description, pa_alsa_path *ignore) {
3772 pa_alsa_path* p;
3773 void *state;
3774
3775 PA_HASHMAP_FOREACH(p, ps->paths, state)
3776 if (p != ignore && pa_streq(p->description, description))
3777 return p;
3778
3779 return NULL;
3780 }
3781
path_set_make_path_descriptions_unique(pa_alsa_path_set * ps)3782 static void path_set_make_path_descriptions_unique(pa_alsa_path_set *ps) {
3783 pa_alsa_path *p, *q;
3784 void *state, *state2;
3785
3786 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3787 unsigned i;
3788 char *old_description;
3789
3790 q = path_set_find_path_by_description(ps, p->description, p);
3791
3792 if (!q)
3793 continue;
3794
3795 old_description = pa_xstrdup(p->description);
3796
3797 /* OK, this description is not unique, hence let's rename */
3798 i = 1;
3799 PA_HASHMAP_FOREACH(q, ps->paths, state2) {
3800 char *new_description;
3801
3802 if (!pa_streq(q->description, old_description))
3803 continue;
3804
3805 new_description = pa_sprintf_malloc("%s %u", q->description, i);
3806 pa_xfree(q->description);
3807 q->description = new_description;
3808
3809 i++;
3810 }
3811
3812 pa_xfree(old_description);
3813 }
3814 }
3815
mapping_free(pa_alsa_mapping * m)3816 static void mapping_free(pa_alsa_mapping *m) {
3817 pa_assert(m);
3818
3819 pa_xfree(m->name);
3820 pa_xfree(m->description);
3821 pa_xfree(m->description_key);
3822
3823 pa_proplist_free(m->proplist);
3824
3825 pa_xstrfreev(m->device_strings);
3826 pa_xstrfreev(m->input_path_names);
3827 pa_xstrfreev(m->output_path_names);
3828 pa_xstrfreev(m->input_element);
3829 pa_xstrfreev(m->output_element);
3830 if (m->input_path_set)
3831 pa_alsa_path_set_free(m->input_path_set);
3832 if (m->output_path_set)
3833 pa_alsa_path_set_free(m->output_path_set);
3834
3835 pa_assert(!m->input_pcm);
3836 pa_assert(!m->output_pcm);
3837
3838 pa_alsa_ucm_mapping_context_free(&m->ucm_context);
3839
3840 pa_xfree(m);
3841 }
3842
profile_free(pa_alsa_profile * p)3843 static void profile_free(pa_alsa_profile *p) {
3844 pa_assert(p);
3845
3846 pa_xfree(p->name);
3847 pa_xfree(p->description);
3848 pa_xfree(p->description_key);
3849 pa_xfree(p->input_name);
3850 pa_xfree(p->output_name);
3851
3852 pa_xstrfreev(p->input_mapping_names);
3853 pa_xstrfreev(p->output_mapping_names);
3854
3855 if (p->input_mappings)
3856 pa_idxset_free(p->input_mappings, NULL);
3857
3858 if (p->output_mappings)
3859 pa_idxset_free(p->output_mappings, NULL);
3860
3861 pa_xfree(p);
3862 }
3863
pa_alsa_profile_set_free(pa_alsa_profile_set * ps)3864 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
3865 pa_assert(ps);
3866
3867 if (ps->input_paths)
3868 pa_hashmap_free(ps->input_paths);
3869
3870 if (ps->output_paths)
3871 pa_hashmap_free(ps->output_paths);
3872
3873 if (ps->profiles)
3874 pa_hashmap_free(ps->profiles);
3875
3876 if (ps->mappings)
3877 pa_hashmap_free(ps->mappings);
3878
3879 if (ps->decibel_fixes)
3880 pa_hashmap_free(ps->decibel_fixes);
3881
3882 pa_xfree(ps);
3883 }
3884
pa_alsa_mapping_get(pa_alsa_profile_set * ps,const char * name)3885 pa_alsa_mapping *pa_alsa_mapping_get(pa_alsa_profile_set *ps, const char *name) {
3886 pa_alsa_mapping *m;
3887
3888 if (!pa_startswith(name, "Mapping "))
3889 return NULL;
3890
3891 name += 8;
3892
3893 if ((m = pa_hashmap_get(ps->mappings, name)))
3894 return m;
3895
3896 m = pa_xnew0(pa_alsa_mapping, 1);
3897 m->profile_set = ps;
3898 m->exact_channels = true;
3899 m->name = pa_xstrdup(name);
3900 pa_sample_spec_init(&m->sample_spec);
3901 pa_channel_map_init(&m->channel_map);
3902 m->proplist = pa_proplist_new();
3903 m->hw_device_index = -1;
3904
3905 pa_hashmap_put(ps->mappings, m->name, m);
3906
3907 return m;
3908 }
3909
profile_get(pa_alsa_profile_set * ps,const char * name)3910 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
3911 pa_alsa_profile *p;
3912
3913 if (!pa_startswith(name, "Profile "))
3914 return NULL;
3915
3916 name += 8;
3917
3918 if ((p = pa_hashmap_get(ps->profiles, name)))
3919 return p;
3920
3921 p = pa_xnew0(pa_alsa_profile, 1);
3922 p->profile_set = ps;
3923 p->name = pa_xstrdup(name);
3924
3925 pa_hashmap_put(ps->profiles, p->name, p);
3926
3927 return p;
3928 }
3929
decibel_fix_get(pa_alsa_profile_set * ps,const char * alsa_id)3930 static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *alsa_id) {
3931 pa_alsa_decibel_fix *db_fix;
3932 char *name;
3933 int index;
3934
3935 if (!pa_startswith(alsa_id, "DecibelFix "))
3936 return NULL;
3937
3938 alsa_id += 11;
3939
3940 if ((db_fix = pa_hashmap_get(ps->decibel_fixes, alsa_id)))
3941 return db_fix;
3942
3943 name = alloca(strlen(alsa_id) + 1);
3944 if (alsa_id_decode(alsa_id, name, &index))
3945 return NULL;
3946
3947 db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
3948 db_fix->profile_set = ps;
3949 db_fix->name = pa_xstrdup(name);
3950 db_fix->index = index;
3951 db_fix->key = pa_xstrdup(alsa_id);
3952
3953 pa_hashmap_put(ps->decibel_fixes, db_fix->key, db_fix);
3954
3955 return db_fix;
3956 }
3957
mapping_parse_device_strings(pa_config_parser_state * state)3958 static int mapping_parse_device_strings(pa_config_parser_state *state) {
3959 pa_alsa_profile_set *ps;
3960 pa_alsa_mapping *m;
3961
3962 pa_assert(state);
3963
3964 ps = state->userdata;
3965
3966 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3967 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3968 return -1;
3969 }
3970
3971 pa_xstrfreev(m->device_strings);
3972 if (!(m->device_strings = pa_split_spaces_strv(state->rvalue))) {
3973 pa_log("[%s:%u] Device string list empty of '%s'", state->filename, state->lineno, state->section);
3974 return -1;
3975 }
3976
3977 return 0;
3978 }
3979
mapping_parse_channel_map(pa_config_parser_state * state)3980 static int mapping_parse_channel_map(pa_config_parser_state *state) {
3981 pa_alsa_profile_set *ps;
3982 pa_alsa_mapping *m;
3983
3984 pa_assert(state);
3985
3986 ps = state->userdata;
3987
3988 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3989 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3990 return -1;
3991 }
3992
3993 if (!(pa_channel_map_parse(&m->channel_map, state->rvalue))) {
3994 pa_log("[%s:%u] Channel map invalid of '%s'", state->filename, state->lineno, state->section);
3995 return -1;
3996 }
3997
3998 return 0;
3999 }
4000
mapping_parse_paths(pa_config_parser_state * state)4001 static int mapping_parse_paths(pa_config_parser_state *state) {
4002 pa_alsa_profile_set *ps;
4003 pa_alsa_mapping *m;
4004
4005 pa_assert(state);
4006
4007 ps = state->userdata;
4008
4009 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
4010 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4011 return -1;
4012 }
4013
4014 if (pa_streq(state->lvalue, "paths-input")) {
4015 pa_xstrfreev(m->input_path_names);
4016 m->input_path_names = pa_split_spaces_strv(state->rvalue);
4017 } else {
4018 pa_xstrfreev(m->output_path_names);
4019 m->output_path_names = pa_split_spaces_strv(state->rvalue);
4020 }
4021
4022 return 0;
4023 }
4024
mapping_parse_exact_channels(pa_config_parser_state * state)4025 static int mapping_parse_exact_channels(pa_config_parser_state *state) {
4026 pa_alsa_profile_set *ps;
4027 pa_alsa_mapping *m;
4028 int b;
4029
4030 pa_assert(state);
4031
4032 ps = state->userdata;
4033
4034 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
4035 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4036 return -1;
4037 }
4038
4039 if ((b = pa_parse_boolean(state->rvalue)) < 0) {
4040 pa_log("[%s:%u] %s has invalid value '%s'", state->filename, state->lineno, state->lvalue, state->section);
4041 return -1;
4042 }
4043
4044 m->exact_channels = b;
4045
4046 return 0;
4047 }
4048
mapping_parse_element(pa_config_parser_state * state)4049 static int mapping_parse_element(pa_config_parser_state *state) {
4050 pa_alsa_profile_set *ps;
4051 pa_alsa_mapping *m;
4052
4053 pa_assert(state);
4054
4055 ps = state->userdata;
4056
4057 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
4058 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4059 return -1;
4060 }
4061
4062 if (pa_streq(state->lvalue, "element-input")) {
4063 pa_xstrfreev(m->input_element);
4064 m->input_element = pa_split_spaces_strv(state->rvalue);
4065 } else {
4066 pa_xstrfreev(m->output_element);
4067 m->output_element = pa_split_spaces_strv(state->rvalue);
4068 }
4069
4070 return 0;
4071 }
4072
mapping_parse_direction(pa_config_parser_state * state)4073 static int mapping_parse_direction(pa_config_parser_state *state) {
4074 pa_alsa_profile_set *ps;
4075 pa_alsa_mapping *m;
4076
4077 pa_assert(state);
4078
4079 ps = state->userdata;
4080
4081 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
4082 pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
4083 return -1;
4084 }
4085
4086 if (pa_streq(state->rvalue, "input"))
4087 m->direction = PA_ALSA_DIRECTION_INPUT;
4088 else if (pa_streq(state->rvalue, "output"))
4089 m->direction = PA_ALSA_DIRECTION_OUTPUT;
4090 else if (pa_streq(state->rvalue, "any"))
4091 m->direction = PA_ALSA_DIRECTION_ANY;
4092 else {
4093 pa_log("[%s:%u] Direction %s invalid.", state->filename, state->lineno, state->rvalue);
4094 return -1;
4095 }
4096
4097 return 0;
4098 }
4099
mapping_parse_description(pa_config_parser_state * state)4100 static int mapping_parse_description(pa_config_parser_state *state) {
4101 pa_alsa_profile_set *ps;
4102 pa_alsa_profile *p;
4103 pa_alsa_mapping *m;
4104
4105 pa_assert(state);
4106
4107 ps = state->userdata;
4108
4109 if ((m = pa_alsa_mapping_get(ps, state->section))) {
4110 pa_xfree(m->description);
4111 m->description = pa_xstrdup(state->rvalue);
4112 } else if ((p = profile_get(ps, state->section))) {
4113 pa_xfree(p->description);
4114 p->description = pa_xstrdup(state->rvalue);
4115 } else {
4116 pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
4117 return -1;
4118 }
4119
4120 return 0;
4121 }
4122
mapping_parse_description_key(pa_config_parser_state * state)4123 static int mapping_parse_description_key(pa_config_parser_state *state) {
4124 pa_alsa_profile_set *ps;
4125 pa_alsa_profile *p;
4126 pa_alsa_mapping *m;
4127
4128 pa_assert(state);
4129
4130 ps = state->userdata;
4131
4132 if ((m = pa_alsa_mapping_get(ps, state->section))) {
4133 pa_xfree(m->description_key);
4134 m->description_key = pa_xstrdup(state->rvalue);
4135 } else if ((p = profile_get(ps, state->section))) {
4136 pa_xfree(p->description_key);
4137 p->description_key = pa_xstrdup(state->rvalue);
4138 } else {
4139 pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
4140 return -1;
4141 }
4142
4143 return 0;
4144 }
4145
4146
mapping_parse_priority(pa_config_parser_state * state)4147 static int mapping_parse_priority(pa_config_parser_state *state) {
4148 pa_alsa_profile_set *ps;
4149 pa_alsa_profile *p;
4150 pa_alsa_mapping *m;
4151 uint32_t prio;
4152
4153 pa_assert(state);
4154
4155 ps = state->userdata;
4156
4157 if (pa_atou(state->rvalue, &prio) < 0) {
4158 pa_log("[%s:%u] Priority invalid of '%s'", state->filename, state->lineno, state->section);
4159 return -1;
4160 }
4161
4162 if ((m = pa_alsa_mapping_get(ps, state->section)))
4163 m->priority = prio;
4164 else if ((p = profile_get(ps, state->section)))
4165 p->priority = prio;
4166 else {
4167 pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
4168 return -1;
4169 }
4170
4171 return 0;
4172 }
4173
mapping_parse_fallback(pa_config_parser_state * state)4174 static int mapping_parse_fallback(pa_config_parser_state *state) {
4175 pa_alsa_profile_set *ps;
4176 pa_alsa_profile *p;
4177 pa_alsa_mapping *m;
4178 int k;
4179
4180 pa_assert(state);
4181
4182 ps = state->userdata;
4183
4184 if ((k = pa_parse_boolean(state->rvalue)) < 0) {
4185 pa_log("[%s:%u] Fallback invalid of '%s'", state->filename, state->lineno, state->section);
4186 return -1;
4187 }
4188
4189 if ((m = pa_alsa_mapping_get(ps, state->section)))
4190 m->fallback = k;
4191 else if ((p = profile_get(ps, state->section)))
4192 p->fallback_input = p->fallback_output = k;
4193 else {
4194 pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
4195 return -1;
4196 }
4197
4198 return 0;
4199 }
4200
mapping_parse_intended_roles(pa_config_parser_state * state)4201 static int mapping_parse_intended_roles(pa_config_parser_state *state) {
4202 pa_alsa_profile_set *ps;
4203 pa_alsa_mapping *m;
4204
4205 pa_assert(state);
4206
4207 ps = state->userdata;
4208
4209 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
4210 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4211 return -1;
4212 }
4213
4214 pa_proplist_sets(m->proplist, PA_PROP_DEVICE_INTENDED_ROLES, state->rvalue);
4215
4216 return 0;
4217 }
4218
4219
profile_parse_mappings(pa_config_parser_state * state)4220 static int profile_parse_mappings(pa_config_parser_state *state) {
4221 pa_alsa_profile_set *ps;
4222 pa_alsa_profile *p;
4223
4224 pa_assert(state);
4225
4226 ps = state->userdata;
4227
4228 if (!(p = profile_get(ps, state->section))) {
4229 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4230 return -1;
4231 }
4232
4233 if (pa_streq(state->lvalue, "input-mappings")) {
4234 pa_xstrfreev(p->input_mapping_names);
4235 p->input_mapping_names = pa_split_spaces_strv(state->rvalue);
4236 } else {
4237 pa_xstrfreev(p->output_mapping_names);
4238 p->output_mapping_names = pa_split_spaces_strv(state->rvalue);
4239 }
4240
4241 return 0;
4242 }
4243
profile_parse_skip_probe(pa_config_parser_state * state)4244 static int profile_parse_skip_probe(pa_config_parser_state *state) {
4245 pa_alsa_profile_set *ps;
4246 pa_alsa_profile *p;
4247 int b;
4248
4249 pa_assert(state);
4250
4251 ps = state->userdata;
4252
4253 if (!(p = profile_get(ps, state->section))) {
4254 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4255 return -1;
4256 }
4257
4258 if ((b = pa_parse_boolean(state->rvalue)) < 0) {
4259 pa_log("[%s:%u] Skip probe invalid of '%s'", state->filename, state->lineno, state->section);
4260 return -1;
4261 }
4262
4263 p->supported = b;
4264
4265 return 0;
4266 }
4267
decibel_fix_parse_db_values(pa_config_parser_state * state)4268 static int decibel_fix_parse_db_values(pa_config_parser_state *state) {
4269 pa_alsa_profile_set *ps;
4270 pa_alsa_decibel_fix *db_fix;
4271 char **items;
4272 char *item;
4273 long *db_values;
4274 unsigned n = 8; /* Current size of the db_values table. */
4275 unsigned min_step = 0;
4276 unsigned max_step = 0;
4277 unsigned i = 0; /* Index to the items table. */
4278 unsigned prev_step = 0;
4279 double prev_db = 0;
4280
4281 pa_assert(state);
4282
4283 ps = state->userdata;
4284
4285 if (!(db_fix = decibel_fix_get(ps, state->section))) {
4286 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4287 return -1;
4288 }
4289
4290 if (!(items = pa_split_spaces_strv(state->rvalue))) {
4291 pa_log("[%s:%u] Value missing", state->filename, state->lineno);
4292 return -1;
4293 }
4294
4295 db_values = pa_xnew(long, n);
4296
4297 while ((item = items[i++])) {
4298 char *s = item; /* Step value string. */
4299 char *d = item; /* dB value string. */
4300 uint32_t step;
4301 double db;
4302
4303 /* Move d forward until it points to a colon or to the end of the item. */
4304 for (; *d && *d != ':'; ++d);
4305
4306 if (d == s) {
4307 /* item started with colon. */
4308 pa_log("[%s:%u] No step value found in %s", state->filename, state->lineno, item);
4309 goto fail;
4310 }
4311
4312 if (!*d || !*(d + 1)) {
4313 /* No colon found, or it was the last character in item. */
4314 pa_log("[%s:%u] No dB value found in %s", state->filename, state->lineno, item);
4315 goto fail;
4316 }
4317
4318 /* pa_atou() needs a null-terminating string. Let's replace the colon
4319 * with a zero byte. */
4320 *d++ = '\0';
4321
4322 if (pa_atou(s, &step) < 0) {
4323 pa_log("[%s:%u] Invalid step value: %s", state->filename, state->lineno, s);
4324 goto fail;
4325 }
4326
4327 if (pa_atod(d, &db) < 0) {
4328 pa_log("[%s:%u] Invalid dB value: %s", state->filename, state->lineno, d);
4329 goto fail;
4330 }
4331
4332 if (step <= prev_step && i != 1) {
4333 pa_log("[%s:%u] Step value %u not greater than the previous value %u", state->filename, state->lineno, step, prev_step);
4334 goto fail;
4335 }
4336
4337 if (db < prev_db && i != 1) {
4338 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", state->filename, state->lineno, db, prev_db);
4339 goto fail;
4340 }
4341
4342 if (i == 1) {
4343 min_step = step;
4344 db_values[0] = (long) (db * 100.0);
4345 prev_step = step;
4346 prev_db = db;
4347 } else {
4348 /* Interpolate linearly. */
4349 double db_increment = (db - prev_db) / (step - prev_step);
4350
4351 for (; prev_step < step; ++prev_step, prev_db += db_increment) {
4352
4353 /* Reallocate the db_values table if it's about to overflow. */
4354 if (prev_step + 1 - min_step == n) {
4355 n *= 2;
4356 db_values = pa_xrenew(long, db_values, n);
4357 }
4358
4359 db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
4360 }
4361 }
4362
4363 max_step = step;
4364 }
4365
4366 db_fix->min_step = min_step;
4367 db_fix->max_step = max_step;
4368 pa_xfree(db_fix->db_values);
4369 db_fix->db_values = db_values;
4370
4371 pa_xstrfreev(items);
4372
4373 return 0;
4374
4375 fail:
4376 pa_xstrfreev(items);
4377 pa_xfree(db_values);
4378
4379 return -1;
4380 }
4381
4382 /* the logic is simple: if we see the jack in multiple paths */
4383 /* assign all those paths to one availability_group */
profile_set_set_availability_groups(pa_alsa_profile_set * ps)4384 static void profile_set_set_availability_groups(pa_alsa_profile_set *ps) {
4385 pa_dynarray *paths;
4386 pa_alsa_path *p;
4387 void *state;
4388 unsigned idx1;
4389 uint32_t num = 1;
4390
4391 /* Merge ps->input_paths and ps->output_paths into one dynarray. */
4392 paths = pa_dynarray_new(NULL);
4393 PA_HASHMAP_FOREACH(p, ps->input_paths, state)
4394 pa_dynarray_append(paths, p);
4395 PA_HASHMAP_FOREACH(p, ps->output_paths, state)
4396 pa_dynarray_append(paths, p);
4397
4398 PA_DYNARRAY_FOREACH(p, paths, idx1) {
4399 pa_alsa_jack *j;
4400 const char *found = NULL;
4401 bool has_control = false;
4402
4403 PA_LLIST_FOREACH(j, p->jacks) {
4404 pa_alsa_path *p2;
4405 unsigned idx2;
4406
4407 if (!j->has_control || j->state_plugged == PA_AVAILABLE_NO)
4408 continue;
4409 has_control = true;
4410 PA_DYNARRAY_FOREACH(p2, paths, idx2) {
4411 pa_alsa_jack *j2;
4412
4413 if (p2 == p)
4414 break;
4415 PA_LLIST_FOREACH(j2, p2->jacks) {
4416 if (!j2->has_control || j2->state_plugged == PA_AVAILABLE_NO)
4417 continue;
4418 if (pa_streq(j->alsa_id.name, j2->alsa_id.name) &&
4419 j->alsa_id.index == j2->alsa_id.index) {
4420 j->state_plugged = PA_AVAILABLE_UNKNOWN;
4421 j2->state_plugged = PA_AVAILABLE_UNKNOWN;
4422 found = p2->availability_group;
4423 break;
4424 }
4425 }
4426 }
4427 if (found)
4428 break;
4429 }
4430 if (!has_control)
4431 continue;
4432 if (!found) {
4433 p->availability_group = pa_sprintf_malloc("Legacy %d", num);
4434 } else {
4435 p->availability_group = pa_xstrdup(found);
4436 }
4437 if (!found)
4438 num++;
4439 }
4440
4441 pa_dynarray_free(paths);
4442 }
4443
mapping_paths_probe(pa_alsa_mapping * m,pa_alsa_profile * profile,pa_alsa_direction_t direction,pa_hashmap * used_paths,pa_hashmap * mixers)4444 static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
4445 pa_alsa_direction_t direction, pa_hashmap *used_paths,
4446 pa_hashmap *mixers) {
4447
4448 pa_alsa_path *p;
4449 void *state;
4450 snd_pcm_t *pcm_handle;
4451 pa_alsa_path_set *ps;
4452 snd_mixer_t *mixer_handle;
4453
4454 if (direction == PA_ALSA_DIRECTION_OUTPUT) {
4455 if (m->output_path_set)
4456 return; /* Already probed */
4457 m->output_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
4458 pcm_handle = m->output_pcm;
4459 } else {
4460 if (m->input_path_set)
4461 return; /* Already probed */
4462 m->input_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
4463 pcm_handle = m->input_pcm;
4464 }
4465
4466 if (!ps)
4467 return; /* No paths */
4468
4469 pa_assert(pcm_handle);
4470
4471 mixer_handle = pa_alsa_open_mixer_for_pcm(mixers, pcm_handle, true);
4472 if (!mixer_handle) {
4473 /* Cannot open mixer, remove all entries */
4474 pa_hashmap_remove_all(ps->paths);
4475 return;
4476 }
4477
4478 PA_HASHMAP_FOREACH(p, ps->paths, state) {
4479 if (p->autodetect_eld_device)
4480 p->eld_device = m->hw_device_index;
4481
4482 if (pa_alsa_path_probe(p, m, mixer_handle, m->profile_set->ignore_dB) < 0)
4483 pa_hashmap_remove(ps->paths, p);
4484 }
4485
4486 path_set_condense(ps, mixer_handle);
4487 path_set_make_path_descriptions_unique(ps);
4488
4489 PA_HASHMAP_FOREACH(p, ps->paths, state)
4490 pa_hashmap_put(used_paths, p, p);
4491
4492 pa_log_debug("Available mixer paths (after tidying):");
4493 pa_alsa_path_set_dump(ps);
4494 }
4495
mapping_verify(pa_alsa_mapping * m,const pa_channel_map * bonus)4496 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
4497
4498 static const struct description_map well_known_descriptions[] = {
4499 { "analog-mono", N_("Analog Mono") },
4500 { "analog-stereo", N_("Analog Stereo") },
4501 { "mono-fallback", N_("Mono") },
4502 { "stereo-fallback", N_("Stereo") },
4503 /* Note: Not translated to "Analog Stereo Input", because the source
4504 * name gets "Input" appended to it automatically, so adding "Input"
4505 * here would lead to the source name to become "Analog Stereo Input
4506 * Input". The same logic applies to analog-stereo-output,
4507 * multichannel-input and multichannel-output. */
4508 { "analog-stereo-input", N_("Analog Stereo") },
4509 { "analog-stereo-output", N_("Analog Stereo") },
4510 { "multichannel-input", N_("Multichannel") },
4511 { "multichannel-output", N_("Multichannel") },
4512 { "analog-surround-21", N_("Analog Surround 2.1") },
4513 { "analog-surround-30", N_("Analog Surround 3.0") },
4514 { "analog-surround-31", N_("Analog Surround 3.1") },
4515 { "analog-surround-40", N_("Analog Surround 4.0") },
4516 { "analog-surround-41", N_("Analog Surround 4.1") },
4517 { "analog-surround-50", N_("Analog Surround 5.0") },
4518 { "analog-surround-51", N_("Analog Surround 5.1") },
4519 { "analog-surround-61", N_("Analog Surround 6.0") },
4520 { "analog-surround-61", N_("Analog Surround 6.1") },
4521 { "analog-surround-70", N_("Analog Surround 7.0") },
4522 { "analog-surround-71", N_("Analog Surround 7.1") },
4523 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
4524 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
4525 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
4526 { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
4527 { "hdmi-stereo", N_("Digital Stereo (HDMI)") },
4528 { "hdmi-surround-51", N_("Digital Surround 5.1 (HDMI)") },
4529 { "gaming-headset-chat", N_("Chat") },
4530 { "gaming-headset-game", N_("Game") },
4531 };
4532 const char *description_key = m->description_key ? m->description_key : m->name;
4533
4534 pa_assert(m);
4535
4536 if (!pa_channel_map_valid(&m->channel_map)) {
4537 pa_log("Mapping %s is missing channel map.", m->name);
4538 return -1;
4539 }
4540
4541 if (!m->device_strings) {
4542 pa_log("Mapping %s is missing device strings.", m->name);
4543 return -1;
4544 }
4545
4546 if ((m->input_path_names && m->input_element) ||
4547 (m->output_path_names && m->output_element)) {
4548 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
4549 return -1;
4550 }
4551
4552 if (!m->description)
4553 m->description = pa_xstrdup(lookup_description(description_key,
4554 well_known_descriptions,
4555 PA_ELEMENTSOF(well_known_descriptions)));
4556
4557 if (!m->description)
4558 m->description = pa_xstrdup(m->name);
4559
4560 if (bonus) {
4561 if (pa_channel_map_equal(&m->channel_map, bonus))
4562 m->priority += 50;
4563 else if (m->channel_map.channels == bonus->channels)
4564 m->priority += 30;
4565 }
4566
4567 return 0;
4568 }
4569
pa_alsa_mapping_dump(pa_alsa_mapping * m)4570 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
4571 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
4572
4573 pa_assert(m);
4574
4575 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
4576 m->name,
4577 pa_strnull(m->description),
4578 m->priority,
4579 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
4580 pa_yes_no(m->supported),
4581 m->direction);
4582 }
4583
profile_set_add_auto_pair(pa_alsa_profile_set * ps,pa_alsa_mapping * m,pa_alsa_mapping * n)4584 static void profile_set_add_auto_pair(
4585 pa_alsa_profile_set *ps,
4586 pa_alsa_mapping *m, /* output */
4587 pa_alsa_mapping *n /* input */) {
4588
4589 char *name;
4590 pa_alsa_profile *p;
4591
4592 pa_assert(ps);
4593 pa_assert(m || n);
4594
4595 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
4596 return;
4597
4598 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
4599 return;
4600
4601 if (m && n)
4602 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
4603 else if (m)
4604 name = pa_sprintf_malloc("output:%s", m->name);
4605 else
4606 name = pa_sprintf_malloc("input:%s", n->name);
4607
4608 if (pa_hashmap_get(ps->profiles, name)) {
4609 pa_xfree(name);
4610 return;
4611 }
4612
4613 p = pa_xnew0(pa_alsa_profile, 1);
4614 p->profile_set = ps;
4615 p->name = name;
4616
4617 if (m) {
4618 p->output_name = pa_xstrdup(m->name);
4619 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4620 pa_idxset_put(p->output_mappings, m, NULL);
4621 p->priority += m->priority * 100;
4622 p->fallback_output = m->fallback;
4623 }
4624
4625 if (n) {
4626 p->input_name = pa_xstrdup(n->name);
4627 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4628 pa_idxset_put(p->input_mappings, n, NULL);
4629 p->priority += n->priority;
4630 p->fallback_input = n->fallback;
4631 }
4632
4633 pa_hashmap_put(ps->profiles, p->name, p);
4634 }
4635
profile_set_add_auto(pa_alsa_profile_set * ps)4636 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
4637 pa_alsa_mapping *m, *n;
4638 void *m_state, *n_state;
4639
4640 pa_assert(ps);
4641
4642 /* The order is important here:
4643 1) try single inputs and outputs before trying their
4644 combination, because if the half-duplex test failed, we don't have
4645 to try full duplex.
4646 2) try the output right before the input combinations with
4647 that output, because then the output_pcm is not closed between tests.
4648 */
4649 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
4650 profile_set_add_auto_pair(ps, NULL, n);
4651
4652 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
4653 profile_set_add_auto_pair(ps, m, NULL);
4654
4655 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
4656 profile_set_add_auto_pair(ps, m, n);
4657 }
4658
4659 }
4660
profile_verify(pa_alsa_profile * p)4661 static int profile_verify(pa_alsa_profile *p) {
4662
4663 static const struct description_map well_known_descriptions[] = {
4664 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
4665 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
4666 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
4667 { "output:multichannel-output+input:multichannel-input", N_("Multichannel Duplex") },
4668 { "output:unknown-stereo+input:unknown-stereo", N_("Stereo Duplex") },
4669 { "off", N_("Off") }
4670 };
4671 const char *description_key = p->description_key ? p->description_key : p->name;
4672
4673 pa_assert(p);
4674
4675 /* Replace the output mapping names by the actual mappings */
4676 if (p->output_mapping_names) {
4677 char **name;
4678
4679 pa_assert(!p->output_mappings);
4680 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4681
4682 for (name = p->output_mapping_names; *name; name++) {
4683 pa_alsa_mapping *m;
4684 char **in;
4685 bool duplicate = false;
4686
4687 for (in = name + 1; *in; in++)
4688 if (pa_streq(*name, *in)) {
4689 duplicate = true;
4690 break;
4691 }
4692
4693 if (duplicate)
4694 continue;
4695
4696 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
4697 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
4698 return -1;
4699 }
4700
4701 pa_idxset_put(p->output_mappings, m, NULL);
4702
4703 if (p->supported)
4704 m->supported++;
4705 }
4706
4707 pa_xstrfreev(p->output_mapping_names);
4708 p->output_mapping_names = NULL;
4709 }
4710
4711 /* Replace the input mapping names by the actual mappings */
4712 if (p->input_mapping_names) {
4713 char **name;
4714
4715 pa_assert(!p->input_mappings);
4716 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4717
4718 for (name = p->input_mapping_names; *name; name++) {
4719 pa_alsa_mapping *m;
4720 char **in;
4721 bool duplicate = false;
4722
4723 for (in = name + 1; *in; in++)
4724 if (pa_streq(*name, *in)) {
4725 duplicate = true;
4726 break;
4727 }
4728
4729 if (duplicate)
4730 continue;
4731
4732 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
4733 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
4734 return -1;
4735 }
4736
4737 pa_idxset_put(p->input_mappings, m, NULL);
4738
4739 if (p->supported)
4740 m->supported++;
4741 }
4742
4743 pa_xstrfreev(p->input_mapping_names);
4744 p->input_mapping_names = NULL;
4745 }
4746
4747 if (!p->input_mappings && !p->output_mappings) {
4748 pa_log("Profile '%s' lacks mappings.", p->name);
4749 return -1;
4750 }
4751
4752 if (!p->description)
4753 p->description = pa_xstrdup(lookup_description(description_key,
4754 well_known_descriptions,
4755 PA_ELEMENTSOF(well_known_descriptions)));
4756
4757 if (!p->description) {
4758 pa_strbuf *sb;
4759 uint32_t idx;
4760 pa_alsa_mapping *m;
4761
4762 sb = pa_strbuf_new();
4763
4764 if (p->output_mappings)
4765 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4766 if (!pa_strbuf_isempty(sb))
4767 pa_strbuf_puts(sb, " + ");
4768
4769 pa_strbuf_printf(sb, _("%s Output"), m->description);
4770 }
4771
4772 if (p->input_mappings)
4773 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4774 if (!pa_strbuf_isempty(sb))
4775 pa_strbuf_puts(sb, " + ");
4776
4777 pa_strbuf_printf(sb, _("%s Input"), m->description);
4778 }
4779
4780 p->description = pa_strbuf_to_string_free(sb);
4781 }
4782
4783 return 0;
4784 }
4785
pa_alsa_profile_dump(pa_alsa_profile * p)4786 void pa_alsa_profile_dump(pa_alsa_profile *p) {
4787 uint32_t idx;
4788 pa_alsa_mapping *m;
4789 pa_assert(p);
4790
4791 pa_log_debug("Profile %s (%s), input=%s, output=%s priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4792 p->name,
4793 pa_strnull(p->description),
4794 pa_strnull(p->input_name),
4795 pa_strnull(p->output_name),
4796 p->priority,
4797 pa_yes_no(p->supported),
4798 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
4799 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
4800
4801 if (p->input_mappings)
4802 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4803 pa_log_debug("Input %s", m->name);
4804
4805 if (p->output_mappings)
4806 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4807 pa_log_debug("Output %s", m->name);
4808 }
4809
decibel_fix_verify(pa_alsa_decibel_fix * db_fix)4810 static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
4811 pa_assert(db_fix);
4812
4813 /* Check that the dB mapping has been configured. Since "db-values" is
4814 * currently the only option in the DecibelFix section, and decibel fix
4815 * objects don't get created if a DecibelFix section is empty, this is
4816 * actually a redundant check. Having this may prevent future bugs,
4817 * however. */
4818 if (!db_fix->db_values) {
4819 pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
4820 return -1;
4821 }
4822
4823 return 0;
4824 }
4825
pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix * db_fix)4826 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
4827 char *db_values = NULL;
4828
4829 pa_assert(db_fix);
4830
4831 if (db_fix->db_values) {
4832 pa_strbuf *buf;
4833 unsigned long i, nsteps;
4834
4835 pa_assert(db_fix->min_step <= db_fix->max_step);
4836 nsteps = db_fix->max_step - db_fix->min_step + 1;
4837
4838 buf = pa_strbuf_new();
4839 for (i = 0; i < nsteps; ++i)
4840 pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
4841
4842 db_values = pa_strbuf_to_string_free(buf);
4843 }
4844
4845 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4846 db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
4847
4848 pa_xfree(db_values);
4849 }
4850
pa_alsa_profile_set_new(const char * fname,const pa_channel_map * bonus)4851 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
4852 pa_alsa_profile_set *ps;
4853 pa_alsa_profile *p;
4854 pa_alsa_mapping *m;
4855 pa_alsa_decibel_fix *db_fix;
4856 char *fn;
4857 int r;
4858 void *state;
4859
4860 static pa_config_item items[] = {
4861 /* [General] */
4862 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
4863
4864 /* [Mapping ...] */
4865 { "device-strings", mapping_parse_device_strings, NULL, NULL },
4866 { "channel-map", mapping_parse_channel_map, NULL, NULL },
4867 { "paths-input", mapping_parse_paths, NULL, NULL },
4868 { "paths-output", mapping_parse_paths, NULL, NULL },
4869 { "element-input", mapping_parse_element, NULL, NULL },
4870 { "element-output", mapping_parse_element, NULL, NULL },
4871 { "direction", mapping_parse_direction, NULL, NULL },
4872 { "exact-channels", mapping_parse_exact_channels, NULL, NULL },
4873 { "intended-roles", mapping_parse_intended_roles, NULL, NULL },
4874
4875 /* Shared by [Mapping ...] and [Profile ...] */
4876 { "description", mapping_parse_description, NULL, NULL },
4877 { "description-key", mapping_parse_description_key,NULL, NULL },
4878 { "priority", mapping_parse_priority, NULL, NULL },
4879 { "fallback", mapping_parse_fallback, NULL, NULL },
4880
4881 /* [Profile ...] */
4882 { "input-mappings", profile_parse_mappings, NULL, NULL },
4883 { "output-mappings", profile_parse_mappings, NULL, NULL },
4884 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
4885
4886 /* [DecibelFix ...] */
4887 { "db-values", decibel_fix_parse_db_values, NULL, NULL },
4888 { NULL, NULL, NULL, NULL }
4889 };
4890
4891 ps = pa_xnew0(pa_alsa_profile_set, 1);
4892 ps->mappings = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) mapping_free);
4893 ps->profiles = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) profile_free);
4894 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);
4895 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);
4896 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);
4897
4898 items[0].data = &ps->auto_profiles;
4899
4900 if (!fname)
4901 fname = "default.conf";
4902
4903 fn = pa_maybe_prefix_path(fname,
4904 #ifdef HAVE_RUNNING_FROM_BUILD_TREE
4905 pa_run_from_build_tree() ? PA_SRCDIR "/modules/alsa/mixer/profile-sets/" :
4906 #endif
4907 PA_ALSA_PROFILE_SETS_DIR);
4908
4909 r = pa_config_parse(fn, NULL, items, NULL, false, ps);
4910 pa_xfree(fn);
4911
4912 if (r < 0)
4913 goto fail;
4914
4915 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4916 if (mapping_verify(m, bonus) < 0)
4917 goto fail;
4918
4919 if (ps->auto_profiles)
4920 profile_set_add_auto(ps);
4921
4922 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4923 if (profile_verify(p) < 0)
4924 goto fail;
4925
4926 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4927 if (decibel_fix_verify(db_fix) < 0)
4928 goto fail;
4929
4930 return ps;
4931
4932 fail:
4933 pa_alsa_profile_set_free(ps);
4934 return NULL;
4935 }
4936
profile_finalize_probing(pa_alsa_profile * to_be_finalized,pa_alsa_profile * next)4937 static void profile_finalize_probing(pa_alsa_profile *to_be_finalized, pa_alsa_profile *next) {
4938 pa_alsa_mapping *m;
4939 uint32_t idx;
4940
4941 if (!to_be_finalized)
4942 return;
4943
4944 if (to_be_finalized->output_mappings)
4945 PA_IDXSET_FOREACH(m, to_be_finalized->output_mappings, idx) {
4946
4947 if (!m->output_pcm)
4948 continue;
4949
4950 if (to_be_finalized->supported)
4951 m->supported++;
4952
4953 /* If this mapping is also in the next profile, we won't close the
4954 * pcm handle here, because it would get immediately reopened
4955 * anyway. */
4956 if (next && next->output_mappings && pa_idxset_get_by_data(next->output_mappings, m, NULL))
4957 continue;
4958
4959 snd_pcm_close(m->output_pcm);
4960 m->output_pcm = NULL;
4961 }
4962
4963 if (to_be_finalized->input_mappings)
4964 PA_IDXSET_FOREACH(m, to_be_finalized->input_mappings, idx) {
4965
4966 if (!m->input_pcm)
4967 continue;
4968
4969 if (to_be_finalized->supported)
4970 m->supported++;
4971
4972 /* If this mapping is also in the next profile, we won't close the
4973 * pcm handle here, because it would get immediately reopened
4974 * anyway. */
4975 if (next && next->input_mappings && pa_idxset_get_by_data(next->input_mappings, m, NULL))
4976 continue;
4977
4978 snd_pcm_close(m->input_pcm);
4979 m->input_pcm = NULL;
4980 }
4981 }
4982
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)4983 static snd_pcm_t* mapping_open_pcm(pa_alsa_mapping *m,
4984 const pa_sample_spec *ss,
4985 const char *dev_id,
4986 bool exact_channels,
4987 int mode,
4988 unsigned default_n_fragments,
4989 unsigned default_fragment_size_msec) {
4990
4991 snd_pcm_t* handle;
4992 pa_sample_spec try_ss = *ss;
4993 pa_channel_map try_map = m->channel_map;
4994 snd_pcm_uframes_t try_period_size, try_buffer_size;
4995
4996 try_ss.channels = try_map.channels;
4997
4998 try_period_size =
4999 pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
5000 pa_frame_size(&try_ss);
5001 try_buffer_size = default_n_fragments * try_period_size;
5002
5003 handle = pa_alsa_open_by_template(
5004 m->device_strings, dev_id, NULL, &try_ss,
5005 &try_map, mode, &try_period_size,
5006 &try_buffer_size, 0, NULL, NULL, exact_channels);
5007 if (handle && !exact_channels && m->channel_map.channels != try_map.channels) {
5008 char buf[PA_CHANNEL_MAP_SNPRINT_MAX];
5009 pa_log_debug("Channel map for mapping '%s' permanently changed to '%s'", m->name,
5010 pa_channel_map_snprint(buf, sizeof(buf), &try_map));
5011 m->channel_map = try_map;
5012 }
5013 return handle;
5014 }
5015
paths_drop_unused(pa_hashmap * h,pa_hashmap * keep)5016 static void paths_drop_unused(pa_hashmap* h, pa_hashmap *keep) {
5017
5018 void* state = NULL;
5019 const void* key;
5020 pa_alsa_path* p;
5021
5022 pa_assert(h);
5023 pa_assert(keep);
5024
5025 p = pa_hashmap_iterate(h, &state, &key);
5026 while (p) {
5027 if (pa_hashmap_get(keep, p) == NULL)
5028 pa_hashmap_remove_and_free(h, key);
5029 p = pa_hashmap_iterate(h, &state, &key);
5030 }
5031 }
5032
add_profiles_to_probe(pa_alsa_profile ** list,pa_hashmap * profiles,bool fallback_output,bool fallback_input)5033 static int add_profiles_to_probe(
5034 pa_alsa_profile **list,
5035 pa_hashmap *profiles,
5036 bool fallback_output,
5037 bool fallback_input) {
5038
5039 int i = 0;
5040 void *state;
5041 pa_alsa_profile *p;
5042 PA_HASHMAP_FOREACH(p, profiles, state)
5043 if (p->fallback_input == fallback_input && p->fallback_output == fallback_output) {
5044 *list = p;
5045 list++;
5046 i++;
5047 }
5048 return i;
5049 }
5050
mapping_query_hw_device(pa_alsa_mapping * mapping,snd_pcm_t * pcm)5051 static void mapping_query_hw_device(pa_alsa_mapping *mapping, snd_pcm_t *pcm) {
5052 int r;
5053 snd_pcm_info_t* pcm_info;
5054 snd_pcm_info_alloca(&pcm_info);
5055
5056 r = snd_pcm_info(pcm, pcm_info);
5057 if (r < 0) {
5058 pa_log("Mapping %s: snd_pcm_info() failed %s: ", mapping->name, pa_alsa_strerror(r));
5059 return;
5060 }
5061
5062 /* XXX: It's not clear what snd_pcm_info_get_device() does if the device is
5063 * not backed by a hw device or if it's backed by multiple hw devices. We
5064 * only use hw_device_index for HDMI devices, however, and for those the
5065 * return value is expected to be always valid, so this shouldn't be a
5066 * significant problem. */
5067 mapping->hw_device_index = snd_pcm_info_get_device(pcm_info);
5068 }
5069
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)5070 void pa_alsa_profile_set_probe(
5071 pa_alsa_profile_set *ps,
5072 pa_hashmap *mixers,
5073 const char *dev_id,
5074 const pa_sample_spec *ss,
5075 unsigned default_n_fragments,
5076 unsigned default_fragment_size_msec) {
5077
5078 bool found_output = false, found_input = false;
5079
5080 pa_alsa_profile *p, *last = NULL;
5081 pa_alsa_profile **pp, **probe_order;
5082 pa_alsa_mapping *m;
5083 pa_hashmap *broken_inputs, *broken_outputs, *used_paths;
5084
5085 pa_assert(ps);
5086 pa_assert(dev_id);
5087 pa_assert(ss);
5088
5089 if (ps->probed)
5090 return;
5091
5092 broken_inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
5093 broken_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
5094 used_paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
5095 pp = probe_order = pa_xnew0(pa_alsa_profile *, pa_hashmap_size(ps->profiles) + 1);
5096
5097 pp += add_profiles_to_probe(pp, ps->profiles, false, false);
5098 pp += add_profiles_to_probe(pp, ps->profiles, false, true);
5099 pp += add_profiles_to_probe(pp, ps->profiles, true, false);
5100 pp += add_profiles_to_probe(pp, ps->profiles, true, true);
5101
5102 for (pp = probe_order; *pp; pp++) {
5103 uint32_t idx;
5104 p = *pp;
5105
5106 /* Skip if fallback and already found something */
5107 if (found_input && p->fallback_input)
5108 continue;
5109 if (found_output && p->fallback_output)
5110 continue;
5111
5112 /* Skip if this is already marked that it is supported (i.e. from the config file) */
5113 if (!p->supported) {
5114
5115 profile_finalize_probing(last, p);
5116 p->supported = true;
5117
5118 if (p->output_mappings) {
5119 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
5120 if (pa_hashmap_get(broken_outputs, m) == m) {
5121 pa_log_debug("Skipping profile %s - will not be able to open output:%s", p->name, m->name);
5122 p->supported = false;
5123 break;
5124 }
5125 }
5126 }
5127
5128 if (p->input_mappings && p->supported) {
5129 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
5130 if (pa_hashmap_get(broken_inputs, m) == m) {
5131 pa_log_debug("Skipping profile %s - will not be able to open input:%s", p->name, m->name);
5132 p->supported = false;
5133 break;
5134 }
5135 }
5136 }
5137
5138 if (p->supported)
5139 pa_log_debug("Looking at profile %s", p->name);
5140
5141 /* Check if we can open all new ones */
5142 if (p->output_mappings && p->supported)
5143 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
5144
5145 if (m->output_pcm)
5146 continue;
5147
5148 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
5149 if (!(m->output_pcm = mapping_open_pcm(m, ss, dev_id, m->exact_channels,
5150 SND_PCM_STREAM_PLAYBACK,
5151 default_n_fragments,
5152 default_fragment_size_msec))) {
5153 p->supported = false;
5154 if (pa_idxset_size(p->output_mappings) == 1 &&
5155 ((!p->input_mappings) || pa_idxset_size(p->input_mappings) == 0)) {
5156 pa_log_debug("Caching failure to open output:%s", m->name);
5157 pa_hashmap_put(broken_outputs, m, m);
5158 }
5159 break;
5160 }
5161
5162 if (m->hw_device_index < 0)
5163 mapping_query_hw_device(m, m->output_pcm);
5164 }
5165
5166 if (p->input_mappings && p->supported)
5167 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
5168
5169 if (m->input_pcm)
5170 continue;
5171
5172 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
5173 if (!(m->input_pcm = mapping_open_pcm(m, ss, dev_id, m->exact_channels,
5174 SND_PCM_STREAM_CAPTURE,
5175 default_n_fragments,
5176 default_fragment_size_msec))) {
5177 p->supported = false;
5178 if (pa_idxset_size(p->input_mappings) == 1 &&
5179 ((!p->output_mappings) || pa_idxset_size(p->output_mappings) == 0)) {
5180 pa_log_debug("Caching failure to open input:%s", m->name);
5181 pa_hashmap_put(broken_inputs, m, m);
5182 }
5183 break;
5184 }
5185
5186 if (m->hw_device_index < 0)
5187 mapping_query_hw_device(m, m->input_pcm);
5188 }
5189
5190 last = p;
5191
5192 if (!p->supported)
5193 continue;
5194 }
5195
5196 pa_log_debug("Profile %s supported.", p->name);
5197
5198 if (p->output_mappings)
5199 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
5200 if (m->output_pcm) {
5201 found_output |= !p->fallback_output;
5202 mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT, used_paths, mixers);
5203 }
5204
5205 if (p->input_mappings)
5206 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
5207 if (m->input_pcm) {
5208 found_input |= !p->fallback_input;
5209 mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT, used_paths, mixers);
5210 }
5211 }
5212
5213 /* Clean up */
5214 profile_finalize_probing(last, NULL);
5215
5216 pa_alsa_profile_set_drop_unsupported(ps);
5217
5218 paths_drop_unused(ps->input_paths, used_paths);
5219 paths_drop_unused(ps->output_paths, used_paths);
5220 pa_hashmap_free(broken_inputs);
5221 pa_hashmap_free(broken_outputs);
5222 pa_hashmap_free(used_paths);
5223 pa_xfree(probe_order);
5224
5225 profile_set_set_availability_groups(ps);
5226
5227 ps->probed = true;
5228 }
5229
pa_alsa_profile_set_dump(pa_alsa_profile_set * ps)5230 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
5231 pa_alsa_profile *p;
5232 pa_alsa_mapping *m;
5233 pa_alsa_decibel_fix *db_fix;
5234 void *state;
5235
5236 pa_assert(ps);
5237
5238 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
5239 (void*)
5240 ps,
5241 pa_yes_no(ps->auto_profiles),
5242 pa_yes_no(ps->probed),
5243 pa_hashmap_size(ps->mappings),
5244 pa_hashmap_size(ps->profiles),
5245 pa_hashmap_size(ps->decibel_fixes));
5246
5247 PA_HASHMAP_FOREACH(m, ps->mappings, state)
5248 pa_alsa_mapping_dump(m);
5249
5250 PA_HASHMAP_FOREACH(p, ps->profiles, state)
5251 pa_alsa_profile_dump(p);
5252
5253 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
5254 pa_alsa_decibel_fix_dump(db_fix);
5255 }
5256
pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set * ps)5257 void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set *ps) {
5258 pa_alsa_profile *p;
5259 pa_alsa_mapping *m;
5260 void *state;
5261
5262 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
5263 if (!p->supported)
5264 pa_hashmap_remove_and_free(ps->profiles, p->name);
5265 }
5266
5267 PA_HASHMAP_FOREACH(m, ps->mappings, state) {
5268 if (m->supported <= 0)
5269 pa_hashmap_remove_and_free(ps->mappings, m->name);
5270 }
5271 }
5272
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)5273 static pa_device_port* device_port_alsa_init(pa_hashmap *ports, /* card ports */
5274 const char* name,
5275 const char* description,
5276 pa_alsa_path *path,
5277 pa_alsa_setting *setting,
5278 pa_card_profile *cp,
5279 pa_hashmap *extra, /* sink/source ports */
5280 pa_core *core) {
5281
5282 pa_device_port *p;
5283
5284 pa_assert(path);
5285
5286 p = pa_hashmap_get(ports, name);
5287
5288 if (!p) {
5289 pa_alsa_port_data *data;
5290 pa_device_port_new_data port_data;
5291
5292 pa_device_port_new_data_init(&port_data);
5293 pa_device_port_new_data_set_name(&port_data, name);
5294 pa_device_port_new_data_set_description(&port_data, description);
5295 pa_device_port_new_data_set_direction(&port_data, path->direction == PA_ALSA_DIRECTION_OUTPUT ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT);
5296 pa_device_port_new_data_set_type(&port_data, path->device_port_type);
5297 pa_device_port_new_data_set_availability_group(&port_data, path->availability_group);
5298
5299 p = pa_device_port_new(core, &port_data, sizeof(pa_alsa_port_data));
5300 pa_device_port_new_data_done(&port_data);
5301 pa_assert(p);
5302 pa_hashmap_put(ports, p->name, p);
5303 pa_proplist_update(p->proplist, PA_UPDATE_REPLACE, path->proplist);
5304
5305 data = PA_DEVICE_PORT_DATA(p);
5306 /* Ownership of the path and setting is not transferred to the port data, so we don't deal with freeing them */
5307 data->path = path;
5308 data->setting = setting;
5309 path->port = p;
5310 }
5311
5312 if (cp)
5313 pa_hashmap_put(p->profiles, cp->name, cp);
5314
5315 if (extra) {
5316 pa_hashmap_put(extra, p->name, p);
5317 pa_device_port_ref(p);
5318 }
5319
5320 return p;
5321 }
5322
pa_alsa_path_set_add_ports(pa_alsa_path_set * ps,pa_card_profile * cp,pa_hashmap * ports,pa_hashmap * extra,pa_core * core)5323 void pa_alsa_path_set_add_ports(
5324 pa_alsa_path_set *ps,
5325 pa_card_profile *cp,
5326 pa_hashmap *ports, /* card ports */
5327 pa_hashmap *extra, /* sink/source ports */
5328 pa_core *core) {
5329
5330 pa_alsa_path *path;
5331 void *state;
5332
5333 pa_assert(ports);
5334
5335 if (!ps)
5336 return;
5337
5338 PA_HASHMAP_FOREACH(path, ps->paths, state) {
5339 if (!path->settings || !path->settings->next) {
5340 /* If there is no or just one setting we only need a
5341 * single entry */
5342 pa_device_port *port = device_port_alsa_init(ports, path->name,
5343 path->description, path, path->settings, cp, extra, core);
5344 port->priority = path->priority * 100;
5345
5346 } else {
5347 pa_alsa_setting *s;
5348 PA_LLIST_FOREACH(s, path->settings) {
5349 pa_device_port *port;
5350 char *n, *d;
5351
5352 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
5353
5354 if (s->description[0])
5355 d = pa_sprintf_malloc("%s / %s", path->description, s->description);
5356 else
5357 d = pa_xstrdup(path->description);
5358
5359 port = device_port_alsa_init(ports, n, d, path, s, cp, extra, core);
5360 port->priority = path->priority * 100 + s->priority;
5361
5362 pa_xfree(n);
5363 pa_xfree(d);
5364 }
5365 }
5366 }
5367 }
5368
pa_alsa_add_ports(void * sink_or_source_new_data,pa_alsa_path_set * ps,pa_card * card)5369 void pa_alsa_add_ports(void *sink_or_source_new_data, pa_alsa_path_set *ps, pa_card *card) {
5370 pa_hashmap *ports;
5371
5372 pa_assert(sink_or_source_new_data);
5373 pa_assert(ps);
5374
5375 if (ps->direction == PA_ALSA_DIRECTION_OUTPUT)
5376 ports = ((pa_sink_new_data *) sink_or_source_new_data)->ports;
5377 else
5378 ports = ((pa_source_new_data *) sink_or_source_new_data)->ports;
5379
5380 if (ps->paths && pa_hashmap_size(ps->paths) > 0) {
5381 pa_assert(card);
5382 pa_alsa_path_set_add_ports(ps, NULL, card->ports, ports, card->core);
5383 }
5384
5385 pa_log_debug("Added %u ports", pa_hashmap_size(ports));
5386 }
5387