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