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