2 This file is part of PulseAudio.
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.
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.
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.
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
30 #include <sys/types.h>
32 #include <asoundlib.h>
34 #ifdef HAVE_VALGRIND_MEMCHECK_H
35 #include <valgrind/memcheck.h>
38 #include <pulse/sample.h>
39 #include <pulse/xmalloc.h>
40 #include <pulse/timeval.h>
41 #include <pulse/util.h>
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>
53 #include "alsa-mixer.h"
54 #include "alsa-util.h"
57 #define PA_UCM_PRE_TAG_OUTPUT "[Out] "
58 #define PA_UCM_PRE_TAG_INPUT "[In] "
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) \
64 if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device)) (device)->playback_priority = (priority); \
65 if (PA_UCM_CAPTURE_PRIORITY_UNSET(device)) (device)->capture_priority = (priority); \
67 #define PA_UCM_IS_MODIFIER_MAPPING(m) ((pa_proplist_gets((m)->proplist, PA_ALSA_PROP_UCM_MODIFIER)) != NULL)
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
},
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},
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},
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
) {
130 for (i
= 0; item
[i
].id
; i
++) {
133 id
= pa_sprintf_malloc("=%s//%s", item
[i
].id
, verb_name
);
134 err
= snd_use_case_get(uc_mgr
, id
, &value
);
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
);
147 static int ucm_device_exists(pa_idxset
*idxset
, pa_alsa_ucm_device
*dev
) {
148 pa_alsa_ucm_device
*d
;
151 PA_IDXSET_FOREACH(d
, idxset
, idx
)
158 static void ucm_add_devices_to_idxset(
160 pa_alsa_ucm_device
*me
,
161 pa_alsa_ucm_device
*devices
,
162 const char **dev_names
,
165 pa_alsa_ucm_device
*d
;
167 PA_LLIST_FOREACH(d
, devices
) {
174 name
= pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_NAME
);
176 for (i
= 0; i
< n
; i
++)
177 if (pa_streq(dev_names
[i
], name
))
178 pa_idxset_put(idxset
, d
, NULL
);
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
) {
190 const char **devices
;
195 int n_confdev
, n_suppdev
;
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
);
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
);
209 /* get direction and channels */
210 value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS
);
211 if (value
) { /* output */
213 if (pa_atou(value
, &ui
) == 0 && ui
< PA_CHANNELS_MAX
)
214 device
->playback_channels
= ui
;
216 pa_log("UCM playback channels %s for device %s out of range", value
, device_name
);
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
);
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
);
226 pa_log("UCM playback device %s fetch pcm failed", device_name
);
230 value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS
);
231 if (value
) { /* input */
233 if (pa_atou(value
, &ui
) == 0 && ui
< PA_CHANNELS_MAX
)
234 device
->capture_channels
= ui
;
236 pa_log("UCM capture channels %s for device %s out of range", value
, device_name
);
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
);
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
);
246 pa_log("UCM capture device %s fetch pcm failed", device_name
);
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;
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
);
261 /* get priority from ucm config */
262 if (pa_atou(value
, &ui
) == 0)
263 device
->playback_priority
= ui
;
265 pa_log_debug("UCM playback priority %s for device %s error", value
, device_name
);
269 if (device
->capture_channels
) { /* source device */
270 value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_CAPTURE_PRIORITY
);
272 /* get priority from ucm config */
273 if (pa_atou(value
, &ui
) == 0)
274 device
->capture_priority
= ui
;
276 pa_log_debug("UCM capture priority %s for device %s error", value
, device_name
);
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
);
290 if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device
)) {
291 /* fall through to default priority */
292 device
->playback_priority
= 100;
295 if (PA_UCM_CAPTURE_PRIORITY_UNSET(device
)) {
296 /* fall through to default priority */
297 device
->capture_priority
= 100;
300 id
= pa_sprintf_malloc("%s/%s", "_conflictingdevs", device_name
);
301 n_confdev
= snd_use_case_get_list(uc_mgr
, id
, &devices
);
305 pa_log_debug("No %s for device %s", "_conflictingdevs", device_name
);
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
);
312 id
= pa_sprintf_malloc("%s/%s", "_supporteddevs", device_name
);
313 n_suppdev
= snd_use_case_get_list(uc_mgr
, id
, &devices
);
317 pa_log_debug("No %s for device %s", "_supporteddevs", device_name
);
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
);
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
) {
333 for (i
= 0; item
[i
].id
; i
++) {
336 id
= pa_sprintf_malloc("=%s/%s", item
[i
].id
, modifier_name
);
337 err
= snd_use_case_get(uc_mgr
, id
, &value
);
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
);
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
);
350 if (modifier
->n_confdev
< 0)
351 pa_log_debug("No %s for modifier %s", "_conflictingdevs", modifier_name
);
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
);
356 if (modifier
->n_suppdev
< 0)
357 pa_log_debug("No %s for modifier %s", "_supporteddevs", modifier_name
);
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
;
367 num_dev
= snd_use_case_get_list(uc_mgr
, "_devices", &dev_list
);
371 for (i
= 0; i
< num_dev
; i
+= 2) {
372 pa_alsa_ucm_device
*d
= pa_xnew0(pa_alsa_ucm_device
, 1);
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]));
378 PA_LLIST_PREPEND(pa_alsa_ucm_device
, verb
->devices
, d
);
381 snd_use_case_free_list(dev_list
, num_dev
);
386 static int ucm_get_modifiers(pa_alsa_ucm_verb
*verb
, snd_use_case_mgr_t
*uc_mgr
) {
387 const char **mod_list
;
390 num_mod
= snd_use_case_get_list(uc_mgr
, "_modifiers", &mod_list
);
394 for (i
= 0; i
< num_mod
; i
+= 2) {
395 pa_alsa_ucm_modifier
*m
;
398 pa_log_warn("Got a modifier with a null name. Skipping.");
402 m
= pa_xnew0(pa_alsa_ucm_modifier
, 1);
403 m
->proplist
= pa_proplist_new();
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]));
408 PA_LLIST_PREPEND(pa_alsa_ucm_modifier
, verb
->modifiers
, m
);
411 snd_use_case_free_list(mod_list
, num_mod
);
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
);
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
);
424 pa_proplist_sets(dev
->proplist
, role_name
, value
);
428 pa_log_info("Add role %s to device %s(%s), result %s", role
, dev_name
, role_name
, pa_proplist_gets(dev
->proplist
,
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
;
435 PA_LLIST_FOREACH(d
, list
) {
436 const char *dev_name
= pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_NAME
);
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
);
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
);
451 static char *modifier_name_to_role(const char *mod_name
, bool *is_sink
) {
452 char *sub
= NULL
, *tmp
;
456 if (pa_startswith(mod_name
, "Play")) {
458 sub
= pa_xstrdup(mod_name
+ 4);
459 } else if (pa_startswith(mod_name
, "Capture"))
460 sub
= pa_xstrdup(mod_name
+ 7);
464 pa_log_warn("Can't match media roles for modifer %s", mod_name
);
471 *tmp
= tolower(*tmp
);
477 static void ucm_set_media_roles(pa_alsa_ucm_modifier
*modifier
, pa_alsa_ucm_device
*list
, const char *mod_name
) {
479 bool is_sink
= false;
481 const char *role_name
;
483 sub
= modifier_name_to_role(mod_name
, &is_sink
);
487 modifier
->action_direction
= is_sink
? PA_DIRECTION_OUTPUT
: PA_DIRECTION_INPUT
;
488 modifier
->media_role
= sub
;
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
);
499 static void append_lost_relationship(pa_alsa_ucm_device
*dev
) {
501 pa_alsa_ucm_device
*d
;
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
);
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
));
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
);
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
));
528 int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config
*ucm
, int card_index
) {
530 const char **verb_list
;
531 int num_verbs
, i
, err
= 0;
533 /* is UCM available for this card ? */
534 err
= snd_card_get_name(card_index
, &card_name
);
536 pa_log("Card can't get card_name from card_index %d", card_index
);
540 err
= snd_use_case_mgr_open(&ucm
->ucm_mgr
, card_name
);
542 pa_log_info("UCM not available for card %s", card_name
);
546 pa_log_info("UCM available for card %s", card_name
);
548 /* get a list of all UCM verbs (profiles) for this card */
549 num_verbs
= snd_use_case_verb_list(ucm
->ucm_mgr
, &verb_list
);
551 pa_log("UCM verb list not found for %s", card_name
);
555 /* get the properties of each UCM verb */
556 for (i
= 0; i
< num_verbs
; i
+= 2) {
557 pa_alsa_ucm_verb
*verb
;
559 /* Get devices and modifiers for each verb */
560 err
= pa_alsa_ucm_get_verb(ucm
->ucm_mgr
, verb_list
[i
], verb_list
[i
+1], &verb
);
562 pa_log("Failed to get the verb %s", verb_list
[i
]);
566 PA_LLIST_PREPEND(pa_alsa_ucm_verb
, ucm
->verbs
, verb
);
570 pa_log("No UCM verb is valid for %s", card_name
);
574 snd_use_case_free_list(verb_list
, num_verbs
);
578 snd_use_case_mgr_close(ucm
->ucm_mgr
);
589 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
) {
590 pa_alsa_ucm_device
*d
;
591 pa_alsa_ucm_modifier
*mod
;
592 pa_alsa_ucm_verb
*verb
;
596 pa_log_info("Set UCM verb to %s", verb_name
);
597 err
= snd_use_case_set(uc_mgr
, "_verb", verb_name
);
601 verb
= pa_xnew0(pa_alsa_ucm_verb
, 1);
602 verb
->proplist
= pa_proplist_new();
604 pa_proplist_sets(verb
->proplist
, PA_ALSA_PROP_UCM_NAME
, pa_strnull(verb_name
));
605 pa_proplist_sets(verb
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
, pa_strna(verb_desc
));
607 err
= ucm_get_devices(verb
, uc_mgr
);
609 pa_log("No UCM devices for verb %s", verb_name
);
611 err
= ucm_get_modifiers(verb
, uc_mgr
);
613 pa_log("No UCM modifiers for verb %s", verb_name
);
615 /* Verb properties */
616 ucm_get_property(verb
, uc_mgr
, verb_name
);
618 PA_LLIST_FOREACH(d
, verb
->devices
) {
619 const char *dev_name
= pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_NAME
);
621 /* Devices properties */
622 ucm_get_device_property(d
, uc_mgr
, verb
, dev_name
);
624 /* make conflicting or supported device mutual */
625 PA_LLIST_FOREACH(d
, verb
->devices
)
626 append_lost_relationship(d
);
628 PA_LLIST_FOREACH(mod
, verb
->modifiers
) {
629 const char *mod_name
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_NAME
);
631 /* Modifier properties */
632 ucm_get_modifier_property(mod
, uc_mgr
, mod_name
);
634 /* Set PA_PROP_DEVICE_INTENDED_ROLES property to devices */
635 pa_log_debug("Set media roles for verb %s, modifier %s", verb_name
, mod_name
);
636 ucm_set_media_roles(mod
, verb
->devices
, mod_name
);
643 static void ucm_add_port_combination(
645 pa_alsa_ucm_mapping_context
*context
,
647 pa_alsa_ucm_device
**pdevices
,
653 pa_device_port
*port
;
657 const char *dev_name
;
658 const char *direction
;
659 pa_alsa_ucm_device
*dev
;
662 dev_name
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
);
664 name
= pa_sprintf_malloc("%s%s", is_sink
? PA_UCM_PRE_TAG_OUTPUT
: PA_UCM_PRE_TAG_INPUT
, dev_name
);
665 desc
= num
== 1 ? pa_xstrdup(pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
))
666 : pa_sprintf_malloc("Combination port for %s", dev_name
);
668 priority
= is_sink
? dev
->playback_priority
: dev
->capture_priority
;
670 for (i
= 1; i
< num
; i
++) {
674 dev_name
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
);
676 tmp
= pa_sprintf_malloc("%s+%s", name
, dev_name
);
680 tmp
= pa_sprintf_malloc("%s,%s", desc
, dev_name
);
684 /* FIXME: Is this true? */
685 priority
+= (is_sink
? dev
->playback_priority
: dev
->capture_priority
);
688 port
= pa_hashmap_get(ports
, name
);
690 pa_device_port_new_data port_data
;
692 pa_device_port_new_data_init(&port_data
);
693 pa_device_port_new_data_set_name(&port_data
, name
);
694 pa_device_port_new_data_set_description(&port_data
, desc
);
695 pa_device_port_new_data_set_direction(&port_data
, is_sink
? PA_DIRECTION_OUTPUT
: PA_DIRECTION_INPUT
);
697 port
= pa_device_port_new(core
, &port_data
, 0);
698 pa_device_port_new_data_done(&port_data
);
701 pa_hashmap_put(ports
, port
->name
, port
);
702 pa_log_debug("Add port %s: %s", port
->name
, port
->description
);
703 port
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
706 port
->priority
= priority
;
711 direction
= is_sink
? "output" : "input";
712 pa_log_debug("Port %s direction %s, priority %d", port
->name
, direction
, priority
);
715 pa_log_debug("Adding port %s to profile %s", port
->name
, cp
->name
);
716 pa_hashmap_put(port
->profiles
, cp
->name
, cp
);
720 pa_hashmap_put(hash
, port
->name
, port
);
721 pa_device_port_ref(port
);
725 static int ucm_port_contains(const char *port_name
, const char *dev_name
, bool is_sink
) {
728 const char *state
= NULL
;
731 if (!port_name
|| !dev_name
)
734 port_name
+= is_sink
? strlen(PA_UCM_PRE_TAG_OUTPUT
) : strlen(PA_UCM_PRE_TAG_INPUT
);
736 while ((r
= pa_split_in_place(port_name
, "+", &len
, &state
))) {
737 if (!strncmp(r
, dev_name
, len
)) {
746 static int ucm_check_conformance(
747 pa_alsa_ucm_mapping_context
*context
,
748 pa_alsa_ucm_device
**pdevices
,
750 pa_alsa_ucm_device
*dev
) {
753 pa_alsa_ucm_device
*d
;
758 pa_log_debug("Check device %s conformance with %d other devices",
759 pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
), dev_num
);
761 pa_log_debug("First device in combination, number 1");
765 if (dev
->conflicting_devices
) { /* the device defines conflicting devices */
766 PA_IDXSET_FOREACH(d
, dev
->conflicting_devices
, idx
) {
767 for (i
= 0; i
< dev_num
; i
++) {
768 if (pdevices
[i
] == d
) {
769 pa_log_debug("Conflicting device found");
774 } else if (dev
->supported_devices
) { /* the device defines supported devices */
775 for (i
= 0; i
< dev_num
; i
++) {
776 if (!ucm_device_exists(dev
->supported_devices
, pdevices
[i
])) {
777 pa_log_debug("Supported device not found");
781 } else { /* not support any other devices */
782 pa_log_debug("Not support any other devices");
786 pa_log_debug("Device added to combination, number %d", dev_num
+ 1);
790 static inline pa_alsa_ucm_device
*get_next_device(pa_idxset
*idxset
, uint32_t *idx
) {
791 pa_alsa_ucm_device
*dev
;
793 if (*idx
== PA_IDXSET_INVALID
)
794 dev
= pa_idxset_first(idxset
, idx
);
796 dev
= pa_idxset_next(idxset
, idx
);
801 static void ucm_add_ports_combination(
803 pa_alsa_ucm_mapping_context
*context
,
805 pa_alsa_ucm_device
**pdevices
,
812 pa_alsa_ucm_device
*dev
;
813 uint32_t idx
= map_index
;
815 if ((dev
= get_next_device(context
->ucm_devices
, &idx
)) == NULL
)
818 /* check if device at map_index can combine with existing devices combination */
819 if (ucm_check_conformance(context
, pdevices
, dev_num
, dev
)) {
820 /* add device at map_index to devices combination */
821 pdevices
[dev_num
] = dev
;
822 /* add current devices combination as a new port */
823 ucm_add_port_combination(hash
, context
, is_sink
, pdevices
, dev_num
+ 1, ports
, cp
, core
);
824 /* try more elements combination */
825 ucm_add_ports_combination(hash
, context
, is_sink
, pdevices
, dev_num
+ 1, idx
, ports
, cp
, core
);
828 /* try other device with current elements number */
829 ucm_add_ports_combination(hash
, context
, is_sink
, pdevices
, dev_num
, idx
, ports
, cp
, core
);
832 static char* merge_roles(const char *cur
, const char *add
) {
834 const char *state
= NULL
;
837 return pa_xstrdup(cur
);
838 else if (cur
== NULL
)
839 return pa_xstrdup(add
);
841 ret
= pa_xstrdup(cur
);
843 while ((r
= pa_split_spaces(add
, &state
))) {
846 if (!pa_str_in_list_spaces(ret
, r
))
847 value
= pa_sprintf_malloc("%s %s", ret
, r
);
861 void pa_alsa_ucm_add_ports_combination(
863 pa_alsa_ucm_mapping_context
*context
,
869 pa_alsa_ucm_device
**pdevices
;
871 pa_assert(context
->ucm_devices
);
873 if (pa_idxset_size(context
->ucm_devices
) > 0) {
874 pdevices
= pa_xnew(pa_alsa_ucm_device
*, pa_idxset_size(context
->ucm_devices
));
875 ucm_add_ports_combination(p
, context
, is_sink
, pdevices
, 0, PA_IDXSET_INVALID
, ports
, cp
, core
);
880 void pa_alsa_ucm_add_ports(
882 pa_proplist
*proplist
,
883 pa_alsa_ucm_mapping_context
*context
,
889 const char *role_name
= is_sink
? PA_ALSA_PROP_UCM_PLAYBACK_ROLES
: PA_ALSA_PROP_UCM_CAPTURE_ROLES
;
890 pa_alsa_ucm_device
*dev
;
891 pa_alsa_ucm_modifier
*mod
;
897 /* add ports first */
898 pa_alsa_ucm_add_ports_combination(*p
, context
, is_sink
, card
->ports
, NULL
, card
->core
);
900 /* then set property PA_PROP_DEVICE_INTENDED_ROLES */
901 merged_roles
= pa_xstrdup(pa_proplist_gets(proplist
, PA_PROP_DEVICE_INTENDED_ROLES
));
902 PA_IDXSET_FOREACH(dev
, context
->ucm_devices
, idx
) {
903 const char *roles
= pa_proplist_gets(dev
->proplist
, role_name
);
904 tmp
= merge_roles(merged_roles
, roles
);
905 pa_xfree(merged_roles
);
909 if (context
->ucm_modifiers
)
910 PA_IDXSET_FOREACH(mod
, context
->ucm_modifiers
, idx
) {
911 tmp
= merge_roles(merged_roles
, mod
->media_role
);
912 pa_xfree(merged_roles
);
917 pa_proplist_sets(proplist
, PA_PROP_DEVICE_INTENDED_ROLES
, merged_roles
);
919 pa_log_info("ALSA device %s roles: %s", pa_proplist_gets(proplist
, PA_PROP_DEVICE_STRING
), pa_strnull(merged_roles
));
920 pa_xfree(merged_roles
);
923 /* Change UCM verb and device to match selected card profile */
924 int pa_alsa_ucm_set_profile(pa_alsa_ucm_config
*ucm
, const char *new_profile
, const char *old_profile
) {
927 pa_alsa_ucm_verb
*verb
;
929 if (new_profile
== old_profile
)
931 else if (new_profile
== NULL
|| old_profile
== NULL
)
932 profile
= new_profile
? new_profile
: SND_USE_CASE_VERB_INACTIVE
;
933 else if (!pa_streq(new_profile
, old_profile
))
934 profile
= new_profile
;
939 pa_log_info("Set UCM verb to %s", profile
);
940 if ((snd_use_case_set(ucm
->ucm_mgr
, "_verb", profile
)) < 0) {
941 pa_log("Failed to set verb %s", profile
);
945 /* find active verb */
946 ucm
->active_verb
= NULL
;
947 PA_LLIST_FOREACH(verb
, ucm
->verbs
) {
948 const char *verb_name
;
949 verb_name
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_NAME
);
950 if (pa_streq(verb_name
, profile
)) {
951 ucm
->active_verb
= verb
;
959 int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context
*context
, pa_device_port
*port
, bool is_sink
) {
962 pa_alsa_ucm_config
*ucm
;
963 const char **enable_devs
;
966 pa_alsa_ucm_device
*dev
;
968 pa_assert(context
&& context
->ucm
);
971 pa_assert(ucm
->ucm_mgr
);
973 enable_devs
= pa_xnew(const char *, pa_idxset_size(context
->ucm_devices
));
975 /* first disable then enable */
976 PA_IDXSET_FOREACH(dev
, context
->ucm_devices
, idx
) {
977 const char *dev_name
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
);
979 if (ucm_port_contains(port
->name
, dev_name
, is_sink
))
980 enable_devs
[enable_num
++] = dev_name
;
982 pa_log_debug("Disable ucm device %s", dev_name
);
983 if (snd_use_case_set(ucm
->ucm_mgr
, "_disdev", dev_name
) > 0) {
984 pa_log("Failed to disable ucm device %s", dev_name
);
991 for (i
= 0; i
< enable_num
; i
++) {
992 pa_log_debug("Enable ucm device %s", enable_devs
[i
]);
993 if (snd_use_case_set(ucm
->ucm_mgr
, "_enadev", enable_devs
[i
]) < 0) {
994 pa_log("Failed to enable ucm device %s", enable_devs
[i
]);
1000 pa_xfree(enable_devs
);
1005 static void ucm_add_mapping(pa_alsa_profile
*p
, pa_alsa_mapping
*m
) {
1007 switch (m
->direction
) {
1008 case PA_ALSA_DIRECTION_ANY
:
1009 pa_idxset_put(p
->output_mappings
, m
, NULL
);
1010 pa_idxset_put(p
->input_mappings
, m
, NULL
);
1012 case PA_ALSA_DIRECTION_OUTPUT
:
1013 pa_idxset_put(p
->output_mappings
, m
, NULL
);
1015 case PA_ALSA_DIRECTION_INPUT
:
1016 pa_idxset_put(p
->input_mappings
, m
, NULL
);
1021 static void alsa_mapping_add_ucm_device(pa_alsa_mapping
*m
, pa_alsa_ucm_device
*device
) {
1023 const char *new_desc
;
1025 pa_idxset_put(m
->ucm_context
.ucm_devices
, device
, NULL
);
1027 new_desc
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
);
1028 cur_desc
= m
->description
;
1030 m
->description
= pa_sprintf_malloc("%s + %s", cur_desc
, new_desc
);
1032 m
->description
= pa_xstrdup(new_desc
);
1035 /* walk around null case */
1036 m
->description
= m
->description
? m
->description
: pa_xstrdup("");
1038 /* save mapping to ucm device */
1039 if (m
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1040 device
->playback_mapping
= m
;
1042 device
->capture_mapping
= m
;
1045 static void alsa_mapping_add_ucm_modifier(pa_alsa_mapping
*m
, pa_alsa_ucm_modifier
*modifier
) {
1047 const char *new_desc
, *mod_name
, *channel_str
;
1048 uint32_t channels
= 0;
1050 pa_idxset_put(m
->ucm_context
.ucm_modifiers
, modifier
, NULL
);
1052 new_desc
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
);
1053 cur_desc
= m
->description
;
1055 m
->description
= pa_sprintf_malloc("%s + %s", cur_desc
, new_desc
);
1057 m
->description
= pa_xstrdup(new_desc
);
1060 if (!m
->description
)
1063 /* Modifier sinks should not be routed to by default */
1066 mod_name
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1067 pa_proplist_sets(m
->proplist
, PA_ALSA_PROP_UCM_MODIFIER
, mod_name
);
1069 /* save mapping to ucm modifier */
1070 if (m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1071 modifier
->playback_mapping
= m
;
1072 channel_str
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS
);
1074 modifier
->capture_mapping
= m
;
1075 channel_str
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS
);
1079 pa_assert_se(pa_atou(channel_str
, &channels
) == 0 && channels
< PA_CHANNELS_MAX
);
1080 pa_log_debug("Got channel count %" PRIu32
" for modifier", channels
);
1084 pa_channel_map_init_extend(&m
->channel_map
, channels
, PA_CHANNEL_MAP_ALSA
);
1086 pa_channel_map_init(&m
->channel_map
);
1089 static int ucm_create_mapping_direction(
1090 pa_alsa_ucm_config
*ucm
,
1091 pa_alsa_profile_set
*ps
,
1093 pa_alsa_ucm_device
*device
,
1094 const char *verb_name
,
1095 const char *device_name
,
1096 const char *device_str
,
1101 unsigned priority
, channels
;
1103 mapping_name
= pa_sprintf_malloc("Mapping %s: %s: %s", verb_name
, device_str
, is_sink
? "sink" : "source");
1105 m
= pa_alsa_mapping_get(ps
, mapping_name
);
1107 pa_log("No mapping for %s", mapping_name
);
1108 pa_xfree(mapping_name
);
1111 pa_log_debug("UCM mapping: %s dev %s", mapping_name
, device_name
);
1112 pa_xfree(mapping_name
);
1114 priority
= is_sink
? device
->playback_priority
: device
->capture_priority
;
1115 channels
= is_sink
? device
->playback_channels
: device
->capture_channels
;
1117 if (!m
->ucm_context
.ucm_devices
) { /* new mapping */
1118 m
->ucm_context
.ucm_devices
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1119 m
->ucm_context
.ucm
= ucm
;
1120 m
->ucm_context
.direction
= is_sink
? PA_DIRECTION_OUTPUT
: PA_DIRECTION_INPUT
;
1122 m
->device_strings
= pa_xnew0(char*, 2);
1123 m
->device_strings
[0] = pa_xstrdup(device_str
);
1124 m
->direction
= is_sink
? PA_ALSA_DIRECTION_OUTPUT
: PA_ALSA_DIRECTION_INPUT
;
1126 ucm_add_mapping(p
, m
);
1127 pa_channel_map_init_extend(&m
->channel_map
, channels
, PA_CHANNEL_MAP_ALSA
);
1130 /* mapping priority is the highest one of ucm devices */
1131 if (priority
> m
->priority
)
1132 m
->priority
= priority
;
1134 /* mapping channels is the lowest one of ucm devices */
1135 if (channels
< m
->channel_map
.channels
)
1136 pa_channel_map_init_extend(&m
->channel_map
, channels
, PA_CHANNEL_MAP_ALSA
);
1138 alsa_mapping_add_ucm_device(m
, device
);
1143 static int ucm_create_mapping_for_modifier(
1144 pa_alsa_ucm_config
*ucm
,
1145 pa_alsa_profile_set
*ps
,
1147 pa_alsa_ucm_modifier
*modifier
,
1148 const char *verb_name
,
1149 const char *mod_name
,
1150 const char *device_str
,
1156 mapping_name
= pa_sprintf_malloc("Mapping %s: %s: %s", verb_name
, device_str
, is_sink
? "sink" : "source");
1158 m
= pa_alsa_mapping_get(ps
, mapping_name
);
1160 pa_log("no mapping for %s", mapping_name
);
1161 pa_xfree(mapping_name
);
1164 pa_log_info("ucm mapping: %s modifier %s", mapping_name
, mod_name
);
1165 pa_xfree(mapping_name
);
1167 if (!m
->ucm_context
.ucm_devices
&& !m
->ucm_context
.ucm_modifiers
) { /* new mapping */
1168 m
->ucm_context
.ucm_devices
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1169 m
->ucm_context
.ucm_modifiers
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1170 m
->ucm_context
.ucm
= ucm
;
1171 m
->ucm_context
.direction
= is_sink
? PA_DIRECTION_OUTPUT
: PA_DIRECTION_INPUT
;
1173 m
->device_strings
= pa_xnew0(char*, 2);
1174 m
->device_strings
[0] = pa_xstrdup(device_str
);
1175 m
->direction
= is_sink
? PA_ALSA_DIRECTION_OUTPUT
: PA_ALSA_DIRECTION_INPUT
;
1176 /* Modifier sinks should not be routed to by default */
1179 ucm_add_mapping(p
, m
);
1180 } else if (!m
->ucm_context
.ucm_modifiers
) /* share pcm with device */
1181 m
->ucm_context
.ucm_modifiers
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1183 alsa_mapping_add_ucm_modifier(m
, modifier
);
1188 static int ucm_create_mapping(
1189 pa_alsa_ucm_config
*ucm
,
1190 pa_alsa_profile_set
*ps
,
1192 pa_alsa_ucm_device
*device
,
1193 const char *verb_name
,
1194 const char *device_name
,
1196 const char *source
) {
1200 if (!sink
&& !source
) {
1201 pa_log("No sink and source at %s: %s", verb_name
, device_name
);
1206 ret
= ucm_create_mapping_direction(ucm
, ps
, p
, device
, verb_name
, device_name
, sink
, true);
1207 if (ret
== 0 && source
)
1208 ret
= ucm_create_mapping_direction(ucm
, ps
, p
, device
, verb_name
, device_name
, source
, false);
1213 static pa_alsa_jack
* ucm_get_jack(pa_alsa_ucm_config
*ucm
, const char *dev_name
, const char *pre_tag
) {
1215 char *name
= pa_sprintf_malloc("%s%s", pre_tag
, dev_name
);
1217 PA_LLIST_FOREACH(j
, ucm
->jacks
)
1218 if (pa_streq(j
->name
, name
))
1221 j
= pa_xnew0(pa_alsa_jack
, 1);
1222 j
->state_unplugged
= PA_AVAILABLE_NO
;
1223 j
->state_plugged
= PA_AVAILABLE_YES
;
1224 j
->name
= pa_xstrdup(name
);
1225 j
->alsa_name
= pa_sprintf_malloc("%s Jack", dev_name
);
1227 PA_LLIST_PREPEND(pa_alsa_jack
, ucm
->jacks
, j
);
1234 static int ucm_create_profile(
1235 pa_alsa_ucm_config
*ucm
,
1236 pa_alsa_profile_set
*ps
,
1237 pa_alsa_ucm_verb
*verb
,
1238 const char *verb_name
,
1239 const char *verb_desc
) {
1242 pa_alsa_ucm_device
*dev
;
1243 pa_alsa_ucm_modifier
*mod
;
1245 const char *name
, *sink
, *source
;
1250 if (pa_hashmap_get(ps
->profiles
, verb_name
)) {
1251 pa_log("Verb %s already exists", verb_name
);
1255 p
= pa_xnew0(pa_alsa_profile
, 1);
1256 p
->profile_set
= ps
;
1257 p
->name
= pa_xstrdup(verb_name
);
1258 p
->description
= pa_xstrdup(verb_desc
);
1260 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1261 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1263 p
->supported
= true;
1264 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
1266 /* TODO: get profile priority from ucm info or policy management */
1267 c
= verb_cmp
= pa_xstrdup(verb_name
);
1269 if (*c
== '_') *c
= ' ';
1273 for (i
= 0; verb_info
[i
].id
; i
++) {
1274 if (strcasecmp(verb_info
[i
].id
, verb_cmp
) == 0) {
1275 p
->priority
= verb_info
[i
].priority
;
1282 if (verb_info
[i
].id
== NULL
)
1285 PA_LLIST_FOREACH(dev
, verb
->devices
) {
1286 name
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1288 sink
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_SINK
);
1289 source
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_SOURCE
);
1291 ucm_create_mapping(ucm
, ps
, p
, dev
, verb_name
, name
, sink
, source
);
1294 dev
->output_jack
= ucm_get_jack(ucm
, name
, PA_UCM_PRE_TAG_OUTPUT
);
1296 dev
->input_jack
= ucm_get_jack(ucm
, name
, PA_UCM_PRE_TAG_INPUT
);
1299 /* Now find modifiers that have their own PlaybackPCM and create
1300 * separate sinks for them. */
1301 PA_LLIST_FOREACH(mod
, verb
->modifiers
) {
1302 name
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1304 sink
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_SINK
);
1305 source
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_SOURCE
);
1308 ucm_create_mapping_for_modifier(ucm
, ps
, p
, mod
, verb_name
, name
, sink
, true);
1310 ucm_create_mapping_for_modifier(ucm
, ps
, p
, mod
, verb_name
, name
, source
, false);
1313 pa_alsa_profile_dump(p
);
1318 static snd_pcm_t
* mapping_open_pcm(pa_alsa_ucm_config
*ucm
, pa_alsa_mapping
*m
, int mode
) {
1320 pa_sample_spec try_ss
= ucm
->core
->default_sample_spec
;
1321 pa_channel_map try_map
;
1322 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
1323 bool exact_channels
= m
->channel_map
.channels
> 0;
1325 if (exact_channels
) {
1326 try_map
= m
->channel_map
;
1327 try_ss
.channels
= try_map
.channels
;
1329 pa_channel_map_init_extend(&try_map
, try_ss
.channels
, PA_CHANNEL_MAP_ALSA
);
1332 pa_usec_to_bytes(ucm
->core
->default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
1333 pa_frame_size(&try_ss
);
1334 try_buffer_size
= ucm
->core
->default_n_fragments
* try_period_size
;
1336 pcm
= pa_alsa_open_by_device_string(m
->device_strings
[0], NULL
, &try_ss
,
1337 &try_map
, mode
, &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
, exact_channels
);
1339 if (pcm
&& !exact_channels
)
1340 m
->channel_map
= try_map
;
1345 static void profile_finalize_probing(pa_alsa_profile
*p
) {
1349 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
1356 snd_pcm_close(m
->output_pcm
);
1357 m
->output_pcm
= NULL
;
1360 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
1367 snd_pcm_close(m
->input_pcm
);
1368 m
->input_pcm
= NULL
;
1372 static void ucm_mapping_jack_probe(pa_alsa_mapping
*m
) {
1373 snd_pcm_t
*pcm_handle
;
1374 snd_mixer_t
*mixer_handle
;
1375 snd_hctl_t
*hctl_handle
;
1376 pa_alsa_ucm_mapping_context
*context
= &m
->ucm_context
;
1377 pa_alsa_ucm_device
*dev
;
1380 pcm_handle
= m
->direction
== PA_ALSA_DIRECTION_OUTPUT
? m
->output_pcm
: m
->input_pcm
;
1381 mixer_handle
= pa_alsa_open_mixer_for_pcm(pcm_handle
, NULL
, &hctl_handle
);
1382 if (!mixer_handle
|| !hctl_handle
)
1385 PA_IDXSET_FOREACH(dev
, context
->ucm_devices
, idx
) {
1387 jack
= m
->direction
== PA_ALSA_DIRECTION_OUTPUT
? dev
->output_jack
: dev
->input_jack
;
1389 jack
->has_control
= pa_alsa_find_jack(hctl_handle
, jack
->alsa_name
) != NULL
;
1390 pa_log_info("UCM jack %s has_control=%d", jack
->name
, jack
->has_control
);
1393 snd_mixer_close(mixer_handle
);
1396 static void ucm_probe_profile_set(pa_alsa_ucm_config
*ucm
, pa_alsa_profile_set
*ps
) {
1402 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
1404 pa_log_info("Set ucm verb to %s", p
->name
);
1406 if ((snd_use_case_set(ucm
->ucm_mgr
, "_verb", p
->name
)) < 0) {
1407 pa_log("Failed to set verb %s", p
->name
);
1408 p
->supported
= false;
1412 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
1413 if (PA_UCM_IS_MODIFIER_MAPPING(m
)) {
1414 /* Skip jack probing on modifier PCMs since we expect this to
1415 * only be controlled on the main device/verb PCM. */
1419 m
->output_pcm
= mapping_open_pcm(ucm
, m
, SND_PCM_STREAM_PLAYBACK
);
1420 if (!m
->output_pcm
) {
1421 p
->supported
= false;
1427 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
1428 if (PA_UCM_IS_MODIFIER_MAPPING(m
)) {
1429 /* Skip jack probing on modifier PCMs since we expect this to
1430 * only be controlled on the main device/verb PCM. */
1434 m
->input_pcm
= mapping_open_pcm(ucm
, m
, SND_PCM_STREAM_CAPTURE
);
1435 if (!m
->input_pcm
) {
1436 p
->supported
= false;
1442 if (!p
->supported
) {
1443 profile_finalize_probing(p
);
1447 pa_log_debug("Profile %s supported.", p
->name
);
1449 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
1450 if (!PA_UCM_IS_MODIFIER_MAPPING(m
))
1451 ucm_mapping_jack_probe(m
);
1453 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
1454 if (!PA_UCM_IS_MODIFIER_MAPPING(m
))
1455 ucm_mapping_jack_probe(m
);
1457 profile_finalize_probing(p
);
1460 /* restore ucm state */
1461 snd_use_case_set(ucm
->ucm_mgr
, "_verb", SND_USE_CASE_VERB_INACTIVE
);
1463 pa_alsa_profile_set_drop_unsupported(ps
);
1466 pa_alsa_profile_set
* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config
*ucm
, pa_channel_map
*default_channel_map
) {
1467 pa_alsa_ucm_verb
*verb
;
1468 pa_alsa_profile_set
*ps
;
1470 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
1471 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
1472 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
1473 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
1475 /* create a profile for each verb */
1476 PA_LLIST_FOREACH(verb
, ucm
->verbs
) {
1477 const char *verb_name
;
1478 const char *verb_desc
;
1480 verb_name
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1481 verb_desc
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
);
1482 if (verb_name
== NULL
) {
1483 pa_log("Verb with no name");
1487 ucm_create_profile(ucm
, ps
, verb
, verb_name
, verb_desc
);
1490 ucm_probe_profile_set(ucm
, ps
);
1496 static void free_verb(pa_alsa_ucm_verb
*verb
) {
1497 pa_alsa_ucm_device
*di
, *dn
;
1498 pa_alsa_ucm_modifier
*mi
, *mn
;
1500 PA_LLIST_FOREACH_SAFE(di
, dn
, verb
->devices
) {
1501 PA_LLIST_REMOVE(pa_alsa_ucm_device
, verb
->devices
, di
);
1502 pa_proplist_free(di
->proplist
);
1503 if (di
->conflicting_devices
)
1504 pa_idxset_free(di
->conflicting_devices
, NULL
);
1505 if (di
->supported_devices
)
1506 pa_idxset_free(di
->supported_devices
, NULL
);
1510 PA_LLIST_FOREACH_SAFE(mi
, mn
, verb
->modifiers
) {
1511 PA_LLIST_REMOVE(pa_alsa_ucm_modifier
, verb
->modifiers
, mi
);
1512 pa_proplist_free(mi
->proplist
);
1513 if (mi
->n_suppdev
> 0)
1514 snd_use_case_free_list(mi
->supported_devices
, mi
->n_suppdev
);
1515 if (mi
->n_confdev
> 0)
1516 snd_use_case_free_list(mi
->conflicting_devices
, mi
->n_confdev
);
1517 pa_xfree(mi
->media_role
);
1520 pa_proplist_free(verb
->proplist
);
1524 void pa_alsa_ucm_free(pa_alsa_ucm_config
*ucm
) {
1525 pa_alsa_ucm_verb
*vi
, *vn
;
1526 pa_alsa_jack
*ji
, *jn
;
1528 PA_LLIST_FOREACH_SAFE(vi
, vn
, ucm
->verbs
) {
1529 PA_LLIST_REMOVE(pa_alsa_ucm_verb
, ucm
->verbs
, vi
);
1532 PA_LLIST_FOREACH_SAFE(ji
, jn
, ucm
->jacks
) {
1533 PA_LLIST_REMOVE(pa_alsa_jack
, ucm
->jacks
, ji
);
1534 pa_xfree(ji
->alsa_name
);
1539 snd_use_case_mgr_close(ucm
->ucm_mgr
);
1540 ucm
->ucm_mgr
= NULL
;
1544 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context
*context
) {
1545 pa_alsa_ucm_device
*dev
;
1546 pa_alsa_ucm_modifier
*mod
;
1549 if (context
->ucm_devices
) {
1550 /* clear ucm device pointer to mapping */
1551 PA_IDXSET_FOREACH(dev
, context
->ucm_devices
, idx
) {
1552 if (context
->direction
== PA_DIRECTION_OUTPUT
)
1553 dev
->playback_mapping
= NULL
;
1555 dev
->capture_mapping
= NULL
;
1558 pa_idxset_free(context
->ucm_devices
, NULL
);
1561 if (context
->ucm_modifiers
) {
1562 PA_IDXSET_FOREACH(mod
, context
->ucm_modifiers
, idx
) {
1563 if (context
->direction
== PA_DIRECTION_OUTPUT
)
1564 mod
->playback_mapping
= NULL
;
1566 mod
->capture_mapping
= NULL
;
1569 pa_idxset_free(context
->ucm_modifiers
, NULL
);
1573 /* Enable the modifier when the first stream with matched role starts */
1574 void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config
*ucm
, const char *role
, pa_direction_t dir
) {
1575 pa_alsa_ucm_modifier
*mod
;
1577 if (!ucm
->active_verb
)
1580 PA_LLIST_FOREACH(mod
, ucm
->active_verb
->modifiers
) {
1581 if ((mod
->action_direction
== dir
) && (pa_streq(mod
->media_role
, role
))) {
1582 if (mod
->enabled_counter
== 0) {
1583 const char *mod_name
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1585 pa_log_info("Enable ucm modifier %s", mod_name
);
1586 if (snd_use_case_set(ucm
->ucm_mgr
, "_enamod", mod_name
) < 0) {
1587 pa_log("Failed to enable ucm modifier %s", mod_name
);
1591 mod
->enabled_counter
++;
1597 /* Disable the modifier when the last stream with matched role ends */
1598 void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config
*ucm
, const char *role
, pa_direction_t dir
) {
1599 pa_alsa_ucm_modifier
*mod
;
1601 if (!ucm
->active_verb
)
1604 PA_LLIST_FOREACH(mod
, ucm
->active_verb
->modifiers
) {
1605 if ((mod
->action_direction
== dir
) && (pa_streq(mod
->media_role
, role
))) {
1607 mod
->enabled_counter
--;
1608 if (mod
->enabled_counter
== 0) {
1609 const char *mod_name
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1611 pa_log_info("Disable ucm modifier %s", mod_name
);
1612 if (snd_use_case_set(ucm
->ucm_mgr
, "_dismod", mod_name
) < 0) {
1613 pa_log("Failed to disable ucm modifier %s", mod_name
);