• 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_value(struct list_head * base)403 void uc_mgr_free_value(struct list_head *base)
404 {
405 	struct list_head *pos, *npos;
406 	struct ucm_value *val;
407 
408 	list_for_each_safe(pos, npos, base) {
409 		val = list_entry(pos, struct ucm_value, list);
410 		free(val->name);
411 		free(val->data);
412 		list_del(&val->list);
413 		free(val);
414 	}
415 }
416 
uc_mgr_free_dev_list(struct dev_list * dev_list)417 void uc_mgr_free_dev_list(struct dev_list *dev_list)
418 {
419 	struct list_head *pos, *npos;
420 	struct dev_list_node *dlist;
421 
422 	list_for_each_safe(pos, npos, &dev_list->list) {
423 		dlist = list_entry(pos, struct dev_list_node, list);
424 		free(dlist->name);
425 		list_del(&dlist->list);
426 		free(dlist);
427 	}
428 }
429 
uc_mgr_put_to_dev_list(struct dev_list * dev_list,const char * name)430 int uc_mgr_put_to_dev_list(struct dev_list *dev_list, const char *name)
431 {
432 	struct list_head *pos;
433 	struct dev_list_node *dlist;
434 	char *n;
435 
436 	list_for_each(pos, &dev_list->list) {
437 		dlist = list_entry(pos, struct dev_list_node, list);
438 		if (strcmp(dlist->name, name) == 0)
439 			return 0;
440 	}
441 
442 	dlist = calloc(1, sizeof(*dlist));
443 	if (dlist == NULL)
444 		return -ENOMEM;
445 	n = strdup(name);
446 	if (n == NULL) {
447 		free(dlist);
448 		return -ENOMEM;
449 	}
450 	dlist->name = n;
451 	list_add(&dlist->list, &dev_list->list);
452 	return 0;
453 }
454 
uc_mgr_rename_in_dev_list(struct dev_list * dev_list,const char * src,const char * dst)455 int uc_mgr_rename_in_dev_list(struct dev_list *dev_list, const char *src,
456 			      const char *dst)
457 {
458 	struct list_head *pos;
459 	struct dev_list_node *dlist;
460 	char *dst1;
461 
462 	list_for_each(pos, &dev_list->list) {
463 		dlist = list_entry(pos, struct dev_list_node, list);
464 		if (strcmp(dlist->name, src) == 0) {
465 			dst1 = strdup(dst);
466 			if (dst1 == NULL)
467 				return -ENOMEM;
468 			free(dlist->name);
469 			dlist->name = dst1;
470 			return 0;
471 		}
472 	}
473 	return -ENODEV;
474 }
475 
uc_mgr_remove_from_dev_list(struct dev_list * dev_list,const char * name)476 int uc_mgr_remove_from_dev_list(struct dev_list *dev_list, const char *name)
477 {
478 	struct list_head *pos;
479 	struct dev_list_node *dlist;
480 
481 	list_for_each(pos, &dev_list->list) {
482 		dlist = list_entry(pos, struct dev_list_node, list);
483 		if (strcmp(dlist->name, name) == 0) {
484 			free(dlist->name);
485 			list_del(&dlist->list);
486 			free(dlist);
487 			return 0;
488 		}
489 	}
490 	return -ENODEV;
491 }
492 
uc_mgr_free_sequence_element(struct sequence_element * seq)493 void uc_mgr_free_sequence_element(struct sequence_element *seq)
494 {
495 	if (seq == NULL)
496 		return;
497 	switch (seq->type) {
498 	case SEQUENCE_ELEMENT_TYPE_CDEV:
499 		free(seq->data.cdev);
500 		break;
501 	case SEQUENCE_ELEMENT_TYPE_CSET:
502 	case SEQUENCE_ELEMENT_TYPE_CSET_NEW:
503 	case SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE:
504 	case SEQUENCE_ELEMENT_TYPE_CSET_TLV:
505 	case SEQUENCE_ELEMENT_TYPE_CTL_REMOVE:
506 		free(seq->data.cset);
507 		break;
508 	case SEQUENCE_ELEMENT_TYPE_SYSSET:
509 		free(seq->data.sysw);
510 		break;
511 	case SEQUENCE_ELEMENT_TYPE_EXEC:
512 	case SEQUENCE_ELEMENT_TYPE_SHELL:
513 		free(seq->data.exec);
514 		break;
515 	case SEQUENCE_ELEMENT_TYPE_CFGSAVE:
516 		free(seq->data.cfgsave);
517 		break;
518 	default:
519 		break;
520 	}
521 	free(seq);
522 }
523 
uc_mgr_free_sequence(struct list_head * base)524 void uc_mgr_free_sequence(struct list_head *base)
525 {
526 	struct list_head *pos, *npos;
527 	struct sequence_element *seq;
528 
529 	list_for_each_safe(pos, npos, base) {
530 		seq = list_entry(pos, struct sequence_element, list);
531 		list_del(&seq->list);
532 		uc_mgr_free_sequence_element(seq);
533 	}
534 }
535 
uc_mgr_free_transition_element(struct transition_sequence * tseq)536 void uc_mgr_free_transition_element(struct transition_sequence *tseq)
537 {
538 	free(tseq->name);
539 	uc_mgr_free_sequence(&tseq->transition_list);
540 	free(tseq);
541 }
542 
uc_mgr_free_transition(struct list_head * base)543 void uc_mgr_free_transition(struct list_head *base)
544 {
545 	struct list_head *pos, *npos;
546 	struct transition_sequence *tseq;
547 
548 	list_for_each_safe(pos, npos, base) {
549 		tseq = list_entry(pos, struct transition_sequence, list);
550 		list_del(&tseq->list);
551 		uc_mgr_free_transition_element(tseq);
552 	}
553 }
554 
uc_mgr_free_dev_name_list(struct list_head * base)555 void uc_mgr_free_dev_name_list(struct list_head *base)
556 {
557 	struct list_head *pos, *npos;
558 	struct ucm_dev_name *dev;
559 
560 	list_for_each_safe(pos, npos, base) {
561 		dev = list_entry(pos, struct ucm_dev_name, list);
562 		list_del(&dev->list);
563 		free(dev->name1);
564 		free(dev->name2);
565 		free(dev);
566 	}
567 }
568 
uc_mgr_free_modifier(struct list_head * base)569 void uc_mgr_free_modifier(struct list_head *base)
570 {
571 	struct list_head *pos, *npos;
572 	struct use_case_modifier *mod;
573 
574 	list_for_each_safe(pos, npos, base) {
575 		mod = list_entry(pos, struct use_case_modifier, list);
576 		free(mod->name);
577 		free(mod->comment);
578 		uc_mgr_free_sequence(&mod->enable_list);
579 		uc_mgr_free_sequence(&mod->disable_list);
580 		uc_mgr_free_transition(&mod->transition_list);
581 		uc_mgr_free_dev_list(&mod->dev_list);
582 		uc_mgr_free_value(&mod->value_list);
583 		list_del(&mod->list);
584 		free(mod);
585 	}
586 }
587 
uc_mgr_free_device(struct use_case_device * dev)588 void uc_mgr_free_device(struct use_case_device *dev)
589 {
590 	free(dev->name);
591 	free(dev->comment);
592 	uc_mgr_free_sequence(&dev->enable_list);
593 	uc_mgr_free_sequence(&dev->disable_list);
594 	uc_mgr_free_transition(&dev->transition_list);
595 	uc_mgr_free_dev_list(&dev->dev_list);
596 	uc_mgr_free_value(&dev->value_list);
597 	list_del(&dev->list);
598 	free(dev);
599 }
600 
uc_mgr_free_device_list(struct list_head * base)601 void uc_mgr_free_device_list(struct list_head *base)
602 {
603 	struct list_head *pos, *npos;
604 	struct use_case_device *dev;
605 
606 	list_for_each_safe(pos, npos, base) {
607 		dev = list_entry(pos, struct use_case_device, list);
608 		uc_mgr_free_device(dev);
609 	}
610 }
611 
uc_mgr_rename_device(struct use_case_verb * verb,const char * src,const char * dst)612 int uc_mgr_rename_device(struct use_case_verb *verb, const char *src,
613 			 const char *dst)
614 {
615 	struct use_case_device *device;
616 	struct list_head *pos, *npos;
617 	char *dst1;
618 
619 	/* no errors when device is not found */
620 	list_for_each_safe(pos, npos, &verb->device_list) {
621 		device = list_entry(pos, struct use_case_device, list);
622 		if (strcmp(device->name, src) == 0) {
623 			dst1 = strdup(dst);
624 			if (dst1 == NULL)
625 				return -ENOMEM;
626 			free(device->name);
627 			device->name = dst1;
628 			continue;
629 		}
630 		uc_mgr_rename_in_dev_list(&device->dev_list, src, dst);
631 	}
632 	return 0;
633 }
634 
uc_mgr_remove_device(struct use_case_verb * verb,const char * name)635 int uc_mgr_remove_device(struct use_case_verb *verb, const char *name)
636 {
637 	struct use_case_device *device;
638 	struct list_head *pos, *npos;
639 	int err, found = 0;
640 
641 	list_for_each_safe(pos, npos, &verb->device_list) {
642 		device = list_entry(pos, struct use_case_device, list);
643 		if (strcmp(device->name, name) == 0) {
644 			uc_mgr_free_device(device);
645 			found++;
646 			continue;
647 		}
648 		err = uc_mgr_remove_from_dev_list(&device->dev_list, name);
649 		if (err < 0 && err != -ENODEV)
650 			return err;
651 		if (err == 0)
652 			found++;
653 	}
654 	return found == 0 ? -ENODEV : 0;
655 }
656 
uc_mgr_get_variable(snd_use_case_mgr_t * uc_mgr,const char * name)657 const char *uc_mgr_get_variable(snd_use_case_mgr_t *uc_mgr, const char *name)
658 {
659 	struct list_head *pos;
660 	struct ucm_value *value;
661 
662 	list_for_each(pos, &uc_mgr->variable_list) {
663 		value = list_entry(pos, struct ucm_value, list);
664 		if (strcmp(value->name, name) == 0)
665 			return value->data;
666 	}
667 	return NULL;
668 }
669 
uc_mgr_set_variable(snd_use_case_mgr_t * uc_mgr,const char * name,const char * val)670 int uc_mgr_set_variable(snd_use_case_mgr_t *uc_mgr, const char *name,
671 			const char *val)
672 {
673 	struct list_head *pos;
674 	struct ucm_value *curr;
675 	char *val2;
676 
677 	list_for_each(pos, &uc_mgr->variable_list) {
678 		curr = list_entry(pos, struct ucm_value, list);
679 		if (strcmp(curr->name, name) == 0) {
680 			val2 = strdup(val);
681 			if (val2 == NULL)
682 				return -ENOMEM;
683 			free(curr->data);
684 			curr->data = val2;
685 			return 0;
686 		}
687 	}
688 
689 	curr = calloc(1, sizeof(struct ucm_value));
690 	if (curr == NULL)
691 		return -ENOMEM;
692 	curr->name = strdup(name);
693 	if (curr->name == NULL) {
694 		free(curr);
695 		return -ENOMEM;
696 	}
697 	curr->data = strdup(val);
698 	if (curr->data == NULL) {
699 		free(curr->name);
700 		free(curr);
701 		return -ENOMEM;
702 	}
703 	list_add_tail(&curr->list, &uc_mgr->variable_list);
704 	return 0;
705 }
706 
uc_mgr_free_verb(snd_use_case_mgr_t * uc_mgr)707 void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr)
708 {
709 	struct list_head *pos, *npos;
710 	struct use_case_verb *verb;
711 
712 	list_for_each_safe(pos, npos, &uc_mgr->verb_list) {
713 		verb = list_entry(pos, struct use_case_verb, list);
714 		free(verb->name);
715 		free(verb->comment);
716 		uc_mgr_free_sequence(&verb->enable_list);
717 		uc_mgr_free_sequence(&verb->disable_list);
718 		uc_mgr_free_transition(&verb->transition_list);
719 		uc_mgr_free_value(&verb->value_list);
720 		uc_mgr_free_device_list(&verb->device_list);
721 		uc_mgr_free_device_list(&verb->cmpt_device_list);
722 		uc_mgr_free_modifier(&verb->modifier_list);
723 		uc_mgr_free_dev_name_list(&verb->rename_list);
724 		uc_mgr_free_dev_name_list(&verb->remove_list);
725 		list_del(&verb->list);
726 		free(verb);
727 	}
728 	uc_mgr_free_sequence(&uc_mgr->fixedboot_list);
729 	uc_mgr_free_sequence(&uc_mgr->boot_list);
730 	uc_mgr_free_sequence(&uc_mgr->default_list);
731 	uc_mgr_free_value(&uc_mgr->value_list);
732 	uc_mgr_free_value(&uc_mgr->variable_list);
733 	free(uc_mgr->comment);
734 	free(uc_mgr->conf_dir_name);
735 	free(uc_mgr->conf_file_name);
736 	uc_mgr->comment = NULL;
737 	uc_mgr->conf_dir_name = NULL;
738 	uc_mgr->conf_file_name = NULL;
739 	uc_mgr->active_verb = NULL;
740 	INIT_LIST_HEAD(&uc_mgr->active_devices);
741 	INIT_LIST_HEAD(&uc_mgr->active_modifiers);
742 }
743 
uc_mgr_free(snd_use_case_mgr_t * uc_mgr)744 void uc_mgr_free(snd_use_case_mgr_t *uc_mgr)
745 {
746 	snd_config_delete(uc_mgr->local_config);
747 	uc_mgr_free_verb(uc_mgr);
748 	uc_mgr_free_ctl_list(uc_mgr);
749 	free(uc_mgr->card_name);
750 	free(uc_mgr);
751 }
752 
753 /*
754  * UCM card list stuff
755  */
756 
757 static pthread_mutex_t ucm_cards_mutex = PTHREAD_MUTEX_INITIALIZER;
758 static LIST_HEAD(ucm_cards);
759 static unsigned int ucm_card_assign;
760 
uc_mgr_card_find(unsigned int card_number)761 static snd_use_case_mgr_t *uc_mgr_card_find(unsigned int card_number)
762 {
763 	struct list_head *pos;
764 	snd_use_case_mgr_t *uc_mgr;
765 
766 	list_for_each(pos, &ucm_cards) {
767 		uc_mgr = list_entry(pos, snd_use_case_mgr_t, cards_list);
768 		if (uc_mgr->ucm_card_number == card_number)
769 			return uc_mgr;
770 	}
771 	return NULL;
772 }
773 
uc_mgr_card_open(snd_use_case_mgr_t * uc_mgr)774 int uc_mgr_card_open(snd_use_case_mgr_t *uc_mgr)
775 {
776 	unsigned int prev;
777 
778 	pthread_mutex_lock(&ucm_cards_mutex);
779 	prev = ucm_card_assign++;
780 	while (uc_mgr_card_find(ucm_card_assign)) {
781 		ucm_card_assign++;
782 		ucm_card_assign &= 0xffff;
783 		/* avoid zero card instance number */
784 		if (ucm_card_assign == 0)
785 			ucm_card_assign++;
786 		if (ucm_card_assign == prev) {
787 			pthread_mutex_unlock(&ucm_cards_mutex);
788 			return -ENOMEM;
789 		}
790 	}
791 	uc_mgr->ucm_card_number = ucm_card_assign;
792 	list_add(&uc_mgr->cards_list, &ucm_cards);
793 	pthread_mutex_unlock(&ucm_cards_mutex);
794 	return 0;
795 }
796 
uc_mgr_card_close(snd_use_case_mgr_t * uc_mgr)797 void uc_mgr_card_close(snd_use_case_mgr_t *uc_mgr)
798 {
799 	pthread_mutex_lock(&ucm_cards_mutex);
800 	list_del(&uc_mgr->cards_list);
801 	pthread_mutex_unlock(&ucm_cards_mutex);
802 }
803 
804 /**
805  * \brief Get library configuration based on the private ALSA device name
806  * \param name[in] ALSA device name
807  * \retval config A configuration tree or NULL
808  *
809  * The returned configuration (non-NULL) should be unreferenced using
810  * snd_config_unref() call.
811  */
uc_mgr_alibcfg_by_device(snd_config_t ** top,const char * name)812 const char *uc_mgr_alibcfg_by_device(snd_config_t **top, const char *name)
813 {
814 	char buf[5];
815 	long card_num;
816 	snd_config_t *config;
817 	snd_use_case_mgr_t *uc_mgr;
818 	int err;
819 
820 	if (strncmp(name, "_ucm", 4) || strlen(name) < 12 || name[8] != '.')
821 		return NULL;
822 	strncpy(buf, name + 4, 4);
823 	buf[4] = '\0';
824 	err = safe_strtol_base(buf, &card_num, 16);
825 	if (err < 0 || card_num < 0 || card_num > 0xffff)
826 		return NULL;
827 	config = NULL;
828 	pthread_mutex_lock(&ucm_cards_mutex);
829 	uc_mgr = uc_mgr_card_find(card_num);
830 	/* non-empty configs are accepted only */
831 	if (uc_mgr_has_local_config(uc_mgr)) {
832 		config = uc_mgr->local_config;
833 		snd_config_ref(config);
834 	}
835 	pthread_mutex_unlock(&ucm_cards_mutex);
836 	if (!config)
837 		return NULL;
838 	*top = config;
839 	return name + 9;
840 }
841