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