xref: /third_party/alsa-lib/src/control/setup.c (revision d5ac70f0)
1/**
2 * \file control/setup.c
3 * \brief Routines to setup control primitives from configuration
4 * \author Abramo Bagnara <abramo@alsa-project.org>
5 * \author Jaroslav Kysela <perex@perex.cz>
6 * \date 2001
7 *
8 * Routines to setup control primitives from configuration
9 */
10/*
11 *  Control Interface - routines for setup from configuration
12 *  Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org>
13 *			  Jaroslav Kysela <perex@perex.cz>
14 *
15 *
16 *   This library is free software; you can redistribute it and/or modify
17 *   it under the terms of the GNU Lesser General Public License as
18 *   published by the Free Software Foundation; either version 2.1 of
19 *   the License, or (at your option) any later version.
20 *
21 *   This program is distributed in the hope that it will be useful,
22 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
23 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 *   GNU Lesser General Public License for more details.
25 *
26 *   You should have received a copy of the GNU Lesser General Public
27 *   License along with this library; if not, write to the Free Software
28 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
29 *
30 */
31
32#include "local.h"
33#include <stdio.h>
34#include <stdlib.h>
35#include <stdarg.h>
36#include <unistd.h>
37#include <string.h>
38#include <ctype.h>
39
40#ifndef DOC_HIDDEN
41typedef struct {
42	unsigned int lock: 1;
43	unsigned int preserve: 1;
44	snd_ctl_elem_id_t *id;
45	snd_ctl_elem_info_t *info;
46	snd_ctl_elem_value_t *val;
47	snd_ctl_elem_value_t *mask;
48	snd_ctl_elem_value_t *old;
49	struct list_head list;
50} snd_sctl_elem_t;
51
52struct _snd_sctl {
53	int mode;
54	snd_ctl_t *ctl;
55	struct list_head elems;
56};
57#endif /* DOC_HIDDEN */
58
59static int free_elems(snd_sctl_t *h)
60{
61	int err = 0;
62	while (!list_empty(&h->elems)) {
63		snd_sctl_elem_t *elem = list_entry(h->elems.next, snd_sctl_elem_t, list);
64		snd_ctl_elem_id_free(elem->id);
65		snd_ctl_elem_info_free(elem->info);
66		snd_ctl_elem_value_free(elem->val);
67		snd_ctl_elem_value_free(elem->mask);
68		snd_ctl_elem_value_free(elem->old);
69		list_del(&elem->list);
70		free(elem);
71	}
72	if ((h->mode & SND_SCTL_NOFREE) == 0)
73		err = snd_ctl_close(h->ctl);
74	free(h);
75	return err;
76}
77
78/**
79 * \brief Install given values to control elements
80 * \param h Setup control handle
81 * \result zero if success, otherwise a negative error code
82 */
83int snd_sctl_install(snd_sctl_t *h)
84{
85	struct list_head *pos;
86	int err;
87	unsigned int k;
88	assert(h);
89	list_for_each(pos, &h->elems) {
90		snd_sctl_elem_t *elem = list_entry(pos, snd_sctl_elem_t, list);
91		unsigned int count;
92		snd_ctl_elem_type_t type;
93		if (elem->lock) {
94			err = snd_ctl_elem_lock(h->ctl, elem->id);
95			if (err < 0) {
96				SNDERR("Cannot lock ctl elem");
97				return err;
98			}
99		}
100		err = snd_ctl_elem_read(h->ctl, elem->old);
101		if (err < 0) {
102			SNDERR("Cannot read ctl elem");
103			return err;
104		}
105		count = snd_ctl_elem_info_get_count(elem->info);
106		type = snd_ctl_elem_info_get_type(elem->info);
107		switch (type) {
108		case SND_CTL_ELEM_TYPE_BOOLEAN:
109			for (k = 0; k < count; ++k) {
110				int old, val, mask;
111				old = snd_ctl_elem_value_get_boolean(elem->old, k);
112				mask = snd_ctl_elem_value_get_boolean(elem->mask, k);
113				old &= ~mask;
114				if (old) {
115					val = snd_ctl_elem_value_get_boolean(elem->val, k);
116					val |= old;
117					snd_ctl_elem_value_set_boolean(elem->val, k, val);
118				}
119			}
120			break;
121		case SND_CTL_ELEM_TYPE_INTEGER:
122			for (k = 0; k < count; ++k) {
123				long old, val, mask;
124				old = snd_ctl_elem_value_get_integer(elem->old, k);
125				mask = snd_ctl_elem_value_get_integer(elem->mask, k);
126				old &= ~mask;
127				if (old) {
128					val = snd_ctl_elem_value_get_integer(elem->val, k);
129					val |= old;
130					snd_ctl_elem_value_set_integer(elem->val, k, val);
131				}
132			}
133			break;
134		case SND_CTL_ELEM_TYPE_ENUMERATED:
135			for (k = 0; k < count; ++k) {
136				unsigned int old, val, mask;
137				old = snd_ctl_elem_value_get_enumerated(elem->old, k);
138				mask = snd_ctl_elem_value_get_enumerated(elem->mask, k);
139				old &= ~mask;
140				if (old) {
141					val = snd_ctl_elem_value_get_enumerated(elem->val, k);
142					val |= old;
143					snd_ctl_elem_value_set_enumerated(elem->val, k, val);
144				}
145			}
146			break;
147		case SND_CTL_ELEM_TYPE_IEC958:
148			count = sizeof(snd_aes_iec958_t);
149			/* Fall through */
150		case SND_CTL_ELEM_TYPE_BYTES:
151			for (k = 0; k < count; ++k) {
152				unsigned char old, val, mask;
153				old = snd_ctl_elem_value_get_byte(elem->old, k);
154				mask = snd_ctl_elem_value_get_byte(elem->mask, k);
155				old &= ~mask;
156				if (old) {
157					val = snd_ctl_elem_value_get_byte(elem->val, k);
158					val |= old;
159					snd_ctl_elem_value_set_byte(elem->val, k, val);
160				}
161			}
162			break;
163		default:
164			assert(0);
165			break;
166		}
167		err = snd_ctl_elem_write(h->ctl, elem->val);
168		if (err < 0) {
169			SNDERR("Cannot write ctl elem");
170			return err;
171		}
172	}
173	return 0;
174}
175
176/**
177 * \brief Remove (restore) previous values from control elements
178 * \param h Setup control handle
179 * \result zero if success, otherwise a negative error code
180 */
181int snd_sctl_remove(snd_sctl_t *h)
182{
183	struct list_head *pos;
184	int err;
185	assert(h);
186	list_for_each(pos, &h->elems) {
187		snd_sctl_elem_t *elem = list_entry(pos, snd_sctl_elem_t, list);
188		if (elem->lock) {
189			err = snd_ctl_elem_unlock(h->ctl, elem->id);
190			if (err < 0) {
191				SNDERR("Cannot unlock ctl elem");
192				return err;
193			}
194		}
195		/* Only restore the old value if it differs from the requested
196		 * value, because if it has changed restoring the old value
197		 * overrides the change.  Take for example, a voice modem with
198		 * a .conf that sets preserve off-hook.  Start playback (on-hook
199		 * to off-hook), start record (off-hook to off-hook), stop
200		 * playback (off-hook to restore on-hook), stop record (on-hook
201		 * to restore off-hook), Clearly you don't want to leave the
202		 * modem "on the phone" now that there isn't any playback or
203		 * recording active.
204		 */
205		if (elem->preserve && snd_ctl_elem_value_compare(elem->val, elem->old)) {
206			err = snd_ctl_elem_write(h->ctl, elem->old);
207			if (err < 0) {
208				SNDERR("Cannot restore ctl elem");
209				return err;
210			}
211		}
212	}
213	return 0;
214}
215
216static int snd_config_get_ctl_elem_enumerated(snd_config_t *n, snd_ctl_t *ctl,
217					      snd_ctl_elem_info_t *info)
218{
219	const char *str;
220	long val;
221	unsigned int idx, items;
222	switch (snd_config_get_type(n)) {
223	case SND_CONFIG_TYPE_INTEGER:
224		snd_config_get_integer(n, &val);
225		return val;
226	case SND_CONFIG_TYPE_STRING:
227		snd_config_get_string(n, &str);
228		break;
229	default:
230		return -1;
231	}
232	items = snd_ctl_elem_info_get_items(info);
233	for (idx = 0; idx < items; idx++) {
234		int err;
235		snd_ctl_elem_info_set_item(info, idx);
236		err = snd_ctl_elem_info(ctl, info);
237		if (err < 0) {
238			SNDERR("Cannot obtain info for CTL elem");
239			return err;
240		}
241		if (strcmp(str, snd_ctl_elem_info_get_item_name(info)) == 0)
242			return idx;
243	}
244	return -1;
245}
246
247static int snd_config_get_ctl_elem_value(snd_config_t *conf,
248					 snd_ctl_t *ctl,
249					 snd_ctl_elem_value_t *val,
250					 snd_ctl_elem_value_t *mask,
251					 snd_ctl_elem_info_t *info)
252{
253	int err;
254	snd_config_iterator_t i, next;
255	snd_ctl_elem_id_t id = {0};
256	snd_ctl_elem_type_t type;
257	unsigned int count;
258	long v;
259	long idx;
260	snd_ctl_elem_value_get_id(val, &id);
261	count = snd_ctl_elem_info_get_count(info);
262	type = snd_ctl_elem_info_get_type(info);
263	if (count == 1) {
264		switch (type) {
265		case SND_CTL_ELEM_TYPE_BOOLEAN:
266			v = snd_config_get_bool(conf);
267			if (v >= 0) {
268				snd_ctl_elem_value_set_boolean(val, 0, v);
269				if (mask)
270					snd_ctl_elem_value_set_boolean(mask, 0, 1);
271				return 0;
272			}
273			break;
274		case SND_CTL_ELEM_TYPE_INTEGER:
275			err = snd_config_get_integer(conf, &v);
276			if (err == 0) {
277				snd_ctl_elem_value_set_integer(val, 0, v);
278				if (mask)
279					snd_ctl_elem_value_set_integer(mask, 0, ~0L);
280				return 0;
281			}
282			break;
283		case SND_CTL_ELEM_TYPE_ENUMERATED:
284			v = snd_config_get_ctl_elem_enumerated(conf, ctl, info);
285			if (v >= 0) {
286				snd_ctl_elem_value_set_enumerated(val, 0, v);
287				if (mask)
288					snd_ctl_elem_value_set_enumerated(mask, 0, ~0);
289				return 0;
290			}
291			break;
292		case SND_CTL_ELEM_TYPE_BYTES:
293		case SND_CTL_ELEM_TYPE_IEC958:
294			break;
295		default:
296			SNDERR("Unknown control type: %d", type);
297			return -EINVAL;
298		}
299	}
300	switch (type) {
301	case SND_CTL_ELEM_TYPE_IEC958:
302		count = sizeof(snd_aes_iec958_t);
303		/* Fall through */
304	case SND_CTL_ELEM_TYPE_BYTES:
305	{
306		const char *buf;
307		err = snd_config_get_string(conf, &buf);
308		if (err >= 0) {
309			int c1 = 0;
310			unsigned int len = strlen(buf);
311			unsigned int idx = 0;
312			if (len % 2 != 0 || len > count * 2) {
313			_bad_content:
314				SNDERR("bad value content");
315				return -EINVAL;
316			}
317			while (*buf) {
318				int c = *buf++;
319				if (c >= '0' && c <= '9')
320					c -= '0';
321				else if (c >= 'a' && c <= 'f')
322					c = c - 'a' + 10;
323				else if (c >= 'A' && c <= 'F')
324					c = c - 'A' + 10;
325				else {
326					goto _bad_content;
327				}
328				if (idx % 2 == 1) {
329					snd_ctl_elem_value_set_byte(val, idx / 2, c1 << 4 | c);
330					if (mask)
331						snd_ctl_elem_value_set_byte(mask, idx / 2, 0xff);
332				} else
333					c1 = c;
334				idx++;
335			}
336			return 0;
337		}
338	}
339	default:
340		break;
341	}
342	if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) {
343		SNDERR("bad value type");
344		return -EINVAL;
345	}
346
347	snd_config_for_each(i, next, conf) {
348		snd_config_t *n = snd_config_iterator_entry(i);
349		const char *id;
350		if (snd_config_get_id(n, &id) < 0)
351			continue;
352		err = safe_strtol(id, &idx);
353		if (err < 0 || idx < 0 || (unsigned int) idx >= count) {
354			SNDERR("bad value index");
355			return -EINVAL;
356		}
357		switch (type) {
358		case SND_CTL_ELEM_TYPE_BOOLEAN:
359			v = snd_config_get_bool(n);
360			if (v < 0)
361				goto _bad_content;
362			snd_ctl_elem_value_set_boolean(val, idx, v);
363			if (mask)
364				snd_ctl_elem_value_set_boolean(mask, idx, 1);
365			break;
366		case SND_CTL_ELEM_TYPE_INTEGER:
367			err = snd_config_get_integer(n, &v);
368			if (err < 0)
369				goto _bad_content;
370			snd_ctl_elem_value_set_integer(val, idx, v);
371			if (mask)
372				snd_ctl_elem_value_set_integer(mask, idx, ~0L);
373			break;
374		case SND_CTL_ELEM_TYPE_ENUMERATED:
375			v = snd_config_get_ctl_elem_enumerated(n, ctl, info);
376			if (v < 0)
377				goto _bad_content;
378			snd_ctl_elem_value_set_enumerated(val, idx, v);
379			if (mask)
380				snd_ctl_elem_value_set_enumerated(mask, idx, ~0);
381			break;
382		case SND_CTL_ELEM_TYPE_BYTES:
383		case SND_CTL_ELEM_TYPE_IEC958:
384			err = snd_config_get_integer(n, &v);
385			if (err < 0 || v < 0 || v > 255)
386				goto _bad_content;
387			snd_ctl_elem_value_set_byte(val, idx, v);
388			if (mask)
389				snd_ctl_elem_value_set_byte(mask, idx, 0xff);
390			break;
391		default:
392			break;
393		}
394	}
395	return 0;
396}
397
398static int add_elem(snd_sctl_t *h, snd_config_t *_conf, snd_config_t *private_data, int *quit)
399{
400	snd_config_t *conf;
401	snd_config_iterator_t i, next;
402	int iface = SND_CTL_ELEM_IFACE_MIXER;
403	const char *name = NULL;
404	long index = 0;
405	long device = -1;
406	long subdevice = -1;
407	int lock = 0;
408	int preserve = 0;
409	int optional = 0;
410	int skip_rest = 0;
411	snd_config_t *value = NULL, *mask = NULL;
412	snd_sctl_elem_t *elem = NULL;
413	int err;
414	err = snd_config_expand(_conf, _conf, NULL, private_data, &conf);
415	if (err < 0)
416		return err;
417	snd_config_for_each(i, next, conf) {
418		snd_config_t *n = snd_config_iterator_entry(i);
419		const char *id;
420		if (snd_config_get_id(n, &id) < 0)
421			continue;
422		if (strcmp(id, "comment") == 0)
423			continue;
424		if (strcmp(id, "iface") == 0 || strcmp(id, "interface") == 0) {
425			const char *ptr;
426			if ((err = snd_config_get_string(n, &ptr)) < 0) {
427				SNDERR("field %s is not a string", id);
428				goto _err;
429			}
430			if ((err = snd_config_get_ctl_iface_ascii(ptr)) < 0) {
431				SNDERR("Invalid value for '%s'", id);
432				goto _err;
433			}
434			iface = err;
435			continue;
436		}
437		if (strcmp(id, "name") == 0) {
438			if ((err = snd_config_get_string(n, &name)) < 0) {
439				SNDERR("field %s is not a string", id);
440				goto _err;
441			}
442			continue;
443		}
444		if (strcmp(id, "index") == 0) {
445			if ((err = snd_config_get_integer(n, &index)) < 0) {
446				SNDERR("field %s is not an integer", id);
447				goto _err;
448			}
449			continue;
450		}
451		if (strcmp(id, "device") == 0) {
452			if ((err = snd_config_get_integer(n, &device)) < 0) {
453				SNDERR("field %s is not an integer", id);
454				goto _err;
455			}
456			continue;
457		}
458		if (strcmp(id, "subdevice") == 0) {
459			if ((err = snd_config_get_integer(n, &subdevice)) < 0) {
460				SNDERR("field %s is not an integer", id);
461				goto _err;
462			}
463			continue;
464		}
465		if (strcmp(id, "lock") == 0) {
466			err = snd_config_get_bool(n);
467			if (err < 0)
468				goto _err;
469			lock = err;
470			continue;
471		}
472		if (strcmp(id, "preserve") == 0) {
473			err = snd_config_get_bool(n);
474			if (err < 0)
475				goto _err;
476			preserve = err;
477			continue;
478		}
479		if (strcmp(id, "value") == 0) {
480			value = n;
481			continue;
482		}
483		if (strcmp(id, "mask") == 0) {
484			mask = n;
485			continue;
486		}
487		if (strcmp(id, "optional") == 0) {
488			err = snd_config_get_bool(n);
489			if (err < 0)
490				goto _err;
491			optional = err;
492			continue;
493		}
494		if (strcmp(id, "skip_rest") == 0) {
495			err = snd_config_get_bool(n);
496			if (err < 0)
497				goto _err;
498			skip_rest = err;
499			continue;
500		}
501		SNDERR("Unknown field %s", id);
502		return -EINVAL;
503	}
504	if (name == NULL) {
505		SNDERR("Missing control name");
506		err = -EINVAL;
507		goto _err;
508	}
509	if (value == NULL) {
510		SNDERR("Missing control value");
511		err = -EINVAL;
512		goto _err;
513	}
514	if (device < 0)
515		device = 0;
516	if (subdevice < 0)
517		subdevice = 0;
518	elem = calloc(1, sizeof(*elem));
519	if (!elem)
520		return -ENOMEM;
521	err = snd_ctl_elem_id_malloc(&elem->id);
522	if (err < 0)
523		goto _err;
524	err = snd_ctl_elem_info_malloc(&elem->info);
525	if (err < 0)
526		goto _err;
527	err = snd_ctl_elem_value_malloc(&elem->val);
528	if (err < 0)
529		goto _err;
530	err = snd_ctl_elem_value_malloc(&elem->mask);
531	if (err < 0)
532		goto _err;
533	err = snd_ctl_elem_value_malloc(&elem->old);
534	if (err < 0)
535		goto _err;
536	elem->lock = lock;
537	elem->preserve = preserve;
538	snd_ctl_elem_id_set_interface(elem->id, iface);
539	snd_ctl_elem_id_set_name(elem->id, name);
540	snd_ctl_elem_id_set_index(elem->id, index);
541	snd_ctl_elem_id_set_device(elem->id, device);
542	snd_ctl_elem_id_set_subdevice(elem->id, subdevice);
543	snd_ctl_elem_info_set_id(elem->info, elem->id);
544	err = snd_ctl_elem_info(h->ctl, elem->info);
545	if (err < 0) {
546		if (! optional)
547			SNDERR("Cannot obtain info for CTL elem (%s,'%s',%li,%li,%li): %s", snd_ctl_elem_iface_name(iface), name, index, device, subdevice, snd_strerror(err));
548		goto _err;
549	} else {
550		if (skip_rest)
551			*quit = 1;
552	}
553	snd_ctl_elem_value_set_id(elem->val, elem->id);
554	snd_ctl_elem_value_set_id(elem->old, elem->id);
555	if (mask) {
556		err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, NULL, elem->info);
557		if (err < 0)
558			goto _err;
559		err = snd_config_get_ctl_elem_value(mask, h->ctl, elem->mask, NULL, elem->info);
560		if (err < 0)
561			goto _err;
562	} else {
563		err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, elem->mask, elem->info);
564		if (err < 0)
565			goto _err;
566	}
567
568	err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, elem->mask, elem->info);
569	if (err < 0)
570		goto _err;
571	list_add_tail(&elem->list, &h->elems);
572
573 _err:
574 	if (err < 0 && elem) {
575		if (elem->id)
576			snd_ctl_elem_id_free(elem->id);
577		if (elem->info)
578			snd_ctl_elem_info_free(elem->info);
579		if (elem->val)
580			snd_ctl_elem_value_free(elem->val);
581		if (elem->mask)
582			snd_ctl_elem_value_free(elem->mask);
583		if (elem->old)
584			snd_ctl_elem_value_free(elem->old);
585		free(elem);
586		if (err != -ENOMEM && optional)
587			err = 0; /* ignore the error */
588	}
589	if (conf)
590		snd_config_delete(conf);
591	return err;
592}
593
594/**
595 * \brief Build setup control handle
596 * \param sctl Result - setup control handle
597 * \param handle Master control handle
598 * \param conf Setup configuration
599 * \param private_data Private data for runtime evaluation
600 * \param mode Build mode - SND_SCTL_xxxx
601 * \result zero if success, otherwise a negative error code
602 */
603int snd_sctl_build(snd_sctl_t **sctl, snd_ctl_t *handle, snd_config_t *conf, snd_config_t *private_data, int mode)
604{
605	snd_sctl_t *h;
606	snd_config_iterator_t i, next;
607	int err, quit = 0;
608
609	assert(sctl);
610	assert(handle);
611	assert(conf);
612	*sctl = NULL;
613	if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND)
614		return -EINVAL;
615	h = calloc(1, sizeof(*h));
616	if (!h) {
617		if (mode & SND_SCTL_NOFREE)
618			return -ENOMEM;
619		snd_ctl_close(handle);
620		return -ENOMEM;
621	}
622	h->mode = mode;
623	h->ctl = handle;
624	INIT_LIST_HEAD(&h->elems);
625	snd_config_for_each(i, next, conf) {
626		snd_config_t *n = snd_config_iterator_entry(i);
627		err = add_elem(h, n, private_data, &quit);
628		if (err < 0) {
629			free_elems(h);
630			return err;
631		}
632		if (quit)
633			break;
634	}
635	*sctl = h;
636	return 0;
637}
638
639/**
640 * \brief Free setup control handle
641 * \param sctl Setup control handle
642 * \result zero if success, otherwise a negative error code
643 */
644int snd_sctl_free(snd_sctl_t *sctl)
645{
646	assert(sctl);
647	return free_elems(sctl);
648}
649