xref: /third_party/alsa-lib/src/ucm/utils.c (revision d5ac70f0)
1/*
2 *  This library is free software; you can redistribute it and/or
3 *  modify it under the terms of the GNU Lesser General Public
4 *  License as published by the Free Software Foundation; either
5 *  version 2 of the License, or (at your option) any later version.
6 *
7 *  This library is distributed in the hope that it will be useful,
8 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10 *  Lesser General Public License for more details.
11 *
12 *  You should have received a copy of the GNU Lesser General Public
13 *  License along with this library; if not, write to the Free Software
14 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
15 *
16 *  Support for the verb/device/modifier core logic and API,
17 *  command line tool and file parser was kindly sponsored by
18 *  Texas Instruments Inc.
19 *  Support for multiple active modifiers and devices,
20 *  transition sequences, multiple client access and user defined use
21 *  cases was kindly sponsored by Wolfson Microelectronics PLC.
22 *
23 *  Copyright (C) 2008-2010 SlimLogic Ltd
24 *  Copyright (C) 2010 Wolfson Microelectronics PLC
25 *  Copyright (C) 2010 Texas Instruments Inc.
26 *  Copyright (C) 2010 Red Hat Inc.
27 *  Authors: Liam Girdwood <lrg@slimlogic.co.uk>
28 *	         Stefan Schmidt <stefan@slimlogic.co.uk>
29 *	         Justin Xu <justinx@slimlogic.co.uk>
30 *               Jaroslav Kysela <perex@perex.cz>
31 */
32
33#include "ucm_local.h"
34
35void uc_mgr_error(const char *fmt,...)
36{
37	va_list va;
38	va_start(va, fmt);
39	fprintf(stderr, "ucm: ");
40	vfprintf(stderr, fmt, va);
41	va_end(va);
42}
43
44void uc_mgr_stdout(const char *fmt,...)
45{
46	va_list va;
47	va_start(va, fmt);
48	vfprintf(stdout, fmt, va);
49	va_end(va);
50}
51
52const char *uc_mgr_sysfs_root(void)
53{
54	const char *e = getenv("SYSFS_PATH");
55	if (e == NULL)
56		return "/sys";
57	if (*e == '\0')
58		uc_error("no sysfs root!");
59	return e;
60}
61
62struct ctl_list *uc_mgr_get_master_ctl(snd_use_case_mgr_t *uc_mgr)
63{
64	struct list_head *pos;
65	struct ctl_list *ctl_list = NULL, *ctl_list2;
66
67	list_for_each(pos, &uc_mgr->ctl_list) {
68		ctl_list2 = list_entry(pos, struct ctl_list, list);
69		if (ctl_list2->slave)
70			continue;
71		if (ctl_list) {
72			uc_error("multiple control device names were found!");
73			return NULL;
74		}
75		ctl_list = ctl_list2;
76	}
77	return ctl_list;
78}
79
80struct ctl_list *uc_mgr_get_ctl_by_card(snd_use_case_mgr_t *uc_mgr, int card)
81{
82	struct ctl_list *ctl_list;
83	char cname[32];
84	int err;
85
86	sprintf(cname, "hw:%d", card);
87	err = uc_mgr_open_ctl(uc_mgr, &ctl_list, cname, 1);
88	if (err < 0)
89		return NULL;
90	return ctl_list;
91}
92
93struct ctl_list *uc_mgr_get_ctl_by_name(snd_use_case_mgr_t *uc_mgr, const char *name, int idx)
94{
95	struct list_head *pos;
96	struct ctl_list *ctl_list;
97	const char *s;
98	int idx2, card;
99
100	idx2 = idx;
101	list_for_each(pos, &uc_mgr->ctl_list) {
102		ctl_list = list_entry(pos, struct ctl_list, list);
103		s = snd_ctl_card_info_get_name(ctl_list->ctl_info);
104		if (s == NULL)
105			continue;
106		if (strcmp(s, name) == 0) {
107			if (idx2 == 0)
108				return ctl_list;
109			idx2--;
110		}
111	}
112
113	idx2 = idx;
114	card = -1;
115	if (snd_card_next(&card) < 0 || card < 0)
116		return NULL;
117
118	while (card >= 0) {
119		ctl_list = uc_mgr_get_ctl_by_card(uc_mgr, card);
120		if (ctl_list == NULL)
121			continue;	/* really? */
122		s = snd_ctl_card_info_get_name(ctl_list->ctl_info);
123		if (s && strcmp(s, name) == 0) {
124			if (idx2 == 0)
125				return ctl_list;
126			idx2--;
127		}
128		if (snd_card_next(&card) < 0)
129			break;
130	}
131
132	return NULL;
133}
134
135snd_ctl_t *uc_mgr_get_ctl(snd_use_case_mgr_t *uc_mgr)
136{
137	struct ctl_list *ctl_list;
138
139	ctl_list = uc_mgr_get_master_ctl(uc_mgr);
140	if (ctl_list)
141		return ctl_list->ctl;
142	return NULL;
143}
144
145static void uc_mgr_free_ctl(struct ctl_list *ctl_list)
146{
147	struct list_head *pos, *npos;
148	struct ctl_dev *ctl_dev;
149
150	list_for_each_safe(pos, npos, &ctl_list->dev_list) {
151		ctl_dev = list_entry(pos, struct ctl_dev, list);
152		free(ctl_dev->device);
153		free(ctl_dev);
154	}
155	snd_ctl_card_info_free(ctl_list->ctl_info);
156	free(ctl_list);
157}
158
159void uc_mgr_free_ctl_list(snd_use_case_mgr_t *uc_mgr)
160{
161	struct list_head *pos, *npos;
162	struct ctl_list *ctl_list;
163
164	list_for_each_safe(pos, npos, &uc_mgr->ctl_list) {
165		ctl_list = list_entry(pos, struct ctl_list, list);
166		snd_ctl_close(ctl_list->ctl);
167		list_del(&ctl_list->list);
168		uc_mgr_free_ctl(ctl_list);
169	}
170}
171
172static int uc_mgr_ctl_add_dev(struct ctl_list *ctl_list, const char *device)
173{
174	struct list_head *pos;
175	struct ctl_dev *ctl_dev;
176
177	/* skip duplicates */
178	list_for_each(pos, &ctl_list->dev_list) {
179		ctl_dev = list_entry(pos, struct ctl_dev, list);
180		if (strcmp(ctl_dev->device, device) == 0)
181			return 0;
182	}
183
184	/* allocate new device name */
185	ctl_dev = malloc(sizeof(*ctl_dev));
186	if (ctl_dev == NULL)
187		return -ENOMEM;
188	ctl_dev->device = strdup(device);
189	if (ctl_dev->device == NULL) {
190		free(ctl_dev);
191		return -ENOMEM;
192	}
193	list_add_tail(&ctl_dev->list, &ctl_list->dev_list);
194	return 0;
195}
196
197static int uc_mgr_ctl_add(snd_use_case_mgr_t *uc_mgr,
198			  struct ctl_list **ctl_list,
199			  snd_ctl_t *ctl, int card,
200			  snd_ctl_card_info_t *info,
201			  const char *device,
202			  int slave)
203{
204	struct ctl_list *cl = NULL;
205	const char *id = snd_ctl_card_info_get_id(info);
206	char dev[MAX_CARD_LONG_NAME];
207	int err, hit = 0;
208
209	if (id == NULL || id[0] == '\0')
210		return -ENOENT;
211	if (!(*ctl_list)) {
212		cl = malloc(sizeof(*cl));
213		if (cl == NULL)
214			return -ENOMEM;
215		INIT_LIST_HEAD(&cl->dev_list);
216		cl->ctl = ctl;
217		if (snd_ctl_card_info_malloc(&cl->ctl_info) < 0) {
218			free(cl);
219			return -ENOMEM;
220		}
221		snd_ctl_card_info_copy(cl->ctl_info, info);
222		cl->slave = slave;
223		*ctl_list = cl;
224	} else {
225		if (!slave)
226			(*ctl_list)->slave = slave;
227	}
228	if (card >= 0) {
229		snprintf(dev, sizeof(dev), "hw:%d", card);
230		hit |= !!(device && (strcmp(dev, device) == 0));
231		err = uc_mgr_ctl_add_dev(*ctl_list, dev);
232		if (err < 0)
233			goto __nomem;
234	}
235	snprintf(dev, sizeof(dev), "hw:%s", id);
236	hit |= !!(device && (strcmp(dev, device) == 0));
237	err = uc_mgr_ctl_add_dev(*ctl_list, dev);
238	if (err < 0)
239		goto __nomem;
240	/* the UCM name not based on the card name / id */
241	if (!hit && device) {
242		err = uc_mgr_ctl_add_dev(*ctl_list, device);
243		if (err < 0)
244			goto __nomem;
245	}
246
247	list_add_tail(&(*ctl_list)->list, &uc_mgr->ctl_list);
248	return 0;
249
250__nomem:
251	if (*ctl_list == cl) {
252		uc_mgr_free_ctl(cl);
253		*ctl_list = NULL;
254	}
255	return -ENOMEM;
256}
257
258int uc_mgr_open_ctl(snd_use_case_mgr_t *uc_mgr,
259		    struct ctl_list **ctll,
260		    const char *device,
261		    int slave)
262{
263	struct list_head *pos1, *pos2;
264	snd_ctl_t *ctl;
265	struct ctl_list *ctl_list;
266	struct ctl_dev *ctl_dev;
267	snd_ctl_card_info_t *info;
268	const char *id;
269	int err, card, ucm_group, ucm_offset;
270
271	snd_ctl_card_info_alloca(&info);
272
273	ucm_group = _snd_is_ucm_device(device);
274	ucm_offset = ucm_group ? 8 : 0;
275
276	/* cache lookup */
277	list_for_each(pos1, &uc_mgr->ctl_list) {
278		ctl_list = list_entry(pos1, struct ctl_list, list);
279		if (ctl_list->ucm_group != ucm_group)
280			continue;
281		list_for_each(pos2, &ctl_list->dev_list) {
282			ctl_dev = list_entry(pos2, struct ctl_dev, list);
283			if (strcmp(ctl_dev->device, device + ucm_offset) == 0) {
284				*ctll = ctl_list;
285				if (!slave)
286					ctl_list->slave = 0;
287				return 0;
288			}
289		}
290	}
291
292	err = snd_ctl_open(&ctl, device, 0);
293	if (err < 0)
294		return err;
295
296	id = NULL;
297	err = snd_ctl_card_info(ctl, info);
298	if (err == 0)
299		id = snd_ctl_card_info_get_id(info);
300	if (err < 0 || id == NULL || id[0] == '\0') {
301		uc_error("control hardware info (%s): %s", device, snd_strerror(err));
302		snd_ctl_close(ctl);
303		return err >= 0 ? -EINVAL : err;
304	}
305
306	/* insert to cache, if just name differs */
307	list_for_each(pos1, &uc_mgr->ctl_list) {
308		ctl_list = list_entry(pos1, struct ctl_list, list);
309		if (ctl_list->ucm_group != ucm_group)
310			continue;
311		if (strcmp(id, snd_ctl_card_info_get_id(ctl_list->ctl_info)) == 0) {
312			card = snd_card_get_index(id);
313			err = uc_mgr_ctl_add(uc_mgr, &ctl_list, ctl, card, info, device + ucm_offset, slave);
314			if (err < 0)
315				goto __nomem;
316			snd_ctl_close(ctl);
317			ctl_list->ucm_group = ucm_group;
318			*ctll = ctl_list;
319			return 0;
320		}
321	}
322
323	ctl_list = NULL;
324	err = uc_mgr_ctl_add(uc_mgr, &ctl_list, ctl, -1, info, device + ucm_offset, slave);
325	if (err < 0)
326		goto __nomem;
327
328	ctl_list->ucm_group = ucm_group;
329	*ctll = ctl_list;
330	return 0;
331
332__nomem:
333	snd_ctl_close(ctl);
334	return -ENOMEM;
335}
336
337const char *uc_mgr_config_dir(int format)
338{
339	const char *path;
340
341	if (format >= 2) {
342		path = getenv(ALSA_CONFIG_UCM2_VAR);
343		if (!path || path[0] == '\0')
344			path = ALSA_CONFIG_DIR "/ucm2";
345	} else {
346		path = getenv(ALSA_CONFIG_UCM_VAR);
347		if (!path || path[0] == '\0')
348			path = ALSA_CONFIG_DIR "/ucm";
349	}
350	return path;
351}
352
353int uc_mgr_config_load_into(int format, const char *file, snd_config_t *top)
354{
355	FILE *fp;
356	snd_input_t *in;
357	const char *default_paths[2];
358	int err;
359
360	fp = fopen(file, "r");
361	if (!fp) {
362		err = -errno;
363  __err_open:
364		uc_error("could not open configuration file %s", file);
365		return err;
366	}
367	err = snd_input_stdio_attach(&in, fp, 1);
368	if (err < 0)
369		goto __err_open;
370
371	default_paths[0] = uc_mgr_config_dir(format);
372	default_paths[1] = NULL;
373	err = _snd_config_load_with_include(top, in, 0, default_paths);
374	if (err < 0) {
375		uc_error("could not load configuration file %s", file);
376		if (in)
377			snd_input_close(in);
378		return err;
379	}
380	err = snd_input_close(in);
381	if (err < 0)
382		return err;
383	return 0;
384}
385
386int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg)
387{
388	snd_config_t *top;
389	int err;
390
391	err = snd_config_top(&top);
392	if (err < 0)
393		return err;
394	err = uc_mgr_config_load_into(format, file, top);
395	if (err < 0) {
396		snd_config_delete(top);
397		return err;
398	}
399	*cfg = top;
400	return 0;
401}
402
403static void uc_mgr_free_value1(struct ucm_value *val)
404{
405	free(val->name);
406	free(val->data);
407	list_del(&val->list);
408	free(val);
409}
410
411void uc_mgr_free_value(struct list_head *base)
412{
413	struct list_head *pos, *npos;
414	struct ucm_value *val;
415
416	list_for_each_safe(pos, npos, base) {
417		val = list_entry(pos, struct ucm_value, list);
418		uc_mgr_free_value1(val);
419	}
420}
421
422void uc_mgr_free_dev_list(struct dev_list *dev_list)
423{
424	struct list_head *pos, *npos;
425	struct dev_list_node *dlist;
426
427	list_for_each_safe(pos, npos, &dev_list->list) {
428		dlist = list_entry(pos, struct dev_list_node, list);
429		free(dlist->name);
430		list_del(&dlist->list);
431		free(dlist);
432	}
433}
434
435int uc_mgr_put_to_dev_list(struct dev_list *dev_list, const char *name)
436{
437	struct list_head *pos;
438	struct dev_list_node *dlist;
439	char *n;
440
441	list_for_each(pos, &dev_list->list) {
442		dlist = list_entry(pos, struct dev_list_node, list);
443		if (strcmp(dlist->name, name) == 0)
444			return 0;
445	}
446
447	dlist = calloc(1, sizeof(*dlist));
448	if (dlist == NULL)
449		return -ENOMEM;
450	n = strdup(name);
451	if (n == NULL) {
452		free(dlist);
453		return -ENOMEM;
454	}
455	dlist->name = n;
456	list_add(&dlist->list, &dev_list->list);
457	return 0;
458}
459
460int uc_mgr_rename_in_dev_list(struct dev_list *dev_list, const char *src,
461			      const char *dst)
462{
463	struct list_head *pos;
464	struct dev_list_node *dlist;
465	char *dst1;
466
467	list_for_each(pos, &dev_list->list) {
468		dlist = list_entry(pos, struct dev_list_node, list);
469		if (strcmp(dlist->name, src) == 0) {
470			dst1 = strdup(dst);
471			if (dst1 == NULL)
472				return -ENOMEM;
473			free(dlist->name);
474			dlist->name = dst1;
475			return 0;
476		}
477	}
478	return -ENODEV;
479}
480
481int uc_mgr_remove_from_dev_list(struct dev_list *dev_list, const char *name)
482{
483	struct list_head *pos;
484	struct dev_list_node *dlist;
485
486	list_for_each(pos, &dev_list->list) {
487		dlist = list_entry(pos, struct dev_list_node, list);
488		if (strcmp(dlist->name, name) == 0) {
489			free(dlist->name);
490			list_del(&dlist->list);
491			free(dlist);
492			return 0;
493		}
494	}
495	return -ENODEV;
496}
497
498void uc_mgr_free_sequence_element(struct sequence_element *seq)
499{
500	if (seq == NULL)
501		return;
502	switch (seq->type) {
503	case SEQUENCE_ELEMENT_TYPE_CDEV:
504		free(seq->data.cdev);
505		break;
506	case SEQUENCE_ELEMENT_TYPE_CSET:
507	case SEQUENCE_ELEMENT_TYPE_CSET_NEW:
508	case SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE:
509	case SEQUENCE_ELEMENT_TYPE_CSET_TLV:
510	case SEQUENCE_ELEMENT_TYPE_CTL_REMOVE:
511		free(seq->data.cset);
512		break;
513	case SEQUENCE_ELEMENT_TYPE_SYSSET:
514		free(seq->data.sysw);
515		break;
516	case SEQUENCE_ELEMENT_TYPE_EXEC:
517	case SEQUENCE_ELEMENT_TYPE_SHELL:
518		free(seq->data.exec);
519		break;
520	case SEQUENCE_ELEMENT_TYPE_CFGSAVE:
521		free(seq->data.cfgsave);
522		break;
523	case SEQUENCE_ELEMENT_TYPE_DEV_ENABLE_SEQ:
524	case SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_SEQ:
525		free(seq->data.device);
526		break;
527	default:
528		break;
529	}
530	free(seq);
531}
532
533void uc_mgr_free_sequence(struct list_head *base)
534{
535	struct list_head *pos, *npos;
536	struct sequence_element *seq;
537
538	list_for_each_safe(pos, npos, base) {
539		seq = list_entry(pos, struct sequence_element, list);
540		list_del(&seq->list);
541		uc_mgr_free_sequence_element(seq);
542	}
543}
544
545void uc_mgr_free_transition_element(struct transition_sequence *tseq)
546{
547	free(tseq->name);
548	uc_mgr_free_sequence(&tseq->transition_list);
549	free(tseq);
550}
551
552void uc_mgr_free_transition(struct list_head *base)
553{
554	struct list_head *pos, *npos;
555	struct transition_sequence *tseq;
556
557	list_for_each_safe(pos, npos, base) {
558		tseq = list_entry(pos, struct transition_sequence, list);
559		list_del(&tseq->list);
560		uc_mgr_free_transition_element(tseq);
561	}
562}
563
564void uc_mgr_free_dev_name_list(struct list_head *base)
565{
566	struct list_head *pos, *npos;
567	struct ucm_dev_name *dev;
568
569	list_for_each_safe(pos, npos, base) {
570		dev = list_entry(pos, struct ucm_dev_name, list);
571		list_del(&dev->list);
572		free(dev->name1);
573		free(dev->name2);
574		free(dev);
575	}
576}
577
578void uc_mgr_free_modifier(struct list_head *base)
579{
580	struct list_head *pos, *npos;
581	struct use_case_modifier *mod;
582
583	list_for_each_safe(pos, npos, base) {
584		mod = list_entry(pos, struct use_case_modifier, list);
585		free(mod->name);
586		free(mod->comment);
587		uc_mgr_free_sequence(&mod->enable_list);
588		uc_mgr_free_sequence(&mod->disable_list);
589		uc_mgr_free_transition(&mod->transition_list);
590		uc_mgr_free_dev_list(&mod->dev_list);
591		uc_mgr_free_value(&mod->value_list);
592		list_del(&mod->list);
593		free(mod);
594	}
595}
596
597void uc_mgr_free_device(struct use_case_device *dev)
598{
599	free(dev->name);
600	free(dev->comment);
601	uc_mgr_free_sequence(&dev->enable_list);
602	uc_mgr_free_sequence(&dev->disable_list);
603	uc_mgr_free_transition(&dev->transition_list);
604	uc_mgr_free_dev_list(&dev->dev_list);
605	uc_mgr_free_value(&dev->value_list);
606	list_del(&dev->list);
607	free(dev);
608}
609
610void uc_mgr_free_device_list(struct list_head *base)
611{
612	struct list_head *pos, *npos;
613	struct use_case_device *dev;
614
615	list_for_each_safe(pos, npos, base) {
616		dev = list_entry(pos, struct use_case_device, list);
617		uc_mgr_free_device(dev);
618	}
619}
620
621int uc_mgr_rename_device(struct use_case_verb *verb, const char *src,
622			 const char *dst)
623{
624	struct use_case_device *device;
625	struct list_head *pos, *npos;
626	char *dst1;
627
628	/* no errors when device is not found */
629	list_for_each_safe(pos, npos, &verb->device_list) {
630		device = list_entry(pos, struct use_case_device, list);
631		if (strcmp(device->name, src) == 0) {
632			dst1 = strdup(dst);
633			if (dst1 == NULL)
634				return -ENOMEM;
635			free(device->name);
636			device->name = dst1;
637			continue;
638		}
639		uc_mgr_rename_in_dev_list(&device->dev_list, src, dst);
640	}
641	return 0;
642}
643
644int uc_mgr_remove_device(struct use_case_verb *verb, const char *name)
645{
646	struct use_case_device *device;
647	struct list_head *pos, *npos;
648	int err, found = 0;
649
650	list_for_each_safe(pos, npos, &verb->device_list) {
651		device = list_entry(pos, struct use_case_device, list);
652		if (strcmp(device->name, name) == 0) {
653			uc_mgr_free_device(device);
654			found++;
655			continue;
656		}
657		err = uc_mgr_remove_from_dev_list(&device->dev_list, name);
658		if (err < 0 && err != -ENODEV)
659			return err;
660		if (err == 0)
661			found++;
662	}
663	return found == 0 ? -ENODEV : 0;
664}
665
666const char *uc_mgr_get_variable(snd_use_case_mgr_t *uc_mgr, const char *name)
667{
668	struct list_head *pos;
669	struct ucm_value *value;
670
671	list_for_each(pos, &uc_mgr->variable_list) {
672		value = list_entry(pos, struct ucm_value, list);
673		if (strcmp(value->name, name) == 0)
674			return value->data;
675	}
676	return NULL;
677}
678
679int uc_mgr_set_variable(snd_use_case_mgr_t *uc_mgr, const char *name,
680			const char *val)
681{
682	struct list_head *pos;
683	struct ucm_value *curr;
684	char *val2;
685
686	list_for_each(pos, &uc_mgr->variable_list) {
687		curr = list_entry(pos, struct ucm_value, list);
688		if (strcmp(curr->name, name) == 0) {
689			val2 = strdup(val);
690			if (val2 == NULL)
691				return -ENOMEM;
692			free(curr->data);
693			curr->data = val2;
694			return 0;
695		}
696	}
697
698	curr = calloc(1, sizeof(struct ucm_value));
699	if (curr == NULL)
700		return -ENOMEM;
701	curr->name = strdup(name);
702	if (curr->name == NULL) {
703		free(curr);
704		return -ENOMEM;
705	}
706	curr->data = strdup(val);
707	if (curr->data == NULL) {
708		free(curr->name);
709		free(curr);
710		return -ENOMEM;
711	}
712	list_add_tail(&curr->list, &uc_mgr->variable_list);
713	return 0;
714}
715
716int uc_mgr_delete_variable(snd_use_case_mgr_t *uc_mgr, const char *name)
717{
718	struct list_head *pos;
719	struct ucm_value *curr;
720
721	list_for_each(pos, &uc_mgr->variable_list) {
722		curr = list_entry(pos, struct ucm_value, list);
723		if (strcmp(curr->name, name) == 0) {
724			uc_mgr_free_value1(curr);
725			return 0;
726		}
727	}
728
729	return -ENOENT;
730}
731
732void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr)
733{
734	struct list_head *pos, *npos;
735	struct use_case_verb *verb;
736
737	if (uc_mgr->local_config) {
738		snd_config_delete(uc_mgr->local_config);
739		uc_mgr->local_config = NULL;
740	}
741	if (uc_mgr->macros) {
742		snd_config_delete(uc_mgr->macros);
743		uc_mgr->macros = NULL;
744	}
745	list_for_each_safe(pos, npos, &uc_mgr->verb_list) {
746		verb = list_entry(pos, struct use_case_verb, list);
747		free(verb->name);
748		free(verb->comment);
749		uc_mgr_free_sequence(&verb->enable_list);
750		uc_mgr_free_sequence(&verb->disable_list);
751		uc_mgr_free_transition(&verb->transition_list);
752		uc_mgr_free_value(&verb->value_list);
753		uc_mgr_free_device_list(&verb->device_list);
754		uc_mgr_free_device_list(&verb->cmpt_device_list);
755		uc_mgr_free_modifier(&verb->modifier_list);
756		uc_mgr_free_dev_name_list(&verb->rename_list);
757		uc_mgr_free_dev_name_list(&verb->remove_list);
758		list_del(&verb->list);
759		free(verb);
760	}
761	uc_mgr_free_sequence(&uc_mgr->fixedboot_list);
762	uc_mgr_free_sequence(&uc_mgr->boot_list);
763	uc_mgr_free_sequence(&uc_mgr->default_list);
764	uc_mgr_free_value(&uc_mgr->value_list);
765	uc_mgr_free_value(&uc_mgr->variable_list);
766	free(uc_mgr->comment);
767	free(uc_mgr->conf_dir_name);
768	free(uc_mgr->conf_file_name);
769	uc_mgr->comment = NULL;
770	uc_mgr->conf_dir_name = NULL;
771	uc_mgr->conf_file_name = NULL;
772	uc_mgr->active_verb = NULL;
773	INIT_LIST_HEAD(&uc_mgr->active_devices);
774	INIT_LIST_HEAD(&uc_mgr->active_modifiers);
775}
776
777void uc_mgr_free(snd_use_case_mgr_t *uc_mgr)
778{
779	uc_mgr_free_verb(uc_mgr);
780	uc_mgr_free_ctl_list(uc_mgr);
781	free(uc_mgr->card_name);
782	free(uc_mgr);
783}
784
785/*
786 * UCM card list stuff
787 */
788
789static pthread_mutex_t ucm_cards_mutex = PTHREAD_MUTEX_INITIALIZER;
790static LIST_HEAD(ucm_cards);
791static unsigned int ucm_card_assign;
792
793static snd_use_case_mgr_t *uc_mgr_card_find(unsigned int card_number)
794{
795	struct list_head *pos;
796	snd_use_case_mgr_t *uc_mgr;
797
798	list_for_each(pos, &ucm_cards) {
799		uc_mgr = list_entry(pos, snd_use_case_mgr_t, cards_list);
800		if (uc_mgr->ucm_card_number == card_number)
801			return uc_mgr;
802	}
803	return NULL;
804}
805
806int uc_mgr_card_open(snd_use_case_mgr_t *uc_mgr)
807{
808	unsigned int prev;
809
810	pthread_mutex_lock(&ucm_cards_mutex);
811	prev = ucm_card_assign++;
812	while (uc_mgr_card_find(ucm_card_assign)) {
813		ucm_card_assign++;
814		ucm_card_assign &= 0xffff;
815		/* avoid zero card instance number */
816		if (ucm_card_assign == 0)
817			ucm_card_assign++;
818		if (ucm_card_assign == prev) {
819			pthread_mutex_unlock(&ucm_cards_mutex);
820			return -ENOMEM;
821		}
822	}
823	uc_mgr->ucm_card_number = ucm_card_assign;
824	list_add(&uc_mgr->cards_list, &ucm_cards);
825	pthread_mutex_unlock(&ucm_cards_mutex);
826	return 0;
827}
828
829void uc_mgr_card_close(snd_use_case_mgr_t *uc_mgr)
830{
831	pthread_mutex_lock(&ucm_cards_mutex);
832	list_del(&uc_mgr->cards_list);
833	pthread_mutex_unlock(&ucm_cards_mutex);
834}
835
836/**
837 * \brief Get library configuration based on the private ALSA device name
838 * \param name[in] ALSA device name
839 * \retval config A configuration tree or NULL
840 *
841 * The returned configuration (non-NULL) should be unreferenced using
842 * snd_config_unref() call.
843 */
844const char *uc_mgr_alibcfg_by_device(snd_config_t **top, const char *name)
845{
846	char buf[5];
847	long card_num;
848	snd_config_t *config;
849	snd_use_case_mgr_t *uc_mgr;
850	int err;
851
852	if (strncmp(name, "_ucm", 4) || strlen(name) < 12 || name[8] != '.')
853		return NULL;
854	strncpy(buf, name + 4, 4);
855	buf[4] = '\0';
856	err = safe_strtol_base(buf, &card_num, 16);
857	if (err < 0 || card_num < 0 || card_num > 0xffff)
858		return NULL;
859	config = NULL;
860	pthread_mutex_lock(&ucm_cards_mutex);
861	uc_mgr = uc_mgr_card_find(card_num);
862	/* non-empty configs are accepted only */
863	if (uc_mgr_has_local_config(uc_mgr)) {
864		config = uc_mgr->local_config;
865		snd_config_ref(config);
866	}
867	pthread_mutex_unlock(&ucm_cards_mutex);
868	if (!config)
869		return NULL;
870	*top = config;
871	return name + 9;
872}
873