• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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