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