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