• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***
2  This file is part of PulseAudio.
3 
4  Copyright 2011 Wolfson Microelectronics PLC
5  Author Margarita Olaya <magi@slimlogic.co.uk>
6  Copyright 2012 Feng Wei <wei.feng@freescale.com>, Freescale Ltd.
7 
8  PulseAudio is free software; you can redistribute it and/or modify
9  it under the terms of the GNU Lesser General Public License as published
10  by the Free Software Foundation; either version 2.1 of the License,
11  or (at your option) any later version.
12 
13  PulseAudio is distributed in the hope that it will be useful, but
14  WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  General Public License for more details.
17 
18  You should have received a copy of the GNU Lesser General Public License
19  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
20 
21 ***/
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include <ctype.h>
28 #include <sys/types.h>
29 #include <limits.h>
30 #include <alsa/asoundlib.h>
31 
32 #ifdef HAVE_VALGRIND_MEMCHECK_H
33 #include <valgrind/memcheck.h>
34 #endif
35 
36 #include <pulse/sample.h>
37 #include <pulse/xmalloc.h>
38 #include <pulse/timeval.h>
39 #include <pulse/util.h>
40 
41 #include <pulsecore/log.h>
42 #include <pulsecore/macro.h>
43 #include <pulsecore/core-util.h>
44 #include <pulsecore/atomic.h>
45 #include <pulsecore/core-error.h>
46 #include <pulsecore/once.h>
47 #include <pulsecore/thread.h>
48 #include <pulsecore/conf-parser.h>
49 #include <pulsecore/strbuf.h>
50 
51 #include "alsa-mixer.h"
52 #include "alsa-util.h"
53 #include "alsa-ucm.h"
54 
55 #define PA_UCM_PRE_TAG_OUTPUT                       "[Out] "
56 #define PA_UCM_PRE_TAG_INPUT                        "[In] "
57 
58 #define PA_UCM_PLAYBACK_PRIORITY_UNSET(device)      ((device)->playback_channels && !(device)->playback_priority)
59 #define PA_UCM_CAPTURE_PRIORITY_UNSET(device)       ((device)->capture_channels && !(device)->capture_priority)
60 #define PA_UCM_DEVICE_PRIORITY_SET(device, priority) \
61     do { \
62         if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device)) (device)->playback_priority = (priority);   \
63         if (PA_UCM_CAPTURE_PRIORITY_UNSET(device))  (device)->capture_priority = (priority);    \
64     } while (0)
65 #define PA_UCM_IS_MODIFIER_MAPPING(m) ((pa_proplist_gets((m)->proplist, PA_ALSA_PROP_UCM_MODIFIER)) != NULL)
66 
67 #ifdef HAVE_ALSA_UCM
68 
69 struct ucm_type {
70     const char *prefix;
71     pa_device_port_type_t type;
72 };
73 
74 struct ucm_items {
75     const char *id;
76     const char *property;
77 };
78 
79 struct ucm_info {
80     const char *id;
81     unsigned priority;
82 };
83 
84 static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *device);
85 static void device_set_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack);
86 static void device_add_hw_mute_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack);
87 
88 static pa_alsa_ucm_device *verb_find_device(pa_alsa_ucm_verb *verb, const char *device_name);
89 
90 
91 static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port,
92                                pa_alsa_ucm_device *device);
93 static void ucm_port_data_free(pa_device_port *port);
94 
95 static struct ucm_type types[] = {
96     {"None", PA_DEVICE_PORT_TYPE_UNKNOWN},
97     {"Speaker", PA_DEVICE_PORT_TYPE_SPEAKER},
98     {"Line", PA_DEVICE_PORT_TYPE_LINE},
99     {"Mic", PA_DEVICE_PORT_TYPE_MIC},
100     {"Headphones", PA_DEVICE_PORT_TYPE_HEADPHONES},
101     {"Headset", PA_DEVICE_PORT_TYPE_HEADSET},
102     {"Handset", PA_DEVICE_PORT_TYPE_HANDSET},
103     {"Bluetooth", PA_DEVICE_PORT_TYPE_BLUETOOTH},
104     {"Earpiece", PA_DEVICE_PORT_TYPE_EARPIECE},
105     {"SPDIF", PA_DEVICE_PORT_TYPE_SPDIF},
106     {"HDMI", PA_DEVICE_PORT_TYPE_HDMI},
107     {NULL, 0}
108 };
109 
110 static struct ucm_items item[] = {
111     {"PlaybackPCM", PA_ALSA_PROP_UCM_SINK},
112     {"CapturePCM", PA_ALSA_PROP_UCM_SOURCE},
113     {"PlaybackCTL", PA_ALSA_PROP_UCM_PLAYBACK_CTL_DEVICE},
114     {"PlaybackVolume", PA_ALSA_PROP_UCM_PLAYBACK_VOLUME},
115     {"PlaybackSwitch", PA_ALSA_PROP_UCM_PLAYBACK_SWITCH},
116     {"PlaybackMixer", PA_ALSA_PROP_UCM_PLAYBACK_MIXER_DEVICE},
117     {"PlaybackMixerElem", PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM},
118     {"PlaybackMasterElem", PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ELEM},
119     {"PlaybackMasterType", PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE},
120     {"PlaybackPriority", PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY},
121     {"PlaybackRate", PA_ALSA_PROP_UCM_PLAYBACK_RATE},
122     {"PlaybackChannels", PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS},
123     {"CaptureCTL", PA_ALSA_PROP_UCM_CAPTURE_CTL_DEVICE},
124     {"CaptureVolume", PA_ALSA_PROP_UCM_CAPTURE_VOLUME},
125     {"CaptureSwitch", PA_ALSA_PROP_UCM_CAPTURE_SWITCH},
126     {"CaptureMixer", PA_ALSA_PROP_UCM_CAPTURE_MIXER_DEVICE},
127     {"CaptureMixerElem", PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM},
128     {"CaptureMasterElem", PA_ALSA_PROP_UCM_CAPTURE_MASTER_ELEM},
129     {"CaptureMasterType", PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE},
130     {"CapturePriority", PA_ALSA_PROP_UCM_CAPTURE_PRIORITY},
131     {"CaptureRate", PA_ALSA_PROP_UCM_CAPTURE_RATE},
132     {"CaptureChannels", PA_ALSA_PROP_UCM_CAPTURE_CHANNELS},
133     {"TQ", PA_ALSA_PROP_UCM_QOS},
134     {"JackCTL", PA_ALSA_PROP_UCM_JACK_DEVICE},
135     {"JackControl", PA_ALSA_PROP_UCM_JACK_CONTROL},
136     {"JackHWMute", PA_ALSA_PROP_UCM_JACK_HW_MUTE},
137     {NULL, NULL},
138 };
139 
140 /* UCM verb info - this should eventually be part of policy manangement */
141 static struct ucm_info verb_info[] = {
142     {SND_USE_CASE_VERB_INACTIVE, 0},
143     {SND_USE_CASE_VERB_HIFI, 8000},
144     {SND_USE_CASE_VERB_HIFI_LOW_POWER, 7000},
145     {SND_USE_CASE_VERB_VOICE, 6000},
146     {SND_USE_CASE_VERB_VOICE_LOW_POWER, 5000},
147     {SND_USE_CASE_VERB_VOICECALL, 4000},
148     {SND_USE_CASE_VERB_IP_VOICECALL, 4000},
149     {SND_USE_CASE_VERB_ANALOG_RADIO, 3000},
150     {SND_USE_CASE_VERB_DIGITAL_RADIO, 3000},
151     {NULL, 0}
152 };
153 
154 /* UCM device info - should be overwritten by ucm property */
155 static struct ucm_info dev_info[] = {
156     {SND_USE_CASE_DEV_SPEAKER, 100},
157     {SND_USE_CASE_DEV_LINE, 100},
158     {SND_USE_CASE_DEV_HEADPHONES, 100},
159     {SND_USE_CASE_DEV_HEADSET, 300},
160     {SND_USE_CASE_DEV_HANDSET, 200},
161     {SND_USE_CASE_DEV_BLUETOOTH, 400},
162     {SND_USE_CASE_DEV_EARPIECE, 100},
163     {SND_USE_CASE_DEV_SPDIF, 100},
164     {SND_USE_CASE_DEV_HDMI, 100},
165     {SND_USE_CASE_DEV_NONE, 100},
166     {NULL, 0}
167 };
168 
169 
ucm_verb_value(snd_use_case_mgr_t * uc_mgr,const char * verb_name,const char * id)170 static char *ucm_verb_value(
171     snd_use_case_mgr_t *uc_mgr,
172     const char *verb_name,
173     const char *id) {
174 
175     const char *value;
176     char *_id = pa_sprintf_malloc("=%s//%s", id, verb_name);
177     int err = snd_use_case_get(uc_mgr, _id, &value);
178     pa_xfree(_id);
179     if (err < 0)
180          return NULL;
181     pa_log_debug("Got %s for verb %s: %s", id, verb_name, value);
182     /* Use the cast here to allow free() call without casting for callers.
183      * The snd_use_case_get() returns mallocated string.
184      * See the Note: in use-case.h for snd_use_case_get().
185      */
186     return (char *)value;
187 }
188 
ucm_add_devices_to_idxset(pa_idxset * idxset,pa_alsa_ucm_device * me,pa_alsa_ucm_device * devices,const char ** dev_names,int n)189 static void ucm_add_devices_to_idxset(
190         pa_idxset *idxset,
191         pa_alsa_ucm_device *me,
192         pa_alsa_ucm_device *devices,
193         const char **dev_names,
194         int n) {
195 
196     pa_alsa_ucm_device *d;
197 
198     PA_LLIST_FOREACH(d, devices) {
199         const char *name;
200         int i;
201 
202         if (d == me)
203             continue;
204 
205         name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
206 
207         for (i = 0; i < n; i++)
208             if (pa_streq(dev_names[i], name))
209                 pa_idxset_put(idxset, d, NULL);
210     }
211 }
212 
213 /* Split a string into words. Like pa_split_spaces() but handle '' and "". */
ucm_split_devnames(const char * c,const char ** state)214 static char *ucm_split_devnames(const char *c, const char **state) {
215     const char *current = *state ? *state : c;
216     char h;
217     size_t l;
218 
219     if (!*current || *c == 0)
220         return NULL;
221 
222     current += strspn(current, "\n\r \t");
223     h = *current;
224     if (h == '\'' || h =='"') {
225         c = ++current;
226         for (l = 0; *c && *c != h; l++) c++;
227         if (*c != h)
228             return NULL;
229         *state = c + 1;
230     } else {
231         l = strcspn(current, "\n\r \t");
232         *state = current+l;
233     }
234 
235     return pa_xstrndup(current, l);
236 }
237 
238 
ucm_volume_free(pa_alsa_ucm_volume * vol)239 static void ucm_volume_free(pa_alsa_ucm_volume *vol) {
240     pa_assert(vol);
241     pa_xfree(vol->mixer_elem);
242     pa_xfree(vol->master_elem);
243     pa_xfree(vol->master_type);
244     pa_xfree(vol);
245 }
246 
247 /* Get the volume identifier */
ucm_get_mixer_id(pa_alsa_ucm_device * device,const char * mprop,const char * cprop,const char * cid)248 static char *ucm_get_mixer_id(
249         pa_alsa_ucm_device *device,
250         const char *mprop,
251         const char *cprop,
252         const char *cid)
253 {
254 #if SND_LIB_VERSION >= 0x10201 /* alsa-lib-1.2.1+ check */
255     snd_ctl_elem_id_t *ctl;
256     int err;
257 #endif
258     const char *value;
259     char *value2;
260     int index;
261 
262     /* mixer element as first, if it's found, return it without modifications */
263     value = pa_proplist_gets(device->proplist, mprop);
264     if (value)
265         return pa_xstrdup(value);
266     /* fallback, get the control element identifier */
267     /* and try to do some heuristic to determine the mixer element name */
268     value = pa_proplist_gets(device->proplist, cprop);
269     if (value == NULL)
270         return NULL;
271 #if SND_LIB_VERSION >= 0x10201 /* alsa-lib-1.2.1+ check */
272     /* The new parser may return also element index. */
273     snd_ctl_elem_id_alloca(&ctl);
274     err = snd_use_case_parse_ctl_elem_id(ctl, cid, value);
275     if (err < 0)
276         return NULL;
277     value = snd_ctl_elem_id_get_name(ctl);
278     index = snd_ctl_elem_id_get_index(ctl);
279 #else
280 #warning "Upgrade to alsa-lib 1.2.1!"
281     index = 0;
282 #endif
283     if (!(value2 = pa_str_strip_suffix(value, " Playback Volume")))
284         if (!(value2 = pa_str_strip_suffix(value, " Capture Volume")))
285             if (!(value2 = pa_str_strip_suffix(value, " Volume")))
286                 value2 = pa_xstrdup(value);
287     if (index > 0) {
288         char *mix = pa_sprintf_malloc("'%s',%d", value2, index);
289         pa_xfree(value2);
290         return mix;
291     }
292     return value2;
293 }
294 
295 /* Get the volume identifier */
ucm_get_mixer_volume(pa_alsa_ucm_device * device,const char * mprop,const char * cprop,const char * cid,const char * masterid,const char * mastertype)296 static pa_alsa_ucm_volume *ucm_get_mixer_volume(
297         pa_alsa_ucm_device *device,
298         const char *mprop,
299         const char *cprop,
300         const char *cid,
301         const char *masterid,
302         const char *mastertype)
303 {
304     pa_alsa_ucm_volume *vol;
305     char *mixer_elem;
306 
307     mixer_elem = ucm_get_mixer_id(device, mprop, cprop, cid);
308     if (mixer_elem == NULL)
309         return NULL;
310     vol = pa_xnew0(pa_alsa_ucm_volume, 1);
311     if (vol == NULL) {
312         pa_xfree(mixer_elem);
313         return NULL;
314     }
315     vol->mixer_elem = mixer_elem;
316     vol->master_elem = pa_xstrdup(pa_proplist_gets(device->proplist, masterid));
317     vol->master_type = pa_xstrdup(pa_proplist_gets(device->proplist, mastertype));
318     return vol;
319 }
320 
321 /* Get the ALSA mixer device for the UCM device */
get_mixer_device(pa_alsa_ucm_device * dev,bool is_sink)322 static const char *get_mixer_device(pa_alsa_ucm_device *dev, bool is_sink)
323 {
324     const char *dev_name;
325 
326     if (is_sink) {
327         dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_PLAYBACK_MIXER_DEVICE);
328         if (!dev_name)
329             dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CTL_DEVICE);
330     } else {
331         dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_CAPTURE_MIXER_DEVICE);
332         if (!dev_name)
333             dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_CAPTURE_CTL_DEVICE);
334     }
335     return dev_name;
336 }
337 
338 /* Get the ALSA mixer device for the UCM jack */
get_jack_mixer_device(pa_alsa_ucm_device * dev,bool is_sink)339 static const char *get_jack_mixer_device(pa_alsa_ucm_device *dev, bool is_sink) {
340     const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_JACK_DEVICE);
341     if (!dev_name)
342         return get_mixer_device(dev, is_sink);
343     return dev_name;
344 }
345 
346 /* Create a property list for this ucm device */
ucm_get_device_property(pa_alsa_ucm_device * device,snd_use_case_mgr_t * uc_mgr,pa_alsa_ucm_verb * verb,const char * device_name)347 static int ucm_get_device_property(
348         pa_alsa_ucm_device *device,
349         snd_use_case_mgr_t *uc_mgr,
350         pa_alsa_ucm_verb *verb,
351         const char *device_name) {
352 
353     const char *value;
354     const char **devices;
355     char *id, *s;
356     int i;
357     int err;
358     uint32_t ui;
359     int n_confdev, n_suppdev;
360     pa_alsa_ucm_volume *vol;
361 
362     /* determine the device type */
363     device->type = PA_DEVICE_PORT_TYPE_UNKNOWN;
364     id = s = pa_xstrdup(device_name);
365     while (s && *s && isalpha(*s)) s++;
366     if (s)
367         *s = '\0';
368     for (i = 0; types[i].prefix; i++)
369         if (pa_streq(id, types[i].prefix)) {
370             device->type = types[i].type;
371             break;
372         }
373     pa_xfree(id);
374 
375     /* set properties */
376     for (i = 0; item[i].id; i++) {
377         id = pa_sprintf_malloc("%s/%s", item[i].id, device_name);
378         err = snd_use_case_get(uc_mgr, id, &value);
379         pa_xfree(id);
380         if (err < 0)
381             continue;
382 
383         pa_log_debug("Got %s for device %s: %s", item[i].id, device_name, value);
384         pa_proplist_sets(device->proplist, item[i].property, value);
385         free((void*)value);
386     }
387 
388     /* get direction and channels */
389     value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS);
390     if (value) { /* output */
391         /* get channels */
392         if (pa_atou(value, &ui) == 0 && pa_channels_valid(ui))
393             device->playback_channels = ui;
394         else
395             pa_log("UCM playback channels %s for device %s out of range", value, device_name);
396 
397         /* get pcm */
398         value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SINK);
399         if (!value) /* take pcm from verb playback default */
400             pa_log("UCM playback device %s fetch pcm failed", device_name);
401     }
402 
403     if (pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SINK) &&
404         device->playback_channels == 0) {
405         pa_log_info("UCM file does not specify 'PlaybackChannels' "
406                     "for device %s, assuming stereo.", device_name);
407         device->playback_channels = 2;
408     }
409 
410     value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS);
411     if (value) { /* input */
412         /* get channels */
413         if (pa_atou(value, &ui) == 0 && pa_channels_valid(ui))
414             device->capture_channels = ui;
415         else
416             pa_log("UCM capture channels %s for device %s out of range", value, device_name);
417 
418         /* get pcm */
419         value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SOURCE);
420         if (!value) /* take pcm from verb capture default */
421             pa_log("UCM capture device %s fetch pcm failed", device_name);
422     }
423 
424     if (pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SOURCE) &&
425         device->capture_channels == 0) {
426         pa_log_info("UCM file does not specify 'CaptureChannels' "
427                     "for device %s, assuming stereo.", device_name);
428         device->capture_channels = 2;
429     }
430 
431     /* get rate and priority of device */
432     if (device->playback_channels) { /* sink device */
433         /* get rate */
434         if ((value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_RATE))) {
435             if (pa_atou(value, &ui) == 0 && pa_sample_rate_valid(ui)) {
436                 pa_log_debug("UCM playback device %s rate %d", device_name, ui);
437                 device->playback_rate = ui;
438             } else
439                 pa_log_debug("UCM playback device %s has bad rate %s", device_name, value);
440         }
441 
442         value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY);
443         if (value) {
444             /* get priority from ucm config */
445             if (pa_atou(value, &ui) == 0)
446                 device->playback_priority = ui;
447             else
448                 pa_log_debug("UCM playback priority %s for device %s error", value, device_name);
449         }
450 
451         vol = ucm_get_mixer_volume(device,
452                                    PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM,
453                                    PA_ALSA_PROP_UCM_PLAYBACK_VOLUME,
454                                    "PlaybackVolume",
455                                    PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ELEM,
456                                    PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE);
457         if (vol)
458             pa_hashmap_put(device->playback_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), vol);
459     }
460 
461     if (device->capture_channels) { /* source device */
462         /* get rate */
463         if ((value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_RATE))) {
464             if (pa_atou(value, &ui) == 0 && pa_sample_rate_valid(ui)) {
465                 pa_log_debug("UCM capture device %s rate %d", device_name, ui);
466                 device->capture_rate = ui;
467             } else
468                 pa_log_debug("UCM capture device %s has bad rate %s", device_name, value);
469         }
470 
471         value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_PRIORITY);
472         if (value) {
473             /* get priority from ucm config */
474             if (pa_atou(value, &ui) == 0)
475                 device->capture_priority = ui;
476             else
477                 pa_log_debug("UCM capture priority %s for device %s error", value, device_name);
478         }
479 
480         vol = ucm_get_mixer_volume(device,
481                                    PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM,
482                                    PA_ALSA_PROP_UCM_CAPTURE_VOLUME,
483                                    "CaptureVolume",
484                                    PA_ALSA_PROP_UCM_CAPTURE_MASTER_ELEM,
485                                    PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE);
486         if (vol)
487           pa_hashmap_put(device->capture_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), vol);
488     }
489 
490     if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device) || PA_UCM_CAPTURE_PRIORITY_UNSET(device)) {
491         /* get priority from static table */
492         for (i = 0; dev_info[i].id; i++) {
493             if (strcasecmp(dev_info[i].id, device_name) == 0) {
494                 PA_UCM_DEVICE_PRIORITY_SET(device, dev_info[i].priority);
495                 break;
496             }
497         }
498     }
499 
500     if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device)) {
501         /* fall through to default priority */
502         device->playback_priority = 100;
503     }
504 
505     if (PA_UCM_CAPTURE_PRIORITY_UNSET(device)) {
506         /* fall through to default priority */
507         device->capture_priority = 100;
508     }
509 
510     id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", device_name);
511     n_confdev = snd_use_case_get_list(uc_mgr, id, &devices);
512     pa_xfree(id);
513 
514     device->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
515     if (n_confdev <= 0)
516         pa_log_debug("No %s for device %s", "_conflictingdevs", device_name);
517     else {
518         ucm_add_devices_to_idxset(device->conflicting_devices, device, verb->devices, devices, n_confdev);
519         snd_use_case_free_list(devices, n_confdev);
520     }
521 
522     id = pa_sprintf_malloc("%s/%s", "_supporteddevs", device_name);
523     n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices);
524     pa_xfree(id);
525 
526     device->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
527     if (n_suppdev <= 0)
528         pa_log_debug("No %s for device %s", "_supporteddevs", device_name);
529     else {
530         ucm_add_devices_to_idxset(device->supported_devices, device, verb->devices, devices, n_suppdev);
531         snd_use_case_free_list(devices, n_suppdev);
532     }
533 
534     return 0;
535 };
536 
537 /* Create a property list for this ucm modifier */
ucm_get_modifier_property(pa_alsa_ucm_modifier * modifier,snd_use_case_mgr_t * uc_mgr,pa_alsa_ucm_verb * verb,const char * modifier_name)538 static int ucm_get_modifier_property(
539         pa_alsa_ucm_modifier *modifier,
540         snd_use_case_mgr_t *uc_mgr,
541         pa_alsa_ucm_verb *verb,
542         const char *modifier_name) {
543     const char *value;
544     char *id;
545     int i;
546     const char **devices;
547     int n_confdev, n_suppdev;
548 
549     for (i = 0; item[i].id; i++) {
550         int err;
551 
552         id = pa_sprintf_malloc("=%s/%s", item[i].id, modifier_name);
553         err = snd_use_case_get(uc_mgr, id, &value);
554         pa_xfree(id);
555         if (err < 0)
556             continue;
557 
558         pa_log_debug("Got %s for modifier %s: %s", item[i].id, modifier_name, value);
559         pa_proplist_sets(modifier->proplist, item[i].property, value);
560         free((void*)value);
561     }
562 
563     id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", modifier_name);
564     n_confdev = snd_use_case_get_list(uc_mgr, id, &devices);
565     pa_xfree(id);
566 
567     modifier->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
568     if (n_confdev <= 0)
569         pa_log_debug("No %s for modifier %s", "_conflictingdevs", modifier_name);
570     else {
571         ucm_add_devices_to_idxset(modifier->conflicting_devices, NULL, verb->devices, devices, n_confdev);
572         snd_use_case_free_list(devices, n_confdev);
573     }
574 
575     id = pa_sprintf_malloc("%s/%s", "_supporteddevs", modifier_name);
576     n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices);
577     pa_xfree(id);
578 
579     modifier->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
580     if (n_suppdev <= 0)
581         pa_log_debug("No %s for modifier %s", "_supporteddevs", modifier_name);
582     else {
583         ucm_add_devices_to_idxset(modifier->supported_devices, NULL, verb->devices, devices, n_suppdev);
584         snd_use_case_free_list(devices, n_suppdev);
585     }
586 
587     return 0;
588 };
589 
590 /* Create a list of devices for this verb */
ucm_get_devices(pa_alsa_ucm_verb * verb,snd_use_case_mgr_t * uc_mgr)591 static int ucm_get_devices(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) {
592     const char **dev_list;
593     int num_dev, i;
594 
595     num_dev = snd_use_case_get_list(uc_mgr, "_devices", &dev_list);
596     if (num_dev < 0)
597         return num_dev;
598 
599     for (i = 0; i < num_dev; i += 2) {
600         pa_alsa_ucm_device *d = pa_xnew0(pa_alsa_ucm_device, 1);
601 
602         d->proplist = pa_proplist_new();
603         pa_proplist_sets(d->proplist, PA_ALSA_PROP_UCM_NAME, pa_strnull(dev_list[i]));
604         pa_proplist_sets(d->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(dev_list[i + 1]));
605         d->ucm_ports = pa_dynarray_new(NULL);
606         d->hw_mute_jacks = pa_dynarray_new(NULL);
607         d->available = PA_AVAILABLE_UNKNOWN;
608 
609         d->playback_volumes = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree,
610                                                   (pa_free_cb_t) ucm_volume_free);
611         d->capture_volumes = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree,
612                                                  (pa_free_cb_t) ucm_volume_free);
613 
614         PA_LLIST_PREPEND(pa_alsa_ucm_device, verb->devices, d);
615     }
616 
617     snd_use_case_free_list(dev_list, num_dev);
618 
619     return 0;
620 };
621 
ucm_device_status(pa_alsa_ucm_config * ucm,pa_alsa_ucm_device * dev)622 static long ucm_device_status(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *dev) {
623     const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
624     char *devstatus;
625     long status = 0;
626 
627     devstatus = pa_sprintf_malloc("_devstatus/%s", dev_name);
628     if (snd_use_case_geti(ucm->ucm_mgr, devstatus, &status) < 0) {
629         pa_log_debug("Failed to get status for UCM device %s", dev_name);
630         status = -1;
631     }
632     pa_xfree(devstatus);
633 
634     return status;
635 }
636 
ucm_device_disable(pa_alsa_ucm_config * ucm,pa_alsa_ucm_device * dev)637 static int ucm_device_disable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *dev) {
638     const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
639 
640     /* If any of dev's conflicting devices is enabled, trying to disable
641      * dev gives an error despite the fact that it's already disabled.
642      * Check that dev is enabled to avoid this error. */
643     if (ucm_device_status(ucm, dev) == 0) {
644         pa_log_debug("UCM device %s is already disabled", dev_name);
645         return 0;
646     }
647 
648     pa_log_debug("Disabling UCM device %s", dev_name);
649     if (snd_use_case_set(ucm->ucm_mgr, "_disdev", dev_name) < 0) {
650         pa_log("Failed to disable UCM device %s", dev_name);
651         return -1;
652     }
653 
654     return 0;
655 }
656 
ucm_device_enable(pa_alsa_ucm_config * ucm,pa_alsa_ucm_device * dev)657 static int ucm_device_enable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *dev) {
658     const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
659 
660     /* We don't need to enable devices that are already enabled */
661     if (ucm_device_status(ucm, dev) > 0) {
662         pa_log_debug("UCM device %s is already enabled", dev_name);
663         return 0;
664     }
665 
666     pa_log_debug("Enabling UCM device %s", dev_name);
667     if (snd_use_case_set(ucm->ucm_mgr, "_enadev", dev_name) < 0) {
668         pa_log("Failed to enable UCM device %s", dev_name);
669         return -1;
670     }
671 
672     return 0;
673 }
674 
ucm_get_modifiers(pa_alsa_ucm_verb * verb,snd_use_case_mgr_t * uc_mgr)675 static int ucm_get_modifiers(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) {
676     const char **mod_list;
677     int num_mod, i;
678 
679     num_mod = snd_use_case_get_list(uc_mgr, "_modifiers", &mod_list);
680     if (num_mod < 0)
681         return num_mod;
682 
683     for (i = 0; i < num_mod; i += 2) {
684         pa_alsa_ucm_modifier *m;
685 
686         if (!mod_list[i]) {
687             pa_log_warn("Got a modifier with a null name. Skipping.");
688             continue;
689         }
690 
691         m = pa_xnew0(pa_alsa_ucm_modifier, 1);
692         m->proplist = pa_proplist_new();
693 
694         pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_NAME, mod_list[i]);
695         pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(mod_list[i + 1]));
696 
697         PA_LLIST_PREPEND(pa_alsa_ucm_modifier, verb->modifiers, m);
698     }
699 
700     snd_use_case_free_list(mod_list, num_mod);
701 
702     return 0;
703 };
704 
ucm_modifier_status(pa_alsa_ucm_config * ucm,pa_alsa_ucm_modifier * mod)705 static long ucm_modifier_status(pa_alsa_ucm_config *ucm, pa_alsa_ucm_modifier *mod) {
706     const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
707     char *modstatus;
708     long status = 0;
709 
710     modstatus = pa_sprintf_malloc("_modstatus/%s", mod_name);
711     if (snd_use_case_geti(ucm->ucm_mgr, modstatus, &status) < 0) {
712         pa_log_debug("Failed to get status for UCM modifier %s", mod_name);
713         status = -1;
714     }
715     pa_xfree(modstatus);
716 
717     return status;
718 }
719 
ucm_modifier_disable(pa_alsa_ucm_config * ucm,pa_alsa_ucm_modifier * mod)720 static int ucm_modifier_disable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_modifier *mod) {
721     const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
722 
723     /* We don't need to disable modifiers that are already disabled */
724     if (ucm_modifier_status(ucm, mod) == 0) {
725         pa_log_debug("UCM modifier %s is already disabled", mod_name);
726         return 0;
727     }
728 
729     pa_log_debug("Disabling UCM modifier %s", mod_name);
730     if (snd_use_case_set(ucm->ucm_mgr, "_dismod", mod_name) < 0) {
731         pa_log("Failed to disable UCM modifier %s", mod_name);
732         return -1;
733     }
734 
735     return 0;
736 }
737 
ucm_modifier_enable(pa_alsa_ucm_config * ucm,pa_alsa_ucm_modifier * mod)738 static int ucm_modifier_enable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_modifier *mod) {
739     const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
740 
741     /* We don't need to enable modifiers that are already enabled */
742     if (ucm_modifier_status(ucm, mod) > 0) {
743         pa_log_debug("UCM modifier %s is already enabled", mod_name);
744         return 0;
745     }
746 
747     pa_log_debug("Enabling UCM modifier %s", mod_name);
748     if (snd_use_case_set(ucm->ucm_mgr, "_enamod", mod_name) < 0) {
749         pa_log("Failed to enable UCM modifier %s", mod_name);
750         return -1;
751     }
752 
753     return 0;
754 }
755 
add_role_to_device(pa_alsa_ucm_device * dev,const char * dev_name,const char * role_name,const char * role)756 static void add_role_to_device(pa_alsa_ucm_device *dev, const char *dev_name, const char *role_name, const char *role) {
757     const char *cur = pa_proplist_gets(dev->proplist, role_name);
758 
759     if (!cur)
760         pa_proplist_sets(dev->proplist, role_name, role);
761     else if (!pa_str_in_list_spaces(cur, role)) { /* does not exist */
762         char *value = pa_sprintf_malloc("%s %s", cur, role);
763 
764         pa_proplist_sets(dev->proplist, role_name, value);
765         pa_xfree(value);
766     }
767 
768     pa_log_info("Add role %s to device %s(%s), result %s", role, dev_name, role_name, pa_proplist_gets(dev->proplist,
769                 role_name));
770 }
771 
add_media_role(pa_alsa_ucm_device * dev,const char * role_name,const char * role,bool is_sink)772 static void add_media_role(pa_alsa_ucm_device *dev, const char *role_name, const char *role, bool is_sink) {
773     const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
774     const char *sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK);
775     const char *source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE);
776 
777     if (is_sink && sink)
778         add_role_to_device(dev, dev_name, role_name, role);
779     else if (!is_sink && source)
780         add_role_to_device(dev, dev_name, role_name, role);
781 }
782 
modifier_name_to_role(const char * mod_name,bool * is_sink)783 static char *modifier_name_to_role(const char *mod_name, bool *is_sink) {
784     char *sub = NULL, *tmp, *pos;
785 
786     *is_sink = false;
787 
788     if (pa_startswith(mod_name, "Play")) {
789         *is_sink = true;
790         sub = pa_xstrdup(mod_name + 4);
791     } else if (pa_startswith(mod_name, "Capture"))
792         sub = pa_xstrdup(mod_name + 7);
793 
794     pos = sub;
795     while (pos && *pos == ' ') pos++;
796 
797     if (!pos || !*pos) {
798         pa_xfree(sub);
799         pa_log_warn("Can't match media roles for modifier %s", mod_name);
800         return NULL;
801     }
802 
803     tmp = pos;
804 
805     do {
806         *tmp = tolower(*tmp);
807     } while (*(++tmp));
808 
809     tmp = pa_xstrdup(pos);
810     pa_xfree(sub);
811     return tmp;
812 }
813 
ucm_set_media_roles(pa_alsa_ucm_modifier * modifier,const char * mod_name)814 static void ucm_set_media_roles(pa_alsa_ucm_modifier *modifier, const char *mod_name) {
815     pa_alsa_ucm_device *dev;
816     bool is_sink = false;
817     char *sub = NULL;
818     const char *role_name;
819     uint32_t idx;
820 
821     sub = modifier_name_to_role(mod_name, &is_sink);
822     if (!sub)
823         return;
824 
825     modifier->action_direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
826     modifier->media_role = sub;
827 
828     role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES;
829     PA_IDXSET_FOREACH(dev, modifier->supported_devices, idx) {
830         /* if modifier has no specific pcm, we add role intent to its supported devices */
831         if (!pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SINK) &&
832                 !pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SOURCE))
833             add_media_role(dev, role_name, sub, is_sink);
834     }
835 }
836 
append_lost_relationship(pa_alsa_ucm_device * dev)837 static void append_lost_relationship(pa_alsa_ucm_device *dev) {
838     uint32_t idx;
839     pa_alsa_ucm_device *d;
840 
841     PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx)
842         if (pa_idxset_put(d->conflicting_devices, dev, NULL) == 0)
843             pa_log_warn("Add lost conflicting device %s to %s",
844                     pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME),
845                     pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME));
846 
847     PA_IDXSET_FOREACH(d, dev->supported_devices, idx)
848         if (pa_idxset_put(d->supported_devices, dev, NULL) == 0)
849             pa_log_warn("Add lost supported device %s to %s",
850                     pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME),
851                     pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME));
852 }
853 
pa_alsa_ucm_query_profiles(pa_alsa_ucm_config * ucm,int card_index)854 int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index) {
855     char *card_name;
856     const char **verb_list, *value;
857     int num_verbs, i, err = 0;
858 
859     /* support multiple card instances, address card directly by index */
860     card_name = pa_sprintf_malloc("hw:%i", card_index);
861     err = snd_use_case_mgr_open(&ucm->ucm_mgr, card_name);
862     if (err < 0) {
863         /* fallback longname: is UCM available for this card ? */
864         pa_xfree(card_name);
865         err = snd_card_get_name(card_index, &card_name);
866         if (err < 0) {
867             pa_log("Card can't get card_name from card_index %d", card_index);
868             err = -PA_ALSA_ERR_UNSPECIFIED;
869             goto name_fail;
870         }
871 
872         err = snd_use_case_mgr_open(&ucm->ucm_mgr, card_name);
873         if (err < 0) {
874             pa_log_info("UCM not available for card %s", card_name);
875             err = -PA_ALSA_ERR_UCM_OPEN;
876             goto ucm_mgr_fail;
877         }
878     }
879 
880     err = snd_use_case_get(ucm->ucm_mgr, "=Linked", &value);
881     if (err >= 0) {
882         if (strcasecmp(value, "true") == 0 || strcasecmp(value, "1") == 0) {
883             free((void *)value);
884             pa_log_info("Empty (linked) UCM for card %s", card_name);
885             err = -PA_ALSA_ERR_UCM_LINKED;
886             goto ucm_verb_fail;
887         }
888         free((void *)value);
889     }
890 
891     pa_log_info("UCM available for card %s", card_name);
892 
893     if (snd_use_case_get(ucm->ucm_mgr, "_alibpref", &value) == 0) {
894         if (value[0]) {
895             ucm->alib_prefix = pa_xstrdup(value);
896             pa_log_debug("UCM _alibpref=%s", ucm->alib_prefix);
897         }
898         free((void *)value);
899     }
900 
901     /* get a list of all UCM verbs for this card */
902     num_verbs = snd_use_case_verb_list(ucm->ucm_mgr, &verb_list);
903     if (num_verbs < 0) {
904         pa_log("UCM verb list not found for %s", card_name);
905         err = -PA_ALSA_ERR_UNSPECIFIED;
906         goto ucm_verb_fail;
907     }
908 
909     /* get the properties of each UCM verb */
910     for (i = 0; i < num_verbs; i += 2) {
911         pa_alsa_ucm_verb *verb;
912 
913         /* Get devices and modifiers for each verb */
914         err = pa_alsa_ucm_get_verb(ucm->ucm_mgr, verb_list[i], verb_list[i+1], &verb);
915         if (err < 0) {
916             pa_log("Failed to get the verb %s", verb_list[i]);
917             continue;
918         }
919 
920         PA_LLIST_PREPEND(pa_alsa_ucm_verb, ucm->verbs, verb);
921     }
922 
923     if (!ucm->verbs) {
924         pa_log("No UCM verb is valid for %s", card_name);
925         err = -PA_ALSA_ERR_UCM_NO_VERB;
926     }
927 
928     snd_use_case_free_list(verb_list, num_verbs);
929 
930 ucm_verb_fail:
931     if (err < 0) {
932         snd_use_case_mgr_close(ucm->ucm_mgr);
933         ucm->ucm_mgr = NULL;
934     }
935 
936 ucm_mgr_fail:
937     pa_xfree(card_name);
938 
939 name_fail:
940     return err;
941 }
942 
pa_alsa_ucm_get_verb(snd_use_case_mgr_t * uc_mgr,const char * verb_name,const char * verb_desc,pa_alsa_ucm_verb ** p_verb)943 int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name, const char *verb_desc, pa_alsa_ucm_verb **p_verb) {
944     pa_alsa_ucm_device *d;
945     pa_alsa_ucm_modifier *mod;
946     pa_alsa_ucm_verb *verb;
947     char *value;
948     unsigned ui;
949     int err = 0;
950 
951     *p_verb = NULL;
952     pa_log_info("Set UCM verb to %s", verb_name);
953     err = snd_use_case_set(uc_mgr, "_verb", verb_name);
954     if (err < 0)
955         return err;
956 
957     verb = pa_xnew0(pa_alsa_ucm_verb, 1);
958     verb->proplist = pa_proplist_new();
959 
960     pa_proplist_sets(verb->proplist, PA_ALSA_PROP_UCM_NAME, pa_strnull(verb_name));
961     pa_proplist_sets(verb->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(verb_desc));
962 
963     value = ucm_verb_value(uc_mgr, verb_name, "Priority");
964     if (value && !pa_atou(value, &ui))
965         verb->priority = ui > 10000 ? 10000 : ui;
966     free(value);
967 
968     err = ucm_get_devices(verb, uc_mgr);
969     if (err < 0)
970         pa_log("No UCM devices for verb %s", verb_name);
971 
972     err = ucm_get_modifiers(verb, uc_mgr);
973     if (err < 0)
974         pa_log("No UCM modifiers for verb %s", verb_name);
975 
976     PA_LLIST_FOREACH(d, verb->devices) {
977         const char *dev_name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
978 
979         /* Devices properties */
980         ucm_get_device_property(d, uc_mgr, verb, dev_name);
981     }
982     /* make conflicting or supported device mutual */
983     PA_LLIST_FOREACH(d, verb->devices)
984         append_lost_relationship(d);
985 
986     PA_LLIST_FOREACH(mod, verb->modifiers) {
987         const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
988 
989         /* Modifier properties */
990         ucm_get_modifier_property(mod, uc_mgr, verb, mod_name);
991 
992         /* Set PA_PROP_DEVICE_INTENDED_ROLES property to devices */
993         pa_log_debug("Set media roles for verb %s, modifier %s", verb_name, mod_name);
994         ucm_set_media_roles(mod, mod_name);
995     }
996 
997     *p_verb = verb;
998     return 0;
999 }
1000 
pa_alsa_ucm_device_cmp(const void * a,const void * b)1001 static int pa_alsa_ucm_device_cmp(const void *a, const void *b) {
1002     const pa_alsa_ucm_device *d1 = *(pa_alsa_ucm_device **)a;
1003     const pa_alsa_ucm_device *d2 = *(pa_alsa_ucm_device **)b;
1004 
1005     return strcmp(pa_proplist_gets(d1->proplist, PA_ALSA_PROP_UCM_NAME), pa_proplist_gets(d2->proplist, PA_ALSA_PROP_UCM_NAME));
1006 }
1007 
set_eld_devices(pa_hashmap * hash)1008 static void set_eld_devices(pa_hashmap *hash)
1009 {
1010     pa_device_port *port;
1011     pa_alsa_ucm_port_data *data;
1012     pa_alsa_ucm_device *dev;
1013     void *state;
1014 
1015     PA_HASHMAP_FOREACH(port, hash, state) {
1016         data = PA_DEVICE_PORT_DATA(port);
1017         dev = data->device;
1018         data->eld_device = dev->eld_device;
1019         data->eld_mixer_device_name = pa_xstrdup(dev->eld_mixer_device_name);
1020     }
1021 }
1022 
update_mixer_paths(pa_hashmap * ports,const char * verb_name)1023 static void update_mixer_paths(pa_hashmap *ports, const char *verb_name) {
1024     pa_device_port *port;
1025     pa_alsa_ucm_port_data *data;
1026     void *state;
1027 
1028     /* select volume controls on ports */
1029     PA_HASHMAP_FOREACH(port, ports, state) {
1030         pa_log_info("Updating mixer path for %s: %s", verb_name, port->name);
1031         data = PA_DEVICE_PORT_DATA(port);
1032         data->path = pa_hashmap_get(data->paths, verb_name);
1033     }
1034 }
1035 
probe_volumes(pa_hashmap * hash,bool is_sink,snd_pcm_t * pcm_handle,pa_hashmap * mixers,bool ignore_dB)1036 static void probe_volumes(pa_hashmap *hash, bool is_sink, snd_pcm_t *pcm_handle, pa_hashmap *mixers, bool ignore_dB) {
1037     pa_device_port *port;
1038     pa_alsa_path *path;
1039     pa_alsa_ucm_port_data *data;
1040     pa_alsa_ucm_device *dev;
1041     snd_mixer_t *mixer_handle;
1042     const char *verb_name, *mdev;
1043     void *state, *state2;
1044 
1045     PA_HASHMAP_FOREACH(port, hash, state) {
1046         data = PA_DEVICE_PORT_DATA(port);
1047 
1048         dev = data->device;
1049         mdev = get_mixer_device(dev, is_sink);
1050         if (mdev == NULL || !(mixer_handle = pa_alsa_open_mixer_by_name(mixers, mdev, true))) {
1051             pa_log_error("Failed to find a working mixer device (%s).", mdev);
1052             goto fail;
1053         }
1054 
1055         PA_HASHMAP_FOREACH_KV(verb_name, path, data->paths, state2) {
1056             if (pa_alsa_path_probe(path, NULL, mixer_handle, ignore_dB) < 0) {
1057                 pa_log_warn("Could not probe path: %s, using s/w volume", path->name);
1058                 pa_hashmap_remove(data->paths, verb_name);
1059             } else if (!path->has_volume && !path->has_mute) {
1060                 pa_log_warn("Path %s is not a volume or mute control", path->name);
1061                 pa_hashmap_remove(data->paths, verb_name);
1062             } else
1063                 pa_log_debug("Set up h/w %s using '%s' for %s:%s", path->has_volume ? "volume" : "mute",
1064                                 path->name, verb_name, port->name);
1065         }
1066     }
1067 
1068     return;
1069 
1070 fail:
1071     /* We could not probe the paths we created. Free them and revert to software volumes. */
1072     PA_HASHMAP_FOREACH(port, hash, state) {
1073         data = PA_DEVICE_PORT_DATA(port);
1074         pa_hashmap_remove_all(data->paths);
1075     }
1076 }
1077 
devset_name(pa_idxset * devices,const char * sep)1078 static char *devset_name(pa_idxset *devices, const char *sep) {
1079     int i = 0;
1080     int num = pa_idxset_size(devices);
1081     pa_alsa_ucm_device *sorted[num], *dev;
1082     char *dev_names = NULL;
1083     char *tmp = NULL;
1084     uint32_t idx;
1085 
1086     PA_IDXSET_FOREACH(dev, devices, idx) {
1087         sorted[i] = dev;
1088         i++;
1089     }
1090 
1091     /* Sort by alphabetical order so as to have a deterministic naming scheme */
1092     qsort(&sorted[0], num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp);
1093 
1094     for (i = 0; i < num; i++) {
1095         dev = sorted[i];
1096         const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
1097 
1098         if (!dev_names) {
1099             dev_names = pa_xstrdup(dev_name);
1100         } else {
1101             tmp = pa_sprintf_malloc("%s%s%s", dev_names, sep, dev_name);
1102             pa_xfree(dev_names);
1103             dev_names = tmp;
1104         }
1105     }
1106 
1107     return dev_names;
1108 }
1109 
devset_description(pa_idxset * devices,const char * sep)1110 PA_UNUSED static char *devset_description(pa_idxset *devices, const char *sep) {
1111     int i = 0;
1112     int num = pa_idxset_size(devices);
1113     pa_alsa_ucm_device *sorted[num], *dev;
1114     char *dev_descs = NULL;
1115     char *tmp = NULL;
1116     uint32_t idx;
1117 
1118     PA_IDXSET_FOREACH(dev, devices, idx) {
1119         sorted[i] = dev;
1120         i++;
1121     }
1122 
1123     /* Sort by alphabetical order to match devset_name() */
1124     qsort(&sorted[0], num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp);
1125 
1126     for (i = 0; i < num; i++) {
1127         dev = sorted[i];
1128         const char *dev_desc = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
1129 
1130         if (!dev_descs) {
1131             dev_descs = pa_xstrdup(dev_desc);
1132         } else {
1133             tmp = pa_sprintf_malloc("%s%s%s", dev_descs, sep, dev_desc);
1134             pa_xfree(dev_descs);
1135             dev_descs = tmp;
1136         }
1137     }
1138 
1139     return dev_descs;
1140 }
1141 
1142 /* If invert is true, uses the formula 1/p = 1/p1 + 1/p2 + ... 1/pn.
1143  * This way, the result will always be less than the individual components,
1144  * yet higher components will lead to higher result. */
devset_playback_priority(pa_idxset * devices,bool invert)1145 static unsigned devset_playback_priority(pa_idxset *devices, bool invert) {
1146     pa_alsa_ucm_device *dev;
1147     uint32_t idx;
1148     double priority = 0;
1149 
1150     PA_IDXSET_FOREACH(dev, devices, idx) {
1151         if (dev->playback_priority > 0 && invert)
1152             priority += 1.0 / dev->playback_priority;
1153         else
1154             priority += dev->playback_priority;
1155     }
1156 
1157     if (priority > 0 && invert)
1158         return 1.0 / priority;
1159 
1160     return (unsigned) priority;
1161 }
1162 
devset_capture_priority(pa_idxset * devices,bool invert)1163 static unsigned devset_capture_priority(pa_idxset *devices, bool invert) {
1164     pa_alsa_ucm_device *dev;
1165     uint32_t idx;
1166     double priority = 0;
1167 
1168     PA_IDXSET_FOREACH(dev, devices, idx) {
1169         if (dev->capture_priority > 0 && invert)
1170             priority += 1.0 / dev->capture_priority;
1171         else
1172             priority += dev->capture_priority;
1173     }
1174 
1175     if (priority > 0 && invert)
1176         return 1.0 / priority;
1177 
1178     return (unsigned) priority;
1179 }
1180 
pa_alsa_ucm_add_port(pa_hashmap * hash,pa_alsa_ucm_mapping_context * context,bool is_sink,pa_hashmap * ports,pa_card_profile * cp,pa_core * core)1181 void pa_alsa_ucm_add_port(
1182         pa_hashmap *hash,
1183         pa_alsa_ucm_mapping_context *context,
1184         bool is_sink,
1185         pa_hashmap *ports,
1186         pa_card_profile *cp,
1187         pa_core *core) {
1188 
1189     pa_device_port *port;
1190     unsigned priority;
1191     char *name, *desc;
1192     const char *dev_name;
1193     const char *direction;
1194     const char *verb_name;
1195     pa_alsa_ucm_device *dev;
1196     pa_alsa_ucm_port_data *data;
1197     pa_alsa_ucm_volume *vol;
1198     pa_alsa_jack *jack;
1199     pa_device_port_type_t type;
1200     void *state;
1201 
1202     dev = context->ucm_device;
1203     if (!dev)
1204         return;
1205 
1206     dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
1207     name = pa_sprintf_malloc("%s%s", is_sink ? PA_UCM_PRE_TAG_OUTPUT : PA_UCM_PRE_TAG_INPUT, dev_name);
1208     desc = pa_xstrdup(pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION));
1209     priority = is_sink ? dev->playback_priority : dev->capture_priority;
1210     jack = ucm_get_jack(context->ucm, dev);
1211     type = dev->type;
1212 
1213     port = pa_hashmap_get(ports, name);
1214     if (!port) {
1215         pa_device_port_new_data port_data;
1216 
1217         pa_device_port_new_data_init(&port_data);
1218         pa_device_port_new_data_set_name(&port_data, name);
1219         pa_device_port_new_data_set_description(&port_data, desc);
1220         pa_device_port_new_data_set_type(&port_data, type);
1221         pa_device_port_new_data_set_direction(&port_data, is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT);
1222         if (jack)
1223             pa_device_port_new_data_set_availability_group(&port_data, jack->name);
1224 
1225         port = pa_device_port_new(core, &port_data, sizeof(pa_alsa_ucm_port_data));
1226         pa_device_port_new_data_done(&port_data);
1227 
1228         data = PA_DEVICE_PORT_DATA(port);
1229         ucm_port_data_init(data, context->ucm, port, dev);
1230         port->impl_free = ucm_port_data_free;
1231 
1232         pa_hashmap_put(ports, port->name, port);
1233         pa_log_debug("Add port %s: %s", port->name, port->description);
1234 
1235         PA_HASHMAP_FOREACH_KV(verb_name, vol, is_sink ? dev->playback_volumes : dev->capture_volumes, state) {
1236             pa_alsa_path *path = pa_alsa_path_synthesize(vol->mixer_elem,
1237                                                          is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT);
1238 
1239             if (!path)
1240                 pa_log_warn("Failed to set up volume control: %s", vol->mixer_elem);
1241             else {
1242                 if (vol->master_elem) {
1243                     pa_alsa_element *e = pa_alsa_element_get(path, vol->master_elem, false);
1244                     e->switch_use = PA_ALSA_SWITCH_MUTE;
1245                     e->volume_use = PA_ALSA_VOLUME_MERGE;
1246                 }
1247 
1248                 pa_hashmap_put(data->paths, pa_xstrdup(verb_name), path);
1249 
1250                 /* Add path also to already created empty path set */
1251                 if (is_sink)
1252                     pa_hashmap_put(dev->playback_mapping->output_path_set->paths, pa_xstrdup(vol->mixer_elem), path);
1253                 else
1254                     pa_hashmap_put(dev->capture_mapping->input_path_set->paths, pa_xstrdup(vol->mixer_elem), path);
1255             }
1256         }
1257     }
1258 
1259     port->priority = priority;
1260 
1261     pa_xfree(name);
1262     pa_xfree(desc);
1263 
1264     direction = is_sink ? "output" : "input";
1265     pa_log_debug("Port %s direction %s, priority %d", port->name, direction, priority);
1266 
1267     if (cp) {
1268         pa_log_debug("Adding profile %s to port %s.", cp->name, port->name);
1269         pa_hashmap_put(port->profiles, cp->name, cp);
1270     }
1271 
1272     if (hash) {
1273         pa_hashmap_put(hash, port->name, port);
1274         pa_device_port_ref(port);
1275     }
1276 
1277     /* ELD devices */
1278     set_eld_devices(ports);
1279 }
1280 
devset_supports_device(pa_idxset * devices,pa_alsa_ucm_device * dev)1281 static bool devset_supports_device(pa_idxset *devices, pa_alsa_ucm_device *dev) {
1282     const char *sink, *sink2, *source, *source2;
1283     pa_alsa_ucm_device *d;
1284     uint32_t idx;
1285 
1286     pa_assert(devices);
1287     pa_assert(dev);
1288 
1289     /* Can add anything to empty group */
1290     if (pa_idxset_isempty(devices))
1291         return true;
1292 
1293     /* Device already selected */
1294     if (pa_idxset_contains(devices, dev))
1295         return true;
1296 
1297     /* No conflicting device must already be selected */
1298     if (!pa_idxset_isdisjoint(devices, dev->conflicting_devices))
1299         return false;
1300 
1301     /* No already selected device must be unsupported */
1302     if (!pa_idxset_isempty(dev->supported_devices))
1303         if (!pa_idxset_issubset(devices, dev->supported_devices))
1304            return false;
1305 
1306     sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK);
1307     source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE);
1308 
1309     PA_IDXSET_FOREACH(d, devices, idx) {
1310         /* Must not be unsupported by any selected device */
1311         if (!pa_idxset_isempty(d->supported_devices))
1312             if (!pa_idxset_contains(d->supported_devices, dev))
1313                 return false;
1314 
1315         /* PlaybackPCM must not be the same as any selected device */
1316         sink2 = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SINK);
1317         if (sink && sink2 && pa_streq(sink, sink2))
1318             return false;
1319 
1320         /* CapturePCM must not be the same as any selected device */
1321         source2 = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SOURCE);
1322         if (source && source2 && pa_streq(source, source2))
1323             return false;
1324     }
1325 
1326     return true;
1327 }
1328 
1329 /* Iterates nonempty subsets of UCM devices that can be simultaneously
1330  * used, including subsets of previously returned subsets. At start,
1331  * *state should be NULL. It's not safe to modify the devices argument
1332  * until iteration ends. The returned idxsets must be freed by the
1333  * caller. */
iterate_device_subsets(pa_idxset * devices,void ** state)1334 static pa_idxset *iterate_device_subsets(pa_idxset *devices, void **state) {
1335     uint32_t idx;
1336     pa_alsa_ucm_device *dev;
1337 
1338     pa_assert(devices);
1339     pa_assert(state);
1340 
1341     if (*state == NULL) {
1342         /* First iteration, start adding from first device */
1343         *state = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1344         dev = pa_idxset_first(devices, &idx);
1345 
1346     } else {
1347         /* Backtrack the most recent device we added and skip it */
1348         dev = pa_idxset_steal_last(*state, NULL);
1349         pa_idxset_get_by_data(devices, dev, &idx);
1350         if (dev)
1351             dev = pa_idxset_next(devices, &idx);
1352     }
1353 
1354     /* Try adding devices we haven't decided on yet */
1355     for (; dev; dev = pa_idxset_next(devices, &idx)) {
1356         if (devset_supports_device(*state, dev))
1357             pa_idxset_put(*state, dev, NULL);
1358     }
1359 
1360     if (pa_idxset_isempty(*state)) {
1361         /* No more choices to backtrack on, therefore no more subsets to
1362          * return after this. Don't return the empty set, instead clean
1363          * up and end iteration. */
1364         pa_idxset_free(*state, NULL);
1365         *state = NULL;
1366         return NULL;
1367     }
1368 
1369     return pa_idxset_copy(*state, NULL);
1370 }
1371 
1372 /* This a wrapper around iterate_device_subsets() that only returns the
1373  * biggest possible groups and not any of their subsets. */
iterate_maximal_device_subsets(pa_idxset * devices,void ** state)1374 static pa_idxset *iterate_maximal_device_subsets(pa_idxset *devices, void **state) {
1375     uint32_t idx;
1376     pa_alsa_ucm_device *dev;
1377     pa_idxset *subset;
1378 
1379     pa_assert(devices);
1380     pa_assert(state);
1381 
1382     subset = iterate_device_subsets(devices, state);
1383     if (!subset)
1384         return subset;
1385 
1386     /* Skip this group if it's incomplete, by checking if we can add any
1387      * other device. If we can, this iteration is a subset of another
1388      * group that we already returned or eventually return. */
1389     PA_IDXSET_FOREACH(dev, devices, idx) {
1390         if (!pa_idxset_contains(subset, dev) && devset_supports_device(subset, dev)) {
1391             pa_idxset_free(subset, NULL);
1392             return iterate_maximal_device_subsets(devices, state);
1393         }
1394     }
1395 
1396     return subset;
1397 }
1398 
merge_roles(const char * cur,const char * add)1399 static char* merge_roles(const char *cur, const char *add) {
1400     char *r, *ret;
1401     const char *state = NULL;
1402 
1403     if (add == NULL)
1404         return pa_xstrdup(cur);
1405     else if (cur == NULL)
1406         return pa_xstrdup(add);
1407 
1408     ret = pa_xstrdup(cur);
1409 
1410     while ((r = pa_split_spaces(add, &state))) {
1411         char *value;
1412 
1413         if (!pa_str_in_list_spaces(ret, r))
1414             value = pa_sprintf_malloc("%s %s", ret, r);
1415         else {
1416             pa_xfree(r);
1417             continue;
1418         }
1419 
1420         pa_xfree(ret);
1421         ret = value;
1422         pa_xfree(r);
1423     }
1424 
1425     return ret;
1426 }
1427 
pa_alsa_ucm_add_ports(pa_hashmap ** p,pa_proplist * proplist,pa_alsa_ucm_mapping_context * context,bool is_sink,pa_card * card,snd_pcm_t * pcm_handle,bool ignore_dB)1428 void pa_alsa_ucm_add_ports(
1429         pa_hashmap **p,
1430         pa_proplist *proplist,
1431         pa_alsa_ucm_mapping_context *context,
1432         bool is_sink,
1433         pa_card *card,
1434         snd_pcm_t *pcm_handle,
1435         bool ignore_dB) {
1436 
1437     char *merged_roles;
1438     const char *role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES;
1439     pa_alsa_ucm_device *dev;
1440     pa_alsa_ucm_modifier *mod;
1441     char *tmp;
1442 
1443     pa_assert(p);
1444     pa_assert(*p);
1445 
1446     /* add ports first */
1447     pa_alsa_ucm_add_port(*p, context, is_sink, card->ports, NULL, card->core);
1448 
1449     /* now set up volume paths if any */
1450     probe_volumes(*p, is_sink, pcm_handle, context->ucm->mixers, ignore_dB);
1451 
1452     /* probe_volumes() removes per-verb paths from ports if probing them
1453      * fails. The path for the current verb is cached in
1454      * pa_alsa_ucm_port_data.path, which is not cleared by probe_volumes() if
1455      * the path gets removed, so we have to call update_mixer_paths() here to
1456      * unset the cached path if needed. */
1457     if (context->ucm->active_verb) {
1458         const char *verb_name;
1459         verb_name = pa_proplist_gets(context->ucm->active_verb->proplist, PA_ALSA_PROP_UCM_NAME);
1460         update_mixer_paths(*p, verb_name);
1461     }
1462 
1463     /* then set property PA_PROP_DEVICE_INTENDED_ROLES */
1464     merged_roles = pa_xstrdup(pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES));
1465 
1466     dev = context->ucm_device;
1467     if (dev) {
1468         const char *roles = pa_proplist_gets(dev->proplist, role_name);
1469         tmp = merge_roles(merged_roles, roles);
1470         pa_xfree(merged_roles);
1471         merged_roles = tmp;
1472     }
1473 
1474     mod = context->ucm_modifier;
1475     if (mod) {
1476         tmp = merge_roles(merged_roles, mod->media_role);
1477         pa_xfree(merged_roles);
1478         merged_roles = tmp;
1479     }
1480 
1481     if (merged_roles)
1482         pa_proplist_sets(proplist, PA_PROP_DEVICE_INTENDED_ROLES, merged_roles);
1483 
1484     pa_log_info("ALSA device %s roles: %s", pa_proplist_gets(proplist, PA_PROP_DEVICE_STRING), pa_strnull(merged_roles));
1485     pa_xfree(merged_roles);
1486 }
1487 
1488 /* Change UCM verb and device to match selected card profile */
pa_alsa_ucm_set_profile(pa_alsa_ucm_config * ucm,pa_card * card,pa_alsa_profile * new_profile,pa_alsa_profile * old_profile)1489 int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, pa_alsa_profile *new_profile, pa_alsa_profile *old_profile) {
1490     int ret = 0;
1491     const char *verb_name, *profile_name;
1492     pa_alsa_ucm_verb *verb;
1493     pa_alsa_mapping *map;
1494     uint32_t idx;
1495 
1496     if (new_profile == old_profile)
1497         return 0;
1498 
1499     if (new_profile == NULL) {
1500         verb = NULL;
1501         profile_name = SND_USE_CASE_VERB_INACTIVE;
1502         verb_name = SND_USE_CASE_VERB_INACTIVE;
1503     } else {
1504         verb = new_profile->ucm_context.verb;
1505         profile_name = new_profile->name;
1506         verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME);
1507     }
1508 
1509     pa_log_info("Set profile to %s", profile_name);
1510     if (ucm->active_verb != verb) {
1511         /* change verb */
1512         pa_log_info("Set UCM verb to %s", verb_name);
1513         if ((snd_use_case_set(ucm->ucm_mgr, "_verb", verb_name)) < 0) {
1514             pa_log("Failed to set verb %s", verb_name);
1515             ret = -1;
1516         }
1517 
1518     } else if (ucm->active_verb) {
1519         /* Disable modifiers not in new profile. Has to be done before
1520          * devices, because _dismod fails if a modifier's supported
1521          * devices are disabled. */
1522         PA_IDXSET_FOREACH(map, old_profile->input_mappings, idx)
1523             if (new_profile && !pa_idxset_contains(new_profile->input_mappings, map))
1524                 if (map->ucm_context.ucm_modifier && ucm_modifier_disable(ucm, map->ucm_context.ucm_modifier) < 0)
1525                     ret = -1;
1526 
1527         PA_IDXSET_FOREACH(map, old_profile->output_mappings, idx)
1528             if (new_profile && !pa_idxset_contains(new_profile->output_mappings, map))
1529                 if (map->ucm_context.ucm_modifier && ucm_modifier_disable(ucm, map->ucm_context.ucm_modifier) < 0)
1530                     ret = -1;
1531 
1532         /* Disable devices not in new profile */
1533         PA_IDXSET_FOREACH(map, old_profile->input_mappings, idx)
1534             if (new_profile && !pa_idxset_contains(new_profile->input_mappings, map))
1535                 if (map->ucm_context.ucm_device && ucm_device_disable(ucm, map->ucm_context.ucm_device) < 0)
1536                     ret = -1;
1537 
1538         PA_IDXSET_FOREACH(map, old_profile->output_mappings, idx)
1539             if (new_profile && !pa_idxset_contains(new_profile->output_mappings, map))
1540                 if (map->ucm_context.ucm_device && ucm_device_disable(ucm, map->ucm_context.ucm_device) < 0)
1541                     ret = -1;
1542     }
1543     ucm->active_verb = verb;
1544 
1545     update_mixer_paths(card->ports, verb_name);
1546 
1547     return ret;
1548 }
1549 
pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context * context,pa_device_port * port)1550 int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port) {
1551     pa_alsa_ucm_config *ucm;
1552     pa_alsa_ucm_device *dev;
1553     pa_alsa_ucm_port_data *data;
1554 
1555     pa_assert(context && context->ucm);
1556 
1557     ucm = context->ucm;
1558     pa_assert(ucm->ucm_mgr);
1559 
1560     data = PA_DEVICE_PORT_DATA(port);
1561     dev = context->ucm_device;
1562     pa_assert(dev == data->device);
1563 
1564     return ucm_device_enable(ucm, dev);
1565 }
1566 
ucm_add_mapping(pa_alsa_profile * p,pa_alsa_mapping * m)1567 static void ucm_add_mapping(pa_alsa_profile *p, pa_alsa_mapping *m) {
1568 
1569     pa_alsa_path_set *ps;
1570 
1571     /* create empty path set for the future path additions */
1572     ps = pa_xnew0(pa_alsa_path_set, 1);
1573     ps->direction = m->direction;
1574     ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1575 
1576     switch (m->direction) {
1577         case PA_ALSA_DIRECTION_ANY:
1578             pa_idxset_put(p->output_mappings, m, NULL);
1579             pa_idxset_put(p->input_mappings, m, NULL);
1580             m->output_path_set = ps;
1581             m->input_path_set = ps;
1582             break;
1583         case PA_ALSA_DIRECTION_OUTPUT:
1584             pa_idxset_put(p->output_mappings, m, NULL);
1585             m->output_path_set = ps;
1586             break;
1587         case PA_ALSA_DIRECTION_INPUT:
1588             pa_idxset_put(p->input_mappings, m, NULL);
1589             m->input_path_set = ps;
1590             break;
1591     }
1592 }
1593 
alsa_mapping_add_ucm_device(pa_alsa_mapping * m,pa_alsa_ucm_device * device)1594 static void alsa_mapping_add_ucm_device(pa_alsa_mapping *m, pa_alsa_ucm_device *device) {
1595     char *cur_desc;
1596     const char *new_desc, *mdev;
1597     bool is_sink = m->direction == PA_ALSA_DIRECTION_OUTPUT;
1598 
1599     m->ucm_context.ucm_device = device;
1600 
1601     new_desc = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
1602     cur_desc = m->description;
1603     if (cur_desc)
1604         m->description = pa_sprintf_malloc("%s + %s", cur_desc, new_desc);
1605     else
1606         m->description = pa_xstrdup(new_desc);
1607     pa_xfree(cur_desc);
1608 
1609     /* walk around null case */
1610     m->description = m->description ? m->description : pa_xstrdup("");
1611 
1612     /* save mapping to ucm device */
1613     if (is_sink)
1614         device->playback_mapping = m;
1615     else
1616         device->capture_mapping = m;
1617 
1618     mdev = get_mixer_device(device, is_sink);
1619     if (mdev)
1620         pa_proplist_sets(m->proplist, "alsa.mixer_device", mdev);
1621 }
1622 
alsa_mapping_add_ucm_modifier(pa_alsa_mapping * m,pa_alsa_ucm_modifier * modifier)1623 static void alsa_mapping_add_ucm_modifier(pa_alsa_mapping *m, pa_alsa_ucm_modifier *modifier) {
1624     char *cur_desc;
1625     const char *new_desc, *mod_name, *channel_str;
1626     uint32_t channels = 0;
1627 
1628     m->ucm_context.ucm_modifier = modifier;
1629 
1630     new_desc = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
1631     cur_desc = m->description;
1632     if (cur_desc)
1633         m->description = pa_sprintf_malloc("%s + %s", cur_desc, new_desc);
1634     else
1635         m->description = pa_xstrdup(new_desc);
1636     pa_xfree(cur_desc);
1637 
1638     m->description = m->description ? m->description : pa_xstrdup("");
1639 
1640     /* Modifier sinks should not be routed to by default */
1641     m->priority = 0;
1642 
1643     mod_name = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_NAME);
1644     pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_MODIFIER, mod_name);
1645 
1646     /* save mapping to ucm modifier */
1647     if (m->direction == PA_ALSA_DIRECTION_OUTPUT) {
1648         modifier->playback_mapping = m;
1649         channel_str = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS);
1650     } else {
1651         modifier->capture_mapping = m;
1652         channel_str = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS);
1653     }
1654 
1655     if (channel_str) {
1656         /* FIXME: channel_str is unsanitized input from the UCM configuration,
1657          * we should do proper error handling instead of asserting.
1658          * https://bugs.freedesktop.org/show_bug.cgi?id=71823 */
1659         pa_assert_se(pa_atou(channel_str, &channels) == 0 && pa_channels_valid(channels));
1660         pa_log_debug("Got channel count %" PRIu32 " for modifier", channels);
1661     }
1662 
1663     if (channels)
1664         pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
1665     else
1666         pa_channel_map_init(&m->channel_map);
1667 }
1668 
ucm_alsa_mapping_get(pa_alsa_ucm_config * ucm,pa_alsa_profile_set * ps,const char * verb_name,const char * ucm_name,bool is_sink)1669 static pa_alsa_mapping* ucm_alsa_mapping_get(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps, const char *verb_name, const char *ucm_name, bool is_sink) {
1670     pa_alsa_mapping *m;
1671     char *mapping_name;
1672 
1673     mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, ucm_name, is_sink ? "sink" : "source");
1674 
1675     m = pa_alsa_mapping_get(ps, mapping_name);
1676 
1677     if (!m)
1678         pa_log("No mapping for %s", mapping_name);
1679 
1680     pa_xfree(mapping_name);
1681 
1682     return m;
1683 }
1684 
ucm_create_mapping_direction(pa_alsa_ucm_config * ucm,pa_alsa_profile_set * ps,pa_alsa_ucm_device * device,const char * verb_name,const char * device_name,const char * device_str,bool is_sink)1685 static int ucm_create_mapping_direction(
1686         pa_alsa_ucm_config *ucm,
1687         pa_alsa_profile_set *ps,
1688         pa_alsa_ucm_device *device,
1689         const char *verb_name,
1690         const char *device_name,
1691         const char *device_str,
1692         bool is_sink) {
1693 
1694     pa_alsa_mapping *m;
1695     unsigned priority, rate, channels;
1696 
1697     m = ucm_alsa_mapping_get(ucm, ps, verb_name, device_name, is_sink);
1698 
1699     if (!m)
1700         return -1;
1701 
1702     pa_log_debug("UCM mapping: %s dev %s", m->name, device_name);
1703 
1704     priority = is_sink ? device->playback_priority : device->capture_priority;
1705     rate = is_sink ? device->playback_rate : device->capture_rate;
1706     channels = is_sink ? device->playback_channels : device->capture_channels;
1707 
1708     if (!m->ucm_context.ucm_device) {   /* new mapping */
1709         m->ucm_context.ucm = ucm;
1710         m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
1711 
1712         m->device_strings = pa_xnew0(char*, 2);
1713         m->device_strings[0] = pa_xstrdup(device_str);
1714         m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT;
1715 
1716         if (rate)
1717             m->sample_spec.rate = rate;
1718         pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
1719     }
1720 
1721     /* mapping priority is the highest one of ucm devices */
1722     if (priority > m->priority)
1723         m->priority = priority;
1724 
1725     /* mapping channels is the lowest one of ucm devices */
1726     if (channels < m->channel_map.channels)
1727         pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
1728 
1729     alsa_mapping_add_ucm_device(m, device);
1730 
1731     return 0;
1732 }
1733 
ucm_create_mapping_for_modifier(pa_alsa_ucm_config * ucm,pa_alsa_profile_set * ps,pa_alsa_ucm_modifier * modifier,const char * verb_name,const char * mod_name,const char * device_str,bool is_sink)1734 static int ucm_create_mapping_for_modifier(
1735         pa_alsa_ucm_config *ucm,
1736         pa_alsa_profile_set *ps,
1737         pa_alsa_ucm_modifier *modifier,
1738         const char *verb_name,
1739         const char *mod_name,
1740         const char *device_str,
1741         bool is_sink) {
1742 
1743     pa_alsa_mapping *m;
1744 
1745     m = ucm_alsa_mapping_get(ucm, ps, verb_name, mod_name, is_sink);
1746 
1747     if (!m)
1748         return -1;
1749 
1750     pa_log_info("UCM mapping: %s modifier %s", m->name, mod_name);
1751 
1752     if (!m->ucm_context.ucm_device && !m->ucm_context.ucm_modifier) {   /* new mapping */
1753         m->ucm_context.ucm = ucm;
1754         m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
1755 
1756         m->device_strings = pa_xnew0(char*, 2);
1757         m->device_strings[0] = pa_xstrdup(device_str);
1758         m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT;
1759         /* Modifier sinks should not be routed to by default */
1760         m->priority = 0;
1761     }
1762 
1763     alsa_mapping_add_ucm_modifier(m, modifier);
1764 
1765     return 0;
1766 }
1767 
ucm_create_mapping(pa_alsa_ucm_config * ucm,pa_alsa_profile_set * ps,pa_alsa_ucm_device * device,const char * verb_name,const char * device_name,const char * sink,const char * source)1768 static int ucm_create_mapping(
1769         pa_alsa_ucm_config *ucm,
1770         pa_alsa_profile_set *ps,
1771         pa_alsa_ucm_device *device,
1772         const char *verb_name,
1773         const char *device_name,
1774         const char *sink,
1775         const char *source) {
1776 
1777     int ret = 0;
1778 
1779     if (!sink && !source) {
1780         pa_log("No sink and source at %s: %s", verb_name, device_name);
1781         return -1;
1782     }
1783 
1784     if (sink)
1785         ret = ucm_create_mapping_direction(ucm, ps, device, verb_name, device_name, sink, true);
1786     if (ret == 0 && source)
1787         ret = ucm_create_mapping_direction(ucm, ps, device, verb_name, device_name, source, false);
1788 
1789     return ret;
1790 }
1791 
ucm_get_jack(pa_alsa_ucm_config * ucm,pa_alsa_ucm_device * device)1792 static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *device) {
1793     pa_alsa_jack *j;
1794     const char *device_name;
1795     const char *jack_control;
1796     const char *mixer_device_name;
1797     char *name;
1798 
1799     pa_assert(ucm);
1800     pa_assert(device);
1801 
1802     device_name = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_NAME);
1803 
1804     jack_control = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_JACK_CONTROL);
1805     if (jack_control) {
1806 #if SND_LIB_VERSION >= 0x10201
1807         snd_ctl_elem_id_t *ctl;
1808         int err, index;
1809         snd_ctl_elem_id_alloca(&ctl);
1810         err = snd_use_case_parse_ctl_elem_id(ctl, "JackControl", jack_control);
1811         if (err < 0)
1812             return NULL;
1813         jack_control = snd_ctl_elem_id_get_name(ctl);
1814         index = snd_ctl_elem_id_get_index(ctl);
1815         if (index > 0) {
1816             pa_log("[%s] Invalid JackControl index value: \"%s\",%d", device_name, jack_control, index);
1817             return NULL;
1818         }
1819 #else
1820 #warning "Upgrade to alsa-lib 1.2.1!"
1821 #endif
1822         if (!pa_endswith(jack_control, " Jack")) {
1823             pa_log("[%s] Invalid JackControl value: \"%s\"", device_name, jack_control);
1824             return NULL;
1825         }
1826 
1827         /* pa_alsa_jack_new() expects a jack name without " Jack" at the
1828          * end, so drop the trailing " Jack". */
1829         name = pa_xstrndup(jack_control, strlen(jack_control) - 5);
1830     } else {
1831         /* The jack control hasn't been explicitly configured, fail. */
1832         return NULL;
1833     }
1834 
1835     PA_LLIST_FOREACH(j, ucm->jacks)
1836         if (pa_streq(j->name, name))
1837             goto finish;
1838 
1839     mixer_device_name = get_jack_mixer_device(device, true);
1840     if (!mixer_device_name)
1841         mixer_device_name = get_jack_mixer_device(device, false);
1842     if (!mixer_device_name) {
1843         pa_log("[%s] No mixer device name for JackControl \"%s\"", device_name, jack_control);
1844         return NULL;
1845     }
1846     j = pa_alsa_jack_new(NULL, mixer_device_name, name, 0);
1847     PA_LLIST_PREPEND(pa_alsa_jack, ucm->jacks, j);
1848 
1849 finish:
1850     pa_xfree(name);
1851 
1852     return j;
1853 }
1854 
ucm_create_profile(pa_alsa_ucm_config * ucm,pa_alsa_profile_set * ps,pa_alsa_ucm_verb * verb,pa_idxset * mappings,const char * profile_name,const char * profile_desc,unsigned int profile_priority)1855 static int ucm_create_profile(
1856         pa_alsa_ucm_config *ucm,
1857         pa_alsa_profile_set *ps,
1858         pa_alsa_ucm_verb *verb,
1859         pa_idxset *mappings,
1860         const char *profile_name,
1861         const char *profile_desc,
1862         unsigned int profile_priority) {
1863 
1864     pa_alsa_profile *p;
1865     pa_alsa_mapping *map;
1866     uint32_t idx;
1867 
1868     pa_assert(ps);
1869 
1870     if (pa_hashmap_get(ps->profiles, profile_name)) {
1871         pa_log("Profile %s already exists", profile_name);
1872         return -1;
1873     }
1874 
1875     p = pa_xnew0(pa_alsa_profile, 1);
1876     p->profile_set = ps;
1877     p->name = pa_xstrdup(profile_name);
1878     p->description = pa_xstrdup(profile_desc);
1879     p->priority = profile_priority;
1880     p->ucm_context.verb = verb;
1881 
1882     p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1883     p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1884 
1885     p->supported = true;
1886     pa_hashmap_put(ps->profiles, p->name, p);
1887 
1888     PA_IDXSET_FOREACH(map, mappings, idx)
1889         ucm_add_mapping(p, map);
1890 
1891     pa_alsa_profile_dump(p);
1892 
1893     return 0;
1894 }
1895 
ucm_create_verb_profiles(pa_alsa_ucm_config * ucm,pa_alsa_profile_set * ps,pa_alsa_ucm_verb * verb,const char * verb_name,const char * verb_desc)1896 static int ucm_create_verb_profiles(
1897         pa_alsa_ucm_config *ucm,
1898         pa_alsa_profile_set *ps,
1899         pa_alsa_ucm_verb *verb,
1900         const char *verb_name,
1901         const char *verb_desc) {
1902 
1903     pa_idxset *verb_devices, *p_devices, *p_mappings;
1904     pa_alsa_ucm_device *dev;
1905     pa_alsa_ucm_modifier *mod;
1906     int i = 0;
1907     int n_profiles = 0;
1908     const char *name, *sink, *source;
1909     char *p_name, *p_desc, *tmp;
1910     unsigned int verb_priority, p_priority;
1911     uint32_t idx;
1912     void *state = NULL;
1913 
1914     /* TODO: get profile priority from policy management */
1915     verb_priority = verb->priority;
1916 
1917     if (verb_priority == 0) {
1918         char *verb_cmp, *c;
1919         c = verb_cmp = pa_xstrdup(verb_name);
1920         while (*c) {
1921             if (*c == '_') *c = ' ';
1922             c++;
1923         }
1924         for (i = 0; verb_info[i].id; i++) {
1925             if (strcasecmp(verb_info[i].id, verb_cmp) == 0) {
1926                 verb_priority = verb_info[i].priority;
1927                 break;
1928             }
1929         }
1930         pa_xfree(verb_cmp);
1931     }
1932 
1933     PA_LLIST_FOREACH(dev, verb->devices) {
1934         pa_alsa_jack *jack;
1935         const char *jack_hw_mute;
1936 
1937         name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
1938 
1939         sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK);
1940         source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE);
1941 
1942         ucm_create_mapping(ucm, ps, dev, verb_name, name, sink, source);
1943 
1944         jack = ucm_get_jack(ucm, dev);
1945         if (jack)
1946             device_set_jack(dev, jack);
1947 
1948         /* JackHWMute contains a list of device names. Each listed device must
1949          * be associated with the jack object that we just created. */
1950         jack_hw_mute = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_JACK_HW_MUTE);
1951         if (jack_hw_mute && !jack) {
1952             pa_log("[%s] JackHWMute set, but JackControl is missing", name);
1953             jack_hw_mute = NULL;
1954         }
1955         if (jack_hw_mute) {
1956             char *hw_mute_device_name;
1957             const char *state = NULL;
1958 
1959             while ((hw_mute_device_name = ucm_split_devnames(jack_hw_mute, &state))) {
1960                 pa_alsa_ucm_verb *verb2;
1961                 bool device_found = false;
1962 
1963                 /* Search the referenced device from all verbs. If there are
1964                  * multiple verbs that have a device with this name, we add the
1965                  * hw mute association to each of those devices. */
1966                 PA_LLIST_FOREACH(verb2, ucm->verbs) {
1967                     pa_alsa_ucm_device *hw_mute_device;
1968 
1969                     hw_mute_device = verb_find_device(verb2, hw_mute_device_name);
1970                     if (hw_mute_device) {
1971                         device_found = true;
1972                         device_add_hw_mute_jack(hw_mute_device, jack);
1973                     }
1974                 }
1975 
1976                 if (!device_found)
1977                     pa_log("[%s] JackHWMute references an unknown device: %s", name, hw_mute_device_name);
1978 
1979                 pa_xfree(hw_mute_device_name);
1980             }
1981         }
1982     }
1983 
1984     /* Now find modifiers that have their own PlaybackPCM and create
1985      * separate sinks for them. */
1986     PA_LLIST_FOREACH(mod, verb->modifiers) {
1987         name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
1988 
1989         sink = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SINK);
1990         source = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SOURCE);
1991 
1992         if (sink)
1993             ucm_create_mapping_for_modifier(ucm, ps, mod, verb_name, name, sink, true);
1994         else if (source)
1995             ucm_create_mapping_for_modifier(ucm, ps, mod, verb_name, name, source, false);
1996     }
1997 
1998     verb_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1999     PA_LLIST_FOREACH(dev, verb->devices)
2000         pa_idxset_put(verb_devices, dev, NULL);
2001 
2002     while ((p_devices = iterate_maximal_device_subsets(verb_devices, &state))) {
2003         p_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2004 
2005         /* Add the mappings that include our selected devices */
2006         PA_IDXSET_FOREACH(dev, p_devices, idx) {
2007             if (dev->playback_mapping)
2008                 pa_idxset_put(p_mappings, dev->playback_mapping, NULL);
2009             if (dev->capture_mapping)
2010                 pa_idxset_put(p_mappings, dev->capture_mapping, NULL);
2011         }
2012 
2013         /* Add mappings only for the modifiers that can work with our
2014          * device selection */
2015         PA_LLIST_FOREACH(mod, verb->modifiers)
2016             if (pa_idxset_isempty(mod->supported_devices) || pa_idxset_issubset(mod->supported_devices, p_devices))
2017                 if (pa_idxset_isdisjoint(mod->conflicting_devices, p_devices)) {
2018                     if (mod->playback_mapping)
2019                         pa_idxset_put(p_mappings, mod->playback_mapping, NULL);
2020                     if (mod->capture_mapping)
2021                         pa_idxset_put(p_mappings, mod->capture_mapping, NULL);
2022                 }
2023 
2024         /* If we'll have multiple profiles for this verb, their names
2025          * must be unique. Use a list of chosen devices to disambiguate
2026          * them. If the profile contains all devices of a verb, we'll
2027          * generate only onle profile whose name should be the verb
2028          * name. GUIs usually show the profile description instead of
2029          * the name, add the device names to those as well. */
2030         tmp = devset_name(p_devices, ", ");
2031         if (pa_idxset_equals(p_devices, verb_devices)) {
2032             p_name = pa_xstrdup(verb_name);
2033             p_desc = pa_xstrdup(verb_desc);
2034         } else {
2035             p_name = pa_sprintf_malloc("%s (%s)", verb_name, tmp);
2036             p_desc = pa_sprintf_malloc("%s (%s)", verb_desc, tmp);
2037         }
2038 
2039         /* Make sure profiles with higher-priority devices are
2040          * prioritized. */
2041         p_priority = verb_priority + devset_playback_priority(p_devices, false) + devset_capture_priority(p_devices, false);
2042 
2043         if (ucm_create_profile(ucm, ps, verb, p_mappings, p_name, p_desc, p_priority) == 0) {
2044             pa_log_debug("Created profile %s for UCM verb %s", p_name, verb_name);
2045             n_profiles++;
2046         }
2047 
2048         pa_xfree(tmp);
2049         pa_xfree(p_name);
2050         pa_xfree(p_desc);
2051         pa_idxset_free(p_mappings, NULL);
2052         pa_idxset_free(p_devices, NULL);
2053     }
2054 
2055     pa_idxset_free(verb_devices, NULL);
2056 
2057     if (n_profiles == 0) {
2058         pa_log("UCM verb %s created no profiles", verb_name);
2059         return -1;
2060     }
2061 
2062     return 0;
2063 }
2064 
mapping_init_eld(pa_alsa_mapping * m,snd_pcm_t * pcm)2065 static void mapping_init_eld(pa_alsa_mapping *m, snd_pcm_t *pcm)
2066 {
2067     pa_alsa_ucm_mapping_context *context = &m->ucm_context;
2068     pa_alsa_ucm_device *dev;
2069     char *mdev, *alib_prefix;
2070     snd_pcm_info_t *info;
2071     int pcm_card, pcm_device;
2072 
2073     snd_pcm_info_alloca(&info);
2074     if (snd_pcm_info(pcm, info) < 0)
2075         return;
2076 
2077     if ((pcm_card = snd_pcm_info_get_card(info)) < 0)
2078         return;
2079     if ((pcm_device = snd_pcm_info_get_device(info)) < 0)
2080         return;
2081 
2082     alib_prefix = context->ucm->alib_prefix;
2083 
2084     dev = context->ucm_device;
2085     mdev = pa_sprintf_malloc("%shw:%i", alib_prefix ? alib_prefix : "", pcm_card);
2086     if (mdev == NULL)
2087         return;
2088     dev->eld_mixer_device_name = mdev;
2089     dev->eld_device = pcm_device;
2090 }
2091 
mapping_open_pcm(pa_alsa_ucm_config * ucm,pa_alsa_mapping * m,int mode)2092 static snd_pcm_t* mapping_open_pcm(pa_alsa_ucm_config *ucm, pa_alsa_mapping *m, int mode) {
2093     snd_pcm_t* pcm;
2094     pa_sample_spec try_ss = ucm->core->default_sample_spec;
2095     pa_channel_map try_map;
2096     snd_pcm_uframes_t try_period_size, try_buffer_size;
2097     bool exact_channels = m->channel_map.channels > 0;
2098 
2099     if (exact_channels) {
2100         try_map = m->channel_map;
2101         try_ss.channels = try_map.channels;
2102     } else
2103         pa_channel_map_init_extend(&try_map, try_ss.channels, PA_CHANNEL_MAP_ALSA);
2104 
2105     try_period_size =
2106         pa_usec_to_bytes(ucm->core->default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
2107         pa_frame_size(&try_ss);
2108     try_buffer_size = ucm->core->default_n_fragments * try_period_size;
2109 
2110     pcm = pa_alsa_open_by_device_string(m->device_strings[0], NULL, &try_ss,
2111             &try_map, mode, &try_period_size, &try_buffer_size, 0, NULL, NULL, NULL, NULL, exact_channels);
2112 
2113     if (pcm) {
2114         if (!exact_channels)
2115             m->channel_map = try_map;
2116         mapping_init_eld(m, pcm);
2117     }
2118 
2119     return pcm;
2120 }
2121 
profile_finalize_probing(pa_alsa_profile * p)2122 static void profile_finalize_probing(pa_alsa_profile *p) {
2123     pa_alsa_mapping *m;
2124     uint32_t idx;
2125 
2126     PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
2127         if (p->supported)
2128             m->supported++;
2129 
2130         if (!m->output_pcm)
2131             continue;
2132 
2133         snd_pcm_close(m->output_pcm);
2134         m->output_pcm = NULL;
2135     }
2136 
2137     PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
2138         if (p->supported)
2139             m->supported++;
2140 
2141         if (!m->input_pcm)
2142             continue;
2143 
2144         snd_pcm_close(m->input_pcm);
2145         m->input_pcm = NULL;
2146     }
2147 }
2148 
ucm_mapping_jack_probe(pa_alsa_mapping * m,pa_hashmap * mixers)2149 static void ucm_mapping_jack_probe(pa_alsa_mapping *m, pa_hashmap *mixers) {
2150     snd_mixer_t *mixer_handle;
2151     pa_alsa_ucm_mapping_context *context = &m->ucm_context;
2152     pa_alsa_ucm_device *dev;
2153     bool has_control;
2154 
2155     dev = context->ucm_device;
2156     if (!dev->jack || !dev->jack->mixer_device_name)
2157         return;
2158 
2159     mixer_handle = pa_alsa_open_mixer_by_name(mixers, dev->jack->mixer_device_name, true);
2160     if (!mixer_handle) {
2161         pa_log_error("Unable to determine open mixer device '%s' for jack %s", dev->jack->mixer_device_name, dev->jack->name);
2162         return;
2163     }
2164 
2165     has_control = pa_alsa_mixer_find_card(mixer_handle, &dev->jack->alsa_id, 0) != NULL;
2166     pa_alsa_jack_set_has_control(dev->jack, has_control);
2167     pa_log_info("UCM jack %s has_control=%d", dev->jack->name, dev->jack->has_control);
2168 }
2169 
ucm_probe_profile_set(pa_alsa_ucm_config * ucm,pa_alsa_profile_set * ps)2170 static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps) {
2171     void *state;
2172     pa_alsa_profile *p;
2173     pa_alsa_mapping *m;
2174     const char *verb_name;
2175     uint32_t idx;
2176 
2177     PA_HASHMAP_FOREACH(p, ps->profiles, state) {
2178         pa_log_info("Probing profile %s", p->name);
2179 
2180         /* change verb */
2181         verb_name = pa_proplist_gets(p->ucm_context.verb->proplist, PA_ALSA_PROP_UCM_NAME);
2182         pa_log_info("Set ucm verb to %s", verb_name);
2183 
2184         if ((snd_use_case_set(ucm->ucm_mgr, "_verb", verb_name)) < 0) {
2185             pa_log("Failed to set verb %s", verb_name);
2186             p->supported = false;
2187             continue;
2188         }
2189 
2190         PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
2191             if (PA_UCM_IS_MODIFIER_MAPPING(m)) {
2192                 /* Skip jack probing on modifier PCMs since we expect this to
2193                  * only be controlled on the main device/verb PCM. */
2194                 continue;
2195             }
2196 
2197             m->output_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_PLAYBACK);
2198             if (!m->output_pcm) {
2199                 p->supported = false;
2200                 break;
2201             }
2202         }
2203 
2204         if (p->supported) {
2205             PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
2206                 if (PA_UCM_IS_MODIFIER_MAPPING(m)) {
2207                     /* Skip jack probing on modifier PCMs since we expect this to
2208                      * only be controlled on the main device/verb PCM. */
2209                     continue;
2210                 }
2211 
2212                 m->input_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_CAPTURE);
2213                 if (!m->input_pcm) {
2214                     p->supported = false;
2215                     break;
2216                 }
2217             }
2218         }
2219 
2220         if (!p->supported) {
2221             profile_finalize_probing(p);
2222             continue;
2223         }
2224 
2225         pa_log_debug("Profile %s supported.", p->name);
2226 
2227         PA_IDXSET_FOREACH(m, p->output_mappings, idx)
2228             if (!PA_UCM_IS_MODIFIER_MAPPING(m))
2229                 ucm_mapping_jack_probe(m, ucm->mixers);
2230 
2231         PA_IDXSET_FOREACH(m, p->input_mappings, idx)
2232             if (!PA_UCM_IS_MODIFIER_MAPPING(m))
2233                 ucm_mapping_jack_probe(m, ucm->mixers);
2234 
2235         profile_finalize_probing(p);
2236     }
2237 
2238     /* restore ucm state */
2239     snd_use_case_set(ucm->ucm_mgr, "_verb", SND_USE_CASE_VERB_INACTIVE);
2240 
2241     pa_alsa_profile_set_drop_unsupported(ps);
2242 }
2243 
pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config * ucm,pa_channel_map * default_channel_map)2244 pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map) {
2245     pa_alsa_ucm_verb *verb;
2246     pa_alsa_profile_set *ps;
2247 
2248     ps = pa_xnew0(pa_alsa_profile_set, 1);
2249     ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
2250     ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
2251     ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
2252 
2253     /* create profiles for each verb */
2254     PA_LLIST_FOREACH(verb, ucm->verbs) {
2255         const char *verb_name;
2256         const char *verb_desc;
2257 
2258         verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME);
2259         verb_desc = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
2260         if (verb_name == NULL) {
2261             pa_log("Verb with no name");
2262             continue;
2263         }
2264 
2265         ucm_create_verb_profiles(ucm, ps, verb, verb_name, verb_desc);
2266     }
2267 
2268     ucm_probe_profile_set(ucm, ps);
2269     ps->probed = true;
2270 
2271     return ps;
2272 }
2273 
free_verb(pa_alsa_ucm_verb * verb)2274 static void free_verb(pa_alsa_ucm_verb *verb) {
2275     pa_alsa_ucm_device *di, *dn;
2276     pa_alsa_ucm_modifier *mi, *mn;
2277 
2278     PA_LLIST_FOREACH_SAFE(di, dn, verb->devices) {
2279         PA_LLIST_REMOVE(pa_alsa_ucm_device, verb->devices, di);
2280 
2281         if (di->hw_mute_jacks)
2282             pa_dynarray_free(di->hw_mute_jacks);
2283 
2284         if (di->ucm_ports)
2285             pa_dynarray_free(di->ucm_ports);
2286 
2287         if (di->playback_volumes)
2288             pa_hashmap_free(di->playback_volumes);
2289         if (di->capture_volumes)
2290             pa_hashmap_free(di->capture_volumes);
2291 
2292         pa_proplist_free(di->proplist);
2293 
2294         pa_idxset_free(di->conflicting_devices, NULL);
2295         pa_idxset_free(di->supported_devices, NULL);
2296 
2297         pa_xfree(di->eld_mixer_device_name);
2298 
2299         pa_xfree(di);
2300     }
2301 
2302     PA_LLIST_FOREACH_SAFE(mi, mn, verb->modifiers) {
2303         PA_LLIST_REMOVE(pa_alsa_ucm_modifier, verb->modifiers, mi);
2304         pa_proplist_free(mi->proplist);
2305         pa_idxset_free(mi->conflicting_devices, NULL);
2306         pa_idxset_free(mi->supported_devices, NULL);
2307         pa_xfree(mi->media_role);
2308         pa_xfree(mi);
2309     }
2310     pa_proplist_free(verb->proplist);
2311     pa_xfree(verb);
2312 }
2313 
verb_find_device(pa_alsa_ucm_verb * verb,const char * device_name)2314 static pa_alsa_ucm_device *verb_find_device(pa_alsa_ucm_verb *verb, const char *device_name) {
2315     pa_alsa_ucm_device *device;
2316 
2317     pa_assert(verb);
2318     pa_assert(device_name);
2319 
2320     PA_LLIST_FOREACH(device, verb->devices) {
2321         const char *name;
2322 
2323         name = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_NAME);
2324         if (pa_streq(name, device_name))
2325             return device;
2326     }
2327 
2328     return NULL;
2329 }
2330 
pa_alsa_ucm_free(pa_alsa_ucm_config * ucm)2331 void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) {
2332     pa_alsa_ucm_verb *vi, *vn;
2333     pa_alsa_jack *ji, *jn;
2334 
2335     PA_LLIST_FOREACH_SAFE(vi, vn, ucm->verbs) {
2336         PA_LLIST_REMOVE(pa_alsa_ucm_verb, ucm->verbs, vi);
2337         free_verb(vi);
2338     }
2339     PA_LLIST_FOREACH_SAFE(ji, jn, ucm->jacks) {
2340         PA_LLIST_REMOVE(pa_alsa_jack, ucm->jacks, ji);
2341         pa_alsa_jack_free(ji);
2342     }
2343     if (ucm->ucm_mgr) {
2344         snd_use_case_mgr_close(ucm->ucm_mgr);
2345         ucm->ucm_mgr = NULL;
2346     }
2347     pa_xfree(ucm->alib_prefix);
2348     ucm->alib_prefix = NULL;
2349 }
2350 
pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context * context)2351 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
2352     pa_alsa_ucm_device *dev;
2353     pa_alsa_ucm_modifier *mod;
2354 
2355     dev = context->ucm_device;
2356     if (dev) {
2357         /* clear ucm device pointer to mapping */
2358         if (context->direction == PA_DIRECTION_OUTPUT)
2359             dev->playback_mapping = NULL;
2360         else
2361             dev->capture_mapping = NULL;
2362     }
2363 
2364     mod = context->ucm_modifier;
2365     if (mod) {
2366         if (context->direction == PA_DIRECTION_OUTPUT)
2367             mod->playback_mapping = NULL;
2368         else
2369             mod->capture_mapping = NULL;
2370     }
2371 }
2372 
2373 /* Enable the modifier when the first stream with matched role starts */
pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config * ucm,const char * role,pa_direction_t dir)2374 void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
2375     pa_alsa_ucm_modifier *mod;
2376 
2377     if (!ucm->active_verb)
2378         return;
2379 
2380     PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) {
2381         if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) {
2382             if (mod->enabled_counter == 0) {
2383                 ucm_modifier_enable(ucm, mod);
2384             }
2385 
2386             mod->enabled_counter++;
2387             break;
2388         }
2389     }
2390 }
2391 
2392 /* Disable the modifier when the last stream with matched role ends */
pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config * ucm,const char * role,pa_direction_t dir)2393 void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
2394     pa_alsa_ucm_modifier *mod;
2395 
2396     if (!ucm->active_verb)
2397         return;
2398 
2399     PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) {
2400         if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) {
2401 
2402             mod->enabled_counter--;
2403             if (mod->enabled_counter == 0)
2404                 ucm_modifier_disable(ucm, mod);
2405 
2406             break;
2407         }
2408     }
2409 }
2410 
device_set_jack(pa_alsa_ucm_device * device,pa_alsa_jack * jack)2411 static void device_set_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack) {
2412     pa_assert(device);
2413     pa_assert(jack);
2414 
2415     device->jack = jack;
2416     pa_alsa_jack_add_ucm_device(jack, device);
2417 
2418     pa_alsa_ucm_device_update_available(device);
2419 }
2420 
device_add_hw_mute_jack(pa_alsa_ucm_device * device,pa_alsa_jack * jack)2421 static void device_add_hw_mute_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack) {
2422     pa_assert(device);
2423     pa_assert(jack);
2424 
2425     pa_dynarray_append(device->hw_mute_jacks, jack);
2426     pa_alsa_jack_add_ucm_hw_mute_device(jack, device);
2427 
2428     pa_alsa_ucm_device_update_available(device);
2429 }
2430 
device_set_available(pa_alsa_ucm_device * device,pa_available_t available)2431 static void device_set_available(pa_alsa_ucm_device *device, pa_available_t available) {
2432     pa_alsa_ucm_port_data *port;
2433     unsigned idx;
2434 
2435     pa_assert(device);
2436 
2437     if (available == device->available)
2438         return;
2439 
2440     device->available = available;
2441 
2442     PA_DYNARRAY_FOREACH(port, device->ucm_ports, idx)
2443         pa_device_port_set_available(port->core_port, port->device->available);
2444 }
2445 
pa_alsa_ucm_device_update_available(pa_alsa_ucm_device * device)2446 void pa_alsa_ucm_device_update_available(pa_alsa_ucm_device *device) {
2447     pa_available_t available = PA_AVAILABLE_UNKNOWN;
2448     pa_alsa_jack *jack;
2449     unsigned idx;
2450 
2451     pa_assert(device);
2452 
2453     if (device->jack && device->jack->has_control)
2454         available = device->jack->plugged_in ? PA_AVAILABLE_YES : PA_AVAILABLE_NO;
2455 
2456     PA_DYNARRAY_FOREACH(jack, device->hw_mute_jacks, idx) {
2457         if (jack->plugged_in) {
2458             available = PA_AVAILABLE_NO;
2459             break;
2460         }
2461     }
2462 
2463     device_set_available(device, available);
2464 }
2465 
ucm_port_data_init(pa_alsa_ucm_port_data * port,pa_alsa_ucm_config * ucm,pa_device_port * core_port,pa_alsa_ucm_device * device)2466 static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port,
2467                                pa_alsa_ucm_device *device) {
2468     pa_assert(ucm);
2469     pa_assert(core_port);
2470     pa_assert(device);
2471 
2472     port->ucm = ucm;
2473     port->core_port = core_port;
2474     port->eld_device = -1;
2475 
2476     port->device = device;
2477     pa_dynarray_append(device->ucm_ports, port);
2478 
2479     port->paths = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree,
2480                                       (pa_free_cb_t) pa_alsa_path_free);
2481 
2482     pa_device_port_set_available(port->core_port, port->device->available);
2483 }
2484 
ucm_port_data_free(pa_device_port * port)2485 static void ucm_port_data_free(pa_device_port *port) {
2486     pa_alsa_ucm_port_data *ucm_port;
2487 
2488     pa_assert(port);
2489 
2490     ucm_port = PA_DEVICE_PORT_DATA(port);
2491 
2492     if (ucm_port->paths)
2493         pa_hashmap_free(ucm_port->paths);
2494 
2495     pa_xfree(ucm_port->eld_mixer_device_name);
2496 }
2497 
2498 #else /* HAVE_ALSA_UCM */
2499 
2500 /* Dummy functions for systems without UCM support */
2501 
pa_alsa_ucm_query_profiles(pa_alsa_ucm_config * ucm,int card_index)2502 int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index) {
2503         pa_log_info("UCM not available.");
2504         return -1;
2505 }
2506 
pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config * ucm,pa_channel_map * default_channel_map)2507 pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map) {
2508     return NULL;
2509 }
2510 
pa_alsa_ucm_set_profile(pa_alsa_ucm_config * ucm,pa_card * card,pa_alsa_profile * new_profile,pa_alsa_profile * old_profile)2511 int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, pa_alsa_profile *new_profile, pa_alsa_profile *old_profile) {
2512     return -1;
2513 }
2514 
pa_alsa_ucm_get_verb(snd_use_case_mgr_t * uc_mgr,const char * verb_name,const char * verb_desc,pa_alsa_ucm_verb ** p_verb)2515 int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name, const char *verb_desc, pa_alsa_ucm_verb **p_verb) {
2516     return -1;
2517 }
2518 
pa_alsa_ucm_add_ports(pa_hashmap ** hash,pa_proplist * proplist,pa_alsa_ucm_mapping_context * context,bool is_sink,pa_card * card,snd_pcm_t * pcm_handle,bool ignore_dB)2519 void pa_alsa_ucm_add_ports(
2520         pa_hashmap **hash,
2521         pa_proplist *proplist,
2522         pa_alsa_ucm_mapping_context *context,
2523         bool is_sink,
2524         pa_card *card,
2525         snd_pcm_t *pcm_handle,
2526         bool ignore_dB) {
2527 }
2528 
pa_alsa_ucm_add_port(pa_hashmap * hash,pa_alsa_ucm_mapping_context * context,bool is_sink,pa_hashmap * ports,pa_card_profile * cp,pa_core * core)2529 void pa_alsa_ucm_add_port(
2530         pa_hashmap *hash,
2531         pa_alsa_ucm_mapping_context *context,
2532         bool is_sink,
2533         pa_hashmap *ports,
2534         pa_card_profile *cp,
2535         pa_core *core) {
2536 }
2537 
pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context * context,pa_device_port * port)2538 int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port) {
2539     return -1;
2540 }
2541 
pa_alsa_ucm_free(pa_alsa_ucm_config * ucm)2542 void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) {
2543 }
2544 
pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context * context)2545 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
2546 }
2547 
pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config * ucm,const char * role,pa_direction_t dir)2548 void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
2549 }
2550 
pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config * ucm,const char * role,pa_direction_t dir)2551 void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
2552 }
2553 
2554 #endif
2555