• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
uc_mgr_error(const char * fmt,...)35 void 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 
uc_mgr_stdout(const char * fmt,...)44 void 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 
uc_mgr_sysfs_root(void)52 const 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 
uc_mgr_get_master_ctl(snd_use_case_mgr_t * uc_mgr)62 struct 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 
uc_mgr_get_ctl_by_card(snd_use_case_mgr_t * uc_mgr,int card)80 struct 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 
uc_mgr_get_ctl_by_name(snd_use_case_mgr_t * uc_mgr,const char * name,int idx)93 struct 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 
uc_mgr_get_ctl(snd_use_case_mgr_t * uc_mgr)135 snd_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 
uc_mgr_free_ctl(struct ctl_list * ctl_list)145 static 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 
uc_mgr_free_ctl_list(snd_use_case_mgr_t * uc_mgr)159 void 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 
uc_mgr_ctl_add_dev(struct ctl_list * ctl_list,const char * device)172 static 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 
uc_mgr_ctl_add(snd_use_case_mgr_t * uc_mgr,struct ctl_list ** ctl_list,snd_ctl_t * ctl,int card,snd_ctl_card_info_t * info,const char * device,int slave)197 static 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 
uc_mgr_open_ctl(snd_use_case_mgr_t * uc_mgr,struct ctl_list ** ctll,const char * device,int slave)258 int 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 
uc_mgr_config_dir(int format)337 const 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 
uc_mgr_config_load_into(int format,const char * file,snd_config_t * top)353 int 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 
uc_mgr_config_load(int format,const char * file,snd_config_t ** cfg)386 int 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 
uc_mgr_free_value1(struct ucm_value * val)403 static 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 
uc_mgr_free_value(struct list_head * base)411 void 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 
uc_mgr_free_dev_list(struct dev_list * dev_list)422 void 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 
uc_mgr_put_to_dev_list(struct dev_list * dev_list,const char * name)435 int 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 
uc_mgr_rename_in_dev_list(struct dev_list * dev_list,const char * src,const char * dst)460 int 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 
uc_mgr_remove_from_dev_list(struct dev_list * dev_list,const char * name)481 int 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 
uc_mgr_free_sequence_element(struct sequence_element * seq)498 void 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 
uc_mgr_free_sequence(struct list_head * base)533 void 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 
uc_mgr_free_transition_element(struct transition_sequence * tseq)545 void 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 
uc_mgr_free_transition(struct list_head * base)552 void 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 
uc_mgr_free_dev_name_list(struct list_head * base)564 void 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 
uc_mgr_free_modifier(struct list_head * base)578 void 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 
uc_mgr_free_device(struct use_case_device * dev)597 void 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 
uc_mgr_free_device_list(struct list_head * base)610 void 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 
uc_mgr_rename_device(struct use_case_verb * verb,const char * src,const char * dst)621 int 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 
uc_mgr_remove_device(struct use_case_verb * verb,const char * name)644 int 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 
uc_mgr_get_variable(snd_use_case_mgr_t * uc_mgr,const char * name)666 const 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 
uc_mgr_set_variable(snd_use_case_mgr_t * uc_mgr,const char * name,const char * val)679 int 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 
uc_mgr_delete_variable(snd_use_case_mgr_t * uc_mgr,const char * name)716 int 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 
uc_mgr_free_verb(snd_use_case_mgr_t * uc_mgr)732 void 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 
uc_mgr_free(snd_use_case_mgr_t * uc_mgr)777 void 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 
789 static pthread_mutex_t ucm_cards_mutex = PTHREAD_MUTEX_INITIALIZER;
790 static LIST_HEAD(ucm_cards);
791 static unsigned int ucm_card_assign;
792 
uc_mgr_card_find(unsigned int card_number)793 static 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 
uc_mgr_card_open(snd_use_case_mgr_t * uc_mgr)806 int 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 
uc_mgr_card_close(snd_use_case_mgr_t * uc_mgr)829 void 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  */
uc_mgr_alibcfg_by_device(snd_config_t ** top,const char * name)844 const 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