• 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 modifier %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     if (snd_use_case_get(ucm->ucm_mgr, "_alibpref", &value) == 0) {
798         if (value[0]) {
799             ucm->alib_prefix = pa_xstrdup(value);
800             pa_log_debug("UCM _alibpref=%s", ucm->alib_prefix);
801         }
802         free((void *)value);
803     }
804 
805     /* get a list of all UCM verbs (profiles) for this card */
806     num_verbs = snd_use_case_verb_list(ucm->ucm_mgr, &verb_list);
807     if (num_verbs < 0) {
808         pa_log("UCM verb list not found for %s", card_name);
809         err = -PA_ALSA_ERR_UNSPECIFIED;
810         goto ucm_verb_fail;
811     }
812 
813     /* get the properties of each UCM verb */
814     for (i = 0; i < num_verbs; i += 2) {
815         pa_alsa_ucm_verb *verb;
816 
817         /* Get devices and modifiers for each verb */
818         err = pa_alsa_ucm_get_verb(ucm->ucm_mgr, verb_list[i], verb_list[i+1], &verb);
819         if (err < 0) {
820             pa_log("Failed to get the verb %s", verb_list[i]);
821             continue;
822         }
823 
824         PA_LLIST_PREPEND(pa_alsa_ucm_verb, ucm->verbs, verb);
825     }
826 
827     if (!ucm->verbs) {
828         pa_log("No UCM verb is valid for %s", card_name);
829         err = -PA_ALSA_ERR_UCM_NO_VERB;
830     }
831 
832     snd_use_case_free_list(verb_list, num_verbs);
833 
834 ucm_verb_fail:
835     if (err < 0) {
836         snd_use_case_mgr_close(ucm->ucm_mgr);
837         ucm->ucm_mgr = NULL;
838     }
839 
840 ucm_mgr_fail:
841     pa_xfree(card_name);
842 
843 name_fail:
844     return err;
845 }
846 
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)847 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) {
848     pa_alsa_ucm_device *d;
849     pa_alsa_ucm_modifier *mod;
850     pa_alsa_ucm_verb *verb;
851     char *value;
852     unsigned ui;
853     int err = 0;
854 
855     *p_verb = NULL;
856     pa_log_info("Set UCM verb to %s", verb_name);
857     err = snd_use_case_set(uc_mgr, "_verb", verb_name);
858     if (err < 0)
859         return err;
860 
861     verb = pa_xnew0(pa_alsa_ucm_verb, 1);
862     verb->proplist = pa_proplist_new();
863 
864     pa_proplist_sets(verb->proplist, PA_ALSA_PROP_UCM_NAME, pa_strnull(verb_name));
865     pa_proplist_sets(verb->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(verb_desc));
866 
867     value = ucm_verb_value(uc_mgr, verb_name, "Priority");
868     if (value && !pa_atou(value, &ui))
869         verb->priority = ui > 10000 ? 10000 : ui;
870     free(value);
871 
872     err = ucm_get_devices(verb, uc_mgr);
873     if (err < 0)
874         pa_log("No UCM devices for verb %s", verb_name);
875 
876     err = ucm_get_modifiers(verb, uc_mgr);
877     if (err < 0)
878         pa_log("No UCM modifiers for verb %s", verb_name);
879 
880     PA_LLIST_FOREACH(d, verb->devices) {
881         const char *dev_name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
882 
883         /* Devices properties */
884         ucm_get_device_property(d, uc_mgr, verb, dev_name);
885     }
886     /* make conflicting or supported device mutual */
887     PA_LLIST_FOREACH(d, verb->devices)
888         append_lost_relationship(d);
889 
890     PA_LLIST_FOREACH(mod, verb->modifiers) {
891         const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
892 
893         /* Modifier properties */
894         ucm_get_modifier_property(mod, uc_mgr, mod_name);
895 
896         /* Set PA_PROP_DEVICE_INTENDED_ROLES property to devices */
897         pa_log_debug("Set media roles for verb %s, modifier %s", verb_name, mod_name);
898         ucm_set_media_roles(mod, verb->devices, mod_name);
899     }
900 
901     *p_verb = verb;
902     return 0;
903 }
904 
pa_alsa_ucm_device_cmp(const void * a,const void * b)905 static int pa_alsa_ucm_device_cmp(const void *a, const void *b) {
906     const pa_alsa_ucm_device *d1 = *(pa_alsa_ucm_device **)a;
907     const pa_alsa_ucm_device *d2 = *(pa_alsa_ucm_device **)b;
908 
909     return strcmp(pa_proplist_gets(d1->proplist, PA_ALSA_PROP_UCM_NAME), pa_proplist_gets(d2->proplist, PA_ALSA_PROP_UCM_NAME));
910 }
911 
set_eld_devices(pa_hashmap * hash)912 static void set_eld_devices(pa_hashmap *hash)
913 {
914     pa_device_port *port;
915     pa_alsa_ucm_port_data *data;
916     pa_alsa_ucm_device *dev;
917     const char *eld_mixer_device_name;
918     void *state;
919     int idx, eld_device;
920 
921     PA_HASHMAP_FOREACH(port, hash, state) {
922         data = PA_DEVICE_PORT_DATA(port);
923         eld_mixer_device_name = NULL;
924         eld_device = -1;
925         PA_DYNARRAY_FOREACH(dev, data->devices, idx) {
926             if (dev->eld_device >= 0 && dev->eld_mixer_device_name) {
927                 if (eld_device >= 0 && eld_device != dev->eld_device) {
928                     pa_log_error("The ELD device is already set!");
929                 } else if (eld_mixer_device_name && pa_streq(dev->eld_mixer_device_name, eld_mixer_device_name)) {
930                     pa_log_error("The ELD mixer device is already set (%s, %s)!", dev->eld_mixer_device_name, dev->eld_mixer_device_name);
931                 } else {
932                     eld_mixer_device_name = dev->eld_mixer_device_name;
933                     eld_device = dev->eld_device;
934                 }
935             }
936         }
937         data->eld_device = eld_device;
938         data->eld_mixer_device_name = pa_xstrdup(eld_mixer_device_name);
939     }
940 }
941 
update_mixer_paths(pa_hashmap * ports,const char * profile)942 static void update_mixer_paths(pa_hashmap *ports, const char *profile) {
943     pa_device_port *port;
944     pa_alsa_ucm_port_data *data;
945     void *state;
946 
947     /* select volume controls on ports */
948     PA_HASHMAP_FOREACH(port, ports, state) {
949         pa_log_info("Updating mixer path for %s: %s", profile, port->name);
950         data = PA_DEVICE_PORT_DATA(port);
951         data->path = pa_hashmap_get(data->paths, profile);
952     }
953 }
954 
probe_volumes(pa_hashmap * hash,bool is_sink,snd_pcm_t * pcm_handle,pa_hashmap * mixers,bool ignore_dB)955 static void probe_volumes(pa_hashmap *hash, bool is_sink, snd_pcm_t *pcm_handle, pa_hashmap *mixers, bool ignore_dB) {
956     pa_device_port *port;
957     pa_alsa_path *path;
958     pa_alsa_ucm_port_data *data;
959     pa_alsa_ucm_device *dev;
960     snd_mixer_t *mixer_handle;
961     const char *profile, *mdev, *mdev2;
962     void *state, *state2;
963     int idx;
964 
965     PA_HASHMAP_FOREACH(port, hash, state) {
966         data = PA_DEVICE_PORT_DATA(port);
967 
968         mdev = NULL;
969         PA_DYNARRAY_FOREACH(dev, data->devices, idx) {
970             mdev2 = get_mixer_device(dev, is_sink);
971             if (mdev && mdev2 && !pa_streq(mdev, mdev2)) {
972                 pa_log_error("Two mixer device names found ('%s', '%s'), using s/w volume", mdev, mdev2);
973                 goto fail;
974             }
975             if (mdev2)
976                 mdev = mdev2;
977         }
978 
979         if (mdev == NULL || !(mixer_handle = pa_alsa_open_mixer_by_name(mixers, mdev, true))) {
980             pa_log_error("Failed to find a working mixer device (%s).", mdev);
981             goto fail;
982         }
983 
984         PA_HASHMAP_FOREACH_KV(profile, path, data->paths, state2) {
985             if (pa_alsa_path_probe(path, NULL, mixer_handle, ignore_dB) < 0) {
986                 pa_log_warn("Could not probe path: %s, using s/w volume", path->name);
987                 pa_hashmap_remove(data->paths, profile);
988             } else if (!path->has_volume && !path->has_mute) {
989                 pa_log_warn("Path %s is not a volume or mute control", path->name);
990                 pa_hashmap_remove(data->paths, profile);
991             } else
992                 pa_log_debug("Set up h/w %s using '%s' for %s:%s", path->has_volume ? "volume" : "mute",
993                                 path->name, profile, port->name);
994         }
995     }
996 
997     return;
998 
999 fail:
1000     /* We could not probe the paths we created. Free them and revert to software volumes. */
1001     PA_HASHMAP_FOREACH(port, hash, state) {
1002         data = PA_DEVICE_PORT_DATA(port);
1003         pa_hashmap_remove_all(data->paths);
1004     }
1005 }
1006 
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)1007 static void ucm_add_port_combination(
1008         pa_hashmap *hash,
1009         pa_alsa_ucm_mapping_context *context,
1010         bool is_sink,
1011         pa_alsa_ucm_device **pdevices,
1012         int num,
1013         pa_hashmap *ports,
1014         pa_card_profile *cp,
1015         pa_core *core) {
1016 
1017     pa_device_port *port;
1018     int i;
1019     unsigned priority;
1020     double prio2;
1021     char *name, *desc;
1022     const char *dev_name;
1023     const char *direction;
1024     const char *profile;
1025     pa_alsa_ucm_device *sorted[num], *dev;
1026     pa_alsa_ucm_port_data *data;
1027     pa_alsa_ucm_volume *vol;
1028     pa_alsa_jack *jack, *jack2;
1029     pa_device_port_type_t type, type2;
1030     void *state;
1031 
1032     for (i = 0; i < num; i++)
1033         sorted[i] = pdevices[i];
1034 
1035     /* Sort by alphabetical order so as to have a deterministic naming scheme
1036      * for combination ports */
1037     qsort(&sorted[0], num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp);
1038 
1039     dev = sorted[0];
1040     dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
1041 
1042     name = pa_sprintf_malloc("%s%s", is_sink ? PA_UCM_PRE_TAG_OUTPUT : PA_UCM_PRE_TAG_INPUT, dev_name);
1043     desc = num == 1 ? pa_xstrdup(pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION))
1044             : pa_sprintf_malloc("Combination port for %s", dev_name);
1045 
1046     priority = is_sink ? dev->playback_priority : dev->capture_priority;
1047     prio2 = (priority == 0 ? 0 : 1.0/priority);
1048     jack = ucm_get_jack(context->ucm, dev);
1049     type = dev->type;
1050 
1051     for (i = 1; i < num; i++) {
1052         char *tmp;
1053 
1054         dev = sorted[i];
1055         dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
1056 
1057         tmp = pa_sprintf_malloc("%s+%s", name, dev_name);
1058         pa_xfree(name);
1059         name = tmp;
1060 
1061         tmp = pa_sprintf_malloc("%s,%s", desc, dev_name);
1062         pa_xfree(desc);
1063         desc = tmp;
1064 
1065         priority = is_sink ? dev->playback_priority : dev->capture_priority;
1066         if (priority != 0 && prio2 > 0)
1067             prio2 += 1.0/priority;
1068 
1069         jack2 = ucm_get_jack(context->ucm, dev);
1070         if (jack2) {
1071             if (jack && jack != jack2)
1072                 pa_log_warn("Multiple jacks per combined device '%s': '%s' '%s'", name, jack->name, jack2->name);
1073             jack = jack2;
1074         }
1075 
1076         type2 = dev->type;
1077         if (type2 != PA_DEVICE_PORT_TYPE_UNKNOWN) {
1078             if (type != PA_DEVICE_PORT_TYPE_UNKNOWN && type != type2)
1079                 pa_log_warn("Multiple device types per combined device '%s': %d %d", name, type, type2);
1080             type = type2;
1081         }
1082     }
1083 
1084     /* Make combination ports always have lower priority, and use the formula
1085        1/p = 1/p1 + 1/p2 + ... 1/pn.
1086        This way, the result will always be less than the individual components,
1087        yet higher components will lead to higher result. */
1088 
1089     if (num > 1)
1090         priority = prio2 > 0 ? 1.0/prio2 : 0;
1091 
1092     port = pa_hashmap_get(ports, name);
1093     if (!port) {
1094         pa_device_port_new_data port_data;
1095 
1096         pa_device_port_new_data_init(&port_data);
1097         pa_device_port_new_data_set_name(&port_data, name);
1098         pa_device_port_new_data_set_description(&port_data, desc);
1099         pa_device_port_new_data_set_type(&port_data, type);
1100         pa_device_port_new_data_set_direction(&port_data, is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT);
1101         if (jack)
1102             pa_device_port_new_data_set_availability_group(&port_data, jack->name);
1103 
1104         port = pa_device_port_new(core, &port_data, sizeof(pa_alsa_ucm_port_data));
1105         pa_device_port_new_data_done(&port_data);
1106 
1107         data = PA_DEVICE_PORT_DATA(port);
1108         ucm_port_data_init(data, context->ucm, port, pdevices, num);
1109         port->impl_free = ucm_port_data_free;
1110 
1111         pa_hashmap_put(ports, port->name, port);
1112         pa_log_debug("Add port %s: %s", port->name, port->description);
1113 
1114         if (num == 1) {
1115             /* To keep things simple and not worry about stacking controls, we only support hardware volumes on non-combination
1116              * ports. */
1117             PA_HASHMAP_FOREACH_KV(profile, vol, is_sink ? dev->playback_volumes : dev->capture_volumes, state) {
1118                 pa_alsa_path *path = pa_alsa_path_synthesize(vol->mixer_elem,
1119                                                              is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT);
1120 
1121                 if (!path)
1122                     pa_log_warn("Failed to set up volume control: %s", vol->mixer_elem);
1123                 else {
1124                     if (vol->master_elem) {
1125                         pa_alsa_element *e = pa_alsa_element_get(path, vol->master_elem, false);
1126                         e->switch_use = PA_ALSA_SWITCH_MUTE;
1127                         e->volume_use = PA_ALSA_VOLUME_MERGE;
1128                     }
1129 
1130                     pa_hashmap_put(data->paths, pa_xstrdup(profile), path);
1131 
1132                     /* Add path also to already created empty path set */
1133                     dev = sorted[0];
1134                     if (is_sink)
1135                         pa_hashmap_put(dev->playback_mapping->output_path_set->paths, pa_xstrdup(vol->mixer_elem), path);
1136                     else
1137                         pa_hashmap_put(dev->capture_mapping->input_path_set->paths, pa_xstrdup(vol->mixer_elem), path);
1138                 }
1139             }
1140         }
1141     }
1142 
1143     port->priority = priority;
1144 
1145     pa_xfree(name);
1146     pa_xfree(desc);
1147 
1148     direction = is_sink ? "output" : "input";
1149     pa_log_debug("Port %s direction %s, priority %d", port->name, direction, priority);
1150 
1151     if (cp) {
1152         pa_log_debug("Adding profile %s to port %s.", cp->name, port->name);
1153         pa_hashmap_put(port->profiles, cp->name, cp);
1154     }
1155 
1156     if (hash) {
1157         pa_hashmap_put(hash, port->name, port);
1158         pa_device_port_ref(port);
1159     }
1160 }
1161 
ucm_port_contains(const char * port_name,const char * dev_name,bool is_sink)1162 static int ucm_port_contains(const char *port_name, const char *dev_name, bool is_sink) {
1163     int ret = 0;
1164     const char *r;
1165     const char *state = NULL;
1166     size_t len;
1167 
1168     if (!port_name || !dev_name)
1169         return false;
1170 
1171     port_name += is_sink ? strlen(PA_UCM_PRE_TAG_OUTPUT) : strlen(PA_UCM_PRE_TAG_INPUT);
1172 
1173     while ((r = pa_split_in_place(port_name, "+", &len, &state))) {
1174         if (strlen(dev_name) == len && !strncmp(r, dev_name, len)) {
1175             ret = 1;
1176             break;
1177         }
1178     }
1179 
1180     return ret;
1181 }
1182 
ucm_check_conformance(pa_alsa_ucm_mapping_context * context,pa_alsa_ucm_device ** pdevices,int dev_num,pa_alsa_ucm_device * dev)1183 static int ucm_check_conformance(
1184         pa_alsa_ucm_mapping_context *context,
1185         pa_alsa_ucm_device **pdevices,
1186         int dev_num,
1187         pa_alsa_ucm_device *dev) {
1188 
1189     uint32_t idx;
1190     pa_alsa_ucm_device *d;
1191     int i;
1192 
1193     pa_assert(dev);
1194 
1195     pa_log_debug("Check device %s conformance with %d other devices",
1196             pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME), dev_num);
1197     if (dev_num == 0) {
1198         pa_log_debug("First device in combination, number 1");
1199         return 1;
1200     }
1201 
1202     if (dev->conflicting_devices) { /* the device defines conflicting devices */
1203         PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) {
1204             for (i = 0; i < dev_num; i++) {
1205                 if (pdevices[i] == d) {
1206                     pa_log_debug("Conflicting device found");
1207                     return 0;
1208                 }
1209             }
1210         }
1211     } else if (dev->supported_devices) { /* the device defines supported devices */
1212         for (i = 0; i < dev_num; i++) {
1213             if (!ucm_device_exists(dev->supported_devices, pdevices[i])) {
1214                 pa_log_debug("Supported device not found");
1215                 return 0;
1216             }
1217         }
1218     } else { /* not support any other devices */
1219         pa_log_debug("Not support any other devices");
1220         return 0;
1221     }
1222 
1223     pa_log_debug("Device added to combination, number %d", dev_num + 1);
1224     return 1;
1225 }
1226 
get_next_device(pa_idxset * idxset,uint32_t * idx)1227 static inline pa_alsa_ucm_device *get_next_device(pa_idxset *idxset, uint32_t *idx) {
1228     pa_alsa_ucm_device *dev;
1229 
1230     if (*idx == PA_IDXSET_INVALID)
1231         dev = pa_idxset_first(idxset, idx);
1232     else
1233         dev = pa_idxset_next(idxset, idx);
1234 
1235     return dev;
1236 }
1237 
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)1238 static void ucm_add_ports_combination(
1239         pa_hashmap *hash,
1240         pa_alsa_ucm_mapping_context *context,
1241         bool is_sink,
1242         pa_alsa_ucm_device **pdevices,
1243         int dev_num,
1244         uint32_t map_index,
1245         pa_hashmap *ports,
1246         pa_card_profile *cp,
1247         pa_core *core) {
1248 
1249     pa_alsa_ucm_device *dev;
1250     uint32_t idx = map_index;
1251 
1252     if ((dev = get_next_device(context->ucm_devices, &idx)) == NULL)
1253         return;
1254 
1255     /* check if device at map_index can combine with existing devices combination */
1256     if (ucm_check_conformance(context, pdevices, dev_num, dev)) {
1257         /* add device at map_index to devices combination */
1258         pdevices[dev_num] = dev;
1259         /* add current devices combination as a new port */
1260         ucm_add_port_combination(hash, context, is_sink, pdevices, dev_num + 1, ports, cp, core);
1261         /* try more elements combination */
1262         ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num + 1, idx, ports, cp, core);
1263     }
1264 
1265     /* try other device with current elements number */
1266     ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num, idx, ports, cp, core);
1267 }
1268 
merge_roles(const char * cur,const char * add)1269 static char* merge_roles(const char *cur, const char *add) {
1270     char *r, *ret;
1271     const char *state = NULL;
1272 
1273     if (add == NULL)
1274         return pa_xstrdup(cur);
1275     else if (cur == NULL)
1276         return pa_xstrdup(add);
1277 
1278     ret = pa_xstrdup(cur);
1279 
1280     while ((r = pa_split_spaces(add, &state))) {
1281         char *value;
1282 
1283         if (!pa_str_in_list_spaces(ret, r))
1284             value = pa_sprintf_malloc("%s %s", ret, r);
1285         else {
1286             pa_xfree(r);
1287             continue;
1288         }
1289 
1290         pa_xfree(ret);
1291         ret = value;
1292         pa_xfree(r);
1293     }
1294 
1295     return ret;
1296 }
1297 
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)1298 void pa_alsa_ucm_add_ports_combination(
1299         pa_hashmap *p,
1300         pa_alsa_ucm_mapping_context *context,
1301         bool is_sink,
1302         pa_hashmap *ports,
1303         pa_card_profile *cp,
1304         pa_core *core) {
1305 
1306     pa_alsa_ucm_device **pdevices;
1307 
1308     pa_assert(context->ucm_devices);
1309 
1310     if (pa_idxset_size(context->ucm_devices) > 0) {
1311         pdevices = pa_xnew(pa_alsa_ucm_device *, pa_idxset_size(context->ucm_devices));
1312         ucm_add_ports_combination(p, context, is_sink, pdevices, 0, PA_IDXSET_INVALID, ports, cp, core);
1313         pa_xfree(pdevices);
1314     }
1315 
1316     /* ELD devices */
1317     set_eld_devices(ports);
1318 }
1319 
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)1320 void pa_alsa_ucm_add_ports(
1321         pa_hashmap **p,
1322         pa_proplist *proplist,
1323         pa_alsa_ucm_mapping_context *context,
1324         bool is_sink,
1325         pa_card *card,
1326         snd_pcm_t *pcm_handle,
1327         bool ignore_dB) {
1328 
1329     uint32_t idx;
1330     char *merged_roles;
1331     const char *role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES;
1332     pa_alsa_ucm_device *dev;
1333     pa_alsa_ucm_modifier *mod;
1334     char *tmp;
1335 
1336     pa_assert(p);
1337     pa_assert(*p);
1338 
1339     /* add ports first */
1340     pa_alsa_ucm_add_ports_combination(*p, context, is_sink, card->ports, NULL, card->core);
1341 
1342     /* now set up volume paths if any */
1343     probe_volumes(*p, is_sink, pcm_handle, context->ucm->mixers, ignore_dB);
1344 
1345     /* probe_volumes() removes per-profile paths from ports if probing them
1346      * fails. The path for the current profile is cached in
1347      * pa_alsa_ucm_port_data.path, which is not cleared by probe_volumes() if
1348      * the path gets removed, so we have to call update_mixer_paths() here to
1349      * unset the cached path if needed. */
1350     if (card->active_profile)
1351         update_mixer_paths(*p, card->active_profile->name);
1352 
1353     /* then set property PA_PROP_DEVICE_INTENDED_ROLES */
1354     merged_roles = pa_xstrdup(pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES));
1355     PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1356         const char *roles = pa_proplist_gets(dev->proplist, role_name);
1357         tmp = merge_roles(merged_roles, roles);
1358         pa_xfree(merged_roles);
1359         merged_roles = tmp;
1360     }
1361 
1362     if (context->ucm_modifiers)
1363         PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) {
1364             tmp = merge_roles(merged_roles, mod->media_role);
1365             pa_xfree(merged_roles);
1366             merged_roles = tmp;
1367         }
1368 
1369     if (merged_roles)
1370         pa_proplist_sets(proplist, PA_PROP_DEVICE_INTENDED_ROLES, merged_roles);
1371 
1372     pa_log_info("ALSA device %s roles: %s", pa_proplist_gets(proplist, PA_PROP_DEVICE_STRING), pa_strnull(merged_roles));
1373     pa_xfree(merged_roles);
1374 }
1375 
1376 /* 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)1377 int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile) {
1378     int ret = 0;
1379     const char *profile;
1380     pa_alsa_ucm_verb *verb;
1381 
1382     if (new_profile == old_profile)
1383         return ret;
1384     else if (new_profile == NULL || old_profile == NULL)
1385         profile = new_profile ? new_profile : SND_USE_CASE_VERB_INACTIVE;
1386     else if (!pa_streq(new_profile, old_profile))
1387         profile = new_profile;
1388     else
1389         return ret;
1390 
1391     /* change verb */
1392     pa_log_info("Set UCM verb to %s", profile);
1393     if ((snd_use_case_set(ucm->ucm_mgr, "_verb", profile)) < 0) {
1394         pa_log("Failed to set verb %s", profile);
1395         ret = -1;
1396     }
1397 
1398     /* find active verb */
1399     ucm->active_verb = NULL;
1400     PA_LLIST_FOREACH(verb, ucm->verbs) {
1401         const char *verb_name;
1402         verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME);
1403         if (pa_streq(verb_name, profile)) {
1404             ucm->active_verb = verb;
1405             break;
1406         }
1407     }
1408 
1409     update_mixer_paths(card->ports, profile);
1410     return ret;
1411 }
1412 
pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context * context,pa_device_port * port,bool is_sink)1413 int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) {
1414     int i;
1415     int ret = 0;
1416     pa_alsa_ucm_config *ucm;
1417     const char **enable_devs;
1418     int enable_num = 0;
1419     uint32_t idx;
1420     pa_alsa_ucm_device *dev;
1421 
1422     pa_assert(context && context->ucm);
1423 
1424     ucm = context->ucm;
1425     pa_assert(ucm->ucm_mgr);
1426 
1427     enable_devs = pa_xnew(const char *, pa_idxset_size(context->ucm_devices));
1428 
1429     /* first disable then enable */
1430     PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1431         const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
1432 
1433         if (ucm_port_contains(port->name, dev_name, is_sink))
1434             enable_devs[enable_num++] = dev_name;
1435         else {
1436             pa_log_debug("Disable ucm device %s", dev_name);
1437             if (snd_use_case_set(ucm->ucm_mgr, "_disdev", dev_name) > 0) {
1438                 pa_log("Failed to disable ucm device %s", dev_name);
1439                 ret = -1;
1440                 break;
1441             }
1442         }
1443     }
1444 
1445     for (i = 0; i < enable_num; i++) {
1446         pa_log_debug("Enable ucm device %s", enable_devs[i]);
1447         if (snd_use_case_set(ucm->ucm_mgr, "_enadev", enable_devs[i]) < 0) {
1448             pa_log("Failed to enable ucm device %s", enable_devs[i]);
1449             ret = -1;
1450             break;
1451         }
1452     }
1453 
1454     pa_xfree(enable_devs);
1455 
1456     return ret;
1457 }
1458 
ucm_add_mapping(pa_alsa_profile * p,pa_alsa_mapping * m)1459 static void ucm_add_mapping(pa_alsa_profile *p, pa_alsa_mapping *m) {
1460 
1461     pa_alsa_path_set *ps;
1462 
1463     /* create empty path set for the future path additions */
1464     ps = pa_xnew0(pa_alsa_path_set, 1);
1465     ps->direction = m->direction;
1466     ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1467 
1468     switch (m->direction) {
1469         case PA_ALSA_DIRECTION_ANY:
1470             pa_idxset_put(p->output_mappings, m, NULL);
1471             pa_idxset_put(p->input_mappings, m, NULL);
1472             m->output_path_set = ps;
1473             m->input_path_set = ps;
1474             break;
1475         case PA_ALSA_DIRECTION_OUTPUT:
1476             pa_idxset_put(p->output_mappings, m, NULL);
1477             m->output_path_set = ps;
1478             break;
1479         case PA_ALSA_DIRECTION_INPUT:
1480             pa_idxset_put(p->input_mappings, m, NULL);
1481             m->input_path_set = ps;
1482             break;
1483     }
1484 }
1485 
alsa_mapping_add_ucm_device(pa_alsa_mapping * m,pa_alsa_ucm_device * device)1486 static void alsa_mapping_add_ucm_device(pa_alsa_mapping *m, pa_alsa_ucm_device *device) {
1487     char *cur_desc;
1488     const char *new_desc, *mdev;
1489     bool is_sink = m->direction == PA_ALSA_DIRECTION_OUTPUT;
1490 
1491     pa_idxset_put(m->ucm_context.ucm_devices, device, NULL);
1492 
1493     new_desc = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
1494     cur_desc = m->description;
1495     if (cur_desc)
1496         m->description = pa_sprintf_malloc("%s + %s", cur_desc, new_desc);
1497     else
1498         m->description = pa_xstrdup(new_desc);
1499     pa_xfree(cur_desc);
1500 
1501     /* walk around null case */
1502     m->description = m->description ? m->description : pa_xstrdup("");
1503 
1504     /* save mapping to ucm device */
1505     if (is_sink)
1506         device->playback_mapping = m;
1507     else
1508         device->capture_mapping = m;
1509 
1510     mdev = get_mixer_device(device, is_sink);
1511     if (mdev)
1512         pa_proplist_sets(m->proplist, "alsa.mixer_device", mdev);
1513 }
1514 
alsa_mapping_add_ucm_modifier(pa_alsa_mapping * m,pa_alsa_ucm_modifier * modifier)1515 static void alsa_mapping_add_ucm_modifier(pa_alsa_mapping *m, pa_alsa_ucm_modifier *modifier) {
1516     char *cur_desc;
1517     const char *new_desc, *mod_name, *channel_str;
1518     uint32_t channels = 0;
1519 
1520     pa_idxset_put(m->ucm_context.ucm_modifiers, modifier, NULL);
1521 
1522     new_desc = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
1523     cur_desc = m->description;
1524     if (cur_desc)
1525         m->description = pa_sprintf_malloc("%s + %s", cur_desc, new_desc);
1526     else
1527         m->description = pa_xstrdup(new_desc);
1528     pa_xfree(cur_desc);
1529 
1530     m->description = m->description ? m->description : pa_xstrdup("");
1531 
1532     /* Modifier sinks should not be routed to by default */
1533     m->priority = 0;
1534 
1535     mod_name = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_NAME);
1536     pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_MODIFIER, mod_name);
1537 
1538     /* save mapping to ucm modifier */
1539     if (m->direction == PA_ALSA_DIRECTION_OUTPUT) {
1540         modifier->playback_mapping = m;
1541         channel_str = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS);
1542     } else {
1543         modifier->capture_mapping = m;
1544         channel_str = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS);
1545     }
1546 
1547     if (channel_str) {
1548         /* FIXME: channel_str is unsanitized input from the UCM configuration,
1549          * we should do proper error handling instead of asserting.
1550          * https://bugs.freedesktop.org/show_bug.cgi?id=71823 */
1551         pa_assert_se(pa_atou(channel_str, &channels) == 0 && pa_channels_valid(channels));
1552         pa_log_debug("Got channel count %" PRIu32 " for modifier", channels);
1553     }
1554 
1555     if (channels)
1556         pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
1557     else
1558         pa_channel_map_init(&m->channel_map);
1559 }
1560 
ucm_alsa_mapping_get(pa_alsa_ucm_config * ucm,pa_alsa_profile_set * ps,const char * verb_name,const char * device_str,bool is_sink)1561 static pa_alsa_mapping* ucm_alsa_mapping_get(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps, const char *verb_name, const char *device_str, bool is_sink) {
1562     pa_alsa_mapping *m;
1563     char *mapping_name;
1564     size_t ucm_alibpref_len = 0;
1565 
1566     /* find private alsa-lib's configuration device prefix */
1567 
1568     if (ucm->alib_prefix && pa_startswith(device_str, ucm->alib_prefix))
1569         ucm_alibpref_len = strlen(ucm->alib_prefix);
1570 
1571     mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, device_str + ucm_alibpref_len, is_sink ? "sink" : "source");
1572 
1573     m = pa_alsa_mapping_get(ps, mapping_name);
1574 
1575     if (!m)
1576         pa_log("No mapping for %s", mapping_name);
1577 
1578     pa_xfree(mapping_name);
1579 
1580     return m;
1581 }
1582 
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)1583 static int ucm_create_mapping_direction(
1584         pa_alsa_ucm_config *ucm,
1585         pa_alsa_profile_set *ps,
1586         pa_alsa_profile *p,
1587         pa_alsa_ucm_device *device,
1588         const char *verb_name,
1589         const char *device_name,
1590         const char *device_str,
1591         bool is_sink) {
1592 
1593     pa_alsa_mapping *m;
1594     unsigned priority, rate, channels;
1595 
1596     m = ucm_alsa_mapping_get(ucm, ps, verb_name, device_str, is_sink);
1597 
1598     if (!m)
1599         return -1;
1600 
1601     pa_log_debug("UCM mapping: %s dev %s", m->name, device_name);
1602 
1603     priority = is_sink ? device->playback_priority : device->capture_priority;
1604     rate = is_sink ? device->playback_rate : device->capture_rate;
1605     channels = is_sink ? device->playback_channels : device->capture_channels;
1606 
1607     if (!m->ucm_context.ucm_devices) {   /* new mapping */
1608         m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1609         m->ucm_context.ucm = ucm;
1610         m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
1611 
1612         m->device_strings = pa_xnew0(char*, 2);
1613         m->device_strings[0] = pa_xstrdup(device_str);
1614         m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT;
1615 
1616         ucm_add_mapping(p, m);
1617         if (rate)
1618             m->sample_spec.rate = rate;
1619         pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
1620     }
1621 
1622     /* mapping priority is the highest one of ucm devices */
1623     if (priority > m->priority)
1624         m->priority = priority;
1625 
1626     /* mapping channels is the lowest one of ucm devices */
1627     if (channels < m->channel_map.channels)
1628         pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
1629 
1630     alsa_mapping_add_ucm_device(m, device);
1631 
1632     return 0;
1633 }
1634 
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)1635 static int ucm_create_mapping_for_modifier(
1636         pa_alsa_ucm_config *ucm,
1637         pa_alsa_profile_set *ps,
1638         pa_alsa_profile *p,
1639         pa_alsa_ucm_modifier *modifier,
1640         const char *verb_name,
1641         const char *mod_name,
1642         const char *device_str,
1643         bool is_sink) {
1644 
1645     pa_alsa_mapping *m;
1646 
1647     m = ucm_alsa_mapping_get(ucm, ps, verb_name, device_str, is_sink);
1648 
1649     if (!m)
1650         return -1;
1651 
1652     pa_log_info("UCM mapping: %s modifier %s", m->name, mod_name);
1653 
1654     if (!m->ucm_context.ucm_devices && !m->ucm_context.ucm_modifiers) {   /* new mapping */
1655         m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1656         m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1657         m->ucm_context.ucm = ucm;
1658         m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
1659 
1660         m->device_strings = pa_xnew0(char*, 2);
1661         m->device_strings[0] = pa_xstrdup(device_str);
1662         m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT;
1663         /* Modifier sinks should not be routed to by default */
1664         m->priority = 0;
1665 
1666         ucm_add_mapping(p, m);
1667     } else if (!m->ucm_context.ucm_modifiers) /* share pcm with device */
1668         m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1669 
1670     alsa_mapping_add_ucm_modifier(m, modifier);
1671 
1672     return 0;
1673 }
1674 
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)1675 static int ucm_create_mapping(
1676         pa_alsa_ucm_config *ucm,
1677         pa_alsa_profile_set *ps,
1678         pa_alsa_profile *p,
1679         pa_alsa_ucm_device *device,
1680         const char *verb_name,
1681         const char *device_name,
1682         const char *sink,
1683         const char *source) {
1684 
1685     int ret = 0;
1686 
1687     if (!sink && !source) {
1688         pa_log("No sink and source at %s: %s", verb_name, device_name);
1689         return -1;
1690     }
1691 
1692     if (sink)
1693         ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, sink, true);
1694     if (ret == 0 && source)
1695         ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, source, false);
1696 
1697     return ret;
1698 }
1699 
ucm_get_jack(pa_alsa_ucm_config * ucm,pa_alsa_ucm_device * device)1700 static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *device) {
1701     pa_alsa_jack *j;
1702     const char *device_name;
1703     const char *jack_control;
1704     const char *mixer_device_name;
1705     char *name;
1706 
1707     pa_assert(ucm);
1708     pa_assert(device);
1709 
1710     device_name = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_NAME);
1711 
1712     jack_control = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_JACK_CONTROL);
1713     if (jack_control) {
1714 #if SND_LIB_VERSION >= 0x10201
1715         snd_ctl_elem_id_t *ctl;
1716         int err, index;
1717         snd_ctl_elem_id_alloca(&ctl);
1718         err = snd_use_case_parse_ctl_elem_id(ctl, "JackControl", jack_control);
1719         if (err < 0)
1720             return NULL;
1721         jack_control = snd_ctl_elem_id_get_name(ctl);
1722         index = snd_ctl_elem_id_get_index(ctl);
1723         if (index > 0) {
1724             pa_log("[%s] Invalid JackControl index value: \"%s\",%d", device_name, jack_control, index);
1725             return NULL;
1726         }
1727 #else
1728 #warning "Upgrade to alsa-lib 1.2.1!"
1729 #endif
1730         if (!pa_endswith(jack_control, " Jack")) {
1731             pa_log("[%s] Invalid JackControl value: \"%s\"", device_name, jack_control);
1732             return NULL;
1733         }
1734 
1735         /* pa_alsa_jack_new() expects a jack name without " Jack" at the
1736          * end, so drop the trailing " Jack". */
1737         name = pa_xstrndup(jack_control, strlen(jack_control) - 5);
1738     } else {
1739         /* The jack control hasn't been explicitly configured, fail. */
1740         return NULL;
1741     }
1742 
1743     PA_LLIST_FOREACH(j, ucm->jacks)
1744         if (pa_streq(j->name, name))
1745             goto finish;
1746 
1747     mixer_device_name = get_jack_mixer_device(device, true);
1748     if (!mixer_device_name)
1749         mixer_device_name = get_jack_mixer_device(device, false);
1750     if (!mixer_device_name) {
1751         pa_log("[%s] No mixer device name for JackControl \"%s\"", device_name, jack_control);
1752         return NULL;
1753     }
1754     j = pa_alsa_jack_new(NULL, mixer_device_name, name, 0);
1755     PA_LLIST_PREPEND(pa_alsa_jack, ucm->jacks, j);
1756 
1757 finish:
1758     pa_xfree(name);
1759 
1760     return j;
1761 }
1762 
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)1763 static int ucm_create_profile(
1764         pa_alsa_ucm_config *ucm,
1765         pa_alsa_profile_set *ps,
1766         pa_alsa_ucm_verb *verb,
1767         const char *verb_name,
1768         const char *verb_desc) {
1769 
1770     pa_alsa_profile *p;
1771     pa_alsa_ucm_device *dev;
1772     pa_alsa_ucm_modifier *mod;
1773     int i = 0;
1774     const char *name, *sink, *source;
1775     unsigned int priority;
1776 
1777     pa_assert(ps);
1778 
1779     if (pa_hashmap_get(ps->profiles, verb_name)) {
1780         pa_log("Verb %s already exists", verb_name);
1781         return -1;
1782     }
1783 
1784     p = pa_xnew0(pa_alsa_profile, 1);
1785     p->profile_set = ps;
1786     p->name = pa_xstrdup(verb_name);
1787     p->description = pa_xstrdup(verb_desc);
1788 
1789     p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1790     p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1791 
1792     p->supported = true;
1793     pa_hashmap_put(ps->profiles, p->name, p);
1794 
1795     /* TODO: get profile priority from policy management */
1796     priority = verb->priority;
1797 
1798     if (priority == 0) {
1799         char *verb_cmp, *c;
1800         c = verb_cmp = pa_xstrdup(verb_name);
1801         while (*c) {
1802             if (*c == '_') *c = ' ';
1803             c++;
1804         }
1805         for (i = 0; verb_info[i].id; i++) {
1806             if (strcasecmp(verb_info[i].id, verb_cmp) == 0) {
1807                 priority = verb_info[i].priority;
1808                 break;
1809             }
1810         }
1811         pa_xfree(verb_cmp);
1812     }
1813 
1814     p->priority = priority;
1815 
1816     PA_LLIST_FOREACH(dev, verb->devices) {
1817         pa_alsa_jack *jack;
1818         const char *jack_hw_mute;
1819 
1820         name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
1821 
1822         sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK);
1823         source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE);
1824 
1825         ucm_create_mapping(ucm, ps, p, dev, verb_name, name, sink, source);
1826 
1827         jack = ucm_get_jack(ucm, dev);
1828         if (jack)
1829             device_set_jack(dev, jack);
1830 
1831         /* JackHWMute contains a list of device names. Each listed device must
1832          * be associated with the jack object that we just created. */
1833         jack_hw_mute = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_JACK_HW_MUTE);
1834         if (jack_hw_mute && !jack) {
1835             pa_log("[%s] JackHWMute set, but JackControl is missing", name);
1836             jack_hw_mute = NULL;
1837         }
1838         if (jack_hw_mute) {
1839             char *hw_mute_device_name;
1840             const char *state = NULL;
1841 
1842             while ((hw_mute_device_name = ucm_split_devnames(jack_hw_mute, &state))) {
1843                 pa_alsa_ucm_verb *verb2;
1844                 bool device_found = false;
1845 
1846                 /* Search the referenced device from all verbs. If there are
1847                  * multiple verbs that have a device with this name, we add the
1848                  * hw mute association to each of those devices. */
1849                 PA_LLIST_FOREACH(verb2, ucm->verbs) {
1850                     pa_alsa_ucm_device *hw_mute_device;
1851 
1852                     hw_mute_device = verb_find_device(verb2, hw_mute_device_name);
1853                     if (hw_mute_device) {
1854                         device_found = true;
1855                         device_add_hw_mute_jack(hw_mute_device, jack);
1856                     }
1857                 }
1858 
1859                 if (!device_found)
1860                     pa_log("[%s] JackHWMute references an unknown device: %s", name, hw_mute_device_name);
1861 
1862                 pa_xfree(hw_mute_device_name);
1863             }
1864         }
1865     }
1866 
1867     /* Now find modifiers that have their own PlaybackPCM and create
1868      * separate sinks for them. */
1869     PA_LLIST_FOREACH(mod, verb->modifiers) {
1870         name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
1871 
1872         sink = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SINK);
1873         source = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SOURCE);
1874 
1875         if (sink)
1876             ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, sink, true);
1877         else if (source)
1878             ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, source, false);
1879     }
1880 
1881     pa_alsa_profile_dump(p);
1882 
1883     return 0;
1884 }
1885 
mapping_init_eld(pa_alsa_mapping * m,snd_pcm_t * pcm)1886 static void mapping_init_eld(pa_alsa_mapping *m, snd_pcm_t *pcm)
1887 {
1888     pa_alsa_ucm_mapping_context *context = &m->ucm_context;
1889     pa_alsa_ucm_device *dev;
1890     uint32_t idx;
1891     char *mdev, *alib_prefix;
1892     snd_pcm_info_t *info;
1893     int pcm_card, pcm_device;
1894 
1895     snd_pcm_info_alloca(&info);
1896     if (snd_pcm_info(pcm, info) < 0)
1897         return;
1898 
1899     if ((pcm_card = snd_pcm_info_get_card(info)) < 0)
1900         return;
1901     if ((pcm_device = snd_pcm_info_get_device(info)) < 0)
1902         return;
1903 
1904     alib_prefix = context->ucm->alib_prefix;
1905 
1906     PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1907        mdev = pa_sprintf_malloc("%shw:%i", alib_prefix ? alib_prefix : "", pcm_card);
1908        if (mdev == NULL)
1909            continue;
1910        dev->eld_mixer_device_name = mdev;
1911        dev->eld_device = pcm_device;
1912     }
1913 }
1914 
mapping_open_pcm(pa_alsa_ucm_config * ucm,pa_alsa_mapping * m,int mode)1915 static snd_pcm_t* mapping_open_pcm(pa_alsa_ucm_config *ucm, pa_alsa_mapping *m, int mode) {
1916     snd_pcm_t* pcm;
1917     pa_sample_spec try_ss = ucm->core->default_sample_spec;
1918     pa_channel_map try_map;
1919     snd_pcm_uframes_t try_period_size, try_buffer_size;
1920     bool exact_channels = m->channel_map.channels > 0;
1921 
1922     if (exact_channels) {
1923         try_map = m->channel_map;
1924         try_ss.channels = try_map.channels;
1925     } else
1926         pa_channel_map_init_extend(&try_map, try_ss.channels, PA_CHANNEL_MAP_ALSA);
1927 
1928     try_period_size =
1929         pa_usec_to_bytes(ucm->core->default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
1930         pa_frame_size(&try_ss);
1931     try_buffer_size = ucm->core->default_n_fragments * try_period_size;
1932 
1933     pcm = pa_alsa_open_by_device_string(m->device_strings[0], NULL, &try_ss,
1934             &try_map, mode, &try_period_size, &try_buffer_size, 0, NULL, NULL, exact_channels);
1935 
1936     if (pcm) {
1937         if (!exact_channels)
1938             m->channel_map = try_map;
1939         mapping_init_eld(m, pcm);
1940     }
1941 
1942     return pcm;
1943 }
1944 
profile_finalize_probing(pa_alsa_profile * p)1945 static void profile_finalize_probing(pa_alsa_profile *p) {
1946     pa_alsa_mapping *m;
1947     uint32_t idx;
1948 
1949     PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
1950         if (p->supported)
1951             m->supported++;
1952 
1953         if (!m->output_pcm)
1954             continue;
1955 
1956         snd_pcm_close(m->output_pcm);
1957         m->output_pcm = NULL;
1958     }
1959 
1960     PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
1961         if (p->supported)
1962             m->supported++;
1963 
1964         if (!m->input_pcm)
1965             continue;
1966 
1967         snd_pcm_close(m->input_pcm);
1968         m->input_pcm = NULL;
1969     }
1970 }
1971 
ucm_mapping_jack_probe(pa_alsa_mapping * m,pa_hashmap * mixers)1972 static void ucm_mapping_jack_probe(pa_alsa_mapping *m, pa_hashmap *mixers) {
1973     snd_mixer_t *mixer_handle;
1974     pa_alsa_ucm_mapping_context *context = &m->ucm_context;
1975     pa_alsa_ucm_device *dev;
1976     uint32_t idx;
1977 
1978     PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1979         bool has_control;
1980 
1981         if (!dev->jack || !dev->jack->mixer_device_name)
1982             continue;
1983 
1984         mixer_handle = pa_alsa_open_mixer_by_name(mixers, dev->jack->mixer_device_name, true);
1985         if (!mixer_handle) {
1986             pa_log_error("Unable to determine open mixer device '%s' for jack %s", dev->jack->mixer_device_name, dev->jack->name);
1987             continue;
1988         }
1989 
1990         has_control = pa_alsa_mixer_find_card(mixer_handle, &dev->jack->alsa_id, 0) != NULL;
1991         pa_alsa_jack_set_has_control(dev->jack, has_control);
1992         pa_log_info("UCM jack %s has_control=%d", dev->jack->name, dev->jack->has_control);
1993     }
1994 }
1995 
ucm_probe_profile_set(pa_alsa_ucm_config * ucm,pa_alsa_profile_set * ps)1996 static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps) {
1997     void *state;
1998     pa_alsa_profile *p;
1999     pa_alsa_mapping *m;
2000     uint32_t idx;
2001 
2002     PA_HASHMAP_FOREACH(p, ps->profiles, state) {
2003         /* change verb */
2004         pa_log_info("Set ucm verb to %s", p->name);
2005 
2006         if ((snd_use_case_set(ucm->ucm_mgr, "_verb", p->name)) < 0) {
2007             pa_log("Failed to set verb %s", p->name);
2008             p->supported = false;
2009             continue;
2010         }
2011 
2012         PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
2013             if (PA_UCM_IS_MODIFIER_MAPPING(m)) {
2014                 /* Skip jack probing on modifier PCMs since we expect this to
2015                  * only be controlled on the main device/verb PCM. */
2016                 continue;
2017             }
2018 
2019             m->output_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_PLAYBACK);
2020             if (!m->output_pcm) {
2021                 p->supported = false;
2022                 break;
2023             }
2024         }
2025 
2026         if (p->supported) {
2027             PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
2028                 if (PA_UCM_IS_MODIFIER_MAPPING(m)) {
2029                     /* Skip jack probing on modifier PCMs since we expect this to
2030                      * only be controlled on the main device/verb PCM. */
2031                     continue;
2032                 }
2033 
2034                 m->input_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_CAPTURE);
2035                 if (!m->input_pcm) {
2036                     p->supported = false;
2037                     break;
2038                 }
2039             }
2040         }
2041 
2042         if (!p->supported) {
2043             profile_finalize_probing(p);
2044             continue;
2045         }
2046 
2047         pa_log_debug("Profile %s supported.", p->name);
2048 
2049         PA_IDXSET_FOREACH(m, p->output_mappings, idx)
2050             if (!PA_UCM_IS_MODIFIER_MAPPING(m))
2051                 ucm_mapping_jack_probe(m, ucm->mixers);
2052 
2053         PA_IDXSET_FOREACH(m, p->input_mappings, idx)
2054             if (!PA_UCM_IS_MODIFIER_MAPPING(m))
2055                 ucm_mapping_jack_probe(m, ucm->mixers);
2056 
2057         profile_finalize_probing(p);
2058     }
2059 
2060     /* restore ucm state */
2061     snd_use_case_set(ucm->ucm_mgr, "_verb", SND_USE_CASE_VERB_INACTIVE);
2062 
2063     pa_alsa_profile_set_drop_unsupported(ps);
2064 }
2065 
pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config * ucm,pa_channel_map * default_channel_map)2066 pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map) {
2067     pa_alsa_ucm_verb *verb;
2068     pa_alsa_profile_set *ps;
2069 
2070     ps = pa_xnew0(pa_alsa_profile_set, 1);
2071     ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
2072     ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
2073     ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
2074 
2075     /* create a profile for each verb */
2076     PA_LLIST_FOREACH(verb, ucm->verbs) {
2077         const char *verb_name;
2078         const char *verb_desc;
2079 
2080         verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME);
2081         verb_desc = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
2082         if (verb_name == NULL) {
2083             pa_log("Verb with no name");
2084             continue;
2085         }
2086 
2087         ucm_create_profile(ucm, ps, verb, verb_name, verb_desc);
2088     }
2089 
2090     ucm_probe_profile_set(ucm, ps);
2091     ps->probed = true;
2092 
2093     return ps;
2094 }
2095 
free_verb(pa_alsa_ucm_verb * verb)2096 static void free_verb(pa_alsa_ucm_verb *verb) {
2097     pa_alsa_ucm_device *di, *dn;
2098     pa_alsa_ucm_modifier *mi, *mn;
2099 
2100     PA_LLIST_FOREACH_SAFE(di, dn, verb->devices) {
2101         PA_LLIST_REMOVE(pa_alsa_ucm_device, verb->devices, di);
2102 
2103         if (di->hw_mute_jacks)
2104             pa_dynarray_free(di->hw_mute_jacks);
2105 
2106         if (di->ucm_ports)
2107             pa_dynarray_free(di->ucm_ports);
2108 
2109         if (di->playback_volumes)
2110             pa_hashmap_free(di->playback_volumes);
2111         if (di->capture_volumes)
2112             pa_hashmap_free(di->capture_volumes);
2113 
2114         pa_proplist_free(di->proplist);
2115 
2116         if (di->conflicting_devices)
2117             pa_idxset_free(di->conflicting_devices, NULL);
2118         if (di->supported_devices)
2119             pa_idxset_free(di->supported_devices, NULL);
2120 
2121         pa_xfree(di->eld_mixer_device_name);
2122 
2123         pa_xfree(di);
2124     }
2125 
2126     PA_LLIST_FOREACH_SAFE(mi, mn, verb->modifiers) {
2127         PA_LLIST_REMOVE(pa_alsa_ucm_modifier, verb->modifiers, mi);
2128         pa_proplist_free(mi->proplist);
2129         if (mi->n_suppdev > 0)
2130             snd_use_case_free_list(mi->supported_devices, mi->n_suppdev);
2131         if (mi->n_confdev > 0)
2132             snd_use_case_free_list(mi->conflicting_devices, mi->n_confdev);
2133         pa_xfree(mi->media_role);
2134         pa_xfree(mi);
2135     }
2136     pa_proplist_free(verb->proplist);
2137     pa_xfree(verb);
2138 }
2139 
verb_find_device(pa_alsa_ucm_verb * verb,const char * device_name)2140 static pa_alsa_ucm_device *verb_find_device(pa_alsa_ucm_verb *verb, const char *device_name) {
2141     pa_alsa_ucm_device *device;
2142 
2143     pa_assert(verb);
2144     pa_assert(device_name);
2145 
2146     PA_LLIST_FOREACH(device, verb->devices) {
2147         const char *name;
2148 
2149         name = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_NAME);
2150         if (pa_streq(name, device_name))
2151             return device;
2152     }
2153 
2154     return NULL;
2155 }
2156 
pa_alsa_ucm_free(pa_alsa_ucm_config * ucm)2157 void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) {
2158     pa_alsa_ucm_verb *vi, *vn;
2159     pa_alsa_jack *ji, *jn;
2160 
2161     PA_LLIST_FOREACH_SAFE(vi, vn, ucm->verbs) {
2162         PA_LLIST_REMOVE(pa_alsa_ucm_verb, ucm->verbs, vi);
2163         free_verb(vi);
2164     }
2165     PA_LLIST_FOREACH_SAFE(ji, jn, ucm->jacks) {
2166         PA_LLIST_REMOVE(pa_alsa_jack, ucm->jacks, ji);
2167         pa_alsa_jack_free(ji);
2168     }
2169     if (ucm->ucm_mgr) {
2170         snd_use_case_mgr_close(ucm->ucm_mgr);
2171         ucm->ucm_mgr = NULL;
2172     }
2173     pa_xfree(ucm->alib_prefix);
2174     ucm->alib_prefix = NULL;
2175 }
2176 
pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context * context)2177 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
2178     pa_alsa_ucm_device *dev;
2179     pa_alsa_ucm_modifier *mod;
2180     uint32_t idx;
2181 
2182     if (context->ucm_devices) {
2183         /* clear ucm device pointer to mapping */
2184         PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
2185             if (context->direction == PA_DIRECTION_OUTPUT)
2186                 dev->playback_mapping = NULL;
2187             else
2188                 dev->capture_mapping = NULL;
2189         }
2190 
2191         pa_idxset_free(context->ucm_devices, NULL);
2192     }
2193 
2194     if (context->ucm_modifiers) {
2195         PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) {
2196             if (context->direction == PA_DIRECTION_OUTPUT)
2197                 mod->playback_mapping = NULL;
2198             else
2199                 mod->capture_mapping = NULL;
2200         }
2201 
2202         pa_idxset_free(context->ucm_modifiers, NULL);
2203     }
2204 }
2205 
2206 /* 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)2207 void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
2208     pa_alsa_ucm_modifier *mod;
2209 
2210     if (!ucm->active_verb)
2211         return;
2212 
2213     PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) {
2214         if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) {
2215             if (mod->enabled_counter == 0) {
2216                 const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
2217 
2218                 pa_log_info("Enable ucm modifier %s", mod_name);
2219                 if (snd_use_case_set(ucm->ucm_mgr, "_enamod", mod_name) < 0) {
2220                     pa_log("Failed to enable ucm modifier %s", mod_name);
2221                 }
2222             }
2223 
2224             mod->enabled_counter++;
2225             break;
2226         }
2227     }
2228 }
2229 
2230 /* 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)2231 void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
2232     pa_alsa_ucm_modifier *mod;
2233 
2234     if (!ucm->active_verb)
2235         return;
2236 
2237     PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) {
2238         if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) {
2239 
2240             mod->enabled_counter--;
2241             if (mod->enabled_counter == 0) {
2242                 const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
2243 
2244                 pa_log_info("Disable ucm modifier %s", mod_name);
2245                 if (snd_use_case_set(ucm->ucm_mgr, "_dismod", mod_name) < 0) {
2246                     pa_log("Failed to disable ucm modifier %s", mod_name);
2247                 }
2248             }
2249 
2250             break;
2251         }
2252     }
2253 }
2254 
device_add_ucm_port(pa_alsa_ucm_device * device,pa_alsa_ucm_port_data * port)2255 static void device_add_ucm_port(pa_alsa_ucm_device *device, pa_alsa_ucm_port_data *port) {
2256     pa_assert(device);
2257     pa_assert(port);
2258 
2259     pa_dynarray_append(device->ucm_ports, port);
2260 }
2261 
device_set_jack(pa_alsa_ucm_device * device,pa_alsa_jack * jack)2262 static void device_set_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack) {
2263     pa_assert(device);
2264     pa_assert(jack);
2265 
2266     device->jack = jack;
2267     pa_alsa_jack_add_ucm_device(jack, device);
2268 
2269     pa_alsa_ucm_device_update_available(device);
2270 }
2271 
device_add_hw_mute_jack(pa_alsa_ucm_device * device,pa_alsa_jack * jack)2272 static void device_add_hw_mute_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack) {
2273     pa_assert(device);
2274     pa_assert(jack);
2275 
2276     pa_dynarray_append(device->hw_mute_jacks, jack);
2277     pa_alsa_jack_add_ucm_hw_mute_device(jack, device);
2278 
2279     pa_alsa_ucm_device_update_available(device);
2280 }
2281 
device_set_available(pa_alsa_ucm_device * device,pa_available_t available)2282 static void device_set_available(pa_alsa_ucm_device *device, pa_available_t available) {
2283     pa_alsa_ucm_port_data *port;
2284     unsigned idx;
2285 
2286     pa_assert(device);
2287 
2288     if (available == device->available)
2289         return;
2290 
2291     device->available = available;
2292 
2293     PA_DYNARRAY_FOREACH(port, device->ucm_ports, idx)
2294         ucm_port_update_available(port);
2295 }
2296 
pa_alsa_ucm_device_update_available(pa_alsa_ucm_device * device)2297 void pa_alsa_ucm_device_update_available(pa_alsa_ucm_device *device) {
2298     pa_available_t available = PA_AVAILABLE_UNKNOWN;
2299     pa_alsa_jack *jack;
2300     unsigned idx;
2301 
2302     pa_assert(device);
2303 
2304     if (device->jack && device->jack->has_control)
2305         available = device->jack->plugged_in ? PA_AVAILABLE_YES : PA_AVAILABLE_NO;
2306 
2307     PA_DYNARRAY_FOREACH(jack, device->hw_mute_jacks, idx) {
2308         if (jack->plugged_in) {
2309             available = PA_AVAILABLE_NO;
2310             break;
2311         }
2312     }
2313 
2314     device_set_available(device, available);
2315 }
2316 
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)2317 static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port,
2318                                pa_alsa_ucm_device **devices, unsigned n_devices) {
2319     unsigned i;
2320 
2321     pa_assert(ucm);
2322     pa_assert(core_port);
2323     pa_assert(devices);
2324 
2325     port->ucm = ucm;
2326     port->core_port = core_port;
2327     port->devices = pa_dynarray_new(NULL);
2328     port->eld_device = -1;
2329 
2330     for (i = 0; i < n_devices; i++) {
2331         pa_dynarray_append(port->devices, devices[i]);
2332         device_add_ucm_port(devices[i], port);
2333     }
2334 
2335     port->paths = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree,
2336                                       (pa_free_cb_t) pa_alsa_path_free);
2337 
2338     ucm_port_update_available(port);
2339 }
2340 
ucm_port_data_free(pa_device_port * port)2341 static void ucm_port_data_free(pa_device_port *port) {
2342     pa_alsa_ucm_port_data *ucm_port;
2343 
2344     pa_assert(port);
2345 
2346     ucm_port = PA_DEVICE_PORT_DATA(port);
2347 
2348     if (ucm_port->devices)
2349         pa_dynarray_free(ucm_port->devices);
2350 
2351     if (ucm_port->paths)
2352         pa_hashmap_free(ucm_port->paths);
2353 
2354     pa_xfree(ucm_port->eld_mixer_device_name);
2355 }
2356 
ucm_port_update_available(pa_alsa_ucm_port_data * port)2357 static void ucm_port_update_available(pa_alsa_ucm_port_data *port) {
2358     pa_alsa_ucm_device *device;
2359     unsigned idx;
2360     pa_available_t available = PA_AVAILABLE_YES;
2361 
2362     pa_assert(port);
2363 
2364     PA_DYNARRAY_FOREACH(device, port->devices, idx) {
2365         if (device->available == PA_AVAILABLE_UNKNOWN)
2366             available = PA_AVAILABLE_UNKNOWN;
2367         else if (device->available == PA_AVAILABLE_NO) {
2368             available = PA_AVAILABLE_NO;
2369             break;
2370         }
2371     }
2372 
2373     pa_device_port_set_available(port->core_port, available);
2374 }
2375 
2376 #else /* HAVE_ALSA_UCM */
2377 
2378 /* Dummy functions for systems without UCM support */
2379 
pa_alsa_ucm_query_profiles(pa_alsa_ucm_config * ucm,int card_index)2380 int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index) {
2381         pa_log_info("UCM not available.");
2382         return -1;
2383 }
2384 
pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config * ucm,pa_channel_map * default_channel_map)2385 pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map) {
2386     return NULL;
2387 }
2388 
pa_alsa_ucm_set_profile(pa_alsa_ucm_config * ucm,pa_card * card,const char * new_profile,const char * old_profile)2389 int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile) {
2390     return -1;
2391 }
2392 
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)2393 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) {
2394     return -1;
2395 }
2396 
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)2397 void pa_alsa_ucm_add_ports(
2398         pa_hashmap **hash,
2399         pa_proplist *proplist,
2400         pa_alsa_ucm_mapping_context *context,
2401         bool is_sink,
2402         pa_card *card,
2403         snd_pcm_t *pcm_handle,
2404         bool ignore_dB) {
2405 }
2406 
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)2407 void pa_alsa_ucm_add_ports_combination(
2408         pa_hashmap *hash,
2409         pa_alsa_ucm_mapping_context *context,
2410         bool is_sink,
2411         pa_hashmap *ports,
2412         pa_card_profile *cp,
2413         pa_core *core) {
2414 }
2415 
pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context * context,pa_device_port * port,bool is_sink)2416 int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) {
2417     return -1;
2418 }
2419 
pa_alsa_ucm_free(pa_alsa_ucm_config * ucm)2420 void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) {
2421 }
2422 
pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context * context)2423 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
2424 }
2425 
pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config * ucm,const char * role,pa_direction_t dir)2426 void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
2427 }
2428 
pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config * ucm,const char * role,pa_direction_t dir)2429 void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
2430 }
2431 
2432 #endif
2433