~binli/ubuntu/vivid/pulseaudio/load-extcon-module

« back to all changes in this revision

Viewing changes to .pc/0202-dont-probe-ucm.patch/src/modules/alsa/alsa-ucm.c

  • Committer: Bin Li
  • Date: 2016-01-23 15:04:48 UTC
  • Revision ID: bin.li@canonical.com-20160123150448-5ockvw4p5xxntda4
init the 1:6.0-0ubuntu9.15 from silo 12

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 <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_items {
 
70
    const char *id;
 
71
    const char *property;
 
72
};
 
73
 
 
74
struct ucm_info {
 
75
    const char *id;
 
76
    unsigned priority;
 
77
};
 
78
 
 
79
static struct ucm_items item[] = {
 
80
    {"PlaybackPCM", PA_ALSA_PROP_UCM_SINK},
 
81
    {"CapturePCM", PA_ALSA_PROP_UCM_SOURCE},
 
82
    {"PlaybackVolume", PA_ALSA_PROP_UCM_PLAYBACK_VOLUME},
 
83
    {"PlaybackSwitch", PA_ALSA_PROP_UCM_PLAYBACK_SWITCH},
 
84
    {"PlaybackPriority", PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY},
 
85
    {"PlaybackRate", PA_ALSA_PROP_UCM_PLAYBACK_RATE},
 
86
    {"PlaybackChannels", PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS},
 
87
    {"CaptureVolume", PA_ALSA_PROP_UCM_CAPTURE_VOLUME},
 
88
    {"CaptureSwitch", PA_ALSA_PROP_UCM_CAPTURE_SWITCH},
 
89
    {"CapturePriority", PA_ALSA_PROP_UCM_CAPTURE_PRIORITY},
 
90
    {"CaptureRate", PA_ALSA_PROP_UCM_CAPTURE_RATE},
 
91
    {"CaptureChannels", PA_ALSA_PROP_UCM_CAPTURE_CHANNELS},
 
92
    {"TQ", PA_ALSA_PROP_UCM_QOS},
 
93
    {NULL, NULL},
 
94
};
 
95
 
 
96
/* UCM verb info - this should eventually be part of policy manangement */
 
97
static struct ucm_info verb_info[] = {
 
98
    {SND_USE_CASE_VERB_INACTIVE, 0},
 
99
    {SND_USE_CASE_VERB_HIFI, 8000},
 
100
    {SND_USE_CASE_VERB_HIFI_LOW_POWER, 7000},
 
101
    {SND_USE_CASE_VERB_VOICE, 6000},
 
102
    {SND_USE_CASE_VERB_VOICE_LOW_POWER, 5000},
 
103
    {SND_USE_CASE_VERB_VOICECALL, 4000},
 
104
    {SND_USE_CASE_VERB_IP_VOICECALL, 4000},
 
105
    {SND_USE_CASE_VERB_ANALOG_RADIO, 3000},
 
106
    {SND_USE_CASE_VERB_DIGITAL_RADIO, 3000},
 
107
    {NULL, 0}
 
108
};
 
109
 
 
110
/* UCM device info - should be overwritten by ucm property */
 
111
static struct ucm_info dev_info[] = {
 
112
    {SND_USE_CASE_DEV_SPEAKER, 100},
 
113
    {SND_USE_CASE_DEV_LINE, 100},
 
114
    {SND_USE_CASE_DEV_HEADPHONES, 100},
 
115
    {SND_USE_CASE_DEV_HEADSET, 300},
 
116
    {SND_USE_CASE_DEV_HANDSET, 200},
 
117
    {SND_USE_CASE_DEV_BLUETOOTH, 400},
 
118
    {SND_USE_CASE_DEV_EARPIECE, 100},
 
119
    {SND_USE_CASE_DEV_SPDIF, 100},
 
120
    {SND_USE_CASE_DEV_HDMI, 100},
 
121
    {SND_USE_CASE_DEV_NONE, 100},
 
122
    {NULL, 0}
 
123
};
 
124
 
 
125
/* UCM profile properties - The verb data is store so it can be used to fill
 
126
 * the new profiles properties */
 
127
static int ucm_get_property(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr, const char *verb_name) {
 
128
    const char *value;
 
129
    char *id;
 
130
    int i;
 
131
 
 
132
    for (i = 0; item[i].id; i++) {
 
133
        int err;
 
134
 
 
135
        id = pa_sprintf_malloc("=%s//%s", item[i].id, verb_name);
 
136
        err = snd_use_case_get(uc_mgr, id, &value);
 
137
        pa_xfree(id);
 
138
        if (err < 0)
 
139
            continue;
 
140
 
 
141
        pa_log_debug("Got %s for verb %s: %s", item[i].id, verb_name, value);
 
142
        pa_proplist_sets(verb->proplist, item[i].property, value);
 
143
        free((void*)value);
 
144
    }
 
145
 
 
146
    return 0;
 
147
};
 
148
 
 
149
static int ucm_device_exists(pa_idxset *idxset, pa_alsa_ucm_device *dev) {
 
150
    pa_alsa_ucm_device *d;
 
151
    uint32_t idx;
 
152
 
 
153
    PA_IDXSET_FOREACH(d, idxset, idx)
 
154
        if (d == dev)
 
155
            return 1;
 
156
 
 
157
    return 0;
 
158
}
 
159
 
 
160
static void ucm_add_devices_to_idxset(
 
161
        pa_idxset *idxset,
 
162
        pa_alsa_ucm_device *me,
 
163
        pa_alsa_ucm_device *devices,
 
164
        const char **dev_names,
 
165
        int n) {
 
166
 
 
167
    pa_alsa_ucm_device *d;
 
168
 
 
169
    PA_LLIST_FOREACH(d, devices) {
 
170
        const char *name;
 
171
        int i;
 
172
 
 
173
        if (d == me)
 
174
            continue;
 
175
 
 
176
        name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
 
177
 
 
178
        for (i = 0; i < n; i++)
 
179
            if (pa_streq(dev_names[i], name))
 
180
                pa_idxset_put(idxset, d, NULL);
 
181
    }
 
182
}
 
183
 
 
184
/* Create a property list for this ucm device */
 
185
static int ucm_get_device_property(
 
186
        pa_alsa_ucm_device *device,
 
187
        snd_use_case_mgr_t *uc_mgr,
 
188
        pa_alsa_ucm_verb *verb,
 
189
        const char *device_name) {
 
190
 
 
191
    const char *value;
 
192
    const char **devices;
 
193
    char *id;
 
194
    int i;
 
195
    int err;
 
196
    uint32_t ui;
 
197
    int n_confdev, n_suppdev;
 
198
 
 
199
    for (i = 0; item[i].id; i++) {
 
200
        id = pa_sprintf_malloc("=%s/%s", item[i].id, device_name);
 
201
        err = snd_use_case_get(uc_mgr, id, &value);
 
202
        pa_xfree(id);
 
203
        if (err < 0)
 
204
            continue;
 
205
 
 
206
        pa_log_debug("Got %s for device %s: %s", item[i].id, device_name, value);
 
207
        pa_proplist_sets(device->proplist, item[i].property, value);
 
208
        free((void*)value);
 
209
    }
 
210
 
 
211
    /* get direction and channels */
 
212
    value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS);
 
213
    if (value) { /* output */
 
214
        /* get channels */
 
215
        if (pa_atou(value, &ui) == 0 && pa_channels_valid(ui))
 
216
            device->playback_channels = ui;
 
217
        else
 
218
            pa_log("UCM playback channels %s for device %s out of range", value, device_name);
 
219
 
 
220
        /* get pcm */
 
221
        value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SINK);
 
222
        if (!value) { /* take pcm from verb playback default */
 
223
            value = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_SINK);
 
224
            if (value) {
 
225
                pa_log_debug("UCM playback device %s fetch pcm from verb default %s", device_name, value);
 
226
                pa_proplist_sets(device->proplist, PA_ALSA_PROP_UCM_SINK, value);
 
227
            } else
 
228
                pa_log("UCM playback device %s fetch pcm failed", device_name);
 
229
        }
 
230
    }
 
231
 
 
232
    value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS);
 
233
    if (value) { /* input */
 
234
        /* get channels */
 
235
        if (pa_atou(value, &ui) == 0 && pa_channels_valid(ui))
 
236
            device->capture_channels = ui;
 
237
        else
 
238
            pa_log("UCM capture channels %s for device %s out of range", value, device_name);
 
239
 
 
240
        /* get pcm */
 
241
        value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SOURCE);
 
242
        if (!value) { /* take pcm from verb capture default */
 
243
            value = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_SOURCE);
 
244
            if (value) {
 
245
                pa_log_debug("UCM capture device %s fetch pcm from verb default %s", device_name, value);
 
246
                pa_proplist_sets(device->proplist, PA_ALSA_PROP_UCM_SOURCE, value);
 
247
            } else
 
248
                pa_log("UCM capture device %s fetch pcm failed", device_name);
 
249
        }
 
250
    }
 
251
 
 
252
    if (device->playback_channels == 0 && device->capture_channels == 0) {
 
253
        pa_log_warn("UCM file does not specify 'PlaybackChannels' or 'CaptureChannels'"
 
254
                    "for device %s, assuming stereo duplex.", device_name);
 
255
        device->playback_channels = 2;
 
256
        device->capture_channels = 2;
 
257
    }
 
258
 
 
259
    /* get rate and priority of device */
 
260
    if (device->playback_channels) { /* sink device */
 
261
        /* get rate */
 
262
        if ((value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_RATE)) ||
 
263
            (value = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_PLAYBACK_RATE))) {
 
264
            if (pa_atou(value, &ui) == 0 && pa_sample_rate_valid(ui)) {
 
265
                pa_log_debug("UCM playback device %s rate %d", device_name, ui);
 
266
                device->playback_rate = ui;
 
267
            } else
 
268
                pa_log_debug("UCM playback device %s has bad rate %s", device_name, value);
 
269
        }
 
270
 
 
271
        value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY);
 
272
        if (value) {
 
273
            /* get priority from ucm config */
 
274
            if (pa_atou(value, &ui) == 0)
 
275
                device->playback_priority = ui;
 
276
            else
 
277
                pa_log_debug("UCM playback priority %s for device %s error", value, device_name);
 
278
        }
 
279
    }
 
280
 
 
281
    if (device->capture_channels) { /* source device */
 
282
        /* get rate */
 
283
        if ((value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_RATE)) ||
 
284
            (value = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_CAPTURE_RATE))) {
 
285
            if (pa_atou(value, &ui) == 0 && pa_sample_rate_valid(ui)) {
 
286
                pa_log_debug("UCM capture device %s rate %d", device_name, ui);
 
287
                device->capture_rate = ui;
 
288
            } else
 
289
                pa_log_debug("UCM capture device %s has bad rate %s", device_name, value);
 
290
        }
 
291
 
 
292
        value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_PRIORITY);
 
293
        if (value) {
 
294
            /* get priority from ucm config */
 
295
            if (pa_atou(value, &ui) == 0)
 
296
                device->capture_priority = ui;
 
297
            else
 
298
                pa_log_debug("UCM capture priority %s for device %s error", value, device_name);
 
299
        }
 
300
    }
 
301
 
 
302
    if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device) || PA_UCM_CAPTURE_PRIORITY_UNSET(device)) {
 
303
        /* get priority from static table */
 
304
        for (i = 0; dev_info[i].id; i++) {
 
305
            if (strcasecmp(dev_info[i].id, device_name) == 0) {
 
306
                PA_UCM_DEVICE_PRIORITY_SET(device, dev_info[i].priority);
 
307
                break;
 
308
            }
 
309
        }
 
310
    }
 
311
 
 
312
    if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device)) {
 
313
        /* fall through to default priority */
 
314
        device->playback_priority = 100;
 
315
    }
 
316
 
 
317
    if (PA_UCM_CAPTURE_PRIORITY_UNSET(device)) {
 
318
        /* fall through to default priority */
 
319
        device->capture_priority = 100;
 
320
    }
 
321
 
 
322
    id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", device_name);
 
323
    n_confdev = snd_use_case_get_list(uc_mgr, id, &devices);
 
324
    pa_xfree(id);
 
325
 
 
326
    if (n_confdev <= 0)
 
327
        pa_log_debug("No %s for device %s", "_conflictingdevs", device_name);
 
328
    else {
 
329
        device->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
330
        ucm_add_devices_to_idxset(device->conflicting_devices, device, verb->devices, devices, n_confdev);
 
331
        snd_use_case_free_list(devices, n_confdev);
 
332
    }
 
333
 
 
334
    id = pa_sprintf_malloc("%s/%s", "_supporteddevs", device_name);
 
335
    n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices);
 
336
    pa_xfree(id);
 
337
 
 
338
    if (n_suppdev <= 0)
 
339
        pa_log_debug("No %s for device %s", "_supporteddevs", device_name);
 
340
    else {
 
341
        device->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
342
        ucm_add_devices_to_idxset(device->supported_devices, device, verb->devices, devices, n_suppdev);
 
343
        snd_use_case_free_list(devices, n_suppdev);
 
344
    }
 
345
 
 
346
    return 0;
 
347
};
 
348
 
 
349
/* Create a property list for this ucm modifier */
 
350
static int ucm_get_modifier_property(pa_alsa_ucm_modifier *modifier, snd_use_case_mgr_t *uc_mgr, const char *modifier_name) {
 
351
    const char *value;
 
352
    char *id;
 
353
    int i;
 
354
 
 
355
    for (i = 0; item[i].id; i++) {
 
356
        int err;
 
357
 
 
358
        id = pa_sprintf_malloc("=%s/%s", item[i].id, modifier_name);
 
359
        err = snd_use_case_get(uc_mgr, id, &value);
 
360
        pa_xfree(id);
 
361
        if (err < 0)
 
362
            continue;
 
363
 
 
364
        pa_log_debug("Got %s for modifier %s: %s", item[i].id, modifier_name, value);
 
365
        pa_proplist_sets(modifier->proplist, item[i].property, value);
 
366
        free((void*)value);
 
367
    }
 
368
 
 
369
    id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", modifier_name);
 
370
    modifier->n_confdev = snd_use_case_get_list(uc_mgr, id, &modifier->conflicting_devices);
 
371
    pa_xfree(id);
 
372
    if (modifier->n_confdev < 0)
 
373
        pa_log_debug("No %s for modifier %s", "_conflictingdevs", modifier_name);
 
374
 
 
375
    id = pa_sprintf_malloc("%s/%s", "_supporteddevs", modifier_name);
 
376
    modifier->n_suppdev = snd_use_case_get_list(uc_mgr, id, &modifier->supported_devices);
 
377
    pa_xfree(id);
 
378
    if (modifier->n_suppdev < 0)
 
379
        pa_log_debug("No %s for modifier %s", "_supporteddevs", modifier_name);
 
380
 
 
381
    return 0;
 
382
};
 
383
 
 
384
/* Create a list of devices for this verb */
 
385
static int ucm_get_devices(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) {
 
386
    const char **dev_list;
 
387
    int num_dev, i;
 
388
 
 
389
    num_dev = snd_use_case_get_list(uc_mgr, "_devices", &dev_list);
 
390
    if (num_dev < 0)
 
391
        return num_dev;
 
392
 
 
393
    for (i = 0; i < num_dev; i += 2) {
 
394
        pa_alsa_ucm_device *d = pa_xnew0(pa_alsa_ucm_device, 1);
 
395
 
 
396
        d->proplist = pa_proplist_new();
 
397
        pa_proplist_sets(d->proplist, PA_ALSA_PROP_UCM_NAME, pa_strnull(dev_list[i]));
 
398
        pa_proplist_sets(d->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(dev_list[i + 1]));
 
399
 
 
400
        PA_LLIST_PREPEND(pa_alsa_ucm_device, verb->devices, d);
 
401
    }
 
402
 
 
403
    snd_use_case_free_list(dev_list, num_dev);
 
404
 
 
405
    return 0;
 
406
};
 
407
 
 
408
static int ucm_get_modifiers(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) {
 
409
    const char **mod_list;
 
410
    int num_mod, i;
 
411
 
 
412
    num_mod = snd_use_case_get_list(uc_mgr, "_modifiers", &mod_list);
 
413
    if (num_mod < 0)
 
414
        return num_mod;
 
415
 
 
416
    for (i = 0; i < num_mod; i += 2) {
 
417
        pa_alsa_ucm_modifier *m;
 
418
 
 
419
        if (!mod_list[i]) {
 
420
            pa_log_warn("Got a modifier with a null name. Skipping.");
 
421
            continue;
 
422
        }
 
423
 
 
424
        m = pa_xnew0(pa_alsa_ucm_modifier, 1);
 
425
        m->proplist = pa_proplist_new();
 
426
 
 
427
        pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_NAME, mod_list[i]);
 
428
        pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(mod_list[i + 1]));
 
429
 
 
430
        PA_LLIST_PREPEND(pa_alsa_ucm_modifier, verb->modifiers, m);
 
431
    }
 
432
 
 
433
    snd_use_case_free_list(mod_list, num_mod);
 
434
 
 
435
    return 0;
 
436
};
 
437
 
 
438
static void add_role_to_device(pa_alsa_ucm_device *dev, const char *dev_name, const char *role_name, const char *role) {
 
439
    const char *cur = pa_proplist_gets(dev->proplist, role_name);
 
440
 
 
441
    if (!cur)
 
442
        pa_proplist_sets(dev->proplist, role_name, role);
 
443
    else if (!pa_str_in_list_spaces(cur, role)) { /* does not exist */
 
444
        char *value = pa_sprintf_malloc("%s %s", cur, role);
 
445
 
 
446
        pa_proplist_sets(dev->proplist, role_name, value);
 
447
        pa_xfree(value);
 
448
    }
 
449
 
 
450
    pa_log_info("Add role %s to device %s(%s), result %s", role, dev_name, role_name, pa_proplist_gets(dev->proplist,
 
451
                role_name));
 
452
}
 
453
 
 
454
static void add_media_role(const char *name, pa_alsa_ucm_device *list, const char *role_name, const char *role, bool is_sink) {
 
455
    pa_alsa_ucm_device *d;
 
456
 
 
457
    PA_LLIST_FOREACH(d, list) {
 
458
        const char *dev_name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
 
459
 
 
460
        if (pa_streq(dev_name, name)) {
 
461
            const char *sink = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SINK);
 
462
            const char *source = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SOURCE);
 
463
 
 
464
            if (is_sink && sink)
 
465
                add_role_to_device(d, dev_name, role_name, role);
 
466
            else if (!is_sink && source)
 
467
                add_role_to_device(d, dev_name, role_name, role);
 
468
            break;
 
469
        }
 
470
    }
 
471
}
 
472
 
 
473
static char *modifier_name_to_role(const char *mod_name, bool *is_sink) {
 
474
    char *sub = NULL, *tmp;
 
475
 
 
476
    *is_sink = false;
 
477
 
 
478
    if (pa_startswith(mod_name, "Play")) {
 
479
        *is_sink = true;
 
480
        sub = pa_xstrdup(mod_name + 4);
 
481
    } else if (pa_startswith(mod_name, "Capture"))
 
482
        sub = pa_xstrdup(mod_name + 7);
 
483
 
 
484
    if (!sub || !*sub) {
 
485
        pa_xfree(sub);
 
486
        pa_log_warn("Can't match media roles for modifer %s", mod_name);
 
487
        return NULL;
 
488
    }
 
489
 
 
490
    tmp = sub;
 
491
 
 
492
    do {
 
493
        *tmp = tolower(*tmp);
 
494
    } while (*(++tmp));
 
495
 
 
496
    return sub;
 
497
}
 
498
 
 
499
static void ucm_set_media_roles(pa_alsa_ucm_modifier *modifier, pa_alsa_ucm_device *list, const char *mod_name) {
 
500
    int i;
 
501
    bool is_sink = false;
 
502
    char *sub = NULL;
 
503
    const char *role_name;
 
504
 
 
505
    sub = modifier_name_to_role(mod_name, &is_sink);
 
506
    if (!sub)
 
507
        return;
 
508
 
 
509
    modifier->action_direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
 
510
    modifier->media_role = sub;
 
511
 
 
512
    role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES;
 
513
    for (i = 0; i < modifier->n_suppdev; i++) {
 
514
        /* if modifier has no specific pcm, we add role intent to its supported devices */
 
515
        if (!pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SINK) &&
 
516
                !pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SOURCE))
 
517
            add_media_role(modifier->supported_devices[i], list, role_name, sub, is_sink);
 
518
    }
 
519
}
 
520
 
 
521
static void append_lost_relationship(pa_alsa_ucm_device *dev) {
 
522
    uint32_t idx;
 
523
    pa_alsa_ucm_device *d;
 
524
 
 
525
    if (dev->conflicting_devices) {
 
526
        PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) {
 
527
            if (!d->conflicting_devices)
 
528
                d->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
529
 
 
530
            if (pa_idxset_put(d->conflicting_devices, dev, NULL) == 0)
 
531
                pa_log_warn("Add lost conflicting device %s to %s",
 
532
                        pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME),
 
533
                        pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME));
 
534
        }
 
535
    }
 
536
 
 
537
    if (dev->supported_devices) {
 
538
        PA_IDXSET_FOREACH(d, dev->supported_devices, idx) {
 
539
            if (!d->supported_devices)
 
540
                d->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
541
 
 
542
            if (pa_idxset_put(d->supported_devices, dev, NULL) == 0)
 
543
                pa_log_warn("Add lost supported device %s to %s",
 
544
                        pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME),
 
545
                        pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME));
 
546
        }
 
547
    }
 
548
}
 
549
 
 
550
int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index) {
 
551
    char *card_name;
 
552
    const char **verb_list;
 
553
    int num_verbs, i, err = 0;
 
554
 
 
555
    /* is UCM available for this card ? */
 
556
    err = snd_card_get_name(card_index, &card_name);
 
557
    if (err < 0) {
 
558
        pa_log("Card can't get card_name from card_index %d", card_index);
 
559
        goto name_fail;
 
560
    }
 
561
 
 
562
    err = snd_use_case_mgr_open(&ucm->ucm_mgr, card_name);
 
563
    if (err < 0) {
 
564
        pa_log_info("UCM not available for card %s", card_name);
 
565
        goto ucm_mgr_fail;
 
566
    }
 
567
 
 
568
    pa_log_info("UCM available for card %s", card_name);
 
569
 
 
570
    /* get a list of all UCM verbs (profiles) for this card */
 
571
    num_verbs = snd_use_case_verb_list(ucm->ucm_mgr, &verb_list);
 
572
    if (num_verbs < 0) {
 
573
        pa_log("UCM verb list not found for %s", card_name);
 
574
        goto ucm_verb_fail;
 
575
    }
 
576
 
 
577
    /* get the properties of each UCM verb */
 
578
    for (i = 0; i < num_verbs; i += 2) {
 
579
        pa_alsa_ucm_verb *verb;
 
580
 
 
581
        /* Get devices and modifiers for each verb */
 
582
        err = pa_alsa_ucm_get_verb(ucm->ucm_mgr, verb_list[i], verb_list[i+1], &verb);
 
583
        if (err < 0) {
 
584
            pa_log("Failed to get the verb %s", verb_list[i]);
 
585
            continue;
 
586
        }
 
587
 
 
588
        PA_LLIST_PREPEND(pa_alsa_ucm_verb, ucm->verbs, verb);
 
589
    }
 
590
 
 
591
    if (!ucm->verbs) {
 
592
        pa_log("No UCM verb is valid for %s", card_name);
 
593
        err = -1;
 
594
    }
 
595
 
 
596
    snd_use_case_free_list(verb_list, num_verbs);
 
597
 
 
598
ucm_verb_fail:
 
599
    if (err < 0) {
 
600
        snd_use_case_mgr_close(ucm->ucm_mgr);
 
601
        ucm->ucm_mgr = NULL;
 
602
    }
 
603
 
 
604
ucm_mgr_fail:
 
605
    free(card_name);
 
606
 
 
607
name_fail:
 
608
    return err;
 
609
}
 
610
 
 
611
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) {
 
612
    pa_alsa_ucm_device *d;
 
613
    pa_alsa_ucm_modifier *mod;
 
614
    pa_alsa_ucm_verb *verb;
 
615
    int err = 0;
 
616
 
 
617
    *p_verb = NULL;
 
618
    pa_log_info("Set UCM verb to %s", verb_name);
 
619
    err = snd_use_case_set(uc_mgr, "_verb", verb_name);
 
620
    if (err < 0)
 
621
        return err;
 
622
 
 
623
    verb = pa_xnew0(pa_alsa_ucm_verb, 1);
 
624
    verb->proplist = pa_proplist_new();
 
625
 
 
626
    pa_proplist_sets(verb->proplist, PA_ALSA_PROP_UCM_NAME, pa_strnull(verb_name));
 
627
    pa_proplist_sets(verb->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(verb_desc));
 
628
 
 
629
    err = ucm_get_devices(verb, uc_mgr);
 
630
    if (err < 0)
 
631
        pa_log("No UCM devices for verb %s", verb_name);
 
632
 
 
633
    err = ucm_get_modifiers(verb, uc_mgr);
 
634
    if (err < 0)
 
635
        pa_log("No UCM modifiers for verb %s", verb_name);
 
636
 
 
637
    /* Verb properties */
 
638
    ucm_get_property(verb, uc_mgr, verb_name);
 
639
 
 
640
    PA_LLIST_FOREACH(d, verb->devices) {
 
641
        const char *dev_name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
 
642
 
 
643
        /* Devices properties */
 
644
        ucm_get_device_property(d, uc_mgr, verb, dev_name);
 
645
    }
 
646
    /* make conflicting or supported device mutual */
 
647
    PA_LLIST_FOREACH(d, verb->devices)
 
648
        append_lost_relationship(d);
 
649
 
 
650
    PA_LLIST_FOREACH(mod, verb->modifiers) {
 
651
        const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
 
652
 
 
653
        /* Modifier properties */
 
654
        ucm_get_modifier_property(mod, uc_mgr, mod_name);
 
655
 
 
656
        /* Set PA_PROP_DEVICE_INTENDED_ROLES property to devices */
 
657
        pa_log_debug("Set media roles for verb %s, modifier %s", verb_name, mod_name);
 
658
        ucm_set_media_roles(mod, verb->devices, mod_name);
 
659
    }
 
660
 
 
661
    *p_verb = verb;
 
662
    return 0;
 
663
}
 
664
 
 
665
static int pa_alsa_ucm_device_cmp(const void *a, const void *b) {
 
666
    const pa_alsa_ucm_device *d1 = *(pa_alsa_ucm_device **)a;
 
667
    const pa_alsa_ucm_device *d2 = *(pa_alsa_ucm_device **)b;
 
668
 
 
669
    return strcmp(pa_proplist_gets(d1->proplist, PA_ALSA_PROP_UCM_NAME), pa_proplist_gets(d2->proplist, PA_ALSA_PROP_UCM_NAME));
 
670
}
 
671
 
 
672
static void ucm_add_port_combination(
 
673
        pa_hashmap *hash,
 
674
        pa_alsa_ucm_mapping_context *context,
 
675
        bool is_sink,
 
676
        pa_alsa_ucm_device **pdevices,
 
677
        int num,
 
678
        pa_hashmap *ports,
 
679
        pa_card_profile *cp,
 
680
        pa_core *core) {
 
681
 
 
682
    pa_device_port *port;
 
683
    int i;
 
684
    unsigned priority;
 
685
    double prio2;
 
686
    char *name, *desc;
 
687
    const char *dev_name;
 
688
    const char *direction;
 
689
    pa_alsa_ucm_device *sorted[num], *dev;
 
690
 
 
691
    for (i = 0; i < num; i++)
 
692
        sorted[i] = pdevices[i];
 
693
 
 
694
    /* Sort by alphabetical order so as to have a deterministic naming scheme
 
695
     * for combination ports */
 
696
    qsort(&sorted[0], num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp);
 
697
 
 
698
    dev = sorted[0];
 
699
    dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
 
700
 
 
701
    name = pa_sprintf_malloc("%s%s", is_sink ? PA_UCM_PRE_TAG_OUTPUT : PA_UCM_PRE_TAG_INPUT, dev_name);
 
702
    desc = num == 1 ? pa_xstrdup(pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION))
 
703
            : pa_sprintf_malloc("Combination port for %s", dev_name);
 
704
 
 
705
    priority = is_sink ? dev->playback_priority : dev->capture_priority;
 
706
    prio2 = (priority == 0 ? 0 : 1.0/priority);
 
707
 
 
708
    for (i = 1; i < num; i++) {
 
709
        char *tmp;
 
710
 
 
711
        dev = sorted[i];
 
712
        dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
 
713
 
 
714
        tmp = pa_sprintf_malloc("%s+%s", name, dev_name);
 
715
        pa_xfree(name);
 
716
        name = tmp;
 
717
 
 
718
        tmp = pa_sprintf_malloc("%s,%s", desc, dev_name);
 
719
        pa_xfree(desc);
 
720
        desc = tmp;
 
721
 
 
722
        priority = is_sink ? dev->playback_priority : dev->capture_priority;
 
723
        if (priority != 0 && prio2 > 0)
 
724
            prio2 += 1.0/priority;
 
725
    }
 
726
 
 
727
    /* Make combination ports always have lower priority, and use the formula
 
728
       1/p = 1/p1 + 1/p2 + ... 1/pn.
 
729
       This way, the result will always be less than the individual components,
 
730
       yet higher components will lead to higher result. */
 
731
 
 
732
    if (num > 1)
 
733
        priority = prio2 > 0 ? 1.0/prio2 : 0;
 
734
 
 
735
    port = pa_hashmap_get(ports, name);
 
736
    if (!port) {
 
737
        pa_device_port_new_data port_data;
 
738
 
 
739
        pa_device_port_new_data_init(&port_data);
 
740
        pa_device_port_new_data_set_name(&port_data, name);
 
741
        pa_device_port_new_data_set_description(&port_data, desc);
 
742
        pa_device_port_new_data_set_direction(&port_data, is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT);
 
743
 
 
744
        port = pa_device_port_new(core, &port_data, 0);
 
745
        pa_device_port_new_data_done(&port_data);
 
746
        pa_assert(port);
 
747
 
 
748
        pa_hashmap_put(ports, port->name, port);
 
749
        pa_log_debug("Add port %s: %s", port->name, port->description);
 
750
        port->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
 
751
    }
 
752
 
 
753
    port->priority = priority;
 
754
 
 
755
    pa_xfree(name);
 
756
    pa_xfree(desc);
 
757
 
 
758
    direction = is_sink ? "output" : "input";
 
759
    pa_log_debug("Port %s direction %s, priority %d", port->name, direction, priority);
 
760
 
 
761
    if (cp) {
 
762
        pa_log_debug("Adding profile %s to port %s.", cp->name, port->name);
 
763
        pa_hashmap_put(port->profiles, cp->name, cp);
 
764
    }
 
765
 
 
766
    if (hash) {
 
767
        pa_hashmap_put(hash, port->name, port);
 
768
        pa_device_port_ref(port);
 
769
    }
 
770
}
 
771
 
 
772
static int ucm_port_contains(const char *port_name, const char *dev_name, bool is_sink) {
 
773
    int ret = 0;
 
774
    const char *r;
 
775
    const char *state = NULL;
 
776
    int len;
 
777
 
 
778
    if (!port_name || !dev_name)
 
779
        return false;
 
780
 
 
781
    port_name += is_sink ? strlen(PA_UCM_PRE_TAG_OUTPUT) : strlen(PA_UCM_PRE_TAG_INPUT);
 
782
 
 
783
    while ((r = pa_split_in_place(port_name, "+", &len, &state))) {
 
784
        if (!strncmp(r, dev_name, len)) {
 
785
            ret = 1;
 
786
            break;
 
787
        }
 
788
    }
 
789
 
 
790
    return ret;
 
791
}
 
792
 
 
793
static int ucm_check_conformance(
 
794
        pa_alsa_ucm_mapping_context *context,
 
795
        pa_alsa_ucm_device **pdevices,
 
796
        int dev_num,
 
797
        pa_alsa_ucm_device *dev) {
 
798
 
 
799
    uint32_t idx;
 
800
    pa_alsa_ucm_device *d;
 
801
    int i;
 
802
 
 
803
    pa_assert(dev);
 
804
 
 
805
    pa_log_debug("Check device %s conformance with %d other devices",
 
806
            pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME), dev_num);
 
807
    if (dev_num == 0) {
 
808
        pa_log_debug("First device in combination, number 1");
 
809
        return 1;
 
810
    }
 
811
 
 
812
    if (dev->conflicting_devices) { /* the device defines conflicting devices */
 
813
        PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) {
 
814
            for (i = 0; i < dev_num; i++) {
 
815
                if (pdevices[i] == d) {
 
816
                    pa_log_debug("Conflicting device found");
 
817
                    return 0;
 
818
                }
 
819
            }
 
820
        }
 
821
    } else if (dev->supported_devices) { /* the device defines supported devices */
 
822
        for (i = 0; i < dev_num; i++) {
 
823
            if (!ucm_device_exists(dev->supported_devices, pdevices[i])) {
 
824
                pa_log_debug("Supported device not found");
 
825
                return 0;
 
826
            }
 
827
        }
 
828
    } else { /* not support any other devices */
 
829
        pa_log_debug("Not support any other devices");
 
830
        return 0;
 
831
    }
 
832
 
 
833
    pa_log_debug("Device added to combination, number %d", dev_num + 1);
 
834
    return 1;
 
835
}
 
836
 
 
837
static inline pa_alsa_ucm_device *get_next_device(pa_idxset *idxset, uint32_t *idx) {
 
838
    pa_alsa_ucm_device *dev;
 
839
 
 
840
    if (*idx == PA_IDXSET_INVALID)
 
841
        dev = pa_idxset_first(idxset, idx);
 
842
    else
 
843
        dev = pa_idxset_next(idxset, idx);
 
844
 
 
845
    return dev;
 
846
}
 
847
 
 
848
static void ucm_add_ports_combination(
 
849
        pa_hashmap *hash,
 
850
        pa_alsa_ucm_mapping_context *context,
 
851
        bool is_sink,
 
852
        pa_alsa_ucm_device **pdevices,
 
853
        int dev_num,
 
854
        uint32_t map_index,
 
855
        pa_hashmap *ports,
 
856
        pa_card_profile *cp,
 
857
        pa_core *core) {
 
858
 
 
859
    pa_alsa_ucm_device *dev;
 
860
    uint32_t idx = map_index;
 
861
 
 
862
    if ((dev = get_next_device(context->ucm_devices, &idx)) == NULL)
 
863
        return;
 
864
 
 
865
    /* check if device at map_index can combine with existing devices combination */
 
866
    if (ucm_check_conformance(context, pdevices, dev_num, dev)) {
 
867
        /* add device at map_index to devices combination */
 
868
        pdevices[dev_num] = dev;
 
869
        /* add current devices combination as a new port */
 
870
        ucm_add_port_combination(hash, context, is_sink, pdevices, dev_num + 1, ports, cp, core);
 
871
        /* try more elements combination */
 
872
        ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num + 1, idx, ports, cp, core);
 
873
    }
 
874
 
 
875
    /* try other device with current elements number */
 
876
    ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num, idx, ports, cp, core);
 
877
}
 
878
 
 
879
static char* merge_roles(const char *cur, const char *add) {
 
880
    char *r, *ret;
 
881
    const char *state = NULL;
 
882
 
 
883
    if (add == NULL)
 
884
        return pa_xstrdup(cur);
 
885
    else if (cur == NULL)
 
886
        return pa_xstrdup(add);
 
887
 
 
888
    ret = pa_xstrdup(cur);
 
889
 
 
890
    while ((r = pa_split_spaces(add, &state))) {
 
891
        char *value;
 
892
 
 
893
        if (!pa_str_in_list_spaces(ret, r))
 
894
            value = pa_sprintf_malloc("%s %s", ret, r);
 
895
        else {
 
896
            pa_xfree(r);
 
897
            continue;
 
898
        }
 
899
 
 
900
        pa_xfree(ret);
 
901
        ret = value;
 
902
        pa_xfree(r);
 
903
    }
 
904
 
 
905
    return ret;
 
906
}
 
907
 
 
908
void pa_alsa_ucm_add_ports_combination(
 
909
        pa_hashmap *p,
 
910
        pa_alsa_ucm_mapping_context *context,
 
911
        bool is_sink,
 
912
        pa_hashmap *ports,
 
913
        pa_card_profile *cp,
 
914
        pa_core *core) {
 
915
 
 
916
    pa_alsa_ucm_device **pdevices;
 
917
 
 
918
    pa_assert(context->ucm_devices);
 
919
 
 
920
    if (pa_idxset_size(context->ucm_devices) > 0) {
 
921
        pdevices = pa_xnew(pa_alsa_ucm_device *, pa_idxset_size(context->ucm_devices));
 
922
        ucm_add_ports_combination(p, context, is_sink, pdevices, 0, PA_IDXSET_INVALID, ports, cp, core);
 
923
        pa_xfree(pdevices);
 
924
    }
 
925
}
 
926
 
 
927
void pa_alsa_ucm_add_ports(
 
928
        pa_hashmap **p,
 
929
        pa_proplist *proplist,
 
930
        pa_alsa_ucm_mapping_context *context,
 
931
        bool is_sink,
 
932
        pa_card *card) {
 
933
 
 
934
    uint32_t idx;
 
935
    char *merged_roles;
 
936
    const char *role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES;
 
937
    pa_alsa_ucm_device *dev;
 
938
    pa_alsa_ucm_modifier *mod;
 
939
    char *tmp;
 
940
 
 
941
    pa_assert(p);
 
942
    pa_assert(*p);
 
943
 
 
944
    /* add ports first */
 
945
    pa_alsa_ucm_add_ports_combination(*p, context, is_sink, card->ports, NULL, card->core);
 
946
 
 
947
    /* then set property PA_PROP_DEVICE_INTENDED_ROLES */
 
948
    merged_roles = pa_xstrdup(pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES));
 
949
    PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
 
950
        const char *roles = pa_proplist_gets(dev->proplist, role_name);
 
951
        tmp = merge_roles(merged_roles, roles);
 
952
        pa_xfree(merged_roles);
 
953
        merged_roles = tmp;
 
954
    }
 
955
 
 
956
    if (context->ucm_modifiers)
 
957
        PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) {
 
958
            tmp = merge_roles(merged_roles, mod->media_role);
 
959
            pa_xfree(merged_roles);
 
960
            merged_roles = tmp;
 
961
        }
 
962
 
 
963
    if (merged_roles)
 
964
        pa_proplist_sets(proplist, PA_PROP_DEVICE_INTENDED_ROLES, merged_roles);
 
965
 
 
966
    pa_log_info("ALSA device %s roles: %s", pa_proplist_gets(proplist, PA_PROP_DEVICE_STRING), pa_strnull(merged_roles));
 
967
    pa_xfree(merged_roles);
 
968
}
 
969
 
 
970
/* Change UCM verb and device to match selected card profile */
 
971
int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, const char *new_profile, const char *old_profile) {
 
972
    int ret = 0;
 
973
    const char *profile;
 
974
    pa_alsa_ucm_verb *verb;
 
975
 
 
976
    if (new_profile == old_profile)
 
977
        return ret;
 
978
    else if (new_profile == NULL || old_profile == NULL)
 
979
        profile = new_profile ? new_profile : SND_USE_CASE_VERB_INACTIVE;
 
980
    else if (!pa_streq(new_profile, old_profile))
 
981
        profile = new_profile;
 
982
    else
 
983
        return ret;
 
984
 
 
985
    /* change verb */
 
986
    pa_log_info("Set UCM verb to %s", profile);
 
987
    if ((snd_use_case_set(ucm->ucm_mgr, "_verb", profile)) < 0) {
 
988
        pa_log("Failed to set verb %s", profile);
 
989
        ret = -1;
 
990
    }
 
991
 
 
992
    /* find active verb */
 
993
    ucm->active_verb = NULL;
 
994
    PA_LLIST_FOREACH(verb, ucm->verbs) {
 
995
        const char *verb_name;
 
996
        verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME);
 
997
        if (pa_streq(verb_name, profile)) {
 
998
            ucm->active_verb = verb;
 
999
            break;
 
1000
        }
 
1001
    }
 
1002
 
 
1003
    return ret;
 
1004
}
 
1005
 
 
1006
int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) {
 
1007
    int i;
 
1008
    int ret = 0;
 
1009
    pa_alsa_ucm_config *ucm;
 
1010
    const char **enable_devs;
 
1011
    int enable_num = 0;
 
1012
    uint32_t idx;
 
1013
    pa_alsa_ucm_device *dev;
 
1014
 
 
1015
    pa_assert(context && context->ucm);
 
1016
 
 
1017
    ucm = context->ucm;
 
1018
    pa_assert(ucm->ucm_mgr);
 
1019
 
 
1020
    enable_devs = pa_xnew(const char *, pa_idxset_size(context->ucm_devices));
 
1021
 
 
1022
    /* first disable then enable */
 
1023
    PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
 
1024
        const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
 
1025
 
 
1026
        if (ucm_port_contains(port->name, dev_name, is_sink))
 
1027
            enable_devs[enable_num++] = dev_name;
 
1028
        else {
 
1029
            pa_log_debug("Disable ucm device %s", dev_name);
 
1030
            if (snd_use_case_set(ucm->ucm_mgr, "_disdev", dev_name) > 0) {
 
1031
                pa_log("Failed to disable ucm device %s", dev_name);
 
1032
                ret = -1;
 
1033
                break;
 
1034
            }
 
1035
        }
 
1036
    }
 
1037
 
 
1038
    for (i = 0; i < enable_num; i++) {
 
1039
        pa_log_debug("Enable ucm device %s", enable_devs[i]);
 
1040
        if (snd_use_case_set(ucm->ucm_mgr, "_enadev", enable_devs[i]) < 0) {
 
1041
            pa_log("Failed to enable ucm device %s", enable_devs[i]);
 
1042
            ret = -1;
 
1043
            break;
 
1044
        }
 
1045
    }
 
1046
 
 
1047
    pa_xfree(enable_devs);
 
1048
 
 
1049
    return ret;
 
1050
}
 
1051
 
 
1052
static void ucm_add_mapping(pa_alsa_profile *p, pa_alsa_mapping *m) {
 
1053
 
 
1054
    switch (m->direction) {
 
1055
        case PA_ALSA_DIRECTION_ANY:
 
1056
            pa_idxset_put(p->output_mappings, m, NULL);
 
1057
            pa_idxset_put(p->input_mappings, m, NULL);
 
1058
            break;
 
1059
        case PA_ALSA_DIRECTION_OUTPUT:
 
1060
            pa_idxset_put(p->output_mappings, m, NULL);
 
1061
            break;
 
1062
        case PA_ALSA_DIRECTION_INPUT:
 
1063
            pa_idxset_put(p->input_mappings, m, NULL);
 
1064
            break;
 
1065
    }
 
1066
}
 
1067
 
 
1068
static void alsa_mapping_add_ucm_device(pa_alsa_mapping *m, pa_alsa_ucm_device *device) {
 
1069
    char *cur_desc;
 
1070
    const char *new_desc;
 
1071
 
 
1072
    pa_idxset_put(m->ucm_context.ucm_devices, device, NULL);
 
1073
 
 
1074
    new_desc = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
 
1075
    cur_desc = m->description;
 
1076
    if (cur_desc)
 
1077
        m->description = pa_sprintf_malloc("%s + %s", cur_desc, new_desc);
 
1078
    else
 
1079
        m->description = pa_xstrdup(new_desc);
 
1080
    pa_xfree(cur_desc);
 
1081
 
 
1082
    /* walk around null case */
 
1083
    m->description = m->description ? m->description : pa_xstrdup("");
 
1084
 
 
1085
    /* save mapping to ucm device */
 
1086
    if (m->direction == PA_ALSA_DIRECTION_OUTPUT)
 
1087
        device->playback_mapping = m;
 
1088
    else
 
1089
        device->capture_mapping = m;
 
1090
}
 
1091
 
 
1092
static void alsa_mapping_add_ucm_modifier(pa_alsa_mapping *m, pa_alsa_ucm_modifier *modifier) {
 
1093
    char *cur_desc;
 
1094
    const char *new_desc, *mod_name, *channel_str;
 
1095
    uint32_t channels = 0;
 
1096
 
 
1097
    pa_idxset_put(m->ucm_context.ucm_modifiers, modifier, NULL);
 
1098
 
 
1099
    new_desc = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
 
1100
    cur_desc = m->description;
 
1101
    if (cur_desc)
 
1102
        m->description = pa_sprintf_malloc("%s + %s", cur_desc, new_desc);
 
1103
    else
 
1104
        m->description = pa_xstrdup(new_desc);
 
1105
    pa_xfree(cur_desc);
 
1106
 
 
1107
    m->description = m->description ? m->description : pa_xstrdup("");
 
1108
 
 
1109
    /* Modifier sinks should not be routed to by default */
 
1110
    m->priority = 0;
 
1111
 
 
1112
    mod_name = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_NAME);
 
1113
    pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_MODIFIER, mod_name);
 
1114
 
 
1115
    /* save mapping to ucm modifier */
 
1116
    if (m->direction == PA_ALSA_DIRECTION_OUTPUT) {
 
1117
        modifier->playback_mapping = m;
 
1118
        channel_str = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS);
 
1119
    } else {
 
1120
        modifier->capture_mapping = m;
 
1121
        channel_str = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS);
 
1122
    }
 
1123
 
 
1124
    if (channel_str) {
 
1125
        /* FIXME: channel_str is unsanitized input from the UCM configuration,
 
1126
         * we should do proper error handling instead of asserting.
 
1127
         * https://bugs.freedesktop.org/show_bug.cgi?id=71823 */
 
1128
        pa_assert_se(pa_atou(channel_str, &channels) == 0 && pa_channels_valid(channels));
 
1129
        pa_log_debug("Got channel count %" PRIu32 " for modifier", channels);
 
1130
    }
 
1131
 
 
1132
    if (channels)
 
1133
        pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
 
1134
    else
 
1135
        pa_channel_map_init(&m->channel_map);
 
1136
}
 
1137
 
 
1138
static int ucm_create_mapping_direction(
 
1139
        pa_alsa_ucm_config *ucm,
 
1140
        pa_alsa_profile_set *ps,
 
1141
        pa_alsa_profile *p,
 
1142
        pa_alsa_ucm_device *device,
 
1143
        const char *verb_name,
 
1144
        const char *device_name,
 
1145
        const char *device_str,
 
1146
        bool is_sink) {
 
1147
 
 
1148
    pa_alsa_mapping *m;
 
1149
    char *mapping_name;
 
1150
    unsigned priority, rate, channels;
 
1151
 
 
1152
    mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, device_str, is_sink ? "sink" : "source");
 
1153
 
 
1154
    m = pa_alsa_mapping_get(ps, mapping_name);
 
1155
    if (!m) {
 
1156
        pa_log("No mapping for %s", mapping_name);
 
1157
        pa_xfree(mapping_name);
 
1158
        return -1;
 
1159
    }
 
1160
    pa_log_debug("UCM mapping: %s dev %s", mapping_name, device_name);
 
1161
    pa_xfree(mapping_name);
 
1162
 
 
1163
    priority = is_sink ? device->playback_priority : device->capture_priority;
 
1164
    rate = is_sink ? device->playback_rate : device->capture_rate;
 
1165
    channels = is_sink ? device->playback_channels : device->capture_channels;
 
1166
 
 
1167
    if (!m->ucm_context.ucm_devices) {   /* new mapping */
 
1168
        m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
1169
        m->ucm_context.ucm = ucm;
 
1170
        m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
 
1171
 
 
1172
        m->device_strings = pa_xnew0(char*, 2);
 
1173
        m->device_strings[0] = pa_xstrdup(device_str);
 
1174
        m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT;
 
1175
 
 
1176
        ucm_add_mapping(p, m);
 
1177
        if (rate)
 
1178
            m->sample_spec.rate = rate;
 
1179
        pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
 
1180
    }
 
1181
 
 
1182
    /* mapping priority is the highest one of ucm devices */
 
1183
    if (priority > m->priority)
 
1184
        m->priority = priority;
 
1185
 
 
1186
    /* mapping channels is the lowest one of ucm devices */
 
1187
    if (channels < m->channel_map.channels)
 
1188
        pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
 
1189
 
 
1190
    alsa_mapping_add_ucm_device(m, device);
 
1191
 
 
1192
    return 0;
 
1193
}
 
1194
 
 
1195
static int ucm_create_mapping_for_modifier(
 
1196
        pa_alsa_ucm_config *ucm,
 
1197
        pa_alsa_profile_set *ps,
 
1198
        pa_alsa_profile *p,
 
1199
        pa_alsa_ucm_modifier *modifier,
 
1200
        const char *verb_name,
 
1201
        const char *mod_name,
 
1202
        const char *device_str,
 
1203
        bool is_sink) {
 
1204
 
 
1205
    pa_alsa_mapping *m;
 
1206
    char *mapping_name;
 
1207
 
 
1208
    mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, device_str, is_sink ? "sink" : "source");
 
1209
 
 
1210
    m = pa_alsa_mapping_get(ps, mapping_name);
 
1211
    if (!m) {
 
1212
        pa_log("no mapping for %s", mapping_name);
 
1213
        pa_xfree(mapping_name);
 
1214
        return -1;
 
1215
    }
 
1216
    pa_log_info("ucm mapping: %s modifier %s", mapping_name, mod_name);
 
1217
    pa_xfree(mapping_name);
 
1218
 
 
1219
    if (!m->ucm_context.ucm_devices && !m->ucm_context.ucm_modifiers) {   /* new mapping */
 
1220
        m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
1221
        m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
1222
        m->ucm_context.ucm = ucm;
 
1223
        m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
 
1224
 
 
1225
        m->device_strings = pa_xnew0(char*, 2);
 
1226
        m->device_strings[0] = pa_xstrdup(device_str);
 
1227
        m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT;
 
1228
        /* Modifier sinks should not be routed to by default */
 
1229
        m->priority = 0;
 
1230
 
 
1231
        ucm_add_mapping(p, m);
 
1232
    } else if (!m->ucm_context.ucm_modifiers) /* share pcm with device */
 
1233
        m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
1234
 
 
1235
    alsa_mapping_add_ucm_modifier(m, modifier);
 
1236
 
 
1237
    return 0;
 
1238
}
 
1239
 
 
1240
static int ucm_create_mapping(
 
1241
        pa_alsa_ucm_config *ucm,
 
1242
        pa_alsa_profile_set *ps,
 
1243
        pa_alsa_profile *p,
 
1244
        pa_alsa_ucm_device *device,
 
1245
        const char *verb_name,
 
1246
        const char *device_name,
 
1247
        const char *sink,
 
1248
        const char *source) {
 
1249
 
 
1250
    int ret = 0;
 
1251
 
 
1252
    if (!sink && !source) {
 
1253
        pa_log("No sink and source at %s: %s", verb_name, device_name);
 
1254
        return -1;
 
1255
    }
 
1256
 
 
1257
    if (sink)
 
1258
        ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, sink, true);
 
1259
    if (ret == 0 && source)
 
1260
        ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, source, false);
 
1261
 
 
1262
    return ret;
 
1263
}
 
1264
 
 
1265
static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, const char *dev_name, const char *pre_tag) {
 
1266
    pa_alsa_jack *j;
 
1267
    char *name = pa_sprintf_malloc("%s%s", pre_tag, dev_name);
 
1268
 
 
1269
    PA_LLIST_FOREACH(j, ucm->jacks)
 
1270
        if (pa_streq(j->name, name))
 
1271
            goto out;
 
1272
 
 
1273
    j = pa_xnew0(pa_alsa_jack, 1);
 
1274
    j->state_unplugged = PA_AVAILABLE_NO;
 
1275
    j->state_plugged = PA_AVAILABLE_YES;
 
1276
    j->name = pa_xstrdup(name);
 
1277
    j->alsa_name = pa_sprintf_malloc("%s Jack", dev_name);
 
1278
 
 
1279
    PA_LLIST_PREPEND(pa_alsa_jack, ucm->jacks, j);
 
1280
 
 
1281
out:
 
1282
    pa_xfree(name);
 
1283
    return j;
 
1284
}
 
1285
 
 
1286
static int ucm_create_profile(
 
1287
        pa_alsa_ucm_config *ucm,
 
1288
        pa_alsa_profile_set *ps,
 
1289
        pa_alsa_ucm_verb *verb,
 
1290
        const char *verb_name,
 
1291
        const char *verb_desc) {
 
1292
 
 
1293
    pa_alsa_profile *p;
 
1294
    pa_alsa_ucm_device *dev;
 
1295
    pa_alsa_ucm_modifier *mod;
 
1296
    int i = 0;
 
1297
    const char *name, *sink, *source;
 
1298
    char *verb_cmp, *c;
 
1299
 
 
1300
    pa_assert(ps);
 
1301
 
 
1302
    if (pa_hashmap_get(ps->profiles, verb_name)) {
 
1303
        pa_log("Verb %s already exists", verb_name);
 
1304
        return -1;
 
1305
    }
 
1306
 
 
1307
    p = pa_xnew0(pa_alsa_profile, 1);
 
1308
    p->profile_set = ps;
 
1309
    p->name = pa_xstrdup(verb_name);
 
1310
    p->description = pa_xstrdup(verb_desc);
 
1311
 
 
1312
    p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
1313
    p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
1314
 
 
1315
    p->supported = true;
 
1316
    pa_hashmap_put(ps->profiles, p->name, p);
 
1317
 
 
1318
    /* TODO: get profile priority from ucm info or policy management */
 
1319
    c = verb_cmp = pa_xstrdup(verb_name);
 
1320
    while (*c) {
 
1321
        if (*c == '_') *c = ' ';
 
1322
        c++;
 
1323
    }
 
1324
 
 
1325
    for (i = 0; verb_info[i].id; i++) {
 
1326
        if (strcasecmp(verb_info[i].id, verb_cmp) == 0) {
 
1327
            p->priority = verb_info[i].priority;
 
1328
            break;
 
1329
        }
 
1330
    }
 
1331
 
 
1332
    pa_xfree(verb_cmp);
 
1333
 
 
1334
    if (verb_info[i].id == NULL)
 
1335
        p->priority = 1000;
 
1336
 
 
1337
    PA_LLIST_FOREACH(dev, verb->devices) {
 
1338
        name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
 
1339
 
 
1340
        sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK);
 
1341
        source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE);
 
1342
 
 
1343
        ucm_create_mapping(ucm, ps, p, dev, verb_name, name, sink, source);
 
1344
 
 
1345
        if (sink)
 
1346
            dev->output_jack = ucm_get_jack(ucm, name, PA_UCM_PRE_TAG_OUTPUT);
 
1347
        if (source)
 
1348
            dev->input_jack = ucm_get_jack(ucm, name, PA_UCM_PRE_TAG_INPUT);
 
1349
    }
 
1350
 
 
1351
    /* Now find modifiers that have their own PlaybackPCM and create
 
1352
     * separate sinks for them. */
 
1353
    PA_LLIST_FOREACH(mod, verb->modifiers) {
 
1354
        name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
 
1355
 
 
1356
        sink = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SINK);
 
1357
        source = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SOURCE);
 
1358
 
 
1359
        if (sink)
 
1360
            ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, sink, true);
 
1361
        else if (source)
 
1362
            ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, source, false);
 
1363
    }
 
1364
 
 
1365
    pa_alsa_profile_dump(p);
 
1366
 
 
1367
    return 0;
 
1368
}
 
1369
 
 
1370
static snd_pcm_t* mapping_open_pcm(pa_alsa_ucm_config *ucm, pa_alsa_mapping *m, int mode) {
 
1371
    snd_pcm_t* pcm;
 
1372
    pa_sample_spec try_ss = ucm->core->default_sample_spec;
 
1373
    pa_channel_map try_map;
 
1374
    snd_pcm_uframes_t try_period_size, try_buffer_size;
 
1375
    bool exact_channels = m->channel_map.channels > 0;
 
1376
 
 
1377
    if (exact_channels) {
 
1378
        try_map = m->channel_map;
 
1379
        try_ss.channels = try_map.channels;
 
1380
    } else
 
1381
        pa_channel_map_init_extend(&try_map, try_ss.channels, PA_CHANNEL_MAP_ALSA);
 
1382
 
 
1383
    try_period_size =
 
1384
        pa_usec_to_bytes(ucm->core->default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
 
1385
        pa_frame_size(&try_ss);
 
1386
    try_buffer_size = ucm->core->default_n_fragments * try_period_size;
 
1387
 
 
1388
    pcm = pa_alsa_open_by_device_string(m->device_strings[0], NULL, &try_ss,
 
1389
            &try_map, mode, &try_period_size, &try_buffer_size, 0, NULL, NULL, exact_channels);
 
1390
 
 
1391
    if (pcm && !exact_channels)
 
1392
        m->channel_map = try_map;
 
1393
 
 
1394
    return pcm;
 
1395
}
 
1396
 
 
1397
static void profile_finalize_probing(pa_alsa_profile *p) {
 
1398
    pa_alsa_mapping *m;
 
1399
    uint32_t idx;
 
1400
 
 
1401
    PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
 
1402
        if (p->supported)
 
1403
            m->supported++;
 
1404
 
 
1405
        if (!m->output_pcm)
 
1406
            continue;
 
1407
 
 
1408
        snd_pcm_close(m->output_pcm);
 
1409
        m->output_pcm = NULL;
 
1410
    }
 
1411
 
 
1412
    PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
 
1413
        if (p->supported)
 
1414
            m->supported++;
 
1415
 
 
1416
        if (!m->input_pcm)
 
1417
            continue;
 
1418
 
 
1419
        snd_pcm_close(m->input_pcm);
 
1420
        m->input_pcm = NULL;
 
1421
    }
 
1422
}
 
1423
 
 
1424
static void ucm_mapping_jack_probe(pa_alsa_mapping *m) {
 
1425
    snd_pcm_t *pcm_handle;
 
1426
    snd_mixer_t *mixer_handle;
 
1427
    pa_alsa_ucm_mapping_context *context = &m->ucm_context;
 
1428
    pa_alsa_ucm_device *dev;
 
1429
    uint32_t idx;
 
1430
 
 
1431
    pcm_handle = m->direction == PA_ALSA_DIRECTION_OUTPUT ? m->output_pcm : m->input_pcm;
 
1432
    mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL);
 
1433
    if (!mixer_handle)
 
1434
        return;
 
1435
 
 
1436
    PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
 
1437
        pa_alsa_jack *jack;
 
1438
        jack = m->direction == PA_ALSA_DIRECTION_OUTPUT ? dev->output_jack : dev->input_jack;
 
1439
        pa_assert (jack);
 
1440
        jack->has_control = pa_alsa_mixer_find(mixer_handle, jack->alsa_name, 0) != NULL;
 
1441
        pa_log_info("UCM jack %s has_control=%d", jack->name, jack->has_control);
 
1442
    }
 
1443
 
 
1444
    snd_mixer_close(mixer_handle);
 
1445
}
 
1446
 
 
1447
static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps) {
 
1448
    void *state;
 
1449
    pa_alsa_profile *p;
 
1450
    pa_alsa_mapping *m;
 
1451
    uint32_t idx;
 
1452
 
 
1453
    PA_HASHMAP_FOREACH(p, ps->profiles, state) {
 
1454
        /* change verb */
 
1455
        pa_log_info("Set ucm verb to %s", p->name);
 
1456
 
 
1457
        if ((snd_use_case_set(ucm->ucm_mgr, "_verb", p->name)) < 0) {
 
1458
            pa_log("Failed to set verb %s", p->name);
 
1459
            p->supported = false;
 
1460
            continue;
 
1461
        }
 
1462
 
 
1463
        PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
 
1464
            if (PA_UCM_IS_MODIFIER_MAPPING(m)) {
 
1465
                /* Skip jack probing on modifier PCMs since we expect this to
 
1466
                 * only be controlled on the main device/verb PCM. */
 
1467
                continue;
 
1468
            }
 
1469
 
 
1470
            m->output_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_PLAYBACK);
 
1471
            if (!m->output_pcm) {
 
1472
                p->supported = false;
 
1473
                break;
 
1474
            }
 
1475
        }
 
1476
 
 
1477
        if (p->supported) {
 
1478
            PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
 
1479
                if (PA_UCM_IS_MODIFIER_MAPPING(m)) {
 
1480
                    /* Skip jack probing on modifier PCMs since we expect this to
 
1481
                     * only be controlled on the main device/verb PCM. */
 
1482
                    continue;
 
1483
                }
 
1484
 
 
1485
                m->input_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_CAPTURE);
 
1486
                if (!m->input_pcm) {
 
1487
                    p->supported = false;
 
1488
                    break;
 
1489
                }
 
1490
            }
 
1491
        }
 
1492
 
 
1493
        if (!p->supported) {
 
1494
            profile_finalize_probing(p);
 
1495
            continue;
 
1496
        }
 
1497
 
 
1498
        pa_log_debug("Profile %s supported.", p->name);
 
1499
 
 
1500
        PA_IDXSET_FOREACH(m, p->output_mappings, idx)
 
1501
            if (!PA_UCM_IS_MODIFIER_MAPPING(m))
 
1502
                ucm_mapping_jack_probe(m);
 
1503
 
 
1504
        PA_IDXSET_FOREACH(m, p->input_mappings, idx)
 
1505
            if (!PA_UCM_IS_MODIFIER_MAPPING(m))
 
1506
                ucm_mapping_jack_probe(m);
 
1507
 
 
1508
        profile_finalize_probing(p);
 
1509
    }
 
1510
 
 
1511
    /* restore ucm state */
 
1512
    snd_use_case_set(ucm->ucm_mgr, "_verb", SND_USE_CASE_VERB_INACTIVE);
 
1513
 
 
1514
    pa_alsa_profile_set_drop_unsupported(ps);
 
1515
}
 
1516
 
 
1517
pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map) {
 
1518
    pa_alsa_ucm_verb *verb;
 
1519
    pa_alsa_profile_set *ps;
 
1520
 
 
1521
    ps = pa_xnew0(pa_alsa_profile_set, 1);
 
1522
    ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
 
1523
    ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
 
1524
    ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
 
1525
 
 
1526
    /* create a profile for each verb */
 
1527
    PA_LLIST_FOREACH(verb, ucm->verbs) {
 
1528
        const char *verb_name;
 
1529
        const char *verb_desc;
 
1530
 
 
1531
        verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME);
 
1532
        verb_desc = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
 
1533
        if (verb_name == NULL) {
 
1534
            pa_log("Verb with no name");
 
1535
            continue;
 
1536
        }
 
1537
 
 
1538
        ucm_create_profile(ucm, ps, verb, verb_name, verb_desc);
 
1539
    }
 
1540
 
 
1541
    ucm_probe_profile_set(ucm, ps);
 
1542
    ps->probed = true;
 
1543
 
 
1544
    return ps;
 
1545
}
 
1546
 
 
1547
static void free_verb(pa_alsa_ucm_verb *verb) {
 
1548
    pa_alsa_ucm_device *di, *dn;
 
1549
    pa_alsa_ucm_modifier *mi, *mn;
 
1550
 
 
1551
    PA_LLIST_FOREACH_SAFE(di, dn, verb->devices) {
 
1552
        PA_LLIST_REMOVE(pa_alsa_ucm_device, verb->devices, di);
 
1553
        pa_proplist_free(di->proplist);
 
1554
        if (di->conflicting_devices)
 
1555
            pa_idxset_free(di->conflicting_devices, NULL);
 
1556
        if (di->supported_devices)
 
1557
            pa_idxset_free(di->supported_devices, NULL);
 
1558
        pa_xfree(di);
 
1559
    }
 
1560
 
 
1561
    PA_LLIST_FOREACH_SAFE(mi, mn, verb->modifiers) {
 
1562
        PA_LLIST_REMOVE(pa_alsa_ucm_modifier, verb->modifiers, mi);
 
1563
        pa_proplist_free(mi->proplist);
 
1564
        if (mi->n_suppdev > 0)
 
1565
            snd_use_case_free_list(mi->supported_devices, mi->n_suppdev);
 
1566
        if (mi->n_confdev > 0)
 
1567
            snd_use_case_free_list(mi->conflicting_devices, mi->n_confdev);
 
1568
        pa_xfree(mi->media_role);
 
1569
        pa_xfree(mi);
 
1570
    }
 
1571
    pa_proplist_free(verb->proplist);
 
1572
    pa_xfree(verb);
 
1573
}
 
1574
 
 
1575
void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) {
 
1576
    pa_alsa_ucm_verb *vi, *vn;
 
1577
    pa_alsa_jack *ji, *jn;
 
1578
 
 
1579
    PA_LLIST_FOREACH_SAFE(vi, vn, ucm->verbs) {
 
1580
        PA_LLIST_REMOVE(pa_alsa_ucm_verb, ucm->verbs, vi);
 
1581
        free_verb(vi);
 
1582
    }
 
1583
    PA_LLIST_FOREACH_SAFE(ji, jn, ucm->jacks) {
 
1584
        PA_LLIST_REMOVE(pa_alsa_jack, ucm->jacks, ji);
 
1585
        pa_xfree(ji->alsa_name);
 
1586
        pa_xfree(ji->name);
 
1587
        pa_xfree(ji);
 
1588
    }
 
1589
    if (ucm->ucm_mgr) {
 
1590
        snd_use_case_mgr_close(ucm->ucm_mgr);
 
1591
        ucm->ucm_mgr = NULL;
 
1592
    }
 
1593
}
 
1594
 
 
1595
void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
 
1596
    pa_alsa_ucm_device *dev;
 
1597
    pa_alsa_ucm_modifier *mod;
 
1598
    uint32_t idx;
 
1599
 
 
1600
    if (context->ucm_devices) {
 
1601
        /* clear ucm device pointer to mapping */
 
1602
        PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
 
1603
            if (context->direction == PA_DIRECTION_OUTPUT)
 
1604
                dev->playback_mapping = NULL;
 
1605
            else
 
1606
                dev->capture_mapping = NULL;
 
1607
        }
 
1608
 
 
1609
        pa_idxset_free(context->ucm_devices, NULL);
 
1610
    }
 
1611
 
 
1612
    if (context->ucm_modifiers) {
 
1613
        PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) {
 
1614
            if (context->direction == PA_DIRECTION_OUTPUT)
 
1615
                mod->playback_mapping = NULL;
 
1616
            else
 
1617
                mod->capture_mapping = NULL;
 
1618
        }
 
1619
 
 
1620
        pa_idxset_free(context->ucm_modifiers, NULL);
 
1621
    }
 
1622
}
 
1623
 
 
1624
/* Enable the modifier when the first stream with matched role starts */
 
1625
void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
 
1626
    pa_alsa_ucm_modifier *mod;
 
1627
 
 
1628
    if (!ucm->active_verb)
 
1629
        return;
 
1630
 
 
1631
    PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) {
 
1632
        if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) {
 
1633
            if (mod->enabled_counter == 0) {
 
1634
                const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
 
1635
 
 
1636
                pa_log_info("Enable ucm modifier %s", mod_name);
 
1637
                if (snd_use_case_set(ucm->ucm_mgr, "_enamod", mod_name) < 0) {
 
1638
                    pa_log("Failed to enable ucm modifier %s", mod_name);
 
1639
                }
 
1640
            }
 
1641
 
 
1642
            mod->enabled_counter++;
 
1643
            break;
 
1644
        }
 
1645
    }
 
1646
}
 
1647
 
 
1648
/* Disable the modifier when the last stream with matched role ends */
 
1649
void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
 
1650
    pa_alsa_ucm_modifier *mod;
 
1651
 
 
1652
    if (!ucm->active_verb)
 
1653
        return;
 
1654
 
 
1655
    PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) {
 
1656
        if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) {
 
1657
 
 
1658
            mod->enabled_counter--;
 
1659
            if (mod->enabled_counter == 0) {
 
1660
                const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
 
1661
 
 
1662
                pa_log_info("Disable ucm modifier %s", mod_name);
 
1663
                if (snd_use_case_set(ucm->ucm_mgr, "_dismod", mod_name) < 0) {
 
1664
                    pa_log("Failed to disable ucm modifier %s", mod_name);
 
1665
                }
 
1666
            }
 
1667
 
 
1668
            break;
 
1669
        }
 
1670
    }
 
1671
}
 
1672
 
 
1673
#else /* HAVE_ALSA_UCM */
 
1674
 
 
1675
/* Dummy functions for systems without UCM support */
 
1676
 
 
1677
int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index) {
 
1678
        pa_log_info("UCM not available.");
 
1679
        return -1;
 
1680
}
 
1681
 
 
1682
pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map) {
 
1683
    return NULL;
 
1684
}
 
1685
 
 
1686
int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, const char *new_profile, const char *old_profile) {
 
1687
    return -1;
 
1688
}
 
1689
 
 
1690
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) {
 
1691
    return -1;
 
1692
}
 
1693
 
 
1694
void pa_alsa_ucm_add_ports(
 
1695
        pa_hashmap **hash,
 
1696
        pa_proplist *proplist,
 
1697
        pa_alsa_ucm_mapping_context *context,
 
1698
        bool is_sink,
 
1699
        pa_card *card) {
 
1700
}
 
1701
 
 
1702
void pa_alsa_ucm_add_ports_combination(
 
1703
        pa_hashmap *hash,
 
1704
        pa_alsa_ucm_mapping_context *context,
 
1705
        bool is_sink,
 
1706
        pa_hashmap *ports,
 
1707
        pa_card_profile *cp,
 
1708
        pa_core *core) {
 
1709
}
 
1710
 
 
1711
int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) {
 
1712
    return -1;
 
1713
}
 
1714
 
 
1715
void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) {
 
1716
}
 
1717
 
 
1718
void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
 
1719
}
 
1720
 
 
1721
void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
 
1722
}
 
1723
 
 
1724
void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
 
1725
}
 
1726
 
 
1727
#endif