Blame src/control/control_ext.c

Packit Service db8eaa
/**
Packit Service db8eaa
 * \file control/control_ext.c
Packit Service db8eaa
 * \ingroup CtlPlugin_SDK
Packit Service db8eaa
 * \brief External Control Plugin SDK
Packit Service db8eaa
 * \author Takashi Iwai <tiwai@suse.de>
Packit Service db8eaa
 * \date 2005
Packit Service db8eaa
 */
Packit Service db8eaa
/*
Packit Service db8eaa
 *  Control Interface - External Control Plugin SDK
Packit Service db8eaa
 *
Packit Service db8eaa
 *  Copyright (c) 2005 Takashi Iwai <tiwai@suse.de>
Packit Service db8eaa
 *
Packit Service db8eaa
 *
Packit Service db8eaa
 *   This library is free software; you can redistribute it and/or modify
Packit Service db8eaa
 *   it under the terms of the GNU Lesser General Public License as
Packit Service db8eaa
 *   published by the Free Software Foundation; either version 2.1 of
Packit Service db8eaa
 *   the License, or (at your option) any later version.
Packit Service db8eaa
 *
Packit Service db8eaa
 *   This program is distributed in the hope that it will be useful,
Packit Service db8eaa
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service db8eaa
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service db8eaa
 *   GNU Lesser General Public License for more details.
Packit Service db8eaa
 *
Packit Service db8eaa
 *   You should have received a copy of the GNU Lesser General Public
Packit Service db8eaa
 *   License along with this library; if not, write to the Free Software
Packit Service db8eaa
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit Service db8eaa
 *
Packit Service db8eaa
 */
Packit Service db8eaa
Packit Service db8eaa
#include <stdio.h>
Packit Service db8eaa
#include <stdlib.h>
Packit Service db8eaa
#include <unistd.h>
Packit Service db8eaa
#include <string.h>
Packit Service db8eaa
#include "control_local.h"
Packit Service db8eaa
#include "control_external.h"
Packit Service db8eaa
Packit Service db8eaa
#ifndef PIC
Packit Service db8eaa
/* entry for static linking */
Packit Service db8eaa
const char *_snd_module_control_ext = "";
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_close(snd_ctl_t *handle)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_ctl_ext_t *ext = handle->private_data;
Packit Service db8eaa
	
Packit Service db8eaa
	if (ext->callback->close)
Packit Service db8eaa
		ext->callback->close(ext);
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_nonblock(snd_ctl_t *handle, int nonblock)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_ctl_ext_t *ext = handle->private_data;
Packit Service db8eaa
Packit Service db8eaa
	ext->nonblock = nonblock;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_async(snd_ctl_t *ctl ATTRIBUTE_UNUSED,
Packit Service db8eaa
			     int sig ATTRIBUTE_UNUSED,
Packit Service db8eaa
			     pid_t pid ATTRIBUTE_UNUSED)
Packit Service db8eaa
{
Packit Service db8eaa
	return -ENOSYS;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_subscribe_events(snd_ctl_t *handle, int subscribe)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_ctl_ext_t *ext = handle->private_data;
Packit Service db8eaa
Packit Service db8eaa
	if (subscribe < 0)
Packit Service db8eaa
		return ext->subscribed;
Packit Service db8eaa
	ext->subscribed = !!subscribe;
Packit Service db8eaa
	if (ext->callback->subscribe_events)
Packit Service db8eaa
		ext->callback->subscribe_events(ext, subscribe);
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_card_info(snd_ctl_t *handle, snd_ctl_card_info_t *info)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_ctl_ext_t *ext = handle->private_data;
Packit Service db8eaa
Packit Service db8eaa
	memset(info, 0, sizeof(*info));
Packit Service db8eaa
	info->card = ext->card_idx;
Packit Service db8eaa
	memcpy(info->id, ext->id, sizeof(info->id));
Packit Service db8eaa
	memcpy(info->driver, ext->driver, sizeof(info->driver));
Packit Service db8eaa
	memcpy(info->name, ext->name, sizeof(info->name));
Packit Service db8eaa
	memcpy(info->longname, ext->longname, sizeof(info->longname));
Packit Service db8eaa
	memcpy(info->mixername, ext->mixername, sizeof(info->mixername));
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_elem_list(snd_ctl_t *handle, snd_ctl_elem_list_t *list)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_ctl_ext_t *ext = handle->private_data;
Packit Service db8eaa
	int ret;
Packit Service db8eaa
	unsigned int i, offset;
Packit Service db8eaa
	snd_ctl_elem_id_t *ids;
Packit Service db8eaa
Packit Service db8eaa
	list->count = ext->callback->elem_count(ext);
Packit Service db8eaa
	list->used = 0;
Packit Service db8eaa
	ids = list->pids;
Packit Service db8eaa
	offset = list->offset;
Packit Service db8eaa
	for (i = 0; i < list->space; i++) {
Packit Service db8eaa
		if (offset >= list->count)
Packit Service db8eaa
			break;
Packit Service db8eaa
		snd_ctl_elem_id_clear(ids);
Packit Service db8eaa
		ret = ext->callback->elem_list(ext, offset, ids);
Packit Service db8eaa
		if (ret < 0)
Packit Service db8eaa
			return ret;
Packit Service db8eaa
		ids->numid = offset + 1; /* fake number */
Packit Service db8eaa
		list->used++;
Packit Service db8eaa
		offset++;
Packit Service db8eaa
		ids++;
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_ctl_ext_key_t get_elem(snd_ctl_ext_t *ext, snd_ctl_elem_id_t *id)
Packit Service db8eaa
{
Packit Service db8eaa
	int numid = id->numid;
Packit Service db8eaa
	if (numid > 0) {
Packit Service db8eaa
		ext->callback->elem_list(ext, numid - 1, id);
Packit Service db8eaa
		id->numid = numid;
Packit Service db8eaa
	} else
Packit Service db8eaa
		id->numid = 0;
Packit Service db8eaa
	return ext->callback->find_elem(ext, id);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_elem_info(snd_ctl_t *handle, snd_ctl_elem_info_t *info)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_ctl_ext_t *ext = handle->private_data;
Packit Service db8eaa
	snd_ctl_ext_key_t key;
Packit Service db8eaa
	int type, ret;
Packit Service db8eaa
Packit Service db8eaa
	key = get_elem(ext, &info->id);
Packit Service db8eaa
	if (key == SND_CTL_EXT_KEY_NOT_FOUND)
Packit Service db8eaa
		return -ENOENT;
Packit Service db8eaa
	ret = ext->callback->get_attribute(ext, key, &type, &info->access, &info->count);
Packit Service db8eaa
	if (ret < 0)
Packit Service db8eaa
		goto err;
Packit Service db8eaa
	info->type = type;
Packit Service db8eaa
	ret = -EINVAL;
Packit Service db8eaa
	switch (info->type) {
Packit Service db8eaa
	case SND_CTL_ELEM_TYPE_BOOLEAN:
Packit Service db8eaa
		info->value.integer.min = 0;
Packit Service db8eaa
		info->value.integer.max = 1;
Packit Service db8eaa
		ret = 0;
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_CTL_ELEM_TYPE_INTEGER:
Packit Service db8eaa
		if (! ext->callback->get_integer_info)
Packit Service db8eaa
			goto err;
Packit Service db8eaa
		ret = ext->callback->get_integer_info(ext, key, &info->value.integer.min,
Packit Service db8eaa
						      &info->value.integer.max,
Packit Service db8eaa
						      &info->value.integer.step);
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_CTL_ELEM_TYPE_INTEGER64:
Packit Service db8eaa
		if (! ext->callback->get_integer64_info)
Packit Service db8eaa
			goto err;
Packit Service db8eaa
		{
Packit Service db8eaa
			int64_t xmin, xmax, xstep;
Packit Service db8eaa
			ret = ext->callback->get_integer64_info(ext, key,
Packit Service db8eaa
								&xmin,
Packit Service db8eaa
								&xmax,
Packit Service db8eaa
								&xstep);
Packit Service db8eaa
			info->value.integer64.min = xmin;
Packit Service db8eaa
			info->value.integer64.max = xmax;
Packit Service db8eaa
			info->value.integer64.step = xstep;
Packit Service db8eaa
		}
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_CTL_ELEM_TYPE_ENUMERATED:
Packit Service db8eaa
		if (! ext->callback->get_enumerated_info)
Packit Service db8eaa
			goto err;
Packit Service db8eaa
		ret = ext->callback->get_enumerated_info(ext, key, &info->value.enumerated.items);
Packit Service db8eaa
		ext->callback->get_enumerated_name(ext, key, info->value.enumerated.item,
Packit Service db8eaa
						   info->value.enumerated.name,
Packit Service db8eaa
						   sizeof(info->value.enumerated.name));
Packit Service db8eaa
		break;
Packit Service db8eaa
	default:
Packit Service db8eaa
		ret = 0;
Packit Service db8eaa
		break;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
 err:
Packit Service db8eaa
	if (ext->callback->free_key)
Packit Service db8eaa
		ext->callback->free_key(ext, key);
Packit Service db8eaa
Packit Service db8eaa
	return ret;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_elem_add(snd_ctl_t *handle ATTRIBUTE_UNUSED,
Packit Service db8eaa
				snd_ctl_elem_info_t *info ATTRIBUTE_UNUSED)
Packit Service db8eaa
{
Packit Service db8eaa
	return -ENXIO;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_elem_replace(snd_ctl_t *handle ATTRIBUTE_UNUSED,
Packit Service db8eaa
				    snd_ctl_elem_info_t *info ATTRIBUTE_UNUSED)
Packit Service db8eaa
{
Packit Service db8eaa
	return -ENXIO;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_elem_remove(snd_ctl_t *handle ATTRIBUTE_UNUSED,
Packit Service db8eaa
				   snd_ctl_elem_id_t *id ATTRIBUTE_UNUSED)
Packit Service db8eaa
{
Packit Service db8eaa
	return -ENXIO;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_elem_read(snd_ctl_t *handle, snd_ctl_elem_value_t *control)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_ctl_ext_t *ext = handle->private_data;
Packit Service db8eaa
	snd_ctl_ext_key_t key;
Packit Service db8eaa
	int type, ret;
Packit Service db8eaa
	unsigned int access, count;
Packit Service db8eaa
Packit Service db8eaa
	key = get_elem(ext, &control->id);
Packit Service db8eaa
	if (key == SND_CTL_EXT_KEY_NOT_FOUND)
Packit Service db8eaa
		return -ENOENT;
Packit Service db8eaa
	ret = ext->callback->get_attribute(ext, key, &type, &access, &count);
Packit Service db8eaa
	if (ret < 0)
Packit Service db8eaa
		goto err;
Packit Service db8eaa
	ret = -EINVAL;
Packit Service db8eaa
	switch (type) {
Packit Service db8eaa
	case SND_CTL_ELEM_TYPE_BOOLEAN:
Packit Service db8eaa
	case SND_CTL_ELEM_TYPE_INTEGER:
Packit Service db8eaa
		if (! ext->callback->read_integer)
Packit Service db8eaa
			goto err;
Packit Service db8eaa
		ret = ext->callback->read_integer(ext, key, control->value.integer.value);
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_CTL_ELEM_TYPE_INTEGER64:
Packit Service db8eaa
		if (! ext->callback->read_integer64)
Packit Service db8eaa
			goto err;
Packit Service db8eaa
		ret = ext->callback->read_integer64(ext, key,
Packit Service db8eaa
						    (int64_t*)control->value.integer64.value);
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_CTL_ELEM_TYPE_ENUMERATED:
Packit Service db8eaa
		if (! ext->callback->read_enumerated)
Packit Service db8eaa
			goto err;
Packit Service db8eaa
		ret = ext->callback->read_enumerated(ext, key, control->value.enumerated.item);
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_CTL_ELEM_TYPE_BYTES:
Packit Service db8eaa
		if (! ext->callback->read_bytes)
Packit Service db8eaa
			goto err;
Packit Service db8eaa
		ret = ext->callback->read_bytes(ext, key, control->value.bytes.data,
Packit Service db8eaa
						sizeof(control->value.bytes.data));
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_CTL_ELEM_TYPE_IEC958:
Packit Service db8eaa
		if (! ext->callback->read_iec958)
Packit Service db8eaa
			goto err;
Packit Service db8eaa
		ret = ext->callback->read_iec958(ext, key, (snd_aes_iec958_t *)&control->value.iec958);
Packit Service db8eaa
		break;
Packit Service db8eaa
	default:
Packit Service db8eaa
		break;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
 err:
Packit Service db8eaa
	if (ext->callback->free_key)
Packit Service db8eaa
		ext->callback->free_key(ext, key);
Packit Service db8eaa
Packit Service db8eaa
	return ret;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_elem_write(snd_ctl_t *handle, snd_ctl_elem_value_t *control)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_ctl_ext_t *ext = handle->private_data;
Packit Service db8eaa
	snd_ctl_ext_key_t key;
Packit Service db8eaa
	int type, ret;
Packit Service db8eaa
	unsigned int access, count;
Packit Service db8eaa
Packit Service db8eaa
	key = get_elem(ext, &control->id);
Packit Service db8eaa
	if (key == SND_CTL_EXT_KEY_NOT_FOUND)
Packit Service db8eaa
		return -ENOENT;
Packit Service db8eaa
	ret = ext->callback->get_attribute(ext, key, &type, &access, &count);
Packit Service db8eaa
	if (ret < 0)
Packit Service db8eaa
		goto err;
Packit Service db8eaa
	ret = -EINVAL;
Packit Service db8eaa
	switch (type) {
Packit Service db8eaa
	case SND_CTL_ELEM_TYPE_BOOLEAN:
Packit Service db8eaa
	case SND_CTL_ELEM_TYPE_INTEGER:
Packit Service db8eaa
		if (! ext->callback->write_integer)
Packit Service db8eaa
			goto err;
Packit Service db8eaa
		ret = ext->callback->write_integer(ext, key, control->value.integer.value);
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_CTL_ELEM_TYPE_INTEGER64:
Packit Service db8eaa
		if (! ext->callback->write_integer64)
Packit Service db8eaa
			goto err;
Packit Service db8eaa
		ret = ext->callback->write_integer64(ext, key, (int64_t *)control->value.integer64.value);
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_CTL_ELEM_TYPE_ENUMERATED:
Packit Service db8eaa
		if (! ext->callback->write_enumerated)
Packit Service db8eaa
			goto err;
Packit Service db8eaa
		ret = ext->callback->write_enumerated(ext, key, control->value.enumerated.item);
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_CTL_ELEM_TYPE_BYTES:
Packit Service db8eaa
		if (! ext->callback->write_bytes)
Packit Service db8eaa
			goto err;
Packit Service db8eaa
		ret = ext->callback->write_bytes(ext, key, control->value.bytes.data,
Packit Service db8eaa
						sizeof(control->value.bytes.data));
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_CTL_ELEM_TYPE_IEC958:
Packit Service db8eaa
		if (! ext->callback->write_iec958)
Packit Service db8eaa
			goto err;
Packit Service db8eaa
		ret = ext->callback->write_iec958(ext, key, (snd_aes_iec958_t *)&control->value.iec958);
Packit Service db8eaa
		break;
Packit Service db8eaa
	default:
Packit Service db8eaa
		break;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
 err:
Packit Service db8eaa
	if (ext->callback->free_key)
Packit Service db8eaa
		ext->callback->free_key(ext, key);
Packit Service db8eaa
Packit Service db8eaa
	return ret;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_elem_lock(snd_ctl_t *handle ATTRIBUTE_UNUSED,
Packit Service db8eaa
				 snd_ctl_elem_id_t *id ATTRIBUTE_UNUSED)
Packit Service db8eaa
{
Packit Service db8eaa
	return -ENXIO;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_elem_unlock(snd_ctl_t *handle ATTRIBUTE_UNUSED,
Packit Service db8eaa
				   snd_ctl_elem_id_t *id ATTRIBUTE_UNUSED)
Packit Service db8eaa
{
Packit Service db8eaa
	return -ENXIO;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_elem_tlv(snd_ctl_t *handle, int op_flag,
Packit Service db8eaa
				unsigned int numid,
Packit Service db8eaa
				unsigned int *tlv, unsigned int tlv_size)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_ctl_ext_t *ext = handle->private_data;
Packit Service db8eaa
	snd_ctl_ext_key_t key;
Packit Service db8eaa
	int type, ret;
Packit Service db8eaa
	unsigned int access, count, len;
Packit Service db8eaa
	snd_ctl_elem_id_t id;
Packit Service db8eaa
Packit Service db8eaa
	/* we don't support TLV on protocol ver 1.0.0 or earlier */
Packit Service db8eaa
	if (ext->version <= SNDRV_PROTOCOL_VERSION(1, 0, 0))
Packit Service db8eaa
		return -ENXIO;
Packit Service db8eaa
Packit Service db8eaa
	snd_ctl_elem_id_clear(&id;;
Packit Service db8eaa
	if (numid > 0) {
Packit Service db8eaa
		ext->callback->elem_list(ext, numid - 1, &id;;
Packit Service db8eaa
		id.numid = numid;
Packit Service db8eaa
	} else
Packit Service db8eaa
		id.numid = 0;
Packit Service db8eaa
	key = ext->callback->find_elem(ext, &id;;
Packit Service db8eaa
Packit Service db8eaa
	if (key == SND_CTL_EXT_KEY_NOT_FOUND)
Packit Service db8eaa
		return -ENOENT;
Packit Service db8eaa
	ret = ext->callback->get_attribute(ext, key, &type, &access, &count);
Packit Service db8eaa
	if (ret < 0)
Packit Service db8eaa
		return ret;
Packit Service db8eaa
Packit Service db8eaa
	if ((op_flag == 0 && (access & SND_CTL_EXT_ACCESS_TLV_READ) == 0) ||
Packit Service db8eaa
	    (op_flag > 0 && (access & SND_CTL_EXT_ACCESS_TLV_WRITE) == 0) ||
Packit Service db8eaa
	    (op_flag < 0 && (access & SND_CTL_EXT_ACCESS_TLV_COMMAND) == 0))
Packit Service db8eaa
		return -ENXIO;
Packit Service db8eaa
	if (access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
Packit Service db8eaa
		return ext->tlv.c(ext, key, op_flag, numid, tlv, tlv_size);
Packit Service db8eaa
	} else {
Packit Service db8eaa
		if (op_flag)
Packit Service db8eaa
			return -ENXIO;
Packit Service db8eaa
		len = ext->tlv.p[1] + 2 * sizeof(unsigned int);
Packit Service db8eaa
		if (tlv_size < len)
Packit Service db8eaa
			return -ENOMEM;
Packit Service db8eaa
		memcpy(tlv, ext->tlv.p, len);
Packit Service db8eaa
		return 0;
Packit Service db8eaa
	}
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_next_device(snd_ctl_t *handle ATTRIBUTE_UNUSED,
Packit Service db8eaa
				   int *device ATTRIBUTE_UNUSED)
Packit Service db8eaa
{
Packit Service db8eaa
	return -ENXIO;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_prefer_subdevice(snd_ctl_t *handle ATTRIBUTE_UNUSED,
Packit Service db8eaa
					int subdev ATTRIBUTE_UNUSED)
Packit Service db8eaa
{
Packit Service db8eaa
	return -ENXIO;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_hwdep_info(snd_ctl_t *handle ATTRIBUTE_UNUSED,
Packit Service db8eaa
				  snd_hwdep_info_t *info ATTRIBUTE_UNUSED)
Packit Service db8eaa
{
Packit Service db8eaa
	return -ENXIO;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_pcm_info(snd_ctl_t *handle ATTRIBUTE_UNUSED,
Packit Service db8eaa
				snd_pcm_info_t *info ATTRIBUTE_UNUSED)
Packit Service db8eaa
{
Packit Service db8eaa
	return -ENXIO;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_rawmidi_info(snd_ctl_t *handle ATTRIBUTE_UNUSED,
Packit Service db8eaa
				    snd_rawmidi_info_t *info ATTRIBUTE_UNUSED)
Packit Service db8eaa
{
Packit Service db8eaa
	return -ENXIO;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_set_power_state(snd_ctl_t *handle ATTRIBUTE_UNUSED,
Packit Service db8eaa
				       unsigned int state ATTRIBUTE_UNUSED)
Packit Service db8eaa
{
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_get_power_state(snd_ctl_t *handle ATTRIBUTE_UNUSED,
Packit Service db8eaa
				       unsigned int *state ATTRIBUTE_UNUSED)
Packit Service db8eaa
{
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_read(snd_ctl_t *handle, snd_ctl_event_t *event)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_ctl_ext_t *ext = handle->private_data;
Packit Service db8eaa
Packit Service db8eaa
	if (ext->callback->read_event) {
Packit Service db8eaa
		memset(event, 0, sizeof(*event));
Packit Service db8eaa
		return ext->callback->read_event(ext, &event->data.elem.id, &event->data.elem.mask);
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	return -EINVAL;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_poll_descriptors_count(snd_ctl_t *handle)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_ctl_ext_t *ext = handle->private_data;
Packit Service db8eaa
Packit Service db8eaa
	if (ext->callback->poll_descriptors_count)
Packit Service db8eaa
		return ext->callback->poll_descriptors_count(ext);
Packit Service db8eaa
	if (ext->poll_fd >= 0)
Packit Service db8eaa
		return 1;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_poll_descriptors(snd_ctl_t *handle, struct pollfd *pfds, unsigned int space)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_ctl_ext_t *ext = handle->private_data;
Packit Service db8eaa
Packit Service db8eaa
	if (ext->callback->poll_descriptors)
Packit Service db8eaa
		return ext->callback->poll_descriptors(ext, pfds, space);
Packit Service db8eaa
	if (ext->poll_fd < 0)
Packit Service db8eaa
		return 0;
Packit Service db8eaa
	if (space > 0) {
Packit Service db8eaa
		pfds->fd = ext->poll_fd;
Packit Service db8eaa
		pfds->events = POLLIN|POLLERR|POLLNVAL;
Packit Service db8eaa
		return 1;
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_ctl_ext_poll_revents(snd_ctl_t *handle, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_ctl_ext_t *ext = handle->private_data;
Packit Service db8eaa
Packit Service db8eaa
	if (ext->callback->poll_revents)
Packit Service db8eaa
		return ext->callback->poll_revents(ext, pfds, nfds, revents);
Packit Service db8eaa
	if (nfds == 1) {
Packit Service db8eaa
		*revents = pfds->revents;
Packit Service db8eaa
                return 0;
Packit Service db8eaa
	}
Packit Service db8eaa
	return -EINVAL;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static const snd_ctl_ops_t snd_ctl_ext_ops = {
Packit Service db8eaa
	.close = snd_ctl_ext_close,
Packit Service db8eaa
	.nonblock = snd_ctl_ext_nonblock,
Packit Service db8eaa
	.async = snd_ctl_ext_async,
Packit Service db8eaa
	.subscribe_events = snd_ctl_ext_subscribe_events,
Packit Service db8eaa
	.card_info = snd_ctl_ext_card_info,
Packit Service db8eaa
	.element_list = snd_ctl_ext_elem_list,
Packit Service db8eaa
	.element_info = snd_ctl_ext_elem_info,
Packit Service db8eaa
	.element_add = snd_ctl_ext_elem_add,
Packit Service db8eaa
	.element_replace = snd_ctl_ext_elem_replace,
Packit Service db8eaa
	.element_remove = snd_ctl_ext_elem_remove,
Packit Service db8eaa
	.element_read = snd_ctl_ext_elem_read,
Packit Service db8eaa
	.element_write = snd_ctl_ext_elem_write,
Packit Service db8eaa
	.element_lock = snd_ctl_ext_elem_lock,
Packit Service db8eaa
	.element_unlock = snd_ctl_ext_elem_unlock,
Packit Service db8eaa
	.element_tlv = snd_ctl_ext_elem_tlv,
Packit Service db8eaa
	.hwdep_next_device = snd_ctl_ext_next_device,
Packit Service db8eaa
	.hwdep_info = snd_ctl_ext_hwdep_info,
Packit Service db8eaa
	.pcm_next_device = snd_ctl_ext_next_device,
Packit Service db8eaa
	.pcm_info = snd_ctl_ext_pcm_info,
Packit Service db8eaa
	.pcm_prefer_subdevice = snd_ctl_ext_prefer_subdevice,
Packit Service db8eaa
	.rawmidi_next_device = snd_ctl_ext_next_device,
Packit Service db8eaa
	.rawmidi_info = snd_ctl_ext_rawmidi_info,
Packit Service db8eaa
	.rawmidi_prefer_subdevice = snd_ctl_ext_prefer_subdevice,
Packit Service db8eaa
	.set_power_state = snd_ctl_ext_set_power_state,
Packit Service db8eaa
	.get_power_state = snd_ctl_ext_get_power_state,
Packit Service db8eaa
	.read = snd_ctl_ext_read,
Packit Service db8eaa
	.poll_descriptors_count = snd_ctl_ext_poll_descriptors_count,
Packit Service db8eaa
	.poll_descriptors = snd_ctl_ext_poll_descriptors,
Packit Service db8eaa
	.poll_revents = snd_ctl_ext_poll_revents,
Packit Service db8eaa
};
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Exported functions
Packit Service db8eaa
 */
Packit Service db8eaa
Packit Service db8eaa
/*! \page ctl_external_plugins External Control Plugin SDK
Packit Service db8eaa
Packit Service db8eaa
\section ctl_externals External Control Plugins
Packit Service db8eaa
Packit Service db8eaa
The external plugins are implemented in a shared object file located
Packit Service db8eaa
at /usr/lib/alsa-lib (the exact location depends on the build option
Packit Service db8eaa
and asoundrc configuration).  It has to be the file like
Packit Service db8eaa
libasound_module_ctl_MYPLUGIN.so, where MYPLUGIN corresponds to your
Packit Service db8eaa
own plugin name.
Packit Service db8eaa
Packit Service db8eaa
The entry point of the plugin is defined via
Packit Service db8eaa
#SND_CTL_PLUGIN_DEFINE_FUNC() macro.  This macro defines the function
Packit Service db8eaa
with a proper name to be referred from alsa-lib.  The function takes
Packit Service db8eaa
the following 5 arguments:
Packit Service db8eaa
\code
Packit Service db8eaa
int (snd_ctl_t **phandle, const char *name, snd_config_t *root,
Packit Service db8eaa
	snd_config_t *conf, int mode)
Packit Service db8eaa
\endcode
Packit Service db8eaa
The first argument, phandle, is the pointer to store the resultant control
Packit Service db8eaa
handle.  The arguments name, root and mode are the parameters
Packit Service db8eaa
to be passed to the plugin constructor.  The conf is the configuration
Packit Service db8eaa
tree for the plugin.  The arguments above are defined in the macro
Packit Service db8eaa
itself, so don't use variables with the same names to shadow
Packit Service db8eaa
parameters.
Packit Service db8eaa
Packit Service db8eaa
After parsing the configuration parameters in the given conf tree,
Packit Service db8eaa
usually you will call the external plugin API function
Packit Service db8eaa
#snd_ctl_ext_create().
Packit Service db8eaa
The control handle must be filled *phandle in return.
Packit Service db8eaa
Then this function must return either a value 0 when succeeded, or a
Packit Service db8eaa
negative value as the error code. 
Packit Service db8eaa
Packit Service db8eaa
Finally, add #SND_CTL_PLUGIN_SYMBOL() with the name of your
Packit Service db8eaa
plugin as the argument at the end.  This defines the proper versioned
Packit Service db8eaa
symbol as the reference.
Packit Service db8eaa
Packit Service db8eaa
The typical code would look like below:
Packit Service db8eaa
\code
Packit Service db8eaa
struct myctl_info {
Packit Service db8eaa
	snd_ctl_ext_t ext;
Packit Service db8eaa
	int my_own_data;
Packit Service db8eaa
	...
Packit Service db8eaa
};
Packit Service db8eaa
Packit Service db8eaa
SND_CTL_PLUGIN_DEFINE_FUNC(myctl)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_config_iterator_t i, next;
Packit Service db8eaa
	struct myctl_info *myctl;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	snd_config_for_each(i, next, conf) {
Packit Service db8eaa
		snd_config_t *n = snd_config_iterator_entry(i);
Packit Service db8eaa
		const char *id;
Packit Service db8eaa
		if (snd_config_get_id(n, &id) < 0)
Packit Service db8eaa
			continue;
Packit Service db8eaa
		if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0)
Packit Service db8eaa
			continue;
Packit Service db8eaa
		if (strcmp(id, "my_own_parameter") == 0) {
Packit Service db8eaa
			....
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
		SNDERR("Unknown field %s", id);
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	myctl = calloc(1, sizeof(*myctl));
Packit Service db8eaa
	if (myctl == NULL)
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
Packit Service db8eaa
	myctl->ext.version = SND_CTL_EXT_VERSION;
Packit Service db8eaa
	myctl->ext.card_idx = 0;
Packit Service db8eaa
	strcpy(myctl->ext.id, "Myctl");
Packit Service db8eaa
	strcpy(myctl->ext.name, "My Control");
Packit Service db8eaa
	strcpy(myctl->ext.longname, "My External Control for Foobar");
Packit Service db8eaa
	strcpy(myctl->ext.mixername, "My Control");
Packit Service db8eaa
	myctl->ext.callback = &my_own_callback;
Packit Service db8eaa
	myctl->ext.private_data = myctl;
Packit Service db8eaa
	....
Packit Service db8eaa
Packit Service db8eaa
	err = snd_pcm_extplug_create(&myctl->ext, name, mode);
Packit Service db8eaa
	if (err < 0) {
Packit Service db8eaa
		myctl_free(myctl);
Packit Service db8eaa
		return err;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	*phandle = myctl->ext.handle;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
SND_CTL_PLUGIN_SYMBOL(myctl);
Packit Service db8eaa
\endcode
Packit Service db8eaa
Packit Service db8eaa
Read the codes in alsa-plugins package for the real examples.
Packit Service db8eaa
Packit Service db8eaa
Packit Service db8eaa
\section ctl_ext_impl Implementation of External Control Plugins
Packit Service db8eaa
Packit Service db8eaa
The following fields have to be filled in external control record before calling
Packit Service db8eaa
#snd_ctl_ext_create() : version, card_idx, id, name, longname, mixername, poll_fd and callback.
Packit Service db8eaa
Otherfields are optional and should be initialized with zero.
Packit Service db8eaa
Packit Service db8eaa
The constant #SND_CTL_EXT_VERSION must be passed to the version
Packit Service db8eaa
field for the version check in alsa-lib.  The card_idx field specifies the card
Packit Service db8eaa
index of this control.  [FIXME: solve confliction of card index in alsa-lib?]
Packit Service db8eaa
Packit Service db8eaa
The id, name, longname and mixername fields are the strings shown in the card_info
Packit Service db8eaa
inqurirys.  They are the char arrays, so you have to copy strings to these
Packit Service db8eaa
fields.
Packit Service db8eaa
Packit Service db8eaa
The callback field contains the  table of callback functions for this plugin (defined as
Packit Service db8eaa
#snd_ctl_ext_callback_t).
Packit Service db8eaa
The poll_fd can be used to specify the poll file descriptor for this control.
Packit Service db8eaa
Set -1 if not available.  Alternatively, you can define poll_descriptors_count and
Packit Service db8eaa
poll_descriptors callbacks in the callback table for handling the poll descriptor(s)
Packit Service db8eaa
dynamically after the creation of plugin instance.
Packit Service db8eaa
Packit Service db8eaa
The driver can set an arbitrary value (pointer) to private_data
Packit Service db8eaa
field to refer its own data in the callbacks.
Packit Service db8eaa
Packit Service db8eaa
The rest fields are filled by #snd_ctl_ext_create().  The handle field
Packit Service db8eaa
is the resultant PCM handle.  The others are the current status of the
Packit Service db8eaa
PCM.
Packit Service db8eaa
Packit Service db8eaa
\section ctl_ext_impl Callback Functions of External Control Plugins
Packit Service db8eaa
Packit Service db8eaa
The callback functions in #snd_ctl_ext_callback_t define the real
Packit Service db8eaa
behavior of the driver.  There are many callbacks but many of them are optional. 
Packit Service db8eaa
Packit Service db8eaa
The close callback is called when the PCM is closed.  If the plugin
Packit Service db8eaa
allocates private resources, this is the place to release them
Packit Service db8eaa
again.  This callback is optional.
Packit Service db8eaa
Packit Service db8eaa
The elem_count and elem_list callbacks are mandatory.  The elem_count returns the
Packit Service db8eaa
total number of control elements.  The elem_list returns the control element ID
Packit Service db8eaa
of the corresponding element offset (the offset is from 0 to elem_count - 1).
Packit Service db8eaa
The id field is initialized to all zero in prior to elem_list callback.  The callback
Packit Service db8eaa
has to fill the necessary field (typically iface, name and index) in return via the
Packit Service db8eaa
standard control API functions like #snd_ctl_elem_id_set_interface,
Packit Service db8eaa
#snd_ctl_elem_id_set_name and #snd_ctl_elem_id_set_index, etc.  The callbacks should
Packit Service db8eaa
return 0 if successful, or a negative error code.
Packit Service db8eaa
Packit Service db8eaa
The find_elem callback is used to convert the given control element ID to the
Packit Service db8eaa
certain key value for the faster access to get, read and write callbacks.
Packit Service db8eaa
The key type is alias of unsigned long, so you can assign some static number
Packit Service db8eaa
(e.g. index of the array) to this value of the corresponding element, or
Packit Service db8eaa
assign the pointer (cast to #snd_ctl_ext_key_t).  When no key is defined or found,
Packit Service db8eaa
return #SND_CTL_EXT_KEY_NOT_FOUND.  This callback is (very likely) required
Packit Service db8eaa
if you use get, read and write callbacks as follows.
Packit Service db8eaa
If you need to create a record dynamically (e.g. via malloc) at each find_elem call,
Packit Service db8eaa
the allocated record can be released with the optional free_key callback.
Packit Service db8eaa
Packit Service db8eaa
The get_attribute is a mandatory callback, which returns the attribute of the 
Packit Service db8eaa
control element given via a key value (converted with find_elem callback).
Packit Service db8eaa
It must fill the control element type (#snd_ctl_elem_type_t), the access type
Packit Service db8eaa
(#snd_ctl_ext_access_t), and the count (element array size).  The callback returns
Packit Service db8eaa
0 if successful, or a negative error code, as usual.
Packit Service db8eaa
Packit Service db8eaa
The get_integer_info, get_integetr64_info and get_enumerated_info callbacks are called
Packit Service db8eaa
to return the information of the given control element for each element type.
Packit Service db8eaa
For integer and integer64 types, the callbacks need to fill the minimal (imin),
Packit Service db8eaa
maximal (imax) and the step (istep) values of the control.  For the enumerated type,
Packit Service db8eaa
the number of enum items must be filled.  Additionally, the enum control has to define
Packit Service db8eaa
get_enumerated_name callback to store the name of the enumerated item of the given control
Packit Service db8eaa
element.  All functions return 0 if successful, or a negative error code.
Packit Service db8eaa
Packit Service db8eaa
For reading the current values of a control element, read_integer, read_integer64,
Packit Service db8eaa
read_enumerated, read_bytes and read_iec958 callbacks are called depending on the
Packit Service db8eaa
element type.  These callbacks have to fill the current values of the element in return.
Packit Service db8eaa
Note that a control element can be an array.  If it contains more than one values
Packit Service db8eaa
(i.e. the count value in get_attribute callback is more than 1), all values
Packit Service db8eaa
must be filled on the given value pointer as an array.  Also, note that the boolean type
Packit Service db8eaa
is handled as integer here (although boolean type doesn't need to define the corresponding
Packit Service db8eaa
info callback since it's obvious).  These callbacks return 0 if successful, or
Packit Service db8eaa
a negative error code.
Packit Service db8eaa
Packit Service db8eaa
For writing the current values, write_integer, write_integer64, write_bytes, and
Packit Service db8eaa
write_iec958 callbacks are called as well as for read.  The callbacks should check the
Packit Service db8eaa
current values and compare with the given values.  If they are identical, the callbacks
Packit Service db8eaa
should do nothing and return 0.  If they differ, update the current values and return 1,
Packit Service db8eaa
instead.  For any errors, return a negative error code.
Packit Service db8eaa
Packit Service db8eaa
The subscribe_events callback is called when the application subscribes or cancels
Packit Service db8eaa
the event notifications (e.g. through mixer API).  The current value of event
Packit Service db8eaa
subscription is kept in the subscribed field.
Packit Service db8eaa
The read_event callback is called for reading a pending notification event.
Packit Service db8eaa
The callback needs to fill the event_mask value, a bit-field defined as SND_CTL_EVENT_MASK_XXX.
Packit Service db8eaa
If no event is pending, return -EAGAIN.  These two callbacks are optional.
Packit Service db8eaa
Packit Service db8eaa
The poll_descriptors_count and poll_descriptors callbacks are used to return
Packit Service db8eaa
the poll descriptor(s) via callbacks.  As already mentioned, if the callback cannot
Packit Service db8eaa
set the static poll_fd, you can define these callbacks to return dynamically.
Packit Service db8eaa
Also, when multiple poll descriptors are required, use these callbacks.
Packit Service db8eaa
The poll_revents callback is used for handle poll revents.
Packit Service db8eaa
Packit Service db8eaa
*/
Packit Service db8eaa
Packit Service db8eaa
/**
Packit Service db8eaa
 * \brief Create an external control plugin instance
Packit Service db8eaa
 * \param ext the plugin handle
Packit Service db8eaa
 * \param name name of control
Packit Service db8eaa
 * \param mode control open mode
Packit Service db8eaa
 * \return 0 if successful, or a negative error code
Packit Service db8eaa
 *
Packit Service db8eaa
 * Creates the external control instance.
Packit Service db8eaa
 *
Packit Service db8eaa
 */
Packit Service db8eaa
int snd_ctl_ext_create(snd_ctl_ext_t *ext, const char *name, int mode)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_ctl_t *ctl;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	if (ext->version < SNDRV_PROTOCOL_VERSION(1, 0, 0) ||
Packit Service db8eaa
	    ext->version > SND_CTL_EXT_VERSION) {
Packit Service db8eaa
		SNDERR("ctl_ext: Plugin version mismatch\n");
Packit Service db8eaa
		return -ENXIO;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	err = snd_ctl_new(&ctl, SND_CTL_TYPE_EXT, name);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
Packit Service db8eaa
	ext->handle = ctl;
Packit Service db8eaa
Packit Service db8eaa
	ctl->ops = &snd_ctl_ext_ops;
Packit Service db8eaa
	ctl->private_data = ext;
Packit Service db8eaa
	ctl->poll_fd = ext->poll_fd;
Packit Service db8eaa
	if (mode & SND_CTL_NONBLOCK)
Packit Service db8eaa
		ext->nonblock = 1;
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/**
Packit Service db8eaa
 * \brief Delete the external control plugin
Packit Service db8eaa
 * \param ext the plugin handle
Packit Service db8eaa
 * \return 0 if successful, or a negative error code
Packit Service db8eaa
 */
Packit Service db8eaa
int snd_ctl_ext_delete(snd_ctl_ext_t *ext)
Packit Service db8eaa
{
Packit Service db8eaa
	return snd_ctl_close(ext->handle);
Packit Service db8eaa
}