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_get_one_ctl(snd_use_case_mgr_t * uc_mgr)52 struct ctl_list *uc_mgr_get_one_ctl(snd_use_case_mgr_t *uc_mgr)
53 {
54 	struct list_head *pos;
55 	struct ctl_list *ctl_list = NULL;
56 
57 	list_for_each(pos, &uc_mgr->ctl_list) {
58 		if (ctl_list) {
59 			uc_error("multiple control device names were found!");
60 			return NULL;
61 		}
62 		ctl_list = list_entry(pos, struct ctl_list, list);
63 	}
64 	return ctl_list;
65 }
66 
uc_mgr_get_ctl(snd_use_case_mgr_t * uc_mgr)67 snd_ctl_t *uc_mgr_get_ctl(snd_use_case_mgr_t *uc_mgr)
68 {
69 	struct ctl_list *ctl_list;
70 
71 	ctl_list = uc_mgr_get_one_ctl(uc_mgr);
72 	if (ctl_list)
73 		return ctl_list->ctl;
74 	return NULL;
75 }
76 
uc_mgr_free_ctl(struct ctl_list * ctl_list)77 static void uc_mgr_free_ctl(struct ctl_list *ctl_list)
78 {
79 	struct list_head *pos, *npos;
80 	struct ctl_dev *ctl_dev;
81 
82 	list_for_each_safe(pos, npos, &ctl_list->dev_list) {
83 		ctl_dev = list_entry(pos, struct ctl_dev, list);
84 		free(ctl_dev->device);
85 		free(ctl_dev);
86 	}
87 	snd_ctl_card_info_free(ctl_list->ctl_info);
88 	free(ctl_list);
89 }
90 
uc_mgr_free_ctl_list(snd_use_case_mgr_t * uc_mgr)91 void uc_mgr_free_ctl_list(snd_use_case_mgr_t *uc_mgr)
92 {
93 	struct list_head *pos, *npos;
94 	struct ctl_list *ctl_list;
95 
96 	list_for_each_safe(pos, npos, &uc_mgr->ctl_list) {
97 		ctl_list = list_entry(pos, struct ctl_list, list);
98 		snd_ctl_close(ctl_list->ctl);
99 		list_del(&ctl_list->list);
100 		uc_mgr_free_ctl(ctl_list);
101 	}
102 }
103 
uc_mgr_ctl_add_dev(struct ctl_list * ctl_list,const char * device)104 static int uc_mgr_ctl_add_dev(struct ctl_list *ctl_list, const char *device)
105 {
106 	struct list_head *pos;
107 	struct ctl_dev *ctl_dev;
108 
109 	/* skip duplicates */
110 	list_for_each(pos, &ctl_list->dev_list) {
111 		ctl_dev = list_entry(pos, struct ctl_dev, list);
112 		if (strcmp(ctl_dev->device, device) == 0)
113 			return 0;
114 	}
115 
116 	/* allocate new device name */
117 	ctl_dev = malloc(sizeof(*ctl_dev));
118 	if (ctl_dev == NULL)
119 		return -ENOMEM;
120 	ctl_dev->device = strdup(device);
121 	if (ctl_dev->device == NULL) {
122 		free(ctl_dev);
123 		return -ENOMEM;
124 	}
125 	list_add_tail(&ctl_dev->list, &ctl_list->dev_list);
126 	return 0;
127 }
128 
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)129 static int uc_mgr_ctl_add(snd_use_case_mgr_t *uc_mgr,
130 			  struct ctl_list *ctl_list,
131 			  snd_ctl_t *ctl, int card,
132 			  snd_ctl_card_info_t *info,
133 			  const char *device)
134 {
135 	struct ctl_list *cl = NULL;
136 	const char *id = snd_ctl_card_info_get_id(info);
137 	char dev[MAX_CARD_LONG_NAME];
138 	int err, hit = 0;
139 
140 	if (id == NULL || id[0] == '\0')
141 		return -ENOENT;
142 	if (!ctl_list) {
143 		cl = malloc(sizeof(*cl));
144 		if (cl == NULL)
145 			return -ENOMEM;
146 		INIT_LIST_HEAD(&cl->dev_list);
147 		cl->ctl = ctl;
148 		if (snd_ctl_card_info_malloc(&cl->ctl_info) < 0) {
149 			free(cl);
150 			return -ENOMEM;
151 		}
152 		snd_ctl_card_info_copy(cl->ctl_info, info);
153 		ctl_list = cl;
154 	}
155 	if (card >= 0) {
156 		snprintf(dev, sizeof(dev), "hw:%d", card);
157 		hit |= !!(device && (strcmp(dev, device) == 0));
158 		err = uc_mgr_ctl_add_dev(ctl_list, dev);
159 		if (err < 0)
160 			goto __nomem;
161 	}
162 	snprintf(dev, sizeof(dev), "hw:%s", id);
163 	hit |= !!(device && (strcmp(dev, device) == 0));
164 	err = uc_mgr_ctl_add_dev(ctl_list, dev);
165 	if (err < 0)
166 		goto __nomem;
167 	/* the UCM name not based on the card name / id */
168 	if (!hit && device) {
169 		err = uc_mgr_ctl_add_dev(ctl_list, device);
170 		if (err < 0)
171 			goto __nomem;
172 	}
173 
174 	list_add_tail(&ctl_list->list, &uc_mgr->ctl_list);
175 	return 0;
176 
177 __nomem:
178 	if (ctl_list == cl)
179 		uc_mgr_free_ctl(cl);
180 	return -ENOMEM;
181 }
182 
uc_mgr_open_ctl(snd_use_case_mgr_t * uc_mgr,snd_ctl_t ** ctl,const char * device)183 int uc_mgr_open_ctl(snd_use_case_mgr_t *uc_mgr,
184 		    snd_ctl_t **ctl,
185 		    const char *device)
186 {
187 	struct list_head *pos1, *pos2;
188 	struct ctl_list *ctl_list;
189 	struct ctl_dev *ctl_dev;
190 	snd_ctl_card_info_t *info;
191 	const char *id;
192 	int err, card;
193 
194 	snd_ctl_card_info_alloca(&info);
195 
196 	/* cache lookup */
197 	list_for_each(pos1, &uc_mgr->ctl_list) {
198 		ctl_list = list_entry(pos1, struct ctl_list, list);
199 		list_for_each(pos2, &ctl_list->dev_list) {
200 			ctl_dev = list_entry(pos2, struct ctl_dev, list);
201 			if (strcmp(ctl_dev->device, device) == 0) {
202 				*ctl = ctl_list->ctl;
203 				return 0;
204 			}
205 		}
206 	}
207 
208 	err = snd_ctl_open(ctl, device, 0);
209 	if (err < 0)
210 		return err;
211 
212 	id = NULL;
213 	err = snd_ctl_card_info(*ctl, info);
214 	if (err == 0)
215 		id = snd_ctl_card_info_get_id(info);
216 	if (err < 0 || id == NULL || id[0] == '\0') {
217 		uc_error("control hardware info (%s): %s", device, snd_strerror(err));
218 		snd_ctl_close(*ctl);
219 		*ctl = NULL;
220 		return err;
221 	}
222 
223 	/* insert to cache, if just name differs */
224 	list_for_each(pos1, &uc_mgr->ctl_list) {
225 		ctl_list = list_entry(pos1, struct ctl_list, list);
226 		if (strcmp(id, snd_ctl_card_info_get_id(ctl_list->ctl_info)) == 0) {
227 			card = snd_card_get_index(id);
228 			err = uc_mgr_ctl_add(uc_mgr, ctl_list, *ctl, card, info, device);
229 			if (err < 0)
230 				goto __nomem;
231 			snd_ctl_close(*ctl);
232 			*ctl = ctl_list->ctl;
233 			return 0;
234 		}
235 	}
236 
237 	err = uc_mgr_ctl_add(uc_mgr, NULL, *ctl, -1, info, device);
238 	if (err < 0)
239 		goto __nomem;
240 
241 	return 0;
242 
243 __nomem:
244 	snd_ctl_close(*ctl);
245 	*ctl = NULL;
246 	return -ENOMEM;
247 }
248 
uc_mgr_config_load(int format,const char * file,snd_config_t ** cfg)249 int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg)
250 {
251 	FILE *fp;
252 	snd_input_t *in;
253 	snd_config_t *top;
254 	const char *path, *default_paths[2];
255 	int err;
256 
257 	fp = fopen(file, "r");
258 	if (!fp) {
259 		err = -errno;
260   __err0:
261 		uc_error("could not open configuration file %s", file);
262 		return err;
263 	}
264 	err = snd_input_stdio_attach(&in, fp, 1);
265 	if (err < 0)
266 		goto __err0;
267 	err = snd_config_top(&top);
268 	if (err < 0)
269 		goto __err1;
270 
271 	if (format >= 2) {
272 		path = getenv(ALSA_CONFIG_UCM2_VAR);
273 		if (!path || path[0] == '\0')
274 			path = ALSA_CONFIG_DIR "/ucm2";
275 	} else {
276 		path = getenv(ALSA_CONFIG_UCM_VAR);
277 		if (!path || path[0] == '\0')
278 			path = ALSA_CONFIG_DIR "/ucm";
279 	}
280 
281 	default_paths[0] = path;
282 	default_paths[1] = NULL;
283 	err = _snd_config_load_with_include(top, in, 0, default_paths);
284 	if (err < 0) {
285 		uc_error("could not load configuration file %s", file);
286 		goto __err2;
287 	}
288 	err = snd_input_close(in);
289 	if (err < 0) {
290 		in = NULL;
291 		goto __err2;
292 	}
293 	*cfg = top;
294 	return 0;
295 
296  __err2:
297 	snd_config_delete(top);
298  __err1:
299 	if (in)
300 		snd_input_close(in);
301 	return err;
302 }
303 
uc_mgr_free_value(struct list_head * base)304 void uc_mgr_free_value(struct list_head *base)
305 {
306 	struct list_head *pos, *npos;
307 	struct ucm_value *val;
308 
309 	list_for_each_safe(pos, npos, base) {
310 		val = list_entry(pos, struct ucm_value, list);
311 		free(val->name);
312 		free(val->data);
313 		list_del(&val->list);
314 		free(val);
315 	}
316 }
317 
uc_mgr_free_dev_list(struct dev_list * dev_list)318 void uc_mgr_free_dev_list(struct dev_list *dev_list)
319 {
320 	struct list_head *pos, *npos;
321 	struct dev_list_node *dlist;
322 
323 	list_for_each_safe(pos, npos, &dev_list->list) {
324 		dlist = list_entry(pos, struct dev_list_node, list);
325 		free(dlist->name);
326 		list_del(&dlist->list);
327 		free(dlist);
328 	}
329 }
330 
uc_mgr_put_to_dev_list(struct dev_list * dev_list,const char * name)331 int uc_mgr_put_to_dev_list(struct dev_list *dev_list, const char *name)
332 {
333 	struct list_head *pos;
334 	struct dev_list_node *dlist;
335 	char *n;
336 
337 	list_for_each(pos, &dev_list->list) {
338 		dlist = list_entry(pos, struct dev_list_node, list);
339 		if (strcmp(dlist->name, name) == 0)
340 			return 0;
341 	}
342 
343 	dlist = calloc(1, sizeof(*dlist));
344 	if (dlist == NULL)
345 		return -ENOMEM;
346 	n = strdup(name);
347 	if (n == NULL) {
348 		free(dlist);
349 		return -ENOMEM;
350 	}
351 	dlist->name = n;
352 	list_add(&dlist->list, &dev_list->list);
353 	return 0;
354 }
355 
uc_mgr_rename_in_dev_list(struct dev_list * dev_list,const char * src,const char * dst)356 int uc_mgr_rename_in_dev_list(struct dev_list *dev_list, const char *src,
357 			      const char *dst)
358 {
359 	struct list_head *pos;
360 	struct dev_list_node *dlist;
361 	char *dst1;
362 
363 	list_for_each(pos, &dev_list->list) {
364 		dlist = list_entry(pos, struct dev_list_node, list);
365 		if (strcmp(dlist->name, src) == 0) {
366 			dst1 = strdup(dst);
367 			if (dst1 == NULL)
368 				return -ENOMEM;
369 			free(dlist->name);
370 			dlist->name = dst1;
371 			return 0;
372 		}
373 	}
374 	return -ENOENT;
375 }
376 
uc_mgr_remove_from_dev_list(struct dev_list * dev_list,const char * name)377 int uc_mgr_remove_from_dev_list(struct dev_list *dev_list, const char *name)
378 {
379 	struct list_head *pos;
380 	struct dev_list_node *dlist;
381 
382 	list_for_each(pos, &dev_list->list) {
383 		dlist = list_entry(pos, struct dev_list_node, list);
384 		if (strcmp(dlist->name, name) == 0) {
385 			free(dlist->name);
386 			list_del(&dlist->list);
387 			free(dlist);
388 			return 0;
389 		}
390 	}
391 	return -ENODEV;
392 }
393 
uc_mgr_free_sequence_element(struct sequence_element * seq)394 void uc_mgr_free_sequence_element(struct sequence_element *seq)
395 {
396 	if (seq == NULL)
397 		return;
398 	switch (seq->type) {
399 	case SEQUENCE_ELEMENT_TYPE_CDEV:
400 		free(seq->data.cdev);
401 		break;
402 	case SEQUENCE_ELEMENT_TYPE_CSET:
403 	case SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE:
404 	case SEQUENCE_ELEMENT_TYPE_CSET_TLV:
405 		free(seq->data.cset);
406 		break;
407 	case SEQUENCE_ELEMENT_TYPE_EXEC:
408 		free(seq->data.exec);
409 		break;
410 	default:
411 		break;
412 	}
413 	free(seq);
414 }
415 
uc_mgr_free_sequence(struct list_head * base)416 void uc_mgr_free_sequence(struct list_head *base)
417 {
418 	struct list_head *pos, *npos;
419 	struct sequence_element *seq;
420 
421 	list_for_each_safe(pos, npos, base) {
422 		seq = list_entry(pos, struct sequence_element, list);
423 		list_del(&seq->list);
424 		uc_mgr_free_sequence_element(seq);
425 	}
426 }
427 
uc_mgr_free_transition_element(struct transition_sequence * tseq)428 void uc_mgr_free_transition_element(struct transition_sequence *tseq)
429 {
430 	free(tseq->name);
431 	uc_mgr_free_sequence(&tseq->transition_list);
432 	free(tseq);
433 }
434 
uc_mgr_free_transition(struct list_head * base)435 void uc_mgr_free_transition(struct list_head *base)
436 {
437 	struct list_head *pos, *npos;
438 	struct transition_sequence *tseq;
439 
440 	list_for_each_safe(pos, npos, base) {
441 		tseq = list_entry(pos, struct transition_sequence, list);
442 		list_del(&tseq->list);
443 		uc_mgr_free_transition_element(tseq);
444 	}
445 }
446 
uc_mgr_free_dev_name_list(struct list_head * base)447 void uc_mgr_free_dev_name_list(struct list_head *base)
448 {
449 	struct list_head *pos, *npos;
450 	struct ucm_dev_name *dev;
451 
452 	list_for_each_safe(pos, npos, base) {
453 		dev = list_entry(pos, struct ucm_dev_name, list);
454 		list_del(&dev->list);
455 		free(dev->name1);
456 		free(dev->name2);
457 		free(dev);
458 	}
459 }
460 
uc_mgr_free_modifier(struct list_head * base)461 void uc_mgr_free_modifier(struct list_head *base)
462 {
463 	struct list_head *pos, *npos;
464 	struct use_case_modifier *mod;
465 
466 	list_for_each_safe(pos, npos, base) {
467 		mod = list_entry(pos, struct use_case_modifier, list);
468 		free(mod->name);
469 		free(mod->comment);
470 		uc_mgr_free_sequence(&mod->enable_list);
471 		uc_mgr_free_sequence(&mod->disable_list);
472 		uc_mgr_free_transition(&mod->transition_list);
473 		uc_mgr_free_dev_list(&mod->dev_list);
474 		uc_mgr_free_value(&mod->value_list);
475 		list_del(&mod->list);
476 		free(mod);
477 	}
478 }
479 
uc_mgr_free_device(struct use_case_device * dev)480 void uc_mgr_free_device(struct use_case_device *dev)
481 {
482 	free(dev->name);
483 	free(dev->comment);
484 	uc_mgr_free_sequence(&dev->enable_list);
485 	uc_mgr_free_sequence(&dev->disable_list);
486 	uc_mgr_free_transition(&dev->transition_list);
487 	uc_mgr_free_dev_list(&dev->dev_list);
488 	uc_mgr_free_value(&dev->value_list);
489 	list_del(&dev->list);
490 	free(dev);
491 }
492 
uc_mgr_free_device_list(struct list_head * base)493 void uc_mgr_free_device_list(struct list_head *base)
494 {
495 	struct list_head *pos, *npos;
496 	struct use_case_device *dev;
497 
498 	list_for_each_safe(pos, npos, base) {
499 		dev = list_entry(pos, struct use_case_device, list);
500 		uc_mgr_free_device(dev);
501 	}
502 }
503 
uc_mgr_rename_device(struct use_case_verb * verb,const char * src,const char * dst)504 int uc_mgr_rename_device(struct use_case_verb *verb, const char *src,
505 			 const char *dst)
506 {
507 	struct use_case_device *device;
508 	struct list_head *pos, *npos;
509 	char *dst1;
510 
511 	/* no errors when device is not found */
512 	list_for_each_safe(pos, npos, &verb->device_list) {
513 		device = list_entry(pos, struct use_case_device, list);
514 		if (strcmp(device->name, src) == 0) {
515 			dst1 = strdup(dst);
516 			if (dst1 == NULL)
517 				return -ENOMEM;
518 			free(device->name);
519 			device->name = dst1;
520 			continue;
521 		}
522 		uc_mgr_rename_in_dev_list(&device->dev_list, src, dst);
523 	}
524 	return 0;
525 }
526 
uc_mgr_remove_device(struct use_case_verb * verb,const char * name)527 int uc_mgr_remove_device(struct use_case_verb *verb, const char *name)
528 {
529 	struct use_case_device *device;
530 	struct list_head *pos, *npos;
531 
532 	list_for_each_safe(pos, npos, &verb->device_list) {
533 		device = list_entry(pos, struct use_case_device, list);
534 		if (strcmp(device->name, name) == 0) {
535 			uc_mgr_free_device(device);
536 			continue;
537 		}
538 		uc_mgr_remove_from_dev_list(&device->dev_list, name);
539 		return 0;
540 	}
541 	return -ENOENT;
542 }
543 
uc_mgr_free_verb(snd_use_case_mgr_t * uc_mgr)544 void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr)
545 {
546 	struct list_head *pos, *npos;
547 	struct use_case_verb *verb;
548 
549 	list_for_each_safe(pos, npos, &uc_mgr->verb_list) {
550 		verb = list_entry(pos, struct use_case_verb, list);
551 		free(verb->name);
552 		free(verb->comment);
553 		uc_mgr_free_sequence(&verb->enable_list);
554 		uc_mgr_free_sequence(&verb->disable_list);
555 		uc_mgr_free_transition(&verb->transition_list);
556 		uc_mgr_free_value(&verb->value_list);
557 		uc_mgr_free_device_list(&verb->device_list);
558 		uc_mgr_free_device_list(&verb->cmpt_device_list);
559 		uc_mgr_free_modifier(&verb->modifier_list);
560 		uc_mgr_free_dev_name_list(&verb->rename_list);
561 		uc_mgr_free_dev_name_list(&verb->remove_list);
562 		list_del(&verb->list);
563 		free(verb);
564 	}
565 	uc_mgr_free_sequence(&uc_mgr->default_list);
566 	uc_mgr_free_value(&uc_mgr->value_list);
567 	free(uc_mgr->comment);
568 	free(uc_mgr->conf_dir_name);
569 	free(uc_mgr->conf_file_name);
570 	uc_mgr->comment = NULL;
571 	uc_mgr->conf_dir_name = NULL;
572 	uc_mgr->conf_file_name = NULL;
573 	uc_mgr->active_verb = NULL;
574 	INIT_LIST_HEAD(&uc_mgr->active_devices);
575 	INIT_LIST_HEAD(&uc_mgr->active_modifiers);
576 }
577 
uc_mgr_free(snd_use_case_mgr_t * uc_mgr)578 void uc_mgr_free(snd_use_case_mgr_t *uc_mgr)
579 {
580 	uc_mgr_free_verb(uc_mgr);
581 	uc_mgr_free_ctl_list(uc_mgr);
582 	free(uc_mgr->card_name);
583 	free(uc_mgr);
584 }
585