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
69struct ucm_type {
70    const char *prefix;
71    pa_device_port_type_t type;
72};
73
74struct ucm_items {
75    const char *id;
76    const char *property;
77};
78
79struct ucm_info {
80    const char *id;
81    unsigned priority;
82};
83
84static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *device);
85static void device_set_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack);
86static void device_add_hw_mute_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack);
87
88static pa_alsa_ucm_device *verb_find_device(pa_alsa_ucm_verb *verb, const char *device_name);
89
90
91static 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);
93static void ucm_port_data_free(pa_device_port *port);
94static void ucm_port_update_available(pa_alsa_ucm_port_data *port);
95
96static 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
111static 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 */
142static 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 */
156static 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
171static 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
190static 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
201static 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 "". */
226static 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
251static 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 */
260static 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 */
308static 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 */
334static 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 */
351static 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 */
359static 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 */
550static 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 */
585static 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
616static 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
646static 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
662static 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
681static 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
707static 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
729static 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
758int 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
834ucm_verb_fail:
835    if (err < 0) {
836        snd_use_case_mgr_close(ucm->ucm_mgr);
837        ucm->ucm_mgr = NULL;
838    }
839
840ucm_mgr_fail:
841    pa_xfree(card_name);
842
843name_fail:
844    return err;
845}
846
847int 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
905static 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
912static 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
942static 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
955static 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
999fail:
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
1007static 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
1162static 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
1183static 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
1227static 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
1238static 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
1269static 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
1298void 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
1320void 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 */
1377int 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
1413int 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
1459static 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
1486static 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
1515static 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
1561static 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
1583static 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
1635static 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
1675static 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
1700static 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
1757finish:
1758    pa_xfree(name);
1759
1760    return j;
1761}
1762
1763static 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
1886static 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
1915static 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
1945static 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
1972static 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
1996static 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
2066pa_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
2096static 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
2140static 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
2157void 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
2177void 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 */
2207void 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 */
2231void 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
2255static 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
2262static 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
2272static 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
2282static 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
2297void 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
2317static 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
2341static 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
2357static 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
2380int 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
2385pa_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
2389int 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
2393int 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
2397void 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
2407void 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
2416int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) {
2417    return -1;
2418}
2419
2420void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) {
2421}
2422
2423void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
2424}
2425
2426void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
2427}
2428
2429void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
2430}
2431
2432#endif
2433