mirror of
				https://github.com/alsa-project/alsa-lib.git
				synced 2025-11-03 09:01:52 -05:00 
			
		
		
		
	Added argument handling for the slave PCMs.
The configuration root (snd_config) can be specified for the internal routines. The pcm_hooks code was recoded (independent code moved to control/setup.c). Improved the pcm_multi plugin (added master configuration).
This commit is contained in:
		
							parent
							
								
									61bf03ce70
								
							
						
					
					
						commit
						bf780a25a5
					
				
					 29 changed files with 1179 additions and 1070 deletions
				
			
		| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
/**
 | 
			
		||||
 * \file control/setup.c
 | 
			
		||||
 * \author Abramo Bagnara <abramo@alsa-project.org>
 | 
			
		||||
 * \author Jaroslav Kysela <perex@suse.cz>
 | 
			
		||||
 * \date 2001
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -7,7 +8,8 @@
 | 
			
		|||
 */
 | 
			
		||||
/*
 | 
			
		||||
 *  Control Interface - routines for setup from configuration
 | 
			
		||||
 *  Copyright (c) 2000 by Jaroslav Kysela <perex@suse.cz>
 | 
			
		||||
 *  Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org>
 | 
			
		||||
 *			  Jaroslav Kysela <perex@suse.cz>
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *   This library is free software; you can redistribute it and/or modify
 | 
			
		||||
| 
						 | 
				
			
			@ -32,258 +34,596 @@
 | 
			
		|||
#include <unistd.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include "control_local.h"
 | 
			
		||||
#include "list.h"
 | 
			
		||||
#include "local.h"
 | 
			
		||||
 | 
			
		||||
typedef struct snd_sctl_elem {
 | 
			
		||||
typedef struct {
 | 
			
		||||
	unsigned int lock: 1;
 | 
			
		||||
	unsigned int preserve: 1;
 | 
			
		||||
	snd_ctl_elem_id_t *id;
 | 
			
		||||
	snd_ctl_elem_info_t *info;
 | 
			
		||||
	snd_ctl_elem_value_t *val;
 | 
			
		||||
	snd_ctl_elem_value_t *mask;
 | 
			
		||||
	snd_ctl_elem_value_t *old;
 | 
			
		||||
	struct list_head list;
 | 
			
		||||
	snd_ctl_elem_value_t *value;
 | 
			
		||||
	int lock: 1,
 | 
			
		||||
	    preserve: 1;
 | 
			
		||||
} snd_sctl_elem_t;
 | 
			
		||||
 | 
			
		||||
struct _snd_sctl {
 | 
			
		||||
	struct list_head elements;
 | 
			
		||||
	int mode;
 | 
			
		||||
	snd_ctl_t *ctl;
 | 
			
		||||
	struct list_head elems;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const char *id_str(snd_ctl_elem_id_t *id)
 | 
			
		||||
static int free_elems(snd_sctl_t *h)
 | 
			
		||||
{
 | 
			
		||||
        static char str[128];
 | 
			
		||||
        assert(id);
 | 
			
		||||
        sprintf(str, "%i,%i,%i,%s,%i",
 | 
			
		||||
		snd_enum_to_int(snd_ctl_elem_id_get_interface(id)),
 | 
			
		||||
		snd_ctl_elem_id_get_device(id),
 | 
			
		||||
		snd_ctl_elem_id_get_subdevice(id),
 | 
			
		||||
		snd_ctl_elem_id_get_name(id),
 | 
			
		||||
		snd_ctl_elem_id_get_index(id));
 | 
			
		||||
	return str;
 | 
			
		||||
	int err = 0;
 | 
			
		||||
	while (!list_empty(&h->elems)) {
 | 
			
		||||
		snd_sctl_elem_t *elem = list_entry(h->elems.next, snd_sctl_elem_t, list);
 | 
			
		||||
		snd_ctl_elem_id_free(elem->id);
 | 
			
		||||
		snd_ctl_elem_info_free(elem->info);
 | 
			
		||||
		snd_ctl_elem_value_free(elem->val);
 | 
			
		||||
		snd_ctl_elem_value_free(elem->mask);
 | 
			
		||||
		snd_ctl_elem_value_free(elem->old);
 | 
			
		||||
		list_del(&elem->list);
 | 
			
		||||
		free(elem);
 | 
			
		||||
	}
 | 
			
		||||
	if ((h->mode & SND_SCTL_NOFREE) == 0)
 | 
			
		||||
		err = snd_ctl_close(h->ctl);
 | 
			
		||||
	free(h);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int config_iface(const char *value)
 | 
			
		||||
int snd_sctl_install(snd_sctl_t *h)
 | 
			
		||||
{
 | 
			
		||||
	int idx;
 | 
			
		||||
	struct list_head *pos;
 | 
			
		||||
	int err;
 | 
			
		||||
	unsigned int k;
 | 
			
		||||
	assert(h);
 | 
			
		||||
	list_for_each(pos, &h->elems) {
 | 
			
		||||
		snd_sctl_elem_t *elem = list_entry(pos, snd_sctl_elem_t, list);
 | 
			
		||||
		unsigned int count;
 | 
			
		||||
		snd_ctl_elem_type_t type;
 | 
			
		||||
		if (elem->lock) {
 | 
			
		||||
			err = snd_ctl_elem_lock(h->ctl, elem->id);
 | 
			
		||||
			if (err < 0) {
 | 
			
		||||
				SNDERR("Cannot lock ctl elem");
 | 
			
		||||
				return err;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		err = snd_ctl_elem_read(h->ctl, elem->old);
 | 
			
		||||
		if (err < 0) {
 | 
			
		||||
			SNDERR("Cannot read ctl elem");
 | 
			
		||||
			return err;
 | 
			
		||||
		}
 | 
			
		||||
		count = snd_ctl_elem_info_get_count(elem->info);
 | 
			
		||||
		type = snd_ctl_elem_info_get_type(elem->info);
 | 
			
		||||
		switch (type) {
 | 
			
		||||
		case SND_CTL_ELEM_TYPE_BOOLEAN:
 | 
			
		||||
			for (k = 0; k < count; ++k) {
 | 
			
		||||
				int old, val, mask;
 | 
			
		||||
				old = snd_ctl_elem_value_get_boolean(elem->old, k);
 | 
			
		||||
				mask = snd_ctl_elem_value_get_boolean(elem->mask, k);
 | 
			
		||||
				old &= ~mask;
 | 
			
		||||
				if (old) {
 | 
			
		||||
					val = snd_ctl_elem_value_get_boolean(elem->val, k);
 | 
			
		||||
					val |= old;
 | 
			
		||||
					snd_ctl_elem_value_set_boolean(elem->val, k, val);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case SND_CTL_ELEM_TYPE_INTEGER:
 | 
			
		||||
			for (k = 0; k < count; ++k) {
 | 
			
		||||
				long old, val, mask;
 | 
			
		||||
				old = snd_ctl_elem_value_get_integer(elem->old, k);
 | 
			
		||||
				mask = snd_ctl_elem_value_get_integer(elem->mask, k);
 | 
			
		||||
				old &= ~mask;
 | 
			
		||||
				if (old) {
 | 
			
		||||
					val = snd_ctl_elem_value_get_integer(elem->val, k);
 | 
			
		||||
					val |= old;
 | 
			
		||||
					snd_ctl_elem_value_set_integer(elem->val, k, val);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case SND_CTL_ELEM_TYPE_ENUMERATED:
 | 
			
		||||
			for (k = 0; k < count; ++k) {
 | 
			
		||||
				unsigned int old, val, mask;
 | 
			
		||||
				old = snd_ctl_elem_value_get_enumerated(elem->old, k);
 | 
			
		||||
				mask = snd_ctl_elem_value_get_enumerated(elem->mask, k);
 | 
			
		||||
				old &= ~mask;
 | 
			
		||||
				if (old) {
 | 
			
		||||
					val = snd_ctl_elem_value_get_enumerated(elem->val, k);
 | 
			
		||||
					val |= old;
 | 
			
		||||
					snd_ctl_elem_value_set_enumerated(elem->val, k, val);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case SND_CTL_ELEM_TYPE_IEC958:
 | 
			
		||||
			count = sizeof(snd_aes_iec958_t);
 | 
			
		||||
			/* Fall through */
 | 
			
		||||
		case SND_CTL_ELEM_TYPE_BYTES:
 | 
			
		||||
			for (k = 0; k < count; ++k) {
 | 
			
		||||
				unsigned char old, val, mask;
 | 
			
		||||
				old = snd_ctl_elem_value_get_byte(elem->old, k);
 | 
			
		||||
				mask = snd_ctl_elem_value_get_byte(elem->mask, k);
 | 
			
		||||
				old &= ~mask;
 | 
			
		||||
				if (old) {
 | 
			
		||||
					val = snd_ctl_elem_value_get_byte(elem->val, k);
 | 
			
		||||
					val |= old;
 | 
			
		||||
					snd_ctl_elem_value_set_byte(elem->val, k, val);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			assert(0);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		err = snd_ctl_elem_write(h->ctl, elem->val);
 | 
			
		||||
		if (err < 0) {
 | 
			
		||||
			SNDERR("Cannot write ctl elem");
 | 
			
		||||
			return err;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	if (isdigit(*value))
 | 
			
		||||
		return atoi(value);
 | 
			
		||||
	for (idx = 0; idx <= SND_CTL_ELEM_IFACE_LAST; idx++)
 | 
			
		||||
		if (strcasecmp(snd_ctl_elem_iface_name(idx), value) == 0)
 | 
			
		||||
			return snd_enum_to_int(idx);
 | 
			
		||||
	SNDERR("iface '%s' error", value);
 | 
			
		||||
int snd_sctl_remove(snd_sctl_t *h)
 | 
			
		||||
{
 | 
			
		||||
	struct list_head *pos;
 | 
			
		||||
	int err;
 | 
			
		||||
	assert(h);
 | 
			
		||||
	list_for_each(pos, &h->elems) {
 | 
			
		||||
		snd_sctl_elem_t *elem = list_entry(pos, snd_sctl_elem_t, list);
 | 
			
		||||
		if (elem->lock) {
 | 
			
		||||
			err = snd_ctl_elem_unlock(h->ctl, elem->id);
 | 
			
		||||
			if (err < 0) {
 | 
			
		||||
				SNDERR("Cannot unlock ctl elem");
 | 
			
		||||
				return err;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (elem->preserve) {
 | 
			
		||||
			err = snd_ctl_elem_write(h->ctl, elem->old);
 | 
			
		||||
			if (err < 0) {
 | 
			
		||||
				SNDERR("Cannot restore ctl elem");
 | 
			
		||||
				return err;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int snd_config_get_ctl_elem_enumerated(snd_config_t *n, snd_ctl_t *ctl,
 | 
			
		||||
					      snd_ctl_elem_info_t *info)
 | 
			
		||||
{
 | 
			
		||||
	const char *str;
 | 
			
		||||
	long val;
 | 
			
		||||
	unsigned int idx, items;
 | 
			
		||||
	switch (snd_enum_to_int(snd_config_get_type(n))) {
 | 
			
		||||
	case SND_CONFIG_TYPE_INTEGER:
 | 
			
		||||
		snd_config_get_integer(n, &val);
 | 
			
		||||
		return val;
 | 
			
		||||
	case SND_CONFIG_TYPE_STRING:
 | 
			
		||||
		snd_config_get_string(n, &str);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	items = snd_ctl_elem_info_get_items(info);
 | 
			
		||||
	for (idx = 0; idx < items; idx++) {
 | 
			
		||||
		int err;
 | 
			
		||||
		snd_ctl_elem_info_set_item(info, idx);
 | 
			
		||||
		err = snd_ctl_elem_info(ctl, info);
 | 
			
		||||
		if (err < 0) {
 | 
			
		||||
			SNDERR("Cannot obtain info for CTL elem");
 | 
			
		||||
			return err;
 | 
			
		||||
		}
 | 
			
		||||
		if (strcmp(str, snd_ctl_elem_info_get_item_name(info)) == 0)
 | 
			
		||||
			return idx;
 | 
			
		||||
	}
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void try_replace(snd_sctl_replace_t *replace, const char *id, const char **value)
 | 
			
		||||
static int snd_config_get_ctl_elem_value(snd_config_t *conf,
 | 
			
		||||
					 snd_ctl_t *ctl,
 | 
			
		||||
					 snd_ctl_elem_value_t *val,
 | 
			
		||||
					 snd_ctl_elem_value_t *mask,
 | 
			
		||||
					 snd_ctl_elem_info_t *info)
 | 
			
		||||
{
 | 
			
		||||
	if (replace == NULL)
 | 
			
		||||
		return;
 | 
			
		||||
	while (replace->key) {
 | 
			
		||||
		if (!strcmp(id, replace->key)) {
 | 
			
		||||
			if (!strcmp(*value, replace->old_value)) {
 | 
			
		||||
				*value = replace->new_value;
 | 
			
		||||
				return;
 | 
			
		||||
	int err;
 | 
			
		||||
	snd_config_iterator_t i, next;
 | 
			
		||||
	snd_ctl_elem_id_t *id;
 | 
			
		||||
	snd_ctl_elem_type_t type;
 | 
			
		||||
	unsigned int count;
 | 
			
		||||
	long v;
 | 
			
		||||
	long idx;
 | 
			
		||||
	snd_ctl_elem_id_alloca(&id);
 | 
			
		||||
	snd_ctl_elem_value_get_id(val, id);
 | 
			
		||||
	count = snd_ctl_elem_info_get_count(info);
 | 
			
		||||
	type = snd_ctl_elem_info_get_type(info);
 | 
			
		||||
	if (count == 1) {
 | 
			
		||||
		switch (snd_enum_to_int(type)) {
 | 
			
		||||
		case SND_CTL_ELEM_TYPE_BOOLEAN:
 | 
			
		||||
			v = snd_config_get_bool(conf);
 | 
			
		||||
			if (v >= 0) {
 | 
			
		||||
				snd_ctl_elem_value_set_boolean(val, 0, v);
 | 
			
		||||
				if (mask)
 | 
			
		||||
					snd_ctl_elem_value_set_boolean(mask, 0, 1);
 | 
			
		||||
				return 0;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case SND_CTL_ELEM_TYPE_INTEGER:
 | 
			
		||||
			err = snd_config_get_integer(conf, &v);
 | 
			
		||||
			if (err == 0) {
 | 
			
		||||
				snd_ctl_elem_value_set_integer(val, 0, v);
 | 
			
		||||
				if (mask)
 | 
			
		||||
					snd_ctl_elem_value_set_integer(mask, 0, ~0L);
 | 
			
		||||
				return 0;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case SND_CTL_ELEM_TYPE_ENUMERATED:
 | 
			
		||||
			v = snd_config_get_ctl_elem_enumerated(conf, ctl, info);
 | 
			
		||||
			if (v >= 0) {
 | 
			
		||||
				snd_ctl_elem_value_set_enumerated(val, 0, v);
 | 
			
		||||
				if (mask)
 | 
			
		||||
					snd_ctl_elem_value_set_enumerated(mask, 0, ~0);
 | 
			
		||||
				return 0;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case SND_CTL_ELEM_TYPE_BYTES:
 | 
			
		||||
		case SND_CTL_ELEM_TYPE_IEC958:
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			SNDERR("Unknow control type: %d", snd_enum_to_int(type));
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
		replace++;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void add_value(snd_ctl_elem_info_t *info, snd_ctl_elem_value_t *evalue, int idx, const char *value)
 | 
			
		||||
{
 | 
			
		||||
	switch (snd_ctl_elem_info_get_type(info)) {
 | 
			
		||||
	case SND_CTL_ELEM_TYPE_BOOLEAN:
 | 
			
		||||
		{
 | 
			
		||||
			int val = 0;
 | 
			
		||||
			if (idx < 0 || idx >= 128) {
 | 
			
		||||
				SNDERR("index out of range (0-127): %i", idx);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			if (!strcasecmp(value, "true") || !strcasecmp(value, "on"))
 | 
			
		||||
				val = 1;
 | 
			
		||||
			else if (isdigit(*value))
 | 
			
		||||
				val = atoi(value);
 | 
			
		||||
			snd_ctl_elem_value_set_boolean(evalue, idx, val);
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case SND_CTL_ELEM_TYPE_INTEGER:
 | 
			
		||||
		{
 | 
			
		||||
			if (idx < 0 || idx >= 128) {
 | 
			
		||||
				SNDERR("index out of range (0-127): %i", idx);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			snd_ctl_elem_value_set_integer(evalue, idx, strtoul(value, NULL, 10));
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
#if 0
 | 
			
		||||
	case SND_CTL_ELEM_TYPE_ENUMERATED:
 | 
			
		||||
	case SND_CTL_ELEM_TYPE_BYTES:
 | 
			
		||||
	switch (snd_enum_to_int(type)) {
 | 
			
		||||
	case SND_CTL_ELEM_TYPE_IEC958:
 | 
			
		||||
		break;
 | 
			
		||||
#endif
 | 
			
		||||
	default:
 | 
			
		||||
		SNDERR("type %i is not supported", snd_ctl_elem_info_get_type(info));
 | 
			
		||||
		count = sizeof(snd_aes_iec958_t);
 | 
			
		||||
		/* Fall through */
 | 
			
		||||
	case SND_CTL_ELEM_TYPE_BYTES:
 | 
			
		||||
	{
 | 
			
		||||
		const char *buf;
 | 
			
		||||
		err = snd_config_get_string(conf, &buf);
 | 
			
		||||
		if (err >= 0) {
 | 
			
		||||
			int c1 = 0;
 | 
			
		||||
			unsigned int len = strlen(buf);
 | 
			
		||||
			unsigned int idx = 0;
 | 
			
		||||
			if (len % 2 != 0 || len > count * 2) {
 | 
			
		||||
			_bad_content:
 | 
			
		||||
				SNDERR("bad value content\n");
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
			}
 | 
			
		||||
			while (*buf) {
 | 
			
		||||
				int c = *buf++;
 | 
			
		||||
				if (c >= '0' && c <= '9')
 | 
			
		||||
					c -= '0';
 | 
			
		||||
				else if (c >= 'a' && c <= 'f')
 | 
			
		||||
					c = c - 'a' + 10;
 | 
			
		||||
				else if (c >= 'A' && c <= 'F')
 | 
			
		||||
					c = c - 'A' + 10;
 | 
			
		||||
				else {
 | 
			
		||||
					goto _bad_content;
 | 
			
		||||
				}
 | 
			
		||||
				if (idx % 2 == 1) {
 | 
			
		||||
					snd_ctl_elem_value_set_byte(val, idx / 2, c1 << 4 | c);
 | 
			
		||||
					if (mask)
 | 
			
		||||
						snd_ctl_elem_value_set_byte(mask, idx / 2, 0xff);
 | 
			
		||||
				} else
 | 
			
		||||
					c1 = c;
 | 
			
		||||
				idx++;
 | 
			
		||||
			}
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) {
 | 
			
		||||
		SNDERR("bad value type");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	snd_config_for_each(i, next, conf) {
 | 
			
		||||
		snd_config_t *n = snd_config_iterator_entry(i);
 | 
			
		||||
		err = safe_strtol(snd_config_get_id(n), &idx);
 | 
			
		||||
		if (err < 0 || idx < 0 || (unsigned int) idx >= count) {
 | 
			
		||||
			SNDERR("bad value index");
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
		switch (snd_enum_to_int(type)) {
 | 
			
		||||
		case SND_CTL_ELEM_TYPE_BOOLEAN:
 | 
			
		||||
			v = snd_config_get_bool(n);
 | 
			
		||||
			if (v < 0)
 | 
			
		||||
				goto _bad_content;
 | 
			
		||||
			snd_ctl_elem_value_set_boolean(val, idx, v);
 | 
			
		||||
			if (mask)
 | 
			
		||||
				snd_ctl_elem_value_set_boolean(mask, idx, 1);
 | 
			
		||||
			break;
 | 
			
		||||
		case SND_CTL_ELEM_TYPE_INTEGER:
 | 
			
		||||
			err = snd_config_get_integer(n, &v);
 | 
			
		||||
			if (err < 0)
 | 
			
		||||
				goto _bad_content;
 | 
			
		||||
			snd_ctl_elem_value_set_integer(val, idx, v);
 | 
			
		||||
			if (mask)
 | 
			
		||||
				snd_ctl_elem_value_set_integer(mask, idx, ~0L);
 | 
			
		||||
			break;
 | 
			
		||||
		case SND_CTL_ELEM_TYPE_ENUMERATED:
 | 
			
		||||
			v = snd_config_get_ctl_elem_enumerated(n, ctl, info);
 | 
			
		||||
			if (v < 0)
 | 
			
		||||
				goto _bad_content;
 | 
			
		||||
			snd_ctl_elem_value_set_enumerated(val, idx, v);
 | 
			
		||||
			if (mask)
 | 
			
		||||
				snd_ctl_elem_value_set_enumerated(mask, idx, ~0);
 | 
			
		||||
			break;
 | 
			
		||||
		case SND_CTL_ELEM_TYPE_BYTES:
 | 
			
		||||
		case SND_CTL_ELEM_TYPE_IEC958:
 | 
			
		||||
			err = snd_config_get_integer(n, &v);
 | 
			
		||||
			if (err < 0 || v < 0 || v > 255)
 | 
			
		||||
				goto _bad_content;
 | 
			
		||||
			snd_ctl_elem_value_set_byte(val, idx, v);
 | 
			
		||||
			if (mask)
 | 
			
		||||
				snd_ctl_elem_value_set_byte(mask, idx, 0xff);
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \brief build and set control primitives from configuration
 | 
			
		||||
 * \param setup container for control primitives
 | 
			
		||||
 * \return 0 on success otherwise a negative error code
 | 
			
		||||
 *
 | 
			
		||||
 * Build and set control primitives from configuration.
 | 
			
		||||
 */
 | 
			
		||||
int snd_sctl_build(snd_ctl_t *handle, snd_sctl_t **r_setup, snd_config_t *config, snd_sctl_replace_t *replace)
 | 
			
		||||
static int config_replace(snd_config_t *conf,
 | 
			
		||||
			  snd_config_string_replace_callback_t *callback,
 | 
			
		||||
			  void *private_data, char **res)
 | 
			
		||||
{
 | 
			
		||||
	snd_sctl_t *setup;
 | 
			
		||||
	int err;
 | 
			
		||||
	char *replace, *tmp;
 | 
			
		||||
	
 | 
			
		||||
	err = snd_config_get_ascii(conf, &tmp);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		return err;
 | 
			
		||||
	err = snd_config_string_replace(tmp, '&', callback, private_data, &replace);
 | 
			
		||||
	free(tmp);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		return err;
 | 
			
		||||
	if (replace == NULL) {
 | 
			
		||||
		SNDERR("Invalid value for '%s'", snd_config_get_id(conf));
 | 
			
		||||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
	*res = replace;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int config_replace_integer(snd_config_t *conf,
 | 
			
		||||
				  snd_config_string_replace_callback_t *callback,
 | 
			
		||||
				  void *private_data, long *res)
 | 
			
		||||
{
 | 
			
		||||
	char *tmp;
 | 
			
		||||
	int err;
 | 
			
		||||
	
 | 
			
		||||
	err = config_replace(conf, callback, private_data, &tmp);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		return err;
 | 
			
		||||
	err = safe_strtol(tmp, res);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("Invalid value for '%s'", snd_config_get_id(conf));
 | 
			
		||||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int config_replace_bool(snd_config_t *conf,
 | 
			
		||||
			       snd_config_string_replace_callback_t *callback,
 | 
			
		||||
			       void *private_data, int *res)
 | 
			
		||||
{
 | 
			
		||||
	char *tmp;
 | 
			
		||||
	int err;
 | 
			
		||||
	
 | 
			
		||||
	err = config_replace(conf, callback, private_data, &tmp);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		return err;
 | 
			
		||||
	err = snd_config_get_bool_ascii(tmp);
 | 
			
		||||
	free(tmp);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("Invalid value for '%s'", snd_config_get_id(conf));
 | 
			
		||||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
	*res = err;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int add_elem(snd_sctl_t *h, snd_config_t *conf,
 | 
			
		||||
		    snd_config_string_replace_callback_t *callback,
 | 
			
		||||
		    void *private_data)
 | 
			
		||||
{
 | 
			
		||||
	snd_config_iterator_t i, next;
 | 
			
		||||
	char *tmp;
 | 
			
		||||
	int iface = SND_CTL_ELEM_IFACE_PCM;
 | 
			
		||||
	char *name = NULL;
 | 
			
		||||
	long index = 0;
 | 
			
		||||
	long device = -1;
 | 
			
		||||
	long subdevice = -1;
 | 
			
		||||
	int lock = 0;
 | 
			
		||||
	int preserve = 0;
 | 
			
		||||
	snd_config_t *value = NULL, *mask = NULL;
 | 
			
		||||
	snd_sctl_elem_t *elem = NULL;
 | 
			
		||||
	int err;
 | 
			
		||||
	snd_config_for_each(i, next, conf) {
 | 
			
		||||
		snd_config_t *n = snd_config_iterator_entry(i);
 | 
			
		||||
		const char *id = snd_config_get_id(n);
 | 
			
		||||
		if (strcmp(id, "comment") == 0)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (strcmp(id, "iface") == 0) {
 | 
			
		||||
			if ((err = config_replace(n, callback, private_data, &tmp)) < 0)
 | 
			
		||||
				goto _err;
 | 
			
		||||
			if ((err = snd_config_get_ctl_iface_ascii(tmp)) < 0) {
 | 
			
		||||
				SNDERR("Invalid value for '%s'", id);
 | 
			
		||||
				free(tmp);
 | 
			
		||||
				goto _err;
 | 
			
		||||
			}
 | 
			
		||||
			free(tmp);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		if (strcmp(id, "name") == 0) {
 | 
			
		||||
			if ((err = config_replace(n, callback, private_data, &name)) < 0)
 | 
			
		||||
				goto _err;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		if (strcmp(id, "index") == 0) {
 | 
			
		||||
			if ((err = config_replace_integer(n, callback, private_data, &index)) < 0)
 | 
			
		||||
				goto _err;
 | 
			
		||||
		}
 | 
			
		||||
		if (strcmp(id, "device") == 0) {
 | 
			
		||||
			if ((err = config_replace_integer(n, callback, private_data, &device)) < 0)
 | 
			
		||||
				goto _err;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		if (strcmp(id, "subdevice") == 0) {
 | 
			
		||||
			if ((err = config_replace_integer(n, callback, private_data, &subdevice)) < 0)
 | 
			
		||||
				goto _err;
 | 
			
		||||
		}
 | 
			
		||||
		if (strcmp(id, "lock") == 0) {
 | 
			
		||||
			if ((err = config_replace_bool(n, callback, private_data, &lock)) < 0)
 | 
			
		||||
				goto _err;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		if (strcmp(id, "preserve") == 0) {
 | 
			
		||||
			if ((err = config_replace_bool(n, callback, private_data, &preserve)) < 0)
 | 
			
		||||
				goto _err;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		if (strcmp(id, "value") == 0) {
 | 
			
		||||
			value = n;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		if (strcmp(id, "mask") == 0) {
 | 
			
		||||
			mask = n;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		SNDERR("Unknown field %s", id);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	if (name == NULL) {
 | 
			
		||||
		SNDERR("Missing control name");
 | 
			
		||||
		err = -EINVAL;
 | 
			
		||||
		goto _err;
 | 
			
		||||
	}
 | 
			
		||||
	if (value == NULL) {
 | 
			
		||||
		SNDERR("Missing control value");
 | 
			
		||||
		err = -EINVAL;
 | 
			
		||||
		goto _err;
 | 
			
		||||
	}
 | 
			
		||||
	if (device < 0)
 | 
			
		||||
		device = 0;
 | 
			
		||||
	if (subdevice < 0)
 | 
			
		||||
		subdevice = 0;
 | 
			
		||||
	elem = calloc(1, sizeof(*elem));
 | 
			
		||||
	if (!elem)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	err = snd_ctl_elem_id_malloc(&elem->id);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		goto _err;
 | 
			
		||||
	err = snd_ctl_elem_info_malloc(&elem->info);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		goto _err;
 | 
			
		||||
	err = snd_ctl_elem_value_malloc(&elem->val);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		goto _err;
 | 
			
		||||
	err = snd_ctl_elem_value_malloc(&elem->mask);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		goto _err;
 | 
			
		||||
	err = snd_ctl_elem_value_malloc(&elem->old);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		goto _err;
 | 
			
		||||
	elem->lock = lock;
 | 
			
		||||
	elem->preserve = preserve;
 | 
			
		||||
	snd_ctl_elem_id_set_interface(elem->id, iface);
 | 
			
		||||
	snd_ctl_elem_id_set_name(elem->id, name);
 | 
			
		||||
	snd_ctl_elem_id_set_index(elem->id, index);
 | 
			
		||||
	snd_ctl_elem_id_set_device(elem->id, device);
 | 
			
		||||
	snd_ctl_elem_id_set_subdevice(elem->id, subdevice);
 | 
			
		||||
	snd_ctl_elem_info_set_id(elem->info, elem->id);
 | 
			
		||||
	err = snd_ctl_elem_info(h->ctl, elem->info);
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		SNDERR("Cannot obtain info for CTL elem");
 | 
			
		||||
		goto _err;
 | 
			
		||||
	}
 | 
			
		||||
	snd_ctl_elem_value_set_id(elem->val, elem->id);
 | 
			
		||||
	snd_ctl_elem_value_set_id(elem->old, elem->id);
 | 
			
		||||
	if (mask) {
 | 
			
		||||
		err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, NULL, elem->info);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			goto _err;
 | 
			
		||||
		err = snd_config_get_ctl_elem_value(mask, h->ctl, elem->mask, NULL, elem->info);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			goto _err;
 | 
			
		||||
	} else {
 | 
			
		||||
		err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, elem->mask, elem->info);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			goto _err;
 | 
			
		||||
	}
 | 
			
		||||
		
 | 
			
		||||
	err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, elem->mask, elem->info);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		goto _err;
 | 
			
		||||
	list_add_tail(&elem->list, &h->elems);
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
 _err:
 | 
			
		||||
 	if (name)
 | 
			
		||||
 		free(name);
 | 
			
		||||
 	if (elem) {
 | 
			
		||||
		if (elem->id)
 | 
			
		||||
			snd_ctl_elem_id_free(elem->id);
 | 
			
		||||
		if (elem->info)
 | 
			
		||||
			snd_ctl_elem_info_free(elem->info);
 | 
			
		||||
		if (elem->val)
 | 
			
		||||
			snd_ctl_elem_value_free(elem->val);
 | 
			
		||||
		if (elem->mask)
 | 
			
		||||
			snd_ctl_elem_value_free(elem->mask);
 | 
			
		||||
		if (elem->old)
 | 
			
		||||
			snd_ctl_elem_value_free(elem->old);
 | 
			
		||||
		free(elem);
 | 
			
		||||
	}
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int snd_sctl_build(snd_sctl_t **sctl, snd_ctl_t *handle, snd_config_t *conf,
 | 
			
		||||
		   snd_config_string_replace_callback_t *callback,
 | 
			
		||||
		   void *private_data,
 | 
			
		||||
		   int mode)
 | 
			
		||||
{
 | 
			
		||||
	snd_sctl_t *h;
 | 
			
		||||
	snd_config_iterator_t i, next;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	assert(handle && r_setup && config);
 | 
			
		||||
	*r_setup = NULL;
 | 
			
		||||
	if ((setup = calloc(1, sizeof(*setup))) == NULL)
 | 
			
		||||
	assert(sctl);
 | 
			
		||||
	assert(conf);
 | 
			
		||||
	if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	h = calloc(1, sizeof(*h));
 | 
			
		||||
	if (!h) {
 | 
			
		||||
		if (mode & SND_SCTL_NOFREE)
 | 
			
		||||
			return -ENOMEM;
 | 
			
		||||
		snd_ctl_close(handle);
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	INIT_LIST_HEAD(&setup->elements);
 | 
			
		||||
	snd_config_for_each(i, next, config) {
 | 
			
		||||
	}
 | 
			
		||||
	h->mode = mode;
 | 
			
		||||
	h->ctl = handle;
 | 
			
		||||
	INIT_LIST_HEAD(&h->elems);
 | 
			
		||||
	snd_config_for_each(i, next, conf) {
 | 
			
		||||
		snd_config_t *n = snd_config_iterator_entry(i);
 | 
			
		||||
		// const char *id = snd_config_get_id(n);
 | 
			
		||||
		if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND) {
 | 
			
		||||
			snd_config_iterator_t i, next;
 | 
			
		||||
			snd_sctl_elem_t *elem;
 | 
			
		||||
			snd_ctl_elem_id_t *eid;
 | 
			
		||||
			snd_ctl_elem_info_t *einfo;
 | 
			
		||||
			snd_ctl_elem_value_t *evalue;
 | 
			
		||||
			snd_ctl_elem_id_alloca(&eid);
 | 
			
		||||
			snd_ctl_elem_info_alloca(&einfo);
 | 
			
		||||
			snd_ctl_elem_value_alloca(&evalue);
 | 
			
		||||
			elem = calloc(1, sizeof(*elem));
 | 
			
		||||
			if (elem == NULL) {
 | 
			
		||||
				SNDERR("malloc problem");
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			snd_config_for_each(i, next, n) {
 | 
			
		||||
				snd_config_t *n = snd_config_iterator_entry(i);
 | 
			
		||||
				const char *id = snd_config_get_id(n);
 | 
			
		||||
				char svalue[16];
 | 
			
		||||
				const char *value;
 | 
			
		||||
				unsigned long i;
 | 
			
		||||
				if (snd_config_get_type(n) == SND_CONFIG_TYPE_INTEGER) {
 | 
			
		||||
					snd_config_get_integer(n, &i);
 | 
			
		||||
					sprintf(svalue, "%li", i);
 | 
			
		||||
					value = svalue;
 | 
			
		||||
				} else if (snd_config_get_type(n) == SND_CONFIG_TYPE_STRING) {
 | 
			
		||||
					snd_config_get_string(n, &value);
 | 
			
		||||
				} else if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND) {
 | 
			
		||||
					value = NULL;
 | 
			
		||||
				} else {
 | 
			
		||||
					SNDERR("unknown configuration entry type");
 | 
			
		||||
					continue;
 | 
			
		||||
				}
 | 
			
		||||
				try_replace(replace, id, &value);
 | 
			
		||||
				if (!strcmp(id, "iface"))
 | 
			
		||||
					snd_ctl_elem_id_set_interface(eid, config_iface(value));
 | 
			
		||||
				else if (!strcmp(id, "name"))
 | 
			
		||||
					snd_ctl_elem_id_set_name(eid, value);
 | 
			
		||||
				else if (!strcmp(id, "index"))
 | 
			
		||||
					snd_ctl_elem_id_set_index(eid, atoi(value));
 | 
			
		||||
				else if (!strcmp(id, "lock"))
 | 
			
		||||
					elem->lock = 1;
 | 
			
		||||
				else if (!strcmp(id, "preserve"))
 | 
			
		||||
					elem->preserve = 1;
 | 
			
		||||
				else if (!strcmp(id, "value")) {
 | 
			
		||||
					if (elem->value == NULL) {
 | 
			
		||||
						if (snd_ctl_elem_value_malloc(&elem->value) < 0) {
 | 
			
		||||
							SNDERR("malloc problem");
 | 
			
		||||
							continue;
 | 
			
		||||
						}
 | 
			
		||||
						snd_ctl_elem_info_set_id(einfo, eid);
 | 
			
		||||
						if ((err = snd_ctl_elem_info(handle, einfo)) < 0) {
 | 
			
		||||
							SNDERR("snd_ctl_elem_info %s : %s", id_str(eid), snd_strerror(err));
 | 
			
		||||
							continue;
 | 
			
		||||
						}
 | 
			
		||||
						snd_ctl_elem_value_set_id(elem->value, eid);
 | 
			
		||||
						if ((err = snd_ctl_elem_read(handle, elem->value)) < 0) {
 | 
			
		||||
							SNDERR("snd_ctl_elem_read %s : %s", id_str(eid), snd_strerror(err));
 | 
			
		||||
							continue;
 | 
			
		||||
						}
 | 
			
		||||
						snd_ctl_elem_value_copy(evalue, elem->value);
 | 
			
		||||
					}
 | 
			
		||||
					if (value == NULL) {
 | 
			
		||||
						snd_config_iterator_t i, next;
 | 
			
		||||
						snd_config_for_each(i, next, n) {
 | 
			
		||||
							snd_config_t *n = snd_config_iterator_entry(i);
 | 
			
		||||
							char svalue[16];
 | 
			
		||||
							const char *value;
 | 
			
		||||
							int idx = atoi(snd_config_get_id(n));
 | 
			
		||||
							unsigned long i;
 | 
			
		||||
							if (snd_config_get_type(n) == SND_CONFIG_TYPE_INTEGER) {
 | 
			
		||||
								snd_config_get_integer(n, &i);
 | 
			
		||||
								sprintf(svalue, "%li", i);
 | 
			
		||||
								value = svalue;
 | 
			
		||||
							} else if (snd_config_get_type(n) == SND_CONFIG_TYPE_STRING) {
 | 
			
		||||
								snd_config_get_string(n, &value);
 | 
			
		||||
							} else {
 | 
			
		||||
								SNDERR("unknown configuration entry type");
 | 
			
		||||
								continue;
 | 
			
		||||
							}
 | 
			
		||||
							add_value(einfo, evalue, idx, value);
 | 
			
		||||
						}
 | 
			
		||||
					} else {
 | 
			
		||||
						add_value(einfo, evalue, 0, value);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			list_add_tail(&elem->list, &setup->elements);
 | 
			
		||||
			if ((err = snd_ctl_elem_write(handle, evalue)) < 0) {
 | 
			
		||||
				SNDERR("snd_ctl_elem_write %s : %s", id_str(eid), snd_strerror(err));
 | 
			
		||||
				snd_sctl_free(handle, setup);
 | 
			
		||||
				return err;
 | 
			
		||||
			}
 | 
			
		||||
			if (elem->lock) {
 | 
			
		||||
				if ((err = snd_ctl_elem_lock(handle, eid)) < 0) {
 | 
			
		||||
					SNDERR("snd_ctl_elem_lock %s : %s", id_str(eid), snd_strerror(err));
 | 
			
		||||
					snd_sctl_free(handle, setup);
 | 
			
		||||
					return err;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if (!elem->preserve) {
 | 
			
		||||
				snd_ctl_elem_value_free(elem->value);
 | 
			
		||||
				elem->value = NULL;
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			SNDERR("compound type expected here");
 | 
			
		||||
		err = add_elem(h, n, callback, private_data);
 | 
			
		||||
		if (err < 0) {
 | 
			
		||||
			free_elems(h);
 | 
			
		||||
			return err;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	*r_setup = setup;
 | 
			
		||||
	*sctl = h;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \brief restore control primitives from configuration
 | 
			
		||||
 * \param setup container for control primitives
 | 
			
		||||
 * \return 0 on success otherwise a negative error code
 | 
			
		||||
 *
 | 
			
		||||
 * Restore control primitives from configuration
 | 
			
		||||
 */
 | 
			
		||||
int snd_sctl_free(snd_ctl_t *handle, snd_sctl_t *setup)
 | 
			
		||||
int snd_sctl_free(snd_sctl_t *sctl)
 | 
			
		||||
{
 | 
			
		||||
	struct list_head *pos;
 | 
			
		||||
	snd_sctl_elem_t *elem;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	assert(setup);
 | 
			
		||||
      __again:
 | 
			
		||||
	list_for_each(pos, &setup->elements) {
 | 
			
		||||
		elem = list_entry(pos, snd_sctl_elem_t, list);
 | 
			
		||||
		if (elem->preserve && elem->value) {
 | 
			
		||||
			if ((err = snd_ctl_elem_write(handle, elem->value)) < 0)
 | 
			
		||||
				SNDERR("snd_ctl_elem_write %s : %s", id_str(&elem->value->id), snd_strerror(err));
 | 
			
		||||
		}
 | 
			
		||||
		if (elem->value) {
 | 
			
		||||
			snd_ctl_elem_value_free(elem->value);
 | 
			
		||||
			elem->value = NULL;
 | 
			
		||||
		}
 | 
			
		||||
		list_del(&elem->list);
 | 
			
		||||
		free(elem);
 | 
			
		||||
		goto __again;
 | 
			
		||||
	}
 | 
			
		||||
	free(setup);
 | 
			
		||||
	return 0;
 | 
			
		||||
	assert(sctl);
 | 
			
		||||
	return free_elems(sctl);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue