1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2011 Wolfson Microelectronics PLC
5 Author Margarita Olaya <magi@slimlogic.co.uk>
6 Copyright 2012 Feng Wei <wei.feng@freescale.com>, Freescale Ltd.
7
8 PulseAudio is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published
10 by the Free Software Foundation; either version 2.1 of the License,
11 or (at your option) any later version.
12
13 PulseAudio is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
20
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <ctype.h>
28 #include <sys/types.h>
29 #include <limits.h>
30 #include <alsa/asoundlib.h>
31
32 #ifdef HAVE_VALGRIND_MEMCHECK_H
33 #include <valgrind/memcheck.h>
34 #endif
35
36 #include <pulse/sample.h>
37 #include <pulse/xmalloc.h>
38 #include <pulse/timeval.h>
39 #include <pulse/util.h>
40
41 #include <pulsecore/log.h>
42 #include <pulsecore/macro.h>
43 #include <pulsecore/core-util.h>
44 #include <pulsecore/atomic.h>
45 #include <pulsecore/core-error.h>
46 #include <pulsecore/once.h>
47 #include <pulsecore/thread.h>
48 #include <pulsecore/conf-parser.h>
49 #include <pulsecore/strbuf.h>
50
51 #include "alsa-mixer.h"
52 #include "alsa-util.h"
53 #include "alsa-ucm.h"
54
55 #define PA_UCM_PRE_TAG_OUTPUT "[Out] "
56 #define PA_UCM_PRE_TAG_INPUT "[In] "
57
58 #define PA_UCM_PLAYBACK_PRIORITY_UNSET(device) ((device)->playback_channels && !(device)->playback_priority)
59 #define PA_UCM_CAPTURE_PRIORITY_UNSET(device) ((device)->capture_channels && !(device)->capture_priority)
60 #define PA_UCM_DEVICE_PRIORITY_SET(device, priority) \
61 do { \
62 if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device)) (device)->playback_priority = (priority); \
63 if (PA_UCM_CAPTURE_PRIORITY_UNSET(device)) (device)->capture_priority = (priority); \
64 } while (0)
65 #define PA_UCM_IS_MODIFIER_MAPPING(m) ((pa_proplist_gets((m)->proplist, PA_ALSA_PROP_UCM_MODIFIER)) != NULL)
66
67 #ifdef HAVE_ALSA_UCM
68
69 struct ucm_type {
70 const char *prefix;
71 pa_device_port_type_t type;
72 };
73
74 struct ucm_items {
75 const char *id;
76 const char *property;
77 };
78
79 struct ucm_info {
80 const char *id;
81 unsigned priority;
82 };
83
84 static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *device);
85 static void device_set_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack);
86 static void device_add_hw_mute_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack);
87
88 static pa_alsa_ucm_device *verb_find_device(pa_alsa_ucm_verb *verb, const char *device_name);
89
90
91 static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port,
92 pa_alsa_ucm_device **devices, unsigned n_devices);
93 static void ucm_port_data_free(pa_device_port *port);
94 static void ucm_port_update_available(pa_alsa_ucm_port_data *port);
95
96 static struct ucm_type types[] = {
97 {"None", PA_DEVICE_PORT_TYPE_UNKNOWN},
98 {"Speaker", PA_DEVICE_PORT_TYPE_SPEAKER},
99 {"Line", PA_DEVICE_PORT_TYPE_LINE},
100 {"Mic", PA_DEVICE_PORT_TYPE_MIC},
101 {"Headphones", PA_DEVICE_PORT_TYPE_HEADPHONES},
102 {"Headset", PA_DEVICE_PORT_TYPE_HEADSET},
103 {"Handset", PA_DEVICE_PORT_TYPE_HANDSET},
104 {"Bluetooth", PA_DEVICE_PORT_TYPE_BLUETOOTH},
105 {"Earpiece", PA_DEVICE_PORT_TYPE_EARPIECE},
106 {"SPDIF", PA_DEVICE_PORT_TYPE_SPDIF},
107 {"HDMI", PA_DEVICE_PORT_TYPE_HDMI},
108 {NULL, 0}
109 };
110
111 static struct ucm_items item[] = {
112 {"PlaybackPCM", PA_ALSA_PROP_UCM_SINK},
113 {"CapturePCM", PA_ALSA_PROP_UCM_SOURCE},
114 {"PlaybackCTL", PA_ALSA_PROP_UCM_PLAYBACK_CTL_DEVICE},
115 {"PlaybackVolume", PA_ALSA_PROP_UCM_PLAYBACK_VOLUME},
116 {"PlaybackSwitch", PA_ALSA_PROP_UCM_PLAYBACK_SWITCH},
117 {"PlaybackMixer", PA_ALSA_PROP_UCM_PLAYBACK_MIXER_DEVICE},
118 {"PlaybackMixerElem", PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM},
119 {"PlaybackMasterElem", PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ELEM},
120 {"PlaybackMasterType", PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE},
121 {"PlaybackPriority", PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY},
122 {"PlaybackRate", PA_ALSA_PROP_UCM_PLAYBACK_RATE},
123 {"PlaybackChannels", PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS},
124 {"CaptureCTL", PA_ALSA_PROP_UCM_CAPTURE_CTL_DEVICE},
125 {"CaptureVolume", PA_ALSA_PROP_UCM_CAPTURE_VOLUME},
126 {"CaptureSwitch", PA_ALSA_PROP_UCM_CAPTURE_SWITCH},
127 {"CaptureMixer", PA_ALSA_PROP_UCM_CAPTURE_MIXER_DEVICE},
128 {"CaptureMixerElem", PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM},
129 {"CaptureMasterElem", PA_ALSA_PROP_UCM_CAPTURE_MASTER_ELEM},
130 {"CaptureMasterType", PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE},
131 {"CapturePriority", PA_ALSA_PROP_UCM_CAPTURE_PRIORITY},
132 {"CaptureRate", PA_ALSA_PROP_UCM_CAPTURE_RATE},
133 {"CaptureChannels", PA_ALSA_PROP_UCM_CAPTURE_CHANNELS},
134 {"TQ", PA_ALSA_PROP_UCM_QOS},
135 {"JackCTL", PA_ALSA_PROP_UCM_JACK_DEVICE},
136 {"JackControl", PA_ALSA_PROP_UCM_JACK_CONTROL},
137 {"JackHWMute", PA_ALSA_PROP_UCM_JACK_HW_MUTE},
138 {NULL, NULL},
139 };
140
141 /* UCM verb info - this should eventually be part of policy manangement */
142 static struct ucm_info verb_info[] = {
143 {SND_USE_CASE_VERB_INACTIVE, 0},
144 {SND_USE_CASE_VERB_HIFI, 8000},
145 {SND_USE_CASE_VERB_HIFI_LOW_POWER, 7000},
146 {SND_USE_CASE_VERB_VOICE, 6000},
147 {SND_USE_CASE_VERB_VOICE_LOW_POWER, 5000},
148 {SND_USE_CASE_VERB_VOICECALL, 4000},
149 {SND_USE_CASE_VERB_IP_VOICECALL, 4000},
150 {SND_USE_CASE_VERB_ANALOG_RADIO, 3000},
151 {SND_USE_CASE_VERB_DIGITAL_RADIO, 3000},
152 {NULL, 0}
153 };
154
155 /* UCM device info - should be overwritten by ucm property */
156 static struct ucm_info dev_info[] = {
157 {SND_USE_CASE_DEV_SPEAKER, 100},
158 {SND_USE_CASE_DEV_LINE, 100},
159 {SND_USE_CASE_DEV_HEADPHONES, 100},
160 {SND_USE_CASE_DEV_HEADSET, 300},
161 {SND_USE_CASE_DEV_HANDSET, 200},
162 {SND_USE_CASE_DEV_BLUETOOTH, 400},
163 {SND_USE_CASE_DEV_EARPIECE, 100},
164 {SND_USE_CASE_DEV_SPDIF, 100},
165 {SND_USE_CASE_DEV_HDMI, 100},
166 {SND_USE_CASE_DEV_NONE, 100},
167 {NULL, 0}
168 };
169
170
ucm_verb_value(snd_use_case_mgr_t * uc_mgr,const char * verb_name,const char * id)171 static char *ucm_verb_value(
172 snd_use_case_mgr_t *uc_mgr,
173 const char *verb_name,
174 const char *id) {
175
176 const char *value;
177 char *_id = pa_sprintf_malloc("=%s//%s", id, verb_name);
178 int err = snd_use_case_get(uc_mgr, _id, &value);
179 pa_xfree(_id);
180 if (err < 0)
181 return NULL;
182 pa_log_debug("Got %s for verb %s: %s", id, verb_name, value);
183 /* Use the cast here to allow free() call without casting for callers.
184 * The snd_use_case_get() returns mallocated string.
185 * See the Note: in use-case.h for snd_use_case_get().
186 */
187 return (char *)value;
188 }
189
ucm_device_exists(pa_idxset * idxset,pa_alsa_ucm_device * dev)190 static int ucm_device_exists(pa_idxset *idxset, pa_alsa_ucm_device *dev) {
191 pa_alsa_ucm_device *d;
192 uint32_t idx;
193
194 PA_IDXSET_FOREACH(d, idxset, idx)
195 if (d == dev)
196 return 1;
197
198 return 0;
199 }
200
ucm_add_devices_to_idxset(pa_idxset * idxset,pa_alsa_ucm_device * me,pa_alsa_ucm_device * devices,const char ** dev_names,int n)201 static void ucm_add_devices_to_idxset(
202 pa_idxset *idxset,
203 pa_alsa_ucm_device *me,
204 pa_alsa_ucm_device *devices,
205 const char **dev_names,
206 int n) {
207
208 pa_alsa_ucm_device *d;
209
210 PA_LLIST_FOREACH(d, devices) {
211 const char *name;
212 int i;
213
214 if (d == me)
215 continue;
216
217 name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
218
219 for (i = 0; i < n; i++)
220 if (pa_streq(dev_names[i], name))
221 pa_idxset_put(idxset, d, NULL);
222 }
223 }
224
225 /* Split a string into words. Like pa_split_spaces() but handle '' and "". */
ucm_split_devnames(const char * c,const char ** state)226 static char *ucm_split_devnames(const char *c, const char **state) {
227 const char *current = *state ? *state : c;
228 char h;
229 size_t l;
230
231 if (!*current || *c == 0)
232 return NULL;
233
234 current += strspn(current, "\n\r \t");
235 h = *current;
236 if (h == '\'' || h =='"') {
237 c = ++current;
238 for (l = 0; *c && *c != h; l++) c++;
239 if (*c != h)
240 return NULL;
241 *state = c + 1;
242 } else {
243 l = strcspn(current, "\n\r \t");
244 *state = current+l;
245 }
246
247 return pa_xstrndup(current, l);
248 }
249
250
ucm_volume_free(pa_alsa_ucm_volume * vol)251 static void ucm_volume_free(pa_alsa_ucm_volume *vol) {
252 pa_assert(vol);
253 pa_xfree(vol->mixer_elem);
254 pa_xfree(vol->master_elem);
255 pa_xfree(vol->master_type);
256 pa_xfree(vol);
257 }
258
259 /* Get the volume identifier */
ucm_get_mixer_id(pa_alsa_ucm_device * device,const char * mprop,const char * cprop,const char * cid)260 static char *ucm_get_mixer_id(
261 pa_alsa_ucm_device *device,
262 const char *mprop,
263 const char *cprop,
264 const char *cid)
265 {
266 #if SND_LIB_VERSION >= 0x10201 /* alsa-lib-1.2.1+ check */
267 snd_ctl_elem_id_t *ctl;
268 int err;
269 #endif
270 const char *value;
271 char *value2;
272 int index;
273
274 /* mixer element as first, if it's found, return it without modifications */
275 value = pa_proplist_gets(device->proplist, mprop);
276 if (value)
277 return pa_xstrdup(value);
278 /* fallback, get the control element identifier */
279 /* and try to do some heuristic to determine the mixer element name */
280 value = pa_proplist_gets(device->proplist, cprop);
281 if (value == NULL)
282 return NULL;
283 #if SND_LIB_VERSION >= 0x10201 /* alsa-lib-1.2.1+ check */
284 /* The new parser may return also element index. */
285 snd_ctl_elem_id_alloca(&ctl);
286 err = snd_use_case_parse_ctl_elem_id(ctl, cid, value);
287 if (err < 0)
288 return NULL;
289 value = snd_ctl_elem_id_get_name(ctl);
290 index = snd_ctl_elem_id_get_index(ctl);
291 #else
292 #warning "Upgrade to alsa-lib 1.2.1!"
293 index = 0;
294 #endif
295 if (!(value2 = pa_str_strip_suffix(value, " Playback Volume")))
296 if (!(value2 = pa_str_strip_suffix(value, " Capture Volume")))
297 if (!(value2 = pa_str_strip_suffix(value, " Volume")))
298 value2 = pa_xstrdup(value);
299 if (index > 0) {
300 char *mix = pa_sprintf_malloc("'%s',%d", value2, index);
301 pa_xfree(value2);
302 return mix;
303 }
304 return value2;
305 }
306
307 /* Get the volume identifier */
ucm_get_mixer_volume(pa_alsa_ucm_device * device,const char * mprop,const char * cprop,const char * cid,const char * masterid,const char * mastertype)308 static pa_alsa_ucm_volume *ucm_get_mixer_volume(
309 pa_alsa_ucm_device *device,
310 const char *mprop,
311 const char *cprop,
312 const char *cid,
313 const char *masterid,
314 const char *mastertype)
315 {
316 pa_alsa_ucm_volume *vol;
317 char *mixer_elem;
318
319 mixer_elem = ucm_get_mixer_id(device, mprop, cprop, cid);
320 if (mixer_elem == NULL)
321 return NULL;
322 vol = pa_xnew0(pa_alsa_ucm_volume, 1);
323 if (vol == NULL) {
324 pa_xfree(mixer_elem);
325 return NULL;
326 }
327 vol->mixer_elem = mixer_elem;
328 vol->master_elem = pa_xstrdup(pa_proplist_gets(device->proplist, masterid));
329 vol->master_type = pa_xstrdup(pa_proplist_gets(device->proplist, mastertype));
330 return vol;
331 }
332
333 /* Get the ALSA mixer device for the UCM device */
get_mixer_device(pa_alsa_ucm_device * dev,bool is_sink)334 static const char *get_mixer_device(pa_alsa_ucm_device *dev, bool is_sink)
335 {
336 const char *dev_name;
337
338 if (is_sink) {
339 dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_PLAYBACK_MIXER_DEVICE);
340 if (!dev_name)
341 dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CTL_DEVICE);
342 } else {
343 dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_CAPTURE_MIXER_DEVICE);
344 if (!dev_name)
345 dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_CAPTURE_CTL_DEVICE);
346 }
347 return dev_name;
348 }
349
350 /* Get the ALSA mixer device for the UCM jack */
get_jack_mixer_device(pa_alsa_ucm_device * dev,bool is_sink)351 static const char *get_jack_mixer_device(pa_alsa_ucm_device *dev, bool is_sink) {
352 const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_JACK_DEVICE);
353 if (!dev_name)
354 return get_mixer_device(dev, is_sink);
355 return dev_name;
356 }
357
358 /* Create a property list for this ucm device */
ucm_get_device_property(pa_alsa_ucm_device * device,snd_use_case_mgr_t * uc_mgr,pa_alsa_ucm_verb * verb,const char * device_name)359 static int ucm_get_device_property(
360 pa_alsa_ucm_device *device,
361 snd_use_case_mgr_t *uc_mgr,
362 pa_alsa_ucm_verb *verb,
363 const char *device_name) {
364
365 const char *value;
366 const char **devices;
367 char *id, *s;
368 int i;
369 int err;
370 uint32_t ui;
371 int n_confdev, n_suppdev;
372 pa_alsa_ucm_volume *vol;
373
374 /* determine the device type */
375 device->type = PA_DEVICE_PORT_TYPE_UNKNOWN;
376 id = s = pa_xstrdup(device_name);
377 while (s && *s && isalpha(*s)) s++;
378 if (s)
379 *s = '\0';
380 for (i = 0; types[i].prefix; i++)
381 if (pa_streq(id, types[i].prefix)) {
382 device->type = types[i].type;
383 break;
384 }
385 pa_xfree(id);
386
387 /* set properties */
388 for (i = 0; item[i].id; i++) {
389 id = pa_sprintf_malloc("%s/%s", item[i].id, device_name);
390 err = snd_use_case_get(uc_mgr, id, &value);
391 pa_xfree(id);
392 if (err < 0)
393 continue;
394
395 pa_log_debug("Got %s for device %s: %s", item[i].id, device_name, value);
396 pa_proplist_sets(device->proplist, item[i].property, value);
397 free((void*)value);
398 }
399
400 /* get direction and channels */
401 value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS);
402 if (value) { /* output */
403 /* get channels */
404 if (pa_atou(value, &ui) == 0 && pa_channels_valid(ui))
405 device->playback_channels = ui;
406 else
407 pa_log("UCM playback channels %s for device %s out of range", value, device_name);
408
409 /* get pcm */
410 value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SINK);
411 if (!value) /* take pcm from verb playback default */
412 pa_log("UCM playback device %s fetch pcm failed", device_name);
413 }
414
415 if (pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SINK) &&
416 device->playback_channels == 0) {
417 pa_log_info("UCM file does not specify 'PlaybackChannels' "
418 "for device %s, assuming stereo.", device_name);
419 device->playback_channels = 2;
420 }
421
422 value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS);
423 if (value) { /* input */
424 /* get channels */
425 if (pa_atou(value, &ui) == 0 && pa_channels_valid(ui))
426 device->capture_channels = ui;
427 else
428 pa_log("UCM capture channels %s for device %s out of range", value, device_name);
429
430 /* get pcm */
431 value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SOURCE);
432 if (!value) /* take pcm from verb capture default */
433 pa_log("UCM capture device %s fetch pcm failed", device_name);
434 }
435
436 if (pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SOURCE) &&
437 device->capture_channels == 0) {
438 pa_log_info("UCM file does not specify 'CaptureChannels' "
439 "for device %s, assuming stereo.", device_name);
440 device->capture_channels = 2;
441 }
442
443 /* get rate and priority of device */
444 if (device->playback_channels) { /* sink device */
445 /* get rate */
446 if ((value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_RATE))) {
447 if (pa_atou(value, &ui) == 0 && pa_sample_rate_valid(ui)) {
448 pa_log_debug("UCM playback device %s rate %d", device_name, ui);
449 device->playback_rate = ui;
450 } else
451 pa_log_debug("UCM playback device %s has bad rate %s", device_name, value);
452 }
453
454 value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY);
455 if (value) {
456 /* get priority from ucm config */
457 if (pa_atou(value, &ui) == 0)
458 device->playback_priority = ui;
459 else
460 pa_log_debug("UCM playback priority %s for device %s error", value, device_name);
461 }
462
463 vol = ucm_get_mixer_volume(device,
464 PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM,
465 PA_ALSA_PROP_UCM_PLAYBACK_VOLUME,
466 "PlaybackVolume",
467 PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ELEM,
468 PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE);
469 if (vol)
470 pa_hashmap_put(device->playback_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), vol);
471 }
472
473 if (device->capture_channels) { /* source device */
474 /* get rate */
475 if ((value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_RATE))) {
476 if (pa_atou(value, &ui) == 0 && pa_sample_rate_valid(ui)) {
477 pa_log_debug("UCM capture device %s rate %d", device_name, ui);
478 device->capture_rate = ui;
479 } else
480 pa_log_debug("UCM capture device %s has bad rate %s", device_name, value);
481 }
482
483 value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_PRIORITY);
484 if (value) {
485 /* get priority from ucm config */
486 if (pa_atou(value, &ui) == 0)
487 device->capture_priority = ui;
488 else
489 pa_log_debug("UCM capture priority %s for device %s error", value, device_name);
490 }
491
492 vol = ucm_get_mixer_volume(device,
493 PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM,
494 PA_ALSA_PROP_UCM_CAPTURE_VOLUME,
495 "CaptureVolume",
496 PA_ALSA_PROP_UCM_CAPTURE_MASTER_ELEM,
497 PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE);
498 if (vol)
499 pa_hashmap_put(device->capture_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), vol);
500 }
501
502 if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device) || PA_UCM_CAPTURE_PRIORITY_UNSET(device)) {
503 /* get priority from static table */
504 for (i = 0; dev_info[i].id; i++) {
505 if (strcasecmp(dev_info[i].id, device_name) == 0) {
506 PA_UCM_DEVICE_PRIORITY_SET(device, dev_info[i].priority);
507 break;
508 }
509 }
510 }
511
512 if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device)) {
513 /* fall through to default priority */
514 device->playback_priority = 100;
515 }
516
517 if (PA_UCM_CAPTURE_PRIORITY_UNSET(device)) {
518 /* fall through to default priority */
519 device->capture_priority = 100;
520 }
521
522 id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", device_name);
523 n_confdev = snd_use_case_get_list(uc_mgr, id, &devices);
524 pa_xfree(id);
525
526 if (n_confdev <= 0)
527 pa_log_debug("No %s for device %s", "_conflictingdevs", device_name);
528 else {
529 device->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
530 ucm_add_devices_to_idxset(device->conflicting_devices, device, verb->devices, devices, n_confdev);
531 snd_use_case_free_list(devices, n_confdev);
532 }
533
534 id = pa_sprintf_malloc("%s/%s", "_supporteddevs", device_name);
535 n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices);
536 pa_xfree(id);
537
538 if (n_suppdev <= 0)
539 pa_log_debug("No %s for device %s", "_supporteddevs", device_name);
540 else {
541 device->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
542 ucm_add_devices_to_idxset(device->supported_devices, device, verb->devices, devices, n_suppdev);
543 snd_use_case_free_list(devices, n_suppdev);
544 }
545
546 return 0;
547 };
548
549 /* Create a property list for this ucm modifier */
ucm_get_modifier_property(pa_alsa_ucm_modifier * modifier,snd_use_case_mgr_t * uc_mgr,const char * modifier_name)550 static int ucm_get_modifier_property(pa_alsa_ucm_modifier *modifier, snd_use_case_mgr_t *uc_mgr, const char *modifier_name) {
551 const char *value;
552 char *id;
553 int i;
554
555 for (i = 0; item[i].id; i++) {
556 int err;
557
558 id = pa_sprintf_malloc("=%s/%s", item[i].id, modifier_name);
559 err = snd_use_case_get(uc_mgr, id, &value);
560 pa_xfree(id);
561 if (err < 0)
562 continue;
563
564 pa_log_debug("Got %s for modifier %s: %s", item[i].id, modifier_name, value);
565 pa_proplist_sets(modifier->proplist, item[i].property, value);
566 free((void*)value);
567 }
568
569 id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", modifier_name);
570 modifier->n_confdev = snd_use_case_get_list(uc_mgr, id, &modifier->conflicting_devices);
571 pa_xfree(id);
572 if (modifier->n_confdev < 0)
573 pa_log_debug("No %s for modifier %s", "_conflictingdevs", modifier_name);
574
575 id = pa_sprintf_malloc("%s/%s", "_supporteddevs", modifier_name);
576 modifier->n_suppdev = snd_use_case_get_list(uc_mgr, id, &modifier->supported_devices);
577 pa_xfree(id);
578 if (modifier->n_suppdev < 0)
579 pa_log_debug("No %s for modifier %s", "_supporteddevs", modifier_name);
580
581 return 0;
582 };
583
584 /* Create a list of devices for this verb */
ucm_get_devices(pa_alsa_ucm_verb * verb,snd_use_case_mgr_t * uc_mgr)585 static int ucm_get_devices(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) {
586 const char **dev_list;
587 int num_dev, i;
588
589 num_dev = snd_use_case_get_list(uc_mgr, "_devices", &dev_list);
590 if (num_dev < 0)
591 return num_dev;
592
593 for (i = 0; i < num_dev; i += 2) {
594 pa_alsa_ucm_device *d = pa_xnew0(pa_alsa_ucm_device, 1);
595
596 d->proplist = pa_proplist_new();
597 pa_proplist_sets(d->proplist, PA_ALSA_PROP_UCM_NAME, pa_strnull(dev_list[i]));
598 pa_proplist_sets(d->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(dev_list[i + 1]));
599 d->ucm_ports = pa_dynarray_new(NULL);
600 d->hw_mute_jacks = pa_dynarray_new(NULL);
601 d->available = PA_AVAILABLE_UNKNOWN;
602
603 d->playback_volumes = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree,
604 (pa_free_cb_t) ucm_volume_free);
605 d->capture_volumes = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree,
606 (pa_free_cb_t) ucm_volume_free);
607
608 PA_LLIST_PREPEND(pa_alsa_ucm_device, verb->devices, d);
609 }
610
611 snd_use_case_free_list(dev_list, num_dev);
612
613 return 0;
614 };
615
ucm_get_modifiers(pa_alsa_ucm_verb * verb,snd_use_case_mgr_t * uc_mgr)616 static int ucm_get_modifiers(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) {
617 const char **mod_list;
618 int num_mod, i;
619
620 num_mod = snd_use_case_get_list(uc_mgr, "_modifiers", &mod_list);
621 if (num_mod < 0)
622 return num_mod;
623
624 for (i = 0; i < num_mod; i += 2) {
625 pa_alsa_ucm_modifier *m;
626
627 if (!mod_list[i]) {
628 pa_log_warn("Got a modifier with a null name. Skipping.");
629 continue;
630 }
631
632 m = pa_xnew0(pa_alsa_ucm_modifier, 1);
633 m->proplist = pa_proplist_new();
634
635 pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_NAME, mod_list[i]);
636 pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(mod_list[i + 1]));
637
638 PA_LLIST_PREPEND(pa_alsa_ucm_modifier, verb->modifiers, m);
639 }
640
641 snd_use_case_free_list(mod_list, num_mod);
642
643 return 0;
644 };
645
add_role_to_device(pa_alsa_ucm_device * dev,const char * dev_name,const char * role_name,const char * role)646 static void add_role_to_device(pa_alsa_ucm_device *dev, const char *dev_name, const char *role_name, const char *role) {
647 const char *cur = pa_proplist_gets(dev->proplist, role_name);
648
649 if (!cur)
650 pa_proplist_sets(dev->proplist, role_name, role);
651 else if (!pa_str_in_list_spaces(cur, role)) { /* does not exist */
652 char *value = pa_sprintf_malloc("%s %s", cur, role);
653
654 pa_proplist_sets(dev->proplist, role_name, value);
655 pa_xfree(value);
656 }
657
658 pa_log_info("Add role %s to device %s(%s), result %s", role, dev_name, role_name, pa_proplist_gets(dev->proplist,
659 role_name));
660 }
661
add_media_role(const char * name,pa_alsa_ucm_device * list,const char * role_name,const char * role,bool is_sink)662 static void add_media_role(const char *name, pa_alsa_ucm_device *list, const char *role_name, const char *role, bool is_sink) {
663 pa_alsa_ucm_device *d;
664
665 PA_LLIST_FOREACH(d, list) {
666 const char *dev_name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
667
668 if (pa_streq(dev_name, name)) {
669 const char *sink = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SINK);
670 const char *source = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SOURCE);
671
672 if (is_sink && sink)
673 add_role_to_device(d, dev_name, role_name, role);
674 else if (!is_sink && source)
675 add_role_to_device(d, dev_name, role_name, role);
676 break;
677 }
678 }
679 }
680
modifier_name_to_role(const char * mod_name,bool * is_sink)681 static char *modifier_name_to_role(const char *mod_name, bool *is_sink) {
682 char *sub = NULL, *tmp;
683
684 *is_sink = false;
685
686 if (pa_startswith(mod_name, "Play")) {
687 *is_sink = true;
688 sub = pa_xstrdup(mod_name + 4);
689 } else if (pa_startswith(mod_name, "Capture"))
690 sub = pa_xstrdup(mod_name + 7);
691
692 if (!sub || !*sub) {
693 pa_xfree(sub);
694 pa_log_warn("Can't match media roles for modifier %s", mod_name);
695 return NULL;
696 }
697
698 tmp = sub;
699
700 do {
701 *tmp = tolower(*tmp);
702 } while (*(++tmp));
703
704 return sub;
705 }
706
ucm_set_media_roles(pa_alsa_ucm_modifier * modifier,pa_alsa_ucm_device * list,const char * mod_name)707 static void ucm_set_media_roles(pa_alsa_ucm_modifier *modifier, pa_alsa_ucm_device *list, const char *mod_name) {
708 int i;
709 bool is_sink = false;
710 char *sub = NULL;
711 const char *role_name;
712
713 sub = modifier_name_to_role(mod_name, &is_sink);
714 if (!sub)
715 return;
716
717 modifier->action_direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
718 modifier->media_role = sub;
719
720 role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES;
721 for (i = 0; i < modifier->n_suppdev; i++) {
722 /* if modifier has no specific pcm, we add role intent to its supported devices */
723 if (!pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SINK) &&
724 !pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SOURCE))
725 add_media_role(modifier->supported_devices[i], list, role_name, sub, is_sink);
726 }
727 }
728
append_lost_relationship(pa_alsa_ucm_device * dev)729 static void append_lost_relationship(pa_alsa_ucm_device *dev) {
730 uint32_t idx;
731 pa_alsa_ucm_device *d;
732
733 if (dev->conflicting_devices) {
734 PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) {
735 if (!d->conflicting_devices)
736 d->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
737
738 if (pa_idxset_put(d->conflicting_devices, dev, NULL) == 0)
739 pa_log_warn("Add lost conflicting device %s to %s",
740 pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME),
741 pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME));
742 }
743 }
744
745 if (dev->supported_devices) {
746 PA_IDXSET_FOREACH(d, dev->supported_devices, idx) {
747 if (!d->supported_devices)
748 d->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
749
750 if (pa_idxset_put(d->supported_devices, dev, NULL) == 0)
751 pa_log_warn("Add lost supported device %s to %s",
752 pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME),
753 pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME));
754 }
755 }
756 }
757
pa_alsa_ucm_query_profiles(pa_alsa_ucm_config * ucm,int card_index)758 int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index) {
759 char *card_name;
760 const char **verb_list, *value;
761 int num_verbs, i, err = 0;
762
763 /* support multiple card instances, address card directly by index */
764 card_name = pa_sprintf_malloc("hw:%i", card_index);
765 err = snd_use_case_mgr_open(&ucm->ucm_mgr, card_name);
766 if (err < 0) {
767 /* fallback longname: is UCM available for this card ? */
768 pa_xfree(card_name);
769 err = snd_card_get_name(card_index, &card_name);
770 if (err < 0) {
771 pa_log("Card can't get card_name from card_index %d", card_index);
772 err = -PA_ALSA_ERR_UNSPECIFIED;
773 goto name_fail;
774 }
775
776 err = snd_use_case_mgr_open(&ucm->ucm_mgr, card_name);
777 if (err < 0) {
778 pa_log_info("UCM not available for card %s", card_name);
779 err = -PA_ALSA_ERR_UCM_OPEN;
780 goto ucm_mgr_fail;
781 }
782 }
783
784 err = snd_use_case_get(ucm->ucm_mgr, "=Linked", &value);
785 if (err >= 0) {
786 if (strcasecmp(value, "true") == 0 || strcasecmp(value, "1") == 0) {
787 free((void *)value);
788 pa_log_info("Empty (linked) UCM for card %s", card_name);
789 err = -PA_ALSA_ERR_UCM_LINKED;
790 goto ucm_verb_fail;
791 }
792 free((void *)value);
793 }
794
795 pa_log_info("UCM available for card %s", card_name);
796
797 if (snd_use_case_get(ucm->ucm_mgr, "_alibpref", &value) == 0) {
798 if (value[0]) {
799 ucm->alib_prefix = pa_xstrdup(value);
800 pa_log_debug("UCM _alibpref=%s", ucm->alib_prefix);
801 }
802 free((void *)value);
803 }
804
805 /* get a list of all UCM verbs (profiles) for this card */
806 num_verbs = snd_use_case_verb_list(ucm->ucm_mgr, &verb_list);
807 if (num_verbs < 0) {
808 pa_log("UCM verb list not found for %s", card_name);
809 err = -PA_ALSA_ERR_UNSPECIFIED;
810 goto ucm_verb_fail;
811 }
812
813 /* get the properties of each UCM verb */
814 for (i = 0; i < num_verbs; i += 2) {
815 pa_alsa_ucm_verb *verb;
816
817 /* Get devices and modifiers for each verb */
818 err = pa_alsa_ucm_get_verb(ucm->ucm_mgr, verb_list[i], verb_list[i+1], &verb);
819 if (err < 0) {
820 pa_log("Failed to get the verb %s", verb_list[i]);
821 continue;
822 }
823
824 PA_LLIST_PREPEND(pa_alsa_ucm_verb, ucm->verbs, verb);
825 }
826
827 if (!ucm->verbs) {
828 pa_log("No UCM verb is valid for %s", card_name);
829 err = -PA_ALSA_ERR_UCM_NO_VERB;
830 }
831
832 snd_use_case_free_list(verb_list, num_verbs);
833
834 ucm_verb_fail:
835 if (err < 0) {
836 snd_use_case_mgr_close(ucm->ucm_mgr);
837 ucm->ucm_mgr = NULL;
838 }
839
840 ucm_mgr_fail:
841 pa_xfree(card_name);
842
843 name_fail:
844 return err;
845 }
846
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)847 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) {
848 pa_alsa_ucm_device *d;
849 pa_alsa_ucm_modifier *mod;
850 pa_alsa_ucm_verb *verb;
851 char *value;
852 unsigned ui;
853 int err = 0;
854
855 *p_verb = NULL;
856 pa_log_info("Set UCM verb to %s", verb_name);
857 err = snd_use_case_set(uc_mgr, "_verb", verb_name);
858 if (err < 0)
859 return err;
860
861 verb = pa_xnew0(pa_alsa_ucm_verb, 1);
862 verb->proplist = pa_proplist_new();
863
864 pa_proplist_sets(verb->proplist, PA_ALSA_PROP_UCM_NAME, pa_strnull(verb_name));
865 pa_proplist_sets(verb->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(verb_desc));
866
867 value = ucm_verb_value(uc_mgr, verb_name, "Priority");
868 if (value && !pa_atou(value, &ui))
869 verb->priority = ui > 10000 ? 10000 : ui;
870 free(value);
871
872 err = ucm_get_devices(verb, uc_mgr);
873 if (err < 0)
874 pa_log("No UCM devices for verb %s", verb_name);
875
876 err = ucm_get_modifiers(verb, uc_mgr);
877 if (err < 0)
878 pa_log("No UCM modifiers for verb %s", verb_name);
879
880 PA_LLIST_FOREACH(d, verb->devices) {
881 const char *dev_name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
882
883 /* Devices properties */
884 ucm_get_device_property(d, uc_mgr, verb, dev_name);
885 }
886 /* make conflicting or supported device mutual */
887 PA_LLIST_FOREACH(d, verb->devices)
888 append_lost_relationship(d);
889
890 PA_LLIST_FOREACH(mod, verb->modifiers) {
891 const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
892
893 /* Modifier properties */
894 ucm_get_modifier_property(mod, uc_mgr, mod_name);
895
896 /* Set PA_PROP_DEVICE_INTENDED_ROLES property to devices */
897 pa_log_debug("Set media roles for verb %s, modifier %s", verb_name, mod_name);
898 ucm_set_media_roles(mod, verb->devices, mod_name);
899 }
900
901 *p_verb = verb;
902 return 0;
903 }
904
pa_alsa_ucm_device_cmp(const void * a,const void * b)905 static int pa_alsa_ucm_device_cmp(const void *a, const void *b) {
906 const pa_alsa_ucm_device *d1 = *(pa_alsa_ucm_device **)a;
907 const pa_alsa_ucm_device *d2 = *(pa_alsa_ucm_device **)b;
908
909 return strcmp(pa_proplist_gets(d1->proplist, PA_ALSA_PROP_UCM_NAME), pa_proplist_gets(d2->proplist, PA_ALSA_PROP_UCM_NAME));
910 }
911
set_eld_devices(pa_hashmap * hash)912 static void set_eld_devices(pa_hashmap *hash)
913 {
914 pa_device_port *port;
915 pa_alsa_ucm_port_data *data;
916 pa_alsa_ucm_device *dev;
917 const char *eld_mixer_device_name;
918 void *state;
919 int idx, eld_device;
920
921 PA_HASHMAP_FOREACH(port, hash, state) {
922 data = PA_DEVICE_PORT_DATA(port);
923 eld_mixer_device_name = NULL;
924 eld_device = -1;
925 PA_DYNARRAY_FOREACH(dev, data->devices, idx) {
926 if (dev->eld_device >= 0 && dev->eld_mixer_device_name) {
927 if (eld_device >= 0 && eld_device != dev->eld_device) {
928 pa_log_error("The ELD device is already set!");
929 } else if (eld_mixer_device_name && pa_streq(dev->eld_mixer_device_name, eld_mixer_device_name)) {
930 pa_log_error("The ELD mixer device is already set (%s, %s)!", dev->eld_mixer_device_name, dev->eld_mixer_device_name);
931 } else {
932 eld_mixer_device_name = dev->eld_mixer_device_name;
933 eld_device = dev->eld_device;
934 }
935 }
936 }
937 data->eld_device = eld_device;
938 data->eld_mixer_device_name = pa_xstrdup(eld_mixer_device_name);
939 }
940 }
941
update_mixer_paths(pa_hashmap * ports,const char * profile)942 static void update_mixer_paths(pa_hashmap *ports, const char *profile) {
943 pa_device_port *port;
944 pa_alsa_ucm_port_data *data;
945 void *state;
946
947 /* select volume controls on ports */
948 PA_HASHMAP_FOREACH(port, ports, state) {
949 pa_log_info("Updating mixer path for %s: %s", profile, port->name);
950 data = PA_DEVICE_PORT_DATA(port);
951 data->path = pa_hashmap_get(data->paths, profile);
952 }
953 }
954
probe_volumes(pa_hashmap * hash,bool is_sink,snd_pcm_t * pcm_handle,pa_hashmap * mixers,bool ignore_dB)955 static void probe_volumes(pa_hashmap *hash, bool is_sink, snd_pcm_t *pcm_handle, pa_hashmap *mixers, bool ignore_dB) {
956 pa_device_port *port;
957 pa_alsa_path *path;
958 pa_alsa_ucm_port_data *data;
959 pa_alsa_ucm_device *dev;
960 snd_mixer_t *mixer_handle;
961 const char *profile, *mdev, *mdev2;
962 void *state, *state2;
963 int idx;
964
965 PA_HASHMAP_FOREACH(port, hash, state) {
966 data = PA_DEVICE_PORT_DATA(port);
967
968 mdev = NULL;
969 PA_DYNARRAY_FOREACH(dev, data->devices, idx) {
970 mdev2 = get_mixer_device(dev, is_sink);
971 if (mdev && mdev2 && !pa_streq(mdev, mdev2)) {
972 pa_log_error("Two mixer device names found ('%s', '%s'), using s/w volume", mdev, mdev2);
973 goto fail;
974 }
975 if (mdev2)
976 mdev = mdev2;
977 }
978
979 if (mdev == NULL || !(mixer_handle = pa_alsa_open_mixer_by_name(mixers, mdev, true))) {
980 pa_log_error("Failed to find a working mixer device (%s).", mdev);
981 goto fail;
982 }
983
984 PA_HASHMAP_FOREACH_KV(profile, path, data->paths, state2) {
985 if (pa_alsa_path_probe(path, NULL, mixer_handle, ignore_dB) < 0) {
986 pa_log_warn("Could not probe path: %s, using s/w volume", path->name);
987 pa_hashmap_remove(data->paths, profile);
988 } else if (!path->has_volume && !path->has_mute) {
989 pa_log_warn("Path %s is not a volume or mute control", path->name);
990 pa_hashmap_remove(data->paths, profile);
991 } else
992 pa_log_debug("Set up h/w %s using '%s' for %s:%s", path->has_volume ? "volume" : "mute",
993 path->name, profile, port->name);
994 }
995 }
996
997 return;
998
999 fail:
1000 /* We could not probe the paths we created. Free them and revert to software volumes. */
1001 PA_HASHMAP_FOREACH(port, hash, state) {
1002 data = PA_DEVICE_PORT_DATA(port);
1003 pa_hashmap_remove_all(data->paths);
1004 }
1005 }
1006
ucm_add_port_combination(pa_hashmap * hash,pa_alsa_ucm_mapping_context * context,bool is_sink,pa_alsa_ucm_device ** pdevices,int num,pa_hashmap * ports,pa_card_profile * cp,pa_core * core)1007 static void ucm_add_port_combination(
1008 pa_hashmap *hash,
1009 pa_alsa_ucm_mapping_context *context,
1010 bool is_sink,
1011 pa_alsa_ucm_device **pdevices,
1012 int num,
1013 pa_hashmap *ports,
1014 pa_card_profile *cp,
1015 pa_core *core) {
1016
1017 pa_device_port *port;
1018 int i;
1019 unsigned priority;
1020 double prio2;
1021 char *name, *desc;
1022 const char *dev_name;
1023 const char *direction;
1024 const char *profile;
1025 pa_alsa_ucm_device *sorted[num], *dev;
1026 pa_alsa_ucm_port_data *data;
1027 pa_alsa_ucm_volume *vol;
1028 pa_alsa_jack *jack, *jack2;
1029 pa_device_port_type_t type, type2;
1030 void *state;
1031
1032 for (i = 0; i < num; i++)
1033 sorted[i] = pdevices[i];
1034
1035 /* Sort by alphabetical order so as to have a deterministic naming scheme
1036 * for combination ports */
1037 qsort(&sorted[0], num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp);
1038
1039 dev = sorted[0];
1040 dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
1041
1042 name = pa_sprintf_malloc("%s%s", is_sink ? PA_UCM_PRE_TAG_OUTPUT : PA_UCM_PRE_TAG_INPUT, dev_name);
1043 desc = num == 1 ? pa_xstrdup(pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION))
1044 : pa_sprintf_malloc("Combination port for %s", dev_name);
1045
1046 priority = is_sink ? dev->playback_priority : dev->capture_priority;
1047 prio2 = (priority == 0 ? 0 : 1.0/priority);
1048 jack = ucm_get_jack(context->ucm, dev);
1049 type = dev->type;
1050
1051 for (i = 1; i < num; i++) {
1052 char *tmp;
1053
1054 dev = sorted[i];
1055 dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
1056
1057 tmp = pa_sprintf_malloc("%s+%s", name, dev_name);
1058 pa_xfree(name);
1059 name = tmp;
1060
1061 tmp = pa_sprintf_malloc("%s,%s", desc, dev_name);
1062 pa_xfree(desc);
1063 desc = tmp;
1064
1065 priority = is_sink ? dev->playback_priority : dev->capture_priority;
1066 if (priority != 0 && prio2 > 0)
1067 prio2 += 1.0/priority;
1068
1069 jack2 = ucm_get_jack(context->ucm, dev);
1070 if (jack2) {
1071 if (jack && jack != jack2)
1072 pa_log_warn("Multiple jacks per combined device '%s': '%s' '%s'", name, jack->name, jack2->name);
1073 jack = jack2;
1074 }
1075
1076 type2 = dev->type;
1077 if (type2 != PA_DEVICE_PORT_TYPE_UNKNOWN) {
1078 if (type != PA_DEVICE_PORT_TYPE_UNKNOWN && type != type2)
1079 pa_log_warn("Multiple device types per combined device '%s': %d %d", name, type, type2);
1080 type = type2;
1081 }
1082 }
1083
1084 /* Make combination ports always have lower priority, and use the formula
1085 1/p = 1/p1 + 1/p2 + ... 1/pn.
1086 This way, the result will always be less than the individual components,
1087 yet higher components will lead to higher result. */
1088
1089 if (num > 1)
1090 priority = prio2 > 0 ? 1.0/prio2 : 0;
1091
1092 port = pa_hashmap_get(ports, name);
1093 if (!port) {
1094 pa_device_port_new_data port_data;
1095
1096 pa_device_port_new_data_init(&port_data);
1097 pa_device_port_new_data_set_name(&port_data, name);
1098 pa_device_port_new_data_set_description(&port_data, desc);
1099 pa_device_port_new_data_set_type(&port_data, type);
1100 pa_device_port_new_data_set_direction(&port_data, is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT);
1101 if (jack)
1102 pa_device_port_new_data_set_availability_group(&port_data, jack->name);
1103
1104 port = pa_device_port_new(core, &port_data, sizeof(pa_alsa_ucm_port_data));
1105 pa_device_port_new_data_done(&port_data);
1106
1107 data = PA_DEVICE_PORT_DATA(port);
1108 ucm_port_data_init(data, context->ucm, port, pdevices, num);
1109 port->impl_free = ucm_port_data_free;
1110
1111 pa_hashmap_put(ports, port->name, port);
1112 pa_log_debug("Add port %s: %s", port->name, port->description);
1113
1114 if (num == 1) {
1115 /* To keep things simple and not worry about stacking controls, we only support hardware volumes on non-combination
1116 * ports. */
1117 PA_HASHMAP_FOREACH_KV(profile, vol, is_sink ? dev->playback_volumes : dev->capture_volumes, state) {
1118 pa_alsa_path *path = pa_alsa_path_synthesize(vol->mixer_elem,
1119 is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT);
1120
1121 if (!path)
1122 pa_log_warn("Failed to set up volume control: %s", vol->mixer_elem);
1123 else {
1124 if (vol->master_elem) {
1125 pa_alsa_element *e = pa_alsa_element_get(path, vol->master_elem, false);
1126 e->switch_use = PA_ALSA_SWITCH_MUTE;
1127 e->volume_use = PA_ALSA_VOLUME_MERGE;
1128 }
1129
1130 pa_hashmap_put(data->paths, pa_xstrdup(profile), path);
1131
1132 /* Add path also to already created empty path set */
1133 dev = sorted[0];
1134 if (is_sink)
1135 pa_hashmap_put(dev->playback_mapping->output_path_set->paths, pa_xstrdup(vol->mixer_elem), path);
1136 else
1137 pa_hashmap_put(dev->capture_mapping->input_path_set->paths, pa_xstrdup(vol->mixer_elem), path);
1138 }
1139 }
1140 }
1141 }
1142
1143 port->priority = priority;
1144
1145 pa_xfree(name);
1146 pa_xfree(desc);
1147
1148 direction = is_sink ? "output" : "input";
1149 pa_log_debug("Port %s direction %s, priority %d", port->name, direction, priority);
1150
1151 if (cp) {
1152 pa_log_debug("Adding profile %s to port %s.", cp->name, port->name);
1153 pa_hashmap_put(port->profiles, cp->name, cp);
1154 }
1155
1156 if (hash) {
1157 pa_hashmap_put(hash, port->name, port);
1158 pa_device_port_ref(port);
1159 }
1160 }
1161
ucm_port_contains(const char * port_name,const char * dev_name,bool is_sink)1162 static int ucm_port_contains(const char *port_name, const char *dev_name, bool is_sink) {
1163 int ret = 0;
1164 const char *r;
1165 const char *state = NULL;
1166 size_t len;
1167
1168 if (!port_name || !dev_name)
1169 return false;
1170
1171 port_name += is_sink ? strlen(PA_UCM_PRE_TAG_OUTPUT) : strlen(PA_UCM_PRE_TAG_INPUT);
1172
1173 while ((r = pa_split_in_place(port_name, "+", &len, &state))) {
1174 if (strlen(dev_name) == len && !strncmp(r, dev_name, len)) {
1175 ret = 1;
1176 break;
1177 }
1178 }
1179
1180 return ret;
1181 }
1182
ucm_check_conformance(pa_alsa_ucm_mapping_context * context,pa_alsa_ucm_device ** pdevices,int dev_num,pa_alsa_ucm_device * dev)1183 static int ucm_check_conformance(
1184 pa_alsa_ucm_mapping_context *context,
1185 pa_alsa_ucm_device **pdevices,
1186 int dev_num,
1187 pa_alsa_ucm_device *dev) {
1188
1189 uint32_t idx;
1190 pa_alsa_ucm_device *d;
1191 int i;
1192
1193 pa_assert(dev);
1194
1195 pa_log_debug("Check device %s conformance with %d other devices",
1196 pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME), dev_num);
1197 if (dev_num == 0) {
1198 pa_log_debug("First device in combination, number 1");
1199 return 1;
1200 }
1201
1202 if (dev->conflicting_devices) { /* the device defines conflicting devices */
1203 PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) {
1204 for (i = 0; i < dev_num; i++) {
1205 if (pdevices[i] == d) {
1206 pa_log_debug("Conflicting device found");
1207 return 0;
1208 }
1209 }
1210 }
1211 } else if (dev->supported_devices) { /* the device defines supported devices */
1212 for (i = 0; i < dev_num; i++) {
1213 if (!ucm_device_exists(dev->supported_devices, pdevices[i])) {
1214 pa_log_debug("Supported device not found");
1215 return 0;
1216 }
1217 }
1218 } else { /* not support any other devices */
1219 pa_log_debug("Not support any other devices");
1220 return 0;
1221 }
1222
1223 pa_log_debug("Device added to combination, number %d", dev_num + 1);
1224 return 1;
1225 }
1226
get_next_device(pa_idxset * idxset,uint32_t * idx)1227 static inline pa_alsa_ucm_device *get_next_device(pa_idxset *idxset, uint32_t *idx) {
1228 pa_alsa_ucm_device *dev;
1229
1230 if (*idx == PA_IDXSET_INVALID)
1231 dev = pa_idxset_first(idxset, idx);
1232 else
1233 dev = pa_idxset_next(idxset, idx);
1234
1235 return dev;
1236 }
1237
ucm_add_ports_combination(pa_hashmap * hash,pa_alsa_ucm_mapping_context * context,bool is_sink,pa_alsa_ucm_device ** pdevices,int dev_num,uint32_t map_index,pa_hashmap * ports,pa_card_profile * cp,pa_core * core)1238 static void ucm_add_ports_combination(
1239 pa_hashmap *hash,
1240 pa_alsa_ucm_mapping_context *context,
1241 bool is_sink,
1242 pa_alsa_ucm_device **pdevices,
1243 int dev_num,
1244 uint32_t map_index,
1245 pa_hashmap *ports,
1246 pa_card_profile *cp,
1247 pa_core *core) {
1248
1249 pa_alsa_ucm_device *dev;
1250 uint32_t idx = map_index;
1251
1252 if ((dev = get_next_device(context->ucm_devices, &idx)) == NULL)
1253 return;
1254
1255 /* check if device at map_index can combine with existing devices combination */
1256 if (ucm_check_conformance(context, pdevices, dev_num, dev)) {
1257 /* add device at map_index to devices combination */
1258 pdevices[dev_num] = dev;
1259 /* add current devices combination as a new port */
1260 ucm_add_port_combination(hash, context, is_sink, pdevices, dev_num + 1, ports, cp, core);
1261 /* try more elements combination */
1262 ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num + 1, idx, ports, cp, core);
1263 }
1264
1265 /* try other device with current elements number */
1266 ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num, idx, ports, cp, core);
1267 }
1268
merge_roles(const char * cur,const char * add)1269 static char* merge_roles(const char *cur, const char *add) {
1270 char *r, *ret;
1271 const char *state = NULL;
1272
1273 if (add == NULL)
1274 return pa_xstrdup(cur);
1275 else if (cur == NULL)
1276 return pa_xstrdup(add);
1277
1278 ret = pa_xstrdup(cur);
1279
1280 while ((r = pa_split_spaces(add, &state))) {
1281 char *value;
1282
1283 if (!pa_str_in_list_spaces(ret, r))
1284 value = pa_sprintf_malloc("%s %s", ret, r);
1285 else {
1286 pa_xfree(r);
1287 continue;
1288 }
1289
1290 pa_xfree(ret);
1291 ret = value;
1292 pa_xfree(r);
1293 }
1294
1295 return ret;
1296 }
1297
pa_alsa_ucm_add_ports_combination(pa_hashmap * p,pa_alsa_ucm_mapping_context * context,bool is_sink,pa_hashmap * ports,pa_card_profile * cp,pa_core * core)1298 void pa_alsa_ucm_add_ports_combination(
1299 pa_hashmap *p,
1300 pa_alsa_ucm_mapping_context *context,
1301 bool is_sink,
1302 pa_hashmap *ports,
1303 pa_card_profile *cp,
1304 pa_core *core) {
1305
1306 pa_alsa_ucm_device **pdevices;
1307
1308 pa_assert(context->ucm_devices);
1309
1310 if (pa_idxset_size(context->ucm_devices) > 0) {
1311 pdevices = pa_xnew(pa_alsa_ucm_device *, pa_idxset_size(context->ucm_devices));
1312 ucm_add_ports_combination(p, context, is_sink, pdevices, 0, PA_IDXSET_INVALID, ports, cp, core);
1313 pa_xfree(pdevices);
1314 }
1315
1316 /* ELD devices */
1317 set_eld_devices(ports);
1318 }
1319
pa_alsa_ucm_add_ports(pa_hashmap ** p,pa_proplist * proplist,pa_alsa_ucm_mapping_context * context,bool is_sink,pa_card * card,snd_pcm_t * pcm_handle,bool ignore_dB)1320 void pa_alsa_ucm_add_ports(
1321 pa_hashmap **p,
1322 pa_proplist *proplist,
1323 pa_alsa_ucm_mapping_context *context,
1324 bool is_sink,
1325 pa_card *card,
1326 snd_pcm_t *pcm_handle,
1327 bool ignore_dB) {
1328
1329 uint32_t idx;
1330 char *merged_roles;
1331 const char *role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES;
1332 pa_alsa_ucm_device *dev;
1333 pa_alsa_ucm_modifier *mod;
1334 char *tmp;
1335
1336 pa_assert(p);
1337 pa_assert(*p);
1338
1339 /* add ports first */
1340 pa_alsa_ucm_add_ports_combination(*p, context, is_sink, card->ports, NULL, card->core);
1341
1342 /* now set up volume paths if any */
1343 probe_volumes(*p, is_sink, pcm_handle, context->ucm->mixers, ignore_dB);
1344
1345 /* probe_volumes() removes per-profile paths from ports if probing them
1346 * fails. The path for the current profile is cached in
1347 * pa_alsa_ucm_port_data.path, which is not cleared by probe_volumes() if
1348 * the path gets removed, so we have to call update_mixer_paths() here to
1349 * unset the cached path if needed. */
1350 if (card->active_profile)
1351 update_mixer_paths(*p, card->active_profile->name);
1352
1353 /* then set property PA_PROP_DEVICE_INTENDED_ROLES */
1354 merged_roles = pa_xstrdup(pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES));
1355 PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1356 const char *roles = pa_proplist_gets(dev->proplist, role_name);
1357 tmp = merge_roles(merged_roles, roles);
1358 pa_xfree(merged_roles);
1359 merged_roles = tmp;
1360 }
1361
1362 if (context->ucm_modifiers)
1363 PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) {
1364 tmp = merge_roles(merged_roles, mod->media_role);
1365 pa_xfree(merged_roles);
1366 merged_roles = tmp;
1367 }
1368
1369 if (merged_roles)
1370 pa_proplist_sets(proplist, PA_PROP_DEVICE_INTENDED_ROLES, merged_roles);
1371
1372 pa_log_info("ALSA device %s roles: %s", pa_proplist_gets(proplist, PA_PROP_DEVICE_STRING), pa_strnull(merged_roles));
1373 pa_xfree(merged_roles);
1374 }
1375
1376 /* Change UCM verb and device to match selected card profile */
pa_alsa_ucm_set_profile(pa_alsa_ucm_config * ucm,pa_card * card,const char * new_profile,const char * old_profile)1377 int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile) {
1378 int ret = 0;
1379 const char *profile;
1380 pa_alsa_ucm_verb *verb;
1381
1382 if (new_profile == old_profile)
1383 return ret;
1384 else if (new_profile == NULL || old_profile == NULL)
1385 profile = new_profile ? new_profile : SND_USE_CASE_VERB_INACTIVE;
1386 else if (!pa_streq(new_profile, old_profile))
1387 profile = new_profile;
1388 else
1389 return ret;
1390
1391 /* change verb */
1392 pa_log_info("Set UCM verb to %s", profile);
1393 if ((snd_use_case_set(ucm->ucm_mgr, "_verb", profile)) < 0) {
1394 pa_log("Failed to set verb %s", profile);
1395 ret = -1;
1396 }
1397
1398 /* find active verb */
1399 ucm->active_verb = NULL;
1400 PA_LLIST_FOREACH(verb, ucm->verbs) {
1401 const char *verb_name;
1402 verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME);
1403 if (pa_streq(verb_name, profile)) {
1404 ucm->active_verb = verb;
1405 break;
1406 }
1407 }
1408
1409 update_mixer_paths(card->ports, profile);
1410 return ret;
1411 }
1412
pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context * context,pa_device_port * port,bool is_sink)1413 int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) {
1414 int i;
1415 int ret = 0;
1416 pa_alsa_ucm_config *ucm;
1417 const char **enable_devs;
1418 int enable_num = 0;
1419 uint32_t idx;
1420 pa_alsa_ucm_device *dev;
1421
1422 pa_assert(context && context->ucm);
1423
1424 ucm = context->ucm;
1425 pa_assert(ucm->ucm_mgr);
1426
1427 enable_devs = pa_xnew(const char *, pa_idxset_size(context->ucm_devices));
1428
1429 /* first disable then enable */
1430 PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1431 const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
1432
1433 if (ucm_port_contains(port->name, dev_name, is_sink))
1434 enable_devs[enable_num++] = dev_name;
1435 else {
1436 pa_log_debug("Disable ucm device %s", dev_name);
1437 if (snd_use_case_set(ucm->ucm_mgr, "_disdev", dev_name) > 0) {
1438 pa_log("Failed to disable ucm device %s", dev_name);
1439 ret = -1;
1440 break;
1441 }
1442 }
1443 }
1444
1445 for (i = 0; i < enable_num; i++) {
1446 pa_log_debug("Enable ucm device %s", enable_devs[i]);
1447 if (snd_use_case_set(ucm->ucm_mgr, "_enadev", enable_devs[i]) < 0) {
1448 pa_log("Failed to enable ucm device %s", enable_devs[i]);
1449 ret = -1;
1450 break;
1451 }
1452 }
1453
1454 pa_xfree(enable_devs);
1455
1456 return ret;
1457 }
1458
ucm_add_mapping(pa_alsa_profile * p,pa_alsa_mapping * m)1459 static void ucm_add_mapping(pa_alsa_profile *p, pa_alsa_mapping *m) {
1460
1461 pa_alsa_path_set *ps;
1462
1463 /* create empty path set for the future path additions */
1464 ps = pa_xnew0(pa_alsa_path_set, 1);
1465 ps->direction = m->direction;
1466 ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1467
1468 switch (m->direction) {
1469 case PA_ALSA_DIRECTION_ANY:
1470 pa_idxset_put(p->output_mappings, m, NULL);
1471 pa_idxset_put(p->input_mappings, m, NULL);
1472 m->output_path_set = ps;
1473 m->input_path_set = ps;
1474 break;
1475 case PA_ALSA_DIRECTION_OUTPUT:
1476 pa_idxset_put(p->output_mappings, m, NULL);
1477 m->output_path_set = ps;
1478 break;
1479 case PA_ALSA_DIRECTION_INPUT:
1480 pa_idxset_put(p->input_mappings, m, NULL);
1481 m->input_path_set = ps;
1482 break;
1483 }
1484 }
1485
alsa_mapping_add_ucm_device(pa_alsa_mapping * m,pa_alsa_ucm_device * device)1486 static void alsa_mapping_add_ucm_device(pa_alsa_mapping *m, pa_alsa_ucm_device *device) {
1487 char *cur_desc;
1488 const char *new_desc, *mdev;
1489 bool is_sink = m->direction == PA_ALSA_DIRECTION_OUTPUT;
1490
1491 pa_idxset_put(m->ucm_context.ucm_devices, device, NULL);
1492
1493 new_desc = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
1494 cur_desc = m->description;
1495 if (cur_desc)
1496 m->description = pa_sprintf_malloc("%s + %s", cur_desc, new_desc);
1497 else
1498 m->description = pa_xstrdup(new_desc);
1499 pa_xfree(cur_desc);
1500
1501 /* walk around null case */
1502 m->description = m->description ? m->description : pa_xstrdup("");
1503
1504 /* save mapping to ucm device */
1505 if (is_sink)
1506 device->playback_mapping = m;
1507 else
1508 device->capture_mapping = m;
1509
1510 mdev = get_mixer_device(device, is_sink);
1511 if (mdev)
1512 pa_proplist_sets(m->proplist, "alsa.mixer_device", mdev);
1513 }
1514
alsa_mapping_add_ucm_modifier(pa_alsa_mapping * m,pa_alsa_ucm_modifier * modifier)1515 static void alsa_mapping_add_ucm_modifier(pa_alsa_mapping *m, pa_alsa_ucm_modifier *modifier) {
1516 char *cur_desc;
1517 const char *new_desc, *mod_name, *channel_str;
1518 uint32_t channels = 0;
1519
1520 pa_idxset_put(m->ucm_context.ucm_modifiers, modifier, NULL);
1521
1522 new_desc = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
1523 cur_desc = m->description;
1524 if (cur_desc)
1525 m->description = pa_sprintf_malloc("%s + %s", cur_desc, new_desc);
1526 else
1527 m->description = pa_xstrdup(new_desc);
1528 pa_xfree(cur_desc);
1529
1530 m->description = m->description ? m->description : pa_xstrdup("");
1531
1532 /* Modifier sinks should not be routed to by default */
1533 m->priority = 0;
1534
1535 mod_name = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_NAME);
1536 pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_MODIFIER, mod_name);
1537
1538 /* save mapping to ucm modifier */
1539 if (m->direction == PA_ALSA_DIRECTION_OUTPUT) {
1540 modifier->playback_mapping = m;
1541 channel_str = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS);
1542 } else {
1543 modifier->capture_mapping = m;
1544 channel_str = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS);
1545 }
1546
1547 if (channel_str) {
1548 /* FIXME: channel_str is unsanitized input from the UCM configuration,
1549 * we should do proper error handling instead of asserting.
1550 * https://bugs.freedesktop.org/show_bug.cgi?id=71823 */
1551 pa_assert_se(pa_atou(channel_str, &channels) == 0 && pa_channels_valid(channels));
1552 pa_log_debug("Got channel count %" PRIu32 " for modifier", channels);
1553 }
1554
1555 if (channels)
1556 pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
1557 else
1558 pa_channel_map_init(&m->channel_map);
1559 }
1560
ucm_alsa_mapping_get(pa_alsa_ucm_config * ucm,pa_alsa_profile_set * ps,const char * verb_name,const char * device_str,bool is_sink)1561 static pa_alsa_mapping* ucm_alsa_mapping_get(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps, const char *verb_name, const char *device_str, bool is_sink) {
1562 pa_alsa_mapping *m;
1563 char *mapping_name;
1564 size_t ucm_alibpref_len = 0;
1565
1566 /* find private alsa-lib's configuration device prefix */
1567
1568 if (ucm->alib_prefix && pa_startswith(device_str, ucm->alib_prefix))
1569 ucm_alibpref_len = strlen(ucm->alib_prefix);
1570
1571 mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, device_str + ucm_alibpref_len, is_sink ? "sink" : "source");
1572
1573 m = pa_alsa_mapping_get(ps, mapping_name);
1574
1575 if (!m)
1576 pa_log("No mapping for %s", mapping_name);
1577
1578 pa_xfree(mapping_name);
1579
1580 return m;
1581 }
1582
ucm_create_mapping_direction(pa_alsa_ucm_config * ucm,pa_alsa_profile_set * ps,pa_alsa_profile * p,pa_alsa_ucm_device * device,const char * verb_name,const char * device_name,const char * device_str,bool is_sink)1583 static int ucm_create_mapping_direction(
1584 pa_alsa_ucm_config *ucm,
1585 pa_alsa_profile_set *ps,
1586 pa_alsa_profile *p,
1587 pa_alsa_ucm_device *device,
1588 const char *verb_name,
1589 const char *device_name,
1590 const char *device_str,
1591 bool is_sink) {
1592
1593 pa_alsa_mapping *m;
1594 unsigned priority, rate, channels;
1595
1596 m = ucm_alsa_mapping_get(ucm, ps, verb_name, device_str, is_sink);
1597
1598 if (!m)
1599 return -1;
1600
1601 pa_log_debug("UCM mapping: %s dev %s", m->name, device_name);
1602
1603 priority = is_sink ? device->playback_priority : device->capture_priority;
1604 rate = is_sink ? device->playback_rate : device->capture_rate;
1605 channels = is_sink ? device->playback_channels : device->capture_channels;
1606
1607 if (!m->ucm_context.ucm_devices) { /* new mapping */
1608 m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1609 m->ucm_context.ucm = ucm;
1610 m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
1611
1612 m->device_strings = pa_xnew0(char*, 2);
1613 m->device_strings[0] = pa_xstrdup(device_str);
1614 m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT;
1615
1616 ucm_add_mapping(p, m);
1617 if (rate)
1618 m->sample_spec.rate = rate;
1619 pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
1620 }
1621
1622 /* mapping priority is the highest one of ucm devices */
1623 if (priority > m->priority)
1624 m->priority = priority;
1625
1626 /* mapping channels is the lowest one of ucm devices */
1627 if (channels < m->channel_map.channels)
1628 pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
1629
1630 alsa_mapping_add_ucm_device(m, device);
1631
1632 return 0;
1633 }
1634
ucm_create_mapping_for_modifier(pa_alsa_ucm_config * ucm,pa_alsa_profile_set * ps,pa_alsa_profile * p,pa_alsa_ucm_modifier * modifier,const char * verb_name,const char * mod_name,const char * device_str,bool is_sink)1635 static int ucm_create_mapping_for_modifier(
1636 pa_alsa_ucm_config *ucm,
1637 pa_alsa_profile_set *ps,
1638 pa_alsa_profile *p,
1639 pa_alsa_ucm_modifier *modifier,
1640 const char *verb_name,
1641 const char *mod_name,
1642 const char *device_str,
1643 bool is_sink) {
1644
1645 pa_alsa_mapping *m;
1646
1647 m = ucm_alsa_mapping_get(ucm, ps, verb_name, device_str, is_sink);
1648
1649 if (!m)
1650 return -1;
1651
1652 pa_log_info("UCM mapping: %s modifier %s", m->name, mod_name);
1653
1654 if (!m->ucm_context.ucm_devices && !m->ucm_context.ucm_modifiers) { /* new mapping */
1655 m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1656 m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1657 m->ucm_context.ucm = ucm;
1658 m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
1659
1660 m->device_strings = pa_xnew0(char*, 2);
1661 m->device_strings[0] = pa_xstrdup(device_str);
1662 m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT;
1663 /* Modifier sinks should not be routed to by default */
1664 m->priority = 0;
1665
1666 ucm_add_mapping(p, m);
1667 } else if (!m->ucm_context.ucm_modifiers) /* share pcm with device */
1668 m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1669
1670 alsa_mapping_add_ucm_modifier(m, modifier);
1671
1672 return 0;
1673 }
1674
ucm_create_mapping(pa_alsa_ucm_config * ucm,pa_alsa_profile_set * ps,pa_alsa_profile * p,pa_alsa_ucm_device * device,const char * verb_name,const char * device_name,const char * sink,const char * source)1675 static int ucm_create_mapping(
1676 pa_alsa_ucm_config *ucm,
1677 pa_alsa_profile_set *ps,
1678 pa_alsa_profile *p,
1679 pa_alsa_ucm_device *device,
1680 const char *verb_name,
1681 const char *device_name,
1682 const char *sink,
1683 const char *source) {
1684
1685 int ret = 0;
1686
1687 if (!sink && !source) {
1688 pa_log("No sink and source at %s: %s", verb_name, device_name);
1689 return -1;
1690 }
1691
1692 if (sink)
1693 ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, sink, true);
1694 if (ret == 0 && source)
1695 ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, source, false);
1696
1697 return ret;
1698 }
1699
ucm_get_jack(pa_alsa_ucm_config * ucm,pa_alsa_ucm_device * device)1700 static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *device) {
1701 pa_alsa_jack *j;
1702 const char *device_name;
1703 const char *jack_control;
1704 const char *mixer_device_name;
1705 char *name;
1706
1707 pa_assert(ucm);
1708 pa_assert(device);
1709
1710 device_name = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_NAME);
1711
1712 jack_control = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_JACK_CONTROL);
1713 if (jack_control) {
1714 #if SND_LIB_VERSION >= 0x10201
1715 snd_ctl_elem_id_t *ctl;
1716 int err, index;
1717 snd_ctl_elem_id_alloca(&ctl);
1718 err = snd_use_case_parse_ctl_elem_id(ctl, "JackControl", jack_control);
1719 if (err < 0)
1720 return NULL;
1721 jack_control = snd_ctl_elem_id_get_name(ctl);
1722 index = snd_ctl_elem_id_get_index(ctl);
1723 if (index > 0) {
1724 pa_log("[%s] Invalid JackControl index value: \"%s\",%d", device_name, jack_control, index);
1725 return NULL;
1726 }
1727 #else
1728 #warning "Upgrade to alsa-lib 1.2.1!"
1729 #endif
1730 if (!pa_endswith(jack_control, " Jack")) {
1731 pa_log("[%s] Invalid JackControl value: \"%s\"", device_name, jack_control);
1732 return NULL;
1733 }
1734
1735 /* pa_alsa_jack_new() expects a jack name without " Jack" at the
1736 * end, so drop the trailing " Jack". */
1737 name = pa_xstrndup(jack_control, strlen(jack_control) - 5);
1738 } else {
1739 /* The jack control hasn't been explicitly configured, fail. */
1740 return NULL;
1741 }
1742
1743 PA_LLIST_FOREACH(j, ucm->jacks)
1744 if (pa_streq(j->name, name))
1745 goto finish;
1746
1747 mixer_device_name = get_jack_mixer_device(device, true);
1748 if (!mixer_device_name)
1749 mixer_device_name = get_jack_mixer_device(device, false);
1750 if (!mixer_device_name) {
1751 pa_log("[%s] No mixer device name for JackControl \"%s\"", device_name, jack_control);
1752 return NULL;
1753 }
1754 j = pa_alsa_jack_new(NULL, mixer_device_name, name, 0);
1755 PA_LLIST_PREPEND(pa_alsa_jack, ucm->jacks, j);
1756
1757 finish:
1758 pa_xfree(name);
1759
1760 return j;
1761 }
1762
ucm_create_profile(pa_alsa_ucm_config * ucm,pa_alsa_profile_set * ps,pa_alsa_ucm_verb * verb,const char * verb_name,const char * verb_desc)1763 static int ucm_create_profile(
1764 pa_alsa_ucm_config *ucm,
1765 pa_alsa_profile_set *ps,
1766 pa_alsa_ucm_verb *verb,
1767 const char *verb_name,
1768 const char *verb_desc) {
1769
1770 pa_alsa_profile *p;
1771 pa_alsa_ucm_device *dev;
1772 pa_alsa_ucm_modifier *mod;
1773 int i = 0;
1774 const char *name, *sink, *source;
1775 unsigned int priority;
1776
1777 pa_assert(ps);
1778
1779 if (pa_hashmap_get(ps->profiles, verb_name)) {
1780 pa_log("Verb %s already exists", verb_name);
1781 return -1;
1782 }
1783
1784 p = pa_xnew0(pa_alsa_profile, 1);
1785 p->profile_set = ps;
1786 p->name = pa_xstrdup(verb_name);
1787 p->description = pa_xstrdup(verb_desc);
1788
1789 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1790 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1791
1792 p->supported = true;
1793 pa_hashmap_put(ps->profiles, p->name, p);
1794
1795 /* TODO: get profile priority from policy management */
1796 priority = verb->priority;
1797
1798 if (priority == 0) {
1799 char *verb_cmp, *c;
1800 c = verb_cmp = pa_xstrdup(verb_name);
1801 while (*c) {
1802 if (*c == '_') *c = ' ';
1803 c++;
1804 }
1805 for (i = 0; verb_info[i].id; i++) {
1806 if (strcasecmp(verb_info[i].id, verb_cmp) == 0) {
1807 priority = verb_info[i].priority;
1808 break;
1809 }
1810 }
1811 pa_xfree(verb_cmp);
1812 }
1813
1814 p->priority = priority;
1815
1816 PA_LLIST_FOREACH(dev, verb->devices) {
1817 pa_alsa_jack *jack;
1818 const char *jack_hw_mute;
1819
1820 name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
1821
1822 sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK);
1823 source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE);
1824
1825 ucm_create_mapping(ucm, ps, p, dev, verb_name, name, sink, source);
1826
1827 jack = ucm_get_jack(ucm, dev);
1828 if (jack)
1829 device_set_jack(dev, jack);
1830
1831 /* JackHWMute contains a list of device names. Each listed device must
1832 * be associated with the jack object that we just created. */
1833 jack_hw_mute = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_JACK_HW_MUTE);
1834 if (jack_hw_mute && !jack) {
1835 pa_log("[%s] JackHWMute set, but JackControl is missing", name);
1836 jack_hw_mute = NULL;
1837 }
1838 if (jack_hw_mute) {
1839 char *hw_mute_device_name;
1840 const char *state = NULL;
1841
1842 while ((hw_mute_device_name = ucm_split_devnames(jack_hw_mute, &state))) {
1843 pa_alsa_ucm_verb *verb2;
1844 bool device_found = false;
1845
1846 /* Search the referenced device from all verbs. If there are
1847 * multiple verbs that have a device with this name, we add the
1848 * hw mute association to each of those devices. */
1849 PA_LLIST_FOREACH(verb2, ucm->verbs) {
1850 pa_alsa_ucm_device *hw_mute_device;
1851
1852 hw_mute_device = verb_find_device(verb2, hw_mute_device_name);
1853 if (hw_mute_device) {
1854 device_found = true;
1855 device_add_hw_mute_jack(hw_mute_device, jack);
1856 }
1857 }
1858
1859 if (!device_found)
1860 pa_log("[%s] JackHWMute references an unknown device: %s", name, hw_mute_device_name);
1861
1862 pa_xfree(hw_mute_device_name);
1863 }
1864 }
1865 }
1866
1867 /* Now find modifiers that have their own PlaybackPCM and create
1868 * separate sinks for them. */
1869 PA_LLIST_FOREACH(mod, verb->modifiers) {
1870 name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
1871
1872 sink = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SINK);
1873 source = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SOURCE);
1874
1875 if (sink)
1876 ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, sink, true);
1877 else if (source)
1878 ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, source, false);
1879 }
1880
1881 pa_alsa_profile_dump(p);
1882
1883 return 0;
1884 }
1885
mapping_init_eld(pa_alsa_mapping * m,snd_pcm_t * pcm)1886 static void mapping_init_eld(pa_alsa_mapping *m, snd_pcm_t *pcm)
1887 {
1888 pa_alsa_ucm_mapping_context *context = &m->ucm_context;
1889 pa_alsa_ucm_device *dev;
1890 uint32_t idx;
1891 char *mdev, *alib_prefix;
1892 snd_pcm_info_t *info;
1893 int pcm_card, pcm_device;
1894
1895 snd_pcm_info_alloca(&info);
1896 if (snd_pcm_info(pcm, info) < 0)
1897 return;
1898
1899 if ((pcm_card = snd_pcm_info_get_card(info)) < 0)
1900 return;
1901 if ((pcm_device = snd_pcm_info_get_device(info)) < 0)
1902 return;
1903
1904 alib_prefix = context->ucm->alib_prefix;
1905
1906 PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1907 mdev = pa_sprintf_malloc("%shw:%i", alib_prefix ? alib_prefix : "", pcm_card);
1908 if (mdev == NULL)
1909 continue;
1910 dev->eld_mixer_device_name = mdev;
1911 dev->eld_device = pcm_device;
1912 }
1913 }
1914
mapping_open_pcm(pa_alsa_ucm_config * ucm,pa_alsa_mapping * m,int mode)1915 static snd_pcm_t* mapping_open_pcm(pa_alsa_ucm_config *ucm, pa_alsa_mapping *m, int mode) {
1916 snd_pcm_t* pcm;
1917 pa_sample_spec try_ss = ucm->core->default_sample_spec;
1918 pa_channel_map try_map;
1919 snd_pcm_uframes_t try_period_size, try_buffer_size;
1920 bool exact_channels = m->channel_map.channels > 0;
1921
1922 if (exact_channels) {
1923 try_map = m->channel_map;
1924 try_ss.channels = try_map.channels;
1925 } else
1926 pa_channel_map_init_extend(&try_map, try_ss.channels, PA_CHANNEL_MAP_ALSA);
1927
1928 try_period_size =
1929 pa_usec_to_bytes(ucm->core->default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
1930 pa_frame_size(&try_ss);
1931 try_buffer_size = ucm->core->default_n_fragments * try_period_size;
1932
1933 pcm = pa_alsa_open_by_device_string(m->device_strings[0], NULL, &try_ss,
1934 &try_map, mode, &try_period_size, &try_buffer_size, 0, NULL, NULL, exact_channels);
1935
1936 if (pcm) {
1937 if (!exact_channels)
1938 m->channel_map = try_map;
1939 mapping_init_eld(m, pcm);
1940 }
1941
1942 return pcm;
1943 }
1944
profile_finalize_probing(pa_alsa_profile * p)1945 static void profile_finalize_probing(pa_alsa_profile *p) {
1946 pa_alsa_mapping *m;
1947 uint32_t idx;
1948
1949 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
1950 if (p->supported)
1951 m->supported++;
1952
1953 if (!m->output_pcm)
1954 continue;
1955
1956 snd_pcm_close(m->output_pcm);
1957 m->output_pcm = NULL;
1958 }
1959
1960 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
1961 if (p->supported)
1962 m->supported++;
1963
1964 if (!m->input_pcm)
1965 continue;
1966
1967 snd_pcm_close(m->input_pcm);
1968 m->input_pcm = NULL;
1969 }
1970 }
1971
ucm_mapping_jack_probe(pa_alsa_mapping * m,pa_hashmap * mixers)1972 static void ucm_mapping_jack_probe(pa_alsa_mapping *m, pa_hashmap *mixers) {
1973 snd_mixer_t *mixer_handle;
1974 pa_alsa_ucm_mapping_context *context = &m->ucm_context;
1975 pa_alsa_ucm_device *dev;
1976 uint32_t idx;
1977
1978 PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1979 bool has_control;
1980
1981 if (!dev->jack || !dev->jack->mixer_device_name)
1982 continue;
1983
1984 mixer_handle = pa_alsa_open_mixer_by_name(mixers, dev->jack->mixer_device_name, true);
1985 if (!mixer_handle) {
1986 pa_log_error("Unable to determine open mixer device '%s' for jack %s", dev->jack->mixer_device_name, dev->jack->name);
1987 continue;
1988 }
1989
1990 has_control = pa_alsa_mixer_find_card(mixer_handle, &dev->jack->alsa_id, 0) != NULL;
1991 pa_alsa_jack_set_has_control(dev->jack, has_control);
1992 pa_log_info("UCM jack %s has_control=%d", dev->jack->name, dev->jack->has_control);
1993 }
1994 }
1995
ucm_probe_profile_set(pa_alsa_ucm_config * ucm,pa_alsa_profile_set * ps)1996 static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps) {
1997 void *state;
1998 pa_alsa_profile *p;
1999 pa_alsa_mapping *m;
2000 uint32_t idx;
2001
2002 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
2003 /* change verb */
2004 pa_log_info("Set ucm verb to %s", p->name);
2005
2006 if ((snd_use_case_set(ucm->ucm_mgr, "_verb", p->name)) < 0) {
2007 pa_log("Failed to set verb %s", p->name);
2008 p->supported = false;
2009 continue;
2010 }
2011
2012 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
2013 if (PA_UCM_IS_MODIFIER_MAPPING(m)) {
2014 /* Skip jack probing on modifier PCMs since we expect this to
2015 * only be controlled on the main device/verb PCM. */
2016 continue;
2017 }
2018
2019 m->output_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_PLAYBACK);
2020 if (!m->output_pcm) {
2021 p->supported = false;
2022 break;
2023 }
2024 }
2025
2026 if (p->supported) {
2027 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
2028 if (PA_UCM_IS_MODIFIER_MAPPING(m)) {
2029 /* Skip jack probing on modifier PCMs since we expect this to
2030 * only be controlled on the main device/verb PCM. */
2031 continue;
2032 }
2033
2034 m->input_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_CAPTURE);
2035 if (!m->input_pcm) {
2036 p->supported = false;
2037 break;
2038 }
2039 }
2040 }
2041
2042 if (!p->supported) {
2043 profile_finalize_probing(p);
2044 continue;
2045 }
2046
2047 pa_log_debug("Profile %s supported.", p->name);
2048
2049 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
2050 if (!PA_UCM_IS_MODIFIER_MAPPING(m))
2051 ucm_mapping_jack_probe(m, ucm->mixers);
2052
2053 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
2054 if (!PA_UCM_IS_MODIFIER_MAPPING(m))
2055 ucm_mapping_jack_probe(m, ucm->mixers);
2056
2057 profile_finalize_probing(p);
2058 }
2059
2060 /* restore ucm state */
2061 snd_use_case_set(ucm->ucm_mgr, "_verb", SND_USE_CASE_VERB_INACTIVE);
2062
2063 pa_alsa_profile_set_drop_unsupported(ps);
2064 }
2065
pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config * ucm,pa_channel_map * default_channel_map)2066 pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map) {
2067 pa_alsa_ucm_verb *verb;
2068 pa_alsa_profile_set *ps;
2069
2070 ps = pa_xnew0(pa_alsa_profile_set, 1);
2071 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
2072 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
2073 ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
2074
2075 /* create a profile for each verb */
2076 PA_LLIST_FOREACH(verb, ucm->verbs) {
2077 const char *verb_name;
2078 const char *verb_desc;
2079
2080 verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME);
2081 verb_desc = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
2082 if (verb_name == NULL) {
2083 pa_log("Verb with no name");
2084 continue;
2085 }
2086
2087 ucm_create_profile(ucm, ps, verb, verb_name, verb_desc);
2088 }
2089
2090 ucm_probe_profile_set(ucm, ps);
2091 ps->probed = true;
2092
2093 return ps;
2094 }
2095
free_verb(pa_alsa_ucm_verb * verb)2096 static void free_verb(pa_alsa_ucm_verb *verb) {
2097 pa_alsa_ucm_device *di, *dn;
2098 pa_alsa_ucm_modifier *mi, *mn;
2099
2100 PA_LLIST_FOREACH_SAFE(di, dn, verb->devices) {
2101 PA_LLIST_REMOVE(pa_alsa_ucm_device, verb->devices, di);
2102
2103 if (di->hw_mute_jacks)
2104 pa_dynarray_free(di->hw_mute_jacks);
2105
2106 if (di->ucm_ports)
2107 pa_dynarray_free(di->ucm_ports);
2108
2109 if (di->playback_volumes)
2110 pa_hashmap_free(di->playback_volumes);
2111 if (di->capture_volumes)
2112 pa_hashmap_free(di->capture_volumes);
2113
2114 pa_proplist_free(di->proplist);
2115
2116 if (di->conflicting_devices)
2117 pa_idxset_free(di->conflicting_devices, NULL);
2118 if (di->supported_devices)
2119 pa_idxset_free(di->supported_devices, NULL);
2120
2121 pa_xfree(di->eld_mixer_device_name);
2122
2123 pa_xfree(di);
2124 }
2125
2126 PA_LLIST_FOREACH_SAFE(mi, mn, verb->modifiers) {
2127 PA_LLIST_REMOVE(pa_alsa_ucm_modifier, verb->modifiers, mi);
2128 pa_proplist_free(mi->proplist);
2129 if (mi->n_suppdev > 0)
2130 snd_use_case_free_list(mi->supported_devices, mi->n_suppdev);
2131 if (mi->n_confdev > 0)
2132 snd_use_case_free_list(mi->conflicting_devices, mi->n_confdev);
2133 pa_xfree(mi->media_role);
2134 pa_xfree(mi);
2135 }
2136 pa_proplist_free(verb->proplist);
2137 pa_xfree(verb);
2138 }
2139
verb_find_device(pa_alsa_ucm_verb * verb,const char * device_name)2140 static pa_alsa_ucm_device *verb_find_device(pa_alsa_ucm_verb *verb, const char *device_name) {
2141 pa_alsa_ucm_device *device;
2142
2143 pa_assert(verb);
2144 pa_assert(device_name);
2145
2146 PA_LLIST_FOREACH(device, verb->devices) {
2147 const char *name;
2148
2149 name = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_NAME);
2150 if (pa_streq(name, device_name))
2151 return device;
2152 }
2153
2154 return NULL;
2155 }
2156
pa_alsa_ucm_free(pa_alsa_ucm_config * ucm)2157 void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) {
2158 pa_alsa_ucm_verb *vi, *vn;
2159 pa_alsa_jack *ji, *jn;
2160
2161 PA_LLIST_FOREACH_SAFE(vi, vn, ucm->verbs) {
2162 PA_LLIST_REMOVE(pa_alsa_ucm_verb, ucm->verbs, vi);
2163 free_verb(vi);
2164 }
2165 PA_LLIST_FOREACH_SAFE(ji, jn, ucm->jacks) {
2166 PA_LLIST_REMOVE(pa_alsa_jack, ucm->jacks, ji);
2167 pa_alsa_jack_free(ji);
2168 }
2169 if (ucm->ucm_mgr) {
2170 snd_use_case_mgr_close(ucm->ucm_mgr);
2171 ucm->ucm_mgr = NULL;
2172 }
2173 pa_xfree(ucm->alib_prefix);
2174 ucm->alib_prefix = NULL;
2175 }
2176
pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context * context)2177 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
2178 pa_alsa_ucm_device *dev;
2179 pa_alsa_ucm_modifier *mod;
2180 uint32_t idx;
2181
2182 if (context->ucm_devices) {
2183 /* clear ucm device pointer to mapping */
2184 PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
2185 if (context->direction == PA_DIRECTION_OUTPUT)
2186 dev->playback_mapping = NULL;
2187 else
2188 dev->capture_mapping = NULL;
2189 }
2190
2191 pa_idxset_free(context->ucm_devices, NULL);
2192 }
2193
2194 if (context->ucm_modifiers) {
2195 PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) {
2196 if (context->direction == PA_DIRECTION_OUTPUT)
2197 mod->playback_mapping = NULL;
2198 else
2199 mod->capture_mapping = NULL;
2200 }
2201
2202 pa_idxset_free(context->ucm_modifiers, NULL);
2203 }
2204 }
2205
2206 /* Enable the modifier when the first stream with matched role starts */
pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config * ucm,const char * role,pa_direction_t dir)2207 void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
2208 pa_alsa_ucm_modifier *mod;
2209
2210 if (!ucm->active_verb)
2211 return;
2212
2213 PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) {
2214 if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) {
2215 if (mod->enabled_counter == 0) {
2216 const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
2217
2218 pa_log_info("Enable ucm modifier %s", mod_name);
2219 if (snd_use_case_set(ucm->ucm_mgr, "_enamod", mod_name) < 0) {
2220 pa_log("Failed to enable ucm modifier %s", mod_name);
2221 }
2222 }
2223
2224 mod->enabled_counter++;
2225 break;
2226 }
2227 }
2228 }
2229
2230 /* Disable the modifier when the last stream with matched role ends */
pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config * ucm,const char * role,pa_direction_t dir)2231 void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
2232 pa_alsa_ucm_modifier *mod;
2233
2234 if (!ucm->active_verb)
2235 return;
2236
2237 PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) {
2238 if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) {
2239
2240 mod->enabled_counter--;
2241 if (mod->enabled_counter == 0) {
2242 const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
2243
2244 pa_log_info("Disable ucm modifier %s", mod_name);
2245 if (snd_use_case_set(ucm->ucm_mgr, "_dismod", mod_name) < 0) {
2246 pa_log("Failed to disable ucm modifier %s", mod_name);
2247 }
2248 }
2249
2250 break;
2251 }
2252 }
2253 }
2254
device_add_ucm_port(pa_alsa_ucm_device * device,pa_alsa_ucm_port_data * port)2255 static void device_add_ucm_port(pa_alsa_ucm_device *device, pa_alsa_ucm_port_data *port) {
2256 pa_assert(device);
2257 pa_assert(port);
2258
2259 pa_dynarray_append(device->ucm_ports, port);
2260 }
2261
device_set_jack(pa_alsa_ucm_device * device,pa_alsa_jack * jack)2262 static void device_set_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack) {
2263 pa_assert(device);
2264 pa_assert(jack);
2265
2266 device->jack = jack;
2267 pa_alsa_jack_add_ucm_device(jack, device);
2268
2269 pa_alsa_ucm_device_update_available(device);
2270 }
2271
device_add_hw_mute_jack(pa_alsa_ucm_device * device,pa_alsa_jack * jack)2272 static void device_add_hw_mute_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack) {
2273 pa_assert(device);
2274 pa_assert(jack);
2275
2276 pa_dynarray_append(device->hw_mute_jacks, jack);
2277 pa_alsa_jack_add_ucm_hw_mute_device(jack, device);
2278
2279 pa_alsa_ucm_device_update_available(device);
2280 }
2281
device_set_available(pa_alsa_ucm_device * device,pa_available_t available)2282 static void device_set_available(pa_alsa_ucm_device *device, pa_available_t available) {
2283 pa_alsa_ucm_port_data *port;
2284 unsigned idx;
2285
2286 pa_assert(device);
2287
2288 if (available == device->available)
2289 return;
2290
2291 device->available = available;
2292
2293 PA_DYNARRAY_FOREACH(port, device->ucm_ports, idx)
2294 ucm_port_update_available(port);
2295 }
2296
pa_alsa_ucm_device_update_available(pa_alsa_ucm_device * device)2297 void pa_alsa_ucm_device_update_available(pa_alsa_ucm_device *device) {
2298 pa_available_t available = PA_AVAILABLE_UNKNOWN;
2299 pa_alsa_jack *jack;
2300 unsigned idx;
2301
2302 pa_assert(device);
2303
2304 if (device->jack && device->jack->has_control)
2305 available = device->jack->plugged_in ? PA_AVAILABLE_YES : PA_AVAILABLE_NO;
2306
2307 PA_DYNARRAY_FOREACH(jack, device->hw_mute_jacks, idx) {
2308 if (jack->plugged_in) {
2309 available = PA_AVAILABLE_NO;
2310 break;
2311 }
2312 }
2313
2314 device_set_available(device, available);
2315 }
2316
ucm_port_data_init(pa_alsa_ucm_port_data * port,pa_alsa_ucm_config * ucm,pa_device_port * core_port,pa_alsa_ucm_device ** devices,unsigned n_devices)2317 static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port,
2318 pa_alsa_ucm_device **devices, unsigned n_devices) {
2319 unsigned i;
2320
2321 pa_assert(ucm);
2322 pa_assert(core_port);
2323 pa_assert(devices);
2324
2325 port->ucm = ucm;
2326 port->core_port = core_port;
2327 port->devices = pa_dynarray_new(NULL);
2328 port->eld_device = -1;
2329
2330 for (i = 0; i < n_devices; i++) {
2331 pa_dynarray_append(port->devices, devices[i]);
2332 device_add_ucm_port(devices[i], port);
2333 }
2334
2335 port->paths = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree,
2336 (pa_free_cb_t) pa_alsa_path_free);
2337
2338 ucm_port_update_available(port);
2339 }
2340
ucm_port_data_free(pa_device_port * port)2341 static void ucm_port_data_free(pa_device_port *port) {
2342 pa_alsa_ucm_port_data *ucm_port;
2343
2344 pa_assert(port);
2345
2346 ucm_port = PA_DEVICE_PORT_DATA(port);
2347
2348 if (ucm_port->devices)
2349 pa_dynarray_free(ucm_port->devices);
2350
2351 if (ucm_port->paths)
2352 pa_hashmap_free(ucm_port->paths);
2353
2354 pa_xfree(ucm_port->eld_mixer_device_name);
2355 }
2356
ucm_port_update_available(pa_alsa_ucm_port_data * port)2357 static void ucm_port_update_available(pa_alsa_ucm_port_data *port) {
2358 pa_alsa_ucm_device *device;
2359 unsigned idx;
2360 pa_available_t available = PA_AVAILABLE_YES;
2361
2362 pa_assert(port);
2363
2364 PA_DYNARRAY_FOREACH(device, port->devices, idx) {
2365 if (device->available == PA_AVAILABLE_UNKNOWN)
2366 available = PA_AVAILABLE_UNKNOWN;
2367 else if (device->available == PA_AVAILABLE_NO) {
2368 available = PA_AVAILABLE_NO;
2369 break;
2370 }
2371 }
2372
2373 pa_device_port_set_available(port->core_port, available);
2374 }
2375
2376 #else /* HAVE_ALSA_UCM */
2377
2378 /* Dummy functions for systems without UCM support */
2379
pa_alsa_ucm_query_profiles(pa_alsa_ucm_config * ucm,int card_index)2380 int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index) {
2381 pa_log_info("UCM not available.");
2382 return -1;
2383 }
2384
pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config * ucm,pa_channel_map * default_channel_map)2385 pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map) {
2386 return NULL;
2387 }
2388
pa_alsa_ucm_set_profile(pa_alsa_ucm_config * ucm,pa_card * card,const char * new_profile,const char * old_profile)2389 int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile) {
2390 return -1;
2391 }
2392
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)2393 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) {
2394 return -1;
2395 }
2396
pa_alsa_ucm_add_ports(pa_hashmap ** hash,pa_proplist * proplist,pa_alsa_ucm_mapping_context * context,bool is_sink,pa_card * card,snd_pcm_t * pcm_handle,bool ignore_dB)2397 void pa_alsa_ucm_add_ports(
2398 pa_hashmap **hash,
2399 pa_proplist *proplist,
2400 pa_alsa_ucm_mapping_context *context,
2401 bool is_sink,
2402 pa_card *card,
2403 snd_pcm_t *pcm_handle,
2404 bool ignore_dB) {
2405 }
2406
pa_alsa_ucm_add_ports_combination(pa_hashmap * hash,pa_alsa_ucm_mapping_context * context,bool is_sink,pa_hashmap * ports,pa_card_profile * cp,pa_core * core)2407 void pa_alsa_ucm_add_ports_combination(
2408 pa_hashmap *hash,
2409 pa_alsa_ucm_mapping_context *context,
2410 bool is_sink,
2411 pa_hashmap *ports,
2412 pa_card_profile *cp,
2413 pa_core *core) {
2414 }
2415
pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context * context,pa_device_port * port,bool is_sink)2416 int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) {
2417 return -1;
2418 }
2419
pa_alsa_ucm_free(pa_alsa_ucm_config * ucm)2420 void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) {
2421 }
2422
pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context * context)2423 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
2424 }
2425
pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config * ucm,const char * role,pa_direction_t dir)2426 void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
2427 }
2428
pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config * ucm,const char * role,pa_direction_t dir)2429 void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
2430 }
2431
2432 #endif
2433