/**
 * \file control/control.c
 * \brief CTL interface - primitive controls
 * \author Abramo Bagnara <abramo@alsa-project.org>
 * \date 2000
 *
 * CTL interface is designed to access primitive controls.
 * See \ref control page for more details.
 */
/*
 *  Control Interface - main file
 *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
 *
 *
 *   This library is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as
 *   published by the Free Software Foundation; either version 2.1 of
 *   the License, or (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public
 *   License along with this library; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

/*! \page control Control interface

<P>Control interface is designed to access primitive controls. There is
also an interface for notifying about control and structure changes.


\section control_general_overview General overview

In Alsa, there are physical sound cards, such as USB headsets, and
virtual sound cards, such as "pulse", which represents the PulseAudio
Sound system. Each sound card offers a control interface, making its
settings (e.g. volume knobs) available. The complete list of available
control interfaces can be obtained using snd_device_name_hint(),
giving -1 as card index and "ctl" as interface type. Each returned
NAME hint identifies a control interface.

Sound cards have an ID (a string), an index (an int, sometimes called
the "card number"), a name, a longname, a mixername and a "components"
property. The file /proc/asound/cards lists most of these properties
for physical sound cards. Virtual sound cards are not listed in that
file. The format is:

\verbatim
index [ID     ] Driver - name
                longname
\endverbatim

Note that the mixername and components are not listed.


\subsection control_cards_id Identifying and Opening Control Interfaces

To work with a control interface, is must be opened first, using
snd_ctl_open(). This function takes the interface name.

For physical sound cards, the control interface can be identified
using the string "hw:<index>" (e.g. `hw:2`). The NAME hint - which is
"hw:CARD=<ID>" - can also be used. Further, its device file (something
like `/dev/snd/controlC0`) is also acceptable. Either of them can be
given to snd_ctl_open().

For virtual sound cards, the NAME hint is given to snd_ctl_open().

The functions snd_card_get_index(), snd_card_get_name() and
snd_card_get_longname() can be used to find an identifying property
when another one is already known.

\section control_elements Elements

In ALSA control feature, each sound card can have control elements. The elements
are managed according to below model.

 - Element set

   - A set of elements with the same attribute (i.e. name, get/put operations).
     Some element sets can be added to a sound card by drivers in kernel and
     userspace applications.

 - Element

   - A control element might be a master volume control, for example, or a
     read-only indicator, such as a sync status. An element has a type (e.g.
     SNDRV_CTL_ELEM_TYPE_INTEGER or SNDRV_CTL_ELEM_TYPE_BOOLEAN) and - depending
     on the type - min/max values, a step size, a set of possible values (for
     enums), etc.

 - Member

   - An element usually includes one or more member(s) to have a value. For
     example, a stereo volume control element has two members (for left/right),
     while a mono volume has only one member. The member count can be obtained
     using snd_ctl_elem_info_get_count(). Elements of type
     "SNDRV_CTL_ELEM_TYPE_BYTES" or "SNDRV_CTL_ELEM_TYPE_IEC958" have no members
     at all (and thus no member count), they have just a single value. The
     members share the same properties (e.g. both volume control members have
     the same min/max values). The value of each member can be changed by both
     of userspace applications and drivers in kernel.


\subsection identifying_elements Identifying Elements

Each element has the following identifying properties:

  - The numid (a numeric identifier, assigned when the sound card is
    detected, constant while the sound card is kept connected)
  - The interface type (e.g. MIXER, CARD or PCM)
  - The device
  - The subdevice
  - Its name
  - Its index

An element can be identified either by its short numid or by the full
set of fields (interface type, device, subdevice, name, index).
This set of fields is always the same (driver updates can change it,
but in practice this is rare). The numid can change on each boot.
In case of an USB sound card, the numid can also change when it
is reconnected. The short numid is used to reduce the lookup time.

\subsection element_lists Element Lists

An element list can be used to obtain a list of all elements of the
sound card. The list contains generic information (e.g. how many
elements the card has), and the identifying properties of the elements
(numid, card, name, ...). See #snd_ctl_elem_list_t to learn more about
element lists.


\subsection working_with_elements Working with Elements

It is possible to obtain information about an element using the
snd_ctl_elem_info_*() functions. For enums, the allowed values can be
obtained, for integers, the min/max values can be obtained, and so
on. In addition, these functions can report the identifying
properties. E.g. when the element is addressed using its numid, the
functions complements the name, index, etc.

To access the members (i.e. values) of a control, use the
snd_ctl_elem_value*() functions.  These allow to get and set the
actual values or settings. It is also possible to get and set the ID
values (such as the numid or the name).


\subsection element_sets Element Sets

The type of element set is one of integer, integer64, boolean, enumerators,
bytes and IEC958 structure. This indicates the type of value for each member in
elements included in the element set.


\section events Events

When the value of a member is changed, corresponding events are transferred to
userspace applications. The applications should subscribe any events in advance.

\section tlv_blob Supplemental data for elements in an element set

TLV feature is designed to transfer data in a shape of Type/Length/Value,
between a driver and any userspace applications. The main purpose is to attach
supplement information for elements to an element set; e.g. dB range.

At first, this feature was implemented to add pre-defined data readable to
userspace applications. Soon, it was extended to handle several operations;
read, write and command. The original implementation remains as the read
operation. The command operation allows drivers to have own implementations
against requests from userspace applications.

This feature was introduced to ALSA control feature in 2006, at commit
c7a0708a2362, corresponding to a series of work for Linux kernel (42750b04c5ba
and 8aa9b586e420).

There's no limitation about maximum size of the data, therefore it can be used
to deliver quite large arbitrary data from userspace to in-kernel drivers via
ALSA control character device. Focusing on this nature, as of 2016, some
in-kernel implementations utilize this feature for I/O operations. This is
against the original design.
*/

#include "control_local.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <poll.h>
#include <stdbool.h>
#include <limits.h>

/**
 * \brief get identifier of CTL handle
 * \param ctl CTL handle
 * \return ascii identifier of CTL handle
 *
 * Returns the ASCII identifier of given CTL handle. It's the same
 * identifier specified in snd_ctl_open().
 */
const char *snd_ctl_name(snd_ctl_t *ctl)
{
	assert(ctl);
	return ctl->name;
}

/**
 * \brief get type of CTL handle
 * \param ctl CTL handle
 * \return type of CTL handle
 *
 * Returns the type #snd_ctl_type_t of given CTL handle.
 */
snd_ctl_type_t snd_ctl_type(snd_ctl_t *ctl)
{
	assert(ctl);
	return ctl->type;
}

/**
 * \brief close CTL handle
 * \param ctl CTL handle
 * \return 0 on success otherwise a negative error code
 *
 * Closes the specified CTL handle and frees all associated
 * resources.
 */
int snd_ctl_close(snd_ctl_t *ctl)
{
	int err;
	while (!list_empty(&ctl->async_handlers)) {
		snd_async_handler_t *h = list_entry(&ctl->async_handlers.next, snd_async_handler_t, hlist);
		snd_async_del_handler(h);
	}
	err = ctl->ops->close(ctl);
	free(ctl->name);
	snd_dlobj_cache_put(ctl->open_func);
	free(ctl);
	return err;
}

/**
 * \brief set nonblock mode
 * \param ctl CTL handle
 * \param nonblock 0 = block, 1 = nonblock mode, 2 = abort
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_nonblock(snd_ctl_t *ctl, int nonblock)
{
	int err;
	assert(ctl);
	err = ctl->ops->nonblock(ctl, nonblock);
	if (err < 0)
		return err;
	ctl->nonblock = nonblock;
	return 0;
}

#ifndef DOC_HIDDEN
int snd_ctl_new(snd_ctl_t **ctlp, snd_ctl_type_t type, const char *name, int mode)
{
	snd_ctl_t *ctl;
	ctl = calloc(1, sizeof(*ctl));
	if (!ctl)
		return -ENOMEM;
	ctl->type = type;
	ctl->mode = mode;
	if (name)
		ctl->name = strdup(name);
	INIT_LIST_HEAD(&ctl->async_handlers);
	*ctlp = ctl;
	return 0;
}
	

/**
 * \brief set async mode
 * \param ctl CTL handle
 * \param sig Signal to raise: < 0 disable, 0 default (SIGIO)
 * \param pid Process ID to signal: 0 current
 * \return 0 on success otherwise a negative error code
 *
 * A signal is raised when a change happens.
 */
int snd_ctl_async(snd_ctl_t *ctl, int sig, pid_t pid)
{
	assert(ctl);
	if (sig == 0)
		sig = SIGIO;
	if (pid == 0)
		pid = getpid();
	return ctl->ops->async(ctl, sig, pid);
}
#endif

/**
 * \brief get count of poll descriptors for CTL handle
 * \param ctl CTL handle
 * \return count of poll descriptors
 */
int snd_ctl_poll_descriptors_count(snd_ctl_t *ctl)
{
	assert(ctl);
	if (ctl->ops->poll_descriptors_count)
		return ctl->ops->poll_descriptors_count(ctl);
	if (ctl->poll_fd < 0)
		return 0;
	return 1;
}

/**
 * \brief get poll descriptors
 * \param ctl CTL handle
 * \param pfds array of poll descriptors
 * \param space space in the poll descriptor array
 * \return count of filled descriptors
 */
int snd_ctl_poll_descriptors(snd_ctl_t *ctl, struct pollfd *pfds, unsigned int space)
{
	assert(ctl && pfds);
	if (ctl->ops->poll_descriptors)
		return ctl->ops->poll_descriptors(ctl, pfds, space);
	if (ctl->poll_fd < 0)
		return 0;
	if (space > 0) {
		pfds->fd = ctl->poll_fd;
		pfds->events = POLLIN|POLLERR|POLLNVAL;
		return 1;
	}
	return 0;
}

/**
 * \brief get returned events from poll descriptors
 * \param ctl CTL handle
 * \param pfds array of poll descriptors
 * \param nfds count of poll descriptors
 * \param revents returned events
 * \return zero if success, otherwise a negative error code
 */
int snd_ctl_poll_descriptors_revents(snd_ctl_t *ctl, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
{
	assert(ctl && pfds && revents);
	if (ctl->ops->poll_revents)
		return ctl->ops->poll_revents(ctl, pfds, nfds, revents);
	if (nfds == 1) {
		*revents = pfds->revents;
                return 0;
	}
	return -EINVAL;
}

/**
 * \brief Ask to be informed about events (poll, #snd_async_add_ctl_handler, #snd_ctl_read)
 * \param ctl CTL handle
 * \param subscribe 0 = unsubscribe, 1 = subscribe, -1 = check subscribe or not
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_subscribe_events(snd_ctl_t *ctl, int subscribe)
{
	assert(ctl);
	return ctl->ops->subscribe_events(ctl, subscribe);
}


/**
 * \brief Get information about the sound card.
 *
 * Obtain information about the sound card previously opened using
 * snd_ctl_open(). The object "info" must be allocated prior to calling this
 * function. See snd_ctl_card_info_t for details.
 *
 * \param ctl The CTL handle.
 * \param info The card information is stored here.
 * \return 0 on success, otherwise a negative error code.
 */
int snd_ctl_card_info(snd_ctl_t *ctl, snd_ctl_card_info_t *info)
{
	assert(ctl && info);
	return ctl->ops->card_info(ctl, info);
}

/**
 * \brief Get a list of element identifiers
 *
 * Before calling this function, memoru must be allocated using
 * snd_ctl_elem_list_malloc().
 *
 * This function obtains data from the sound card driver and puts it
 * into the list.
 *
 * If there was space allocated for the element identifiers (using
 * snd_ctl_elem_list_alloc_space()), information will be filled in. If
 * too little space was allocated, only a part of the elements will be
 * queried. If there was too much space allocated, some of it remains
 * unused. Use snd_ctl_elem_list_get_count() and
 * snd_ctl_elem_list_get_used() to obtain information about space
 * usage. See #snd_ctl_elem_list_t to learn more.
 *
 * \param ctl CTL handle
 * \param list CTL element identifiers list pointer
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_elem_list(snd_ctl_t *ctl, snd_ctl_elem_list_t *list)
{
	assert(ctl && list);
	assert(list->space == 0 || list->pids);
	return ctl->ops->element_list(ctl, list);
}

/**
 * \brief Get CTL element information
 * \param ctl CTL handle
 * \param info CTL element id/information pointer
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_elem_info(snd_ctl_t *ctl, snd_ctl_elem_info_t *info)
{
	assert(ctl && info && (info->id.name[0] || info->id.numid));
	return ctl->ops->element_info(ctl, info);
}

#ifndef DOC_HIDDEN
#if 0 /* deprecated */
static bool validate_element_member_dimension(snd_ctl_elem_info_t *info)
{
	unsigned int members;
	unsigned int i;

	if (info->dimen.d[0] == 0)
		return true;

	members = 1;
	for (i = 0; i < ARRAY_SIZE(info->dimen.d); ++i) {
		if (info->dimen.d[i] == 0)
			break;
		members *= info->dimen.d[i];

		if (members > info->count)
			return false;
	}

	for (++i; i < ARRAY_SIZE(info->dimen.d); ++i) {
		if (info->dimen.d[i] > 0)
			return false;
	}

	return members == info->count;
}
#else /* deprecated */
#define validate_element_member_dimension(info)		true
#endif /* deprecated */

#define USER_ACCESS_DEFAULT (\
	SNDRV_CTL_ELEM_ACCESS_READWRITE |\
	SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE |\
	SNDRV_CTL_ELEM_ACCESS_USER)

#define USER_ACCESS_SETTABLE (\
	SNDRV_CTL_ELEM_ACCESS_READWRITE |\
	SNDRV_CTL_ELEM_ACCESS_VOLATILE |\
	SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE |\
	SNDRV_CTL_ELEM_ACCESS_INACTIVE |\
	SNDRV_CTL_ELEM_ACCESS_USER)

static inline int set_user_access(snd_ctl_elem_info_t *info)
{
	if (info->access == 0) {
		info->access = USER_ACCESS_DEFAULT;
	} else {
		if ((info->access & SNDRV_CTL_ELEM_ACCESS_READWRITE) == 0)
			return -1;
		if (info->access & ~USER_ACCESS_SETTABLE)
			return -1;
		info->access |= SNDRV_CTL_ELEM_ACCESS_USER;
	}
	return 0;
}

int __snd_ctl_add_elem_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info,
			   unsigned int element_count,
			   unsigned int member_count)
{
	if (ctl == NULL || info->id.name[0] == '\0')
		return -EINVAL;

	if (set_user_access(info))
		return -EINVAL;

	info->owner = element_count;
	info->count = member_count;

	if (!validate_element_member_dimension(info))
		return -EINVAL;

	return ctl->ops->element_add(ctl, info);
}

#endif /* DOC_HIDDEN */

/**
 * \brief Create and add some user-defined control elements of integer type.
 * \param ctl A handle of backend module for control interface.
 * \param info Common information for a new element set, with ID of the first new
 *	       element.
 * \param element_count The number of elements added by this operation.
 * \param member_count The number of members which a element has to
 *			   represent its states.
 * \param min Minimum value for each member of the elements.
 * \param max Maximum value for each member of the elements.
 * \param step The step of value for each member in the elements.
 * \return Zero on success, otherwise a negative error code.
 *
 * This function creates some user elements with integer type. These elements
 * are not controlled by device drivers in kernel. They can be operated by the
 * same way as usual elements added by the device drivers.
 *
 * The name field of \a id must be set with unique value to identify new control
 * elements. After returning, all fields of \a id are filled. A element can be
 * identified by the combination of name and index, or by numid.
 *
 * All of members in the new elements are locked. The value of each member is
 * initialized with the minimum value.
 *
 * \par Errors:
 * <dl>
 * <dt>-EBUSY
 * <dd>A element with ID \a id already exists.
 * <dt>-EINVAL
 * <dd>Some arguments include invalid value; i.e. ID field in \a info has no
 *     name, or the number of members is not between 1 to 127.
 * <dt>-ENOMEM
 * <dd>Out of memory, or there are too many user elements.
 * <dt>-ENXIO
 * <dd>This backend module does not support user elements of integer type.
 * <dt>-ENODEV
 * <dd>Device unplugged.
 * </dl>
 *
 * \par Compatibility:
 * This function is added in version 1.1.2.
 */
int snd_ctl_add_integer_elem_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info,
				 unsigned int element_count,
				 unsigned int member_count,
				 long min, long max, long step)
{
	snd_ctl_elem_value_t data = {0};
	unsigned int i;
	unsigned int j;
	unsigned int numid;
	int err;

	if (info == NULL)
		return -EINVAL;

	info->type = SND_CTL_ELEM_TYPE_INTEGER;
	info->value.integer.min = min;
	info->value.integer.max = max;
	info->value.integer.step = step;

	err = __snd_ctl_add_elem_set(ctl, info, element_count, member_count);
	if (err < 0)
		return err;
	numid = snd_ctl_elem_id_get_numid(&info->id);

	/* Set initial value to all of members in all of added elements. */
	data.id = info->id;
	for (i = 0; i < element_count; i++) {
		snd_ctl_elem_id_set_numid(&data.id, numid + i);

		for (j = 0; j < member_count; j++)
			data.value.integer.value[j] = min;

		err = ctl->ops->element_write(ctl, &data);
		if (err < 0)
			return err;
	}

	return 0;
}

/**
 * \brief Create and add some user-defined control elements of integer64 type.
 * \param ctl A handle of backend module for control interface.
 * \param info Common information for a new element set, with ID of the first new
 *	       element.
 * \param element_count The number of elements added by this operation.
 * \param member_count The number of members which a element has to
 *	    	   represent its states.
 * \param min Minimum value for each member of the elements.
 * \param max Maximum value for each member of the elements.
 * \param step The step of value for each member in the elements.
 * \return Zero on success, otherwise a negative error code.
 *
 * This function creates some user elements with integer64 type. These elements
 * are not controlled by device drivers in kernel. They can be operated by the
 * same way as usual elements added by the device drivers.
 *
 * The name field of \a id must be set with unique value to identify new control
 * elements. After returning, all fields of \a id are filled. A element can be
 * identified by the combination of name and index, or by numid.
 *
 * All of members in the new elements are locked. The value of each member is
 * initialized with the minimum value.
 *
 * \par Errors:
 * <dl>
 * <dt>-EBUSY
 * <dd>A element with ID \a id already exists.
 * <dt>-EINVAL
 * <dd>Some arguments include invalid value; i.e. ID has no name, or the number
 *     of members is not between 1 to 127.
 * <dt>-ENOMEM
 * <dd>Out of memory, or there are too many user elements.
 * <dt>-ENXIO
 * <dd>This backend module does not support user elements of integer64 type.
 * <dt>-ENODEV
 * <dd>Device unplugged.
 * </dl>
 *
 * \par Compatibility:
 * This function is added in version 1.1.2.
 */
int snd_ctl_add_integer64_elem_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info,
				   unsigned int element_count,
				   unsigned int member_count,
				   long long min, long long max, long long step)
{
	snd_ctl_elem_value_t data = {0};
	unsigned int i;
	unsigned int j;
	unsigned int numid;
	int err;

	if (info == NULL)
		return -EINVAL;

	info->type = SND_CTL_ELEM_TYPE_INTEGER64;
	info->value.integer64.min = min;
	info->value.integer64.max = max;
	info->value.integer64.step = step;

	err = __snd_ctl_add_elem_set(ctl, info, element_count, member_count);
	if (err < 0)
		return err;
	numid = snd_ctl_elem_id_get_numid(&info->id);

	/* Set initial value to all of members in all of added elements. */
	data.id = info->id;
	for (i = 0; i < element_count; i++) {
		snd_ctl_elem_id_set_numid(&data.id, numid + i);

		for (j = 0; j < member_count; j++)
			data.value.integer64.value[j] = min;

		err = ctl->ops->element_write(ctl, &data);
		if (err < 0)
			return err;
	}

	return 0;
}

/**
 * \brief Create and add some user-defined control elements of boolean type.
 * \param ctl A handle of backend module for control interface.
 * \param info Common information for a new element set, with ID of the first new
 *	       element.
 * \param element_count The number of elements added by this operation.
 * \param member_count The number of members which a element has to
 *			   represent its states.
 *
 * This function creates some user elements with boolean type. These elements
 * are not controlled by device drivers in kernel. They can be operated by the
 * same way as usual elements added by the device drivers.
 *
 * The name field of \a id must be set with unique value to identify new control
 * elements. After returning, all fields of \a id are filled. A element can be
 * identified by the combination of name and index, or by numid.
 *
 * All of members in the new elements are locked. The value of each member is
 * initialized with false.
 *
 * \par Errors:
 * <dl>
 * <dt>-EBUSY
 * <dd>A element with ID \a id already exists.
 * <dt>-EINVAL
 * <dd>Some parameters include invalid value; i.e. ID has no name, or the number
 *      of members is not between 1 to 127.
 * <dt>-ENOMEM
 * <dd>Out of memory, or there are too many user elements.
 * <dt>-ENXIO
 * <dd>This backend module does not support user elements of boolean type.
 * <dt>-ENODEV
 * <dd>Device unplugged.
 * </dl>
 *
 * \par Compatibility:
 * This function is added in version 1.1.2.
 */
int snd_ctl_add_boolean_elem_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info,
				 unsigned int element_count,
				 unsigned int member_count)
{
	if (info == NULL)
		return -EINVAL;

	info->type = SND_CTL_ELEM_TYPE_BOOLEAN;
	info->value.integer.min = 0;
	info->value.integer.max = 1;

	return __snd_ctl_add_elem_set(ctl, info, element_count, member_count);
}

/**
 * \brief Create and add some user-defined control elements of enumerated type.
 * \param ctl A handle of backend module for control interface.
 * \param info Common information for a new element set, with ID of the first new
 *	       element.
 * \param element_count The number of elements added by this operation.
 * \param member_count The number of members which a element has to
 *	    	   represent its states.
 * \param items Range of possible values (0 ... \a items - 1).
 * \param labels An array containing \a items strings.
 * \return Zero on success, otherwise a negative error code.
 *
 * This function creates some user elements with enumerated type. These elements
 * are not controlled by device drivers in kernel. They can be operated by the
 * same way as usual elements added by the device drivers.
 *
 * The name field of \a id must be set with unique value to identify new control
 * elements. After returning, all fields of \a id are filled. A element can be
 * identified by the combination of name and index, or by numid.
 *
 * All of members in the new elements are locked. The value of each member is
 * initialized with the first entry of labels.
 *
 * \par Errors:
 * <dl>
 * <dt>-EBUSY
 * <dd>A control element with ID \a id already exists.
 * <dt>-EINVAL
 * <dd>Some arguments include invalid value; i.e. \a element_count is not
 *     between 1 to 127, or \a items is not at least one, or a string in \a
 *     labels is empty, or longer than 63 bytes, or total length of the labels
 *     requires more than 64 KiB storage.
 * <dt>-ENOMEM
 * <dd>Out of memory, or there are too many user control elements.
 * <dt>-ENXIO
 * <dd>This driver does not support (enumerated) user controls.
 * <dt>-ENODEV
 * <dd>Device unplugged.
 * </dl>
 *
 * \par Compatibility:
 * This function is added in version 1.1.2.
 */
int snd_ctl_add_enumerated_elem_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info,
				    unsigned int element_count,
				    unsigned int member_count,
				    unsigned int items,
				    const char *const labels[])
{
	unsigned int i, bytes;
	char *buf, *p;
	int err;

	if (info == NULL || labels == NULL)
		return -EINVAL;

	info->type = SND_CTL_ELEM_TYPE_ENUMERATED;
	info->owner = element_count;
	info->count = member_count;
	info->value.enumerated.items = items;

	bytes = 0;
	for (i = 0; i < items; ++i)
		bytes += strlen(labels[i]) + 1;
	if (bytes == 0)
		return -EINVAL;
	buf = malloc(bytes);
	if (buf == NULL)
		return -ENOMEM;
	info->value.enumerated.names_ptr = (uintptr_t)buf;
	info->value.enumerated.names_length = bytes;
	p = buf;
	for (i = 0; i < items; ++i) {
		strcpy(p, labels[i]);
		p += strlen(labels[i]) + 1;
	}

	err = __snd_ctl_add_elem_set(ctl, info, element_count, member_count);

	free(buf);

	return err;
}

/**
 * \brief Create and add some user-defined control elements of bytes type.
 * \param ctl A handle of backend module for control interface.
 * \param info Common information for a new element set, with ID of the first new
 *	       element.
 * \param element_count The number of elements added by this operation.
 * \param member_count The number of members which a element has to
 *			   represent its states.
 * \return Zero on success, otherwise a negative error code.
 *
 * This function creates some user elements with bytes type. These elements are
 * not controlled by device drivers in kernel. They can be operated by the same
 * way as usual elements added by the device drivers.
 *
 * The name field of \a id must be set with unique value to identify new control
 * elements. After returning, all fields of \a id are filled. A element can be
 * identified by the combination of name and index, or by numid.
 *
 * All of members in the new elements are locked. The value of each member is
 * initialized with the minimum value.
 *
 * \par Errors:
 * <dl>
 * <dt>-EBUSY
 * <dd>A element with ID \a id already exists.
 * <dt>-EINVAL
 * <dd>Some arguments include invalid value; i.e. ID has no name, or the number
 *     of members is not between 1 to 511.
 * <dt>-ENOMEM
 * <dd>Out of memory, or there are too many user elements.
 * <dt>-ENXIO
 * <dd>This backend module does not support user elements of bytes type.
 * <dt>-ENODEV
 * <dd>Device unplugged.
 * </dl>
 *
 * \par Compatibility:
 * This function is added in version 1.1.2.
 */
int snd_ctl_add_bytes_elem_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info,
			       unsigned int element_count,
			       unsigned int member_count)
{
	if (info == NULL)
		return -EINVAL;

	info->type = SND_CTL_ELEM_TYPE_BYTES;

	return __snd_ctl_add_elem_set(ctl, info, element_count, member_count);
}

/**
 * \brief Create and add an user-defined control element of integer type.
 *
 * This is a wrapper function to snd_ctl_add_integer_elem_set() for a control
 * element. This doesn't fill the id data with full information, thus it's
 * recommended to use snd_ctl_add_integer_elem_set(), instead.
 */
int snd_ctl_elem_add_integer(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id,
			     unsigned int member_count,
			     long min, long max, long step)
{
	snd_ctl_elem_info_t info = {0};

	assert(ctl && id && id->name[0]);

	info.id = *id;

	return snd_ctl_add_integer_elem_set(ctl, &info, 1, member_count,
					    min, max, step);
}

/**
 * \brief Create and add an user-defined control element of integer64 type.
 *
 * This is a wrapper function to snd_ctl_add_integer64_elem_set() for a single
 * control element. This doesn't fill the id data with full information, thus
 * it's recommended to use snd_ctl_add_integer64_elem_set(), instead.
 */
int snd_ctl_elem_add_integer64(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id,
			       unsigned int member_count,
			       long long min, long long max, long long step)
{
	snd_ctl_elem_info_t info = {0};

	assert(ctl && id && id->name[0]);

	info.id = *id;

	return snd_ctl_add_integer64_elem_set(ctl, &info, 1, member_count,
					      min, max, step);
}

/**
 * \brief Create and add an user-defined control element of boolean type.
 *
 * This is a wrapper function to snd_ctl_add_boolean_elem_set() for a single
 * control element. This doesn't fill the id data with full information, thus
 * it's recommended to use snd_ctl_add_boolean_elem_set(), instead.
 */
int snd_ctl_elem_add_boolean(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id,
			     unsigned int member_count)
{
	snd_ctl_elem_info_t info = {0};

	assert(ctl && id && id->name[0]);

	info.id = *id;

	return snd_ctl_add_boolean_elem_set(ctl, &info, 1, member_count);
}

/**
 * \brief Create and add a user-defined control element of enumerated type.
 *
 * This is a wrapper function to snd_ctl_add_enumerated_elem_set() for a single
 * control element. This doesn't fill the id data with full information, thus
 * it's recommended to use snd_ctl_add_enumerated_elem_set(), instead.
 *
 * This function is added in version 1.0.25.
 */
int snd_ctl_elem_add_enumerated(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id,
				unsigned int member_count, unsigned int items,
				const char *const labels[])
{
	snd_ctl_elem_info_t info = {0};

	assert(ctl && id && id->name[0] && labels);

	info.id = *id;

	return snd_ctl_add_enumerated_elem_set(ctl, &info, 1, member_count,
					       items, labels);
}

/**
 * \brief Create and add a user-defined control element of IEC958 type.
 * \param[in] ctl A handle of backend module for control interface.
 * \param[in,out] id ID of the new control element.
 *
 * This function creates an user element with IEC958 type. This element is not
 * controlled by device drivers in kernel. It can be operated by the same way as
 * usual elements added by the device drivers.
 *
 * The name field of \a id must be set with unique value to identify a new
 * control element. After returning, all fields of \a id are filled. A element
 * can be identified by the combination of name and index, or by numid.
 *
 * A member in the new element is locked and filled with zero.
 *
 * \par Errors:
 * <dl>
 * <dt>-EBUSY
 * <dd>A control element with ID \a id already exists.
 * <dt>-EINVAL
 * <dd>ID has no name.
 * <dt>-ENOMEM
 * <dd>Out of memory, or there are too many user elements.
 * <dt>-ENXIO
 * <dd>This backend module does not support user elements of IEC958 type.
 * <dt>-ENODEV
 * <dd>Device unplugged.
 * </dl>
 */
int snd_ctl_elem_add_iec958(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id)
{
	snd_ctl_elem_info_t info = {0};

	assert(ctl && id && id->name[0]);

	info.id = *id;
	info.type = SND_CTL_ELEM_TYPE_IEC958;
	info.owner = 1;
	info.count = 1;
	return ctl->ops->element_add(ctl, &info);
}

/**
 * \brief Remove an user CTL element
 * \param ctl CTL handle
 * \param id CTL element identification
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_elem_remove(snd_ctl_t *ctl, snd_ctl_elem_id_t *id)
{
	assert(ctl && id && (id->name[0] || id->numid));
	return ctl->ops->element_remove(ctl, id);
}

/**
 * \brief Get CTL element value.
 *
 * Read information from sound card. You must set the ID of the
 * element before calling this function.
 *
 * See snd_ctl_elem_value_t for details.
 *
 * \param ctl CTL handle.
 * \param data The element value. The ID must be set before calling
 *             the function, and the actual value will be returned
 *             here.
 *
 * \return 0 on success otherwise a negative error code.
 */
int snd_ctl_elem_read(snd_ctl_t *ctl, snd_ctl_elem_value_t *data)
{
	assert(ctl && data && (data->id.name[0] || data->id.numid));
	return ctl->ops->element_read(ctl, data);
}

/**
 * \brief Set CTL element value.
 *
 * Write new value(s) to the sound card. You must set the ID and the
 * value of the element before calling this function.
 *
 * See snd_ctl_elem_value_t for details.
 *
 * \param ctl CTL handle.
 * \param data The new value.
 *
 * \retval 0 on success
 * \retval >0 on success when value was changed
 * \retval <0 a negative error code
 */
int snd_ctl_elem_write(snd_ctl_t *ctl, snd_ctl_elem_value_t *data)
{
	assert(ctl && data && (data->id.name[0] || data->id.numid));
	return ctl->ops->element_write(ctl, data);
}

static int snd_ctl_tlv_do(snd_ctl_t *ctl, int op_flag,
			  const snd_ctl_elem_id_t *id,
		          unsigned int *tlv, unsigned int tlv_size)
{
	snd_ctl_elem_info_t *info = NULL;
	int err;

	if (id->numid == 0) {
		info = calloc(1, sizeof(*info));
		if (info == NULL)
			return -ENOMEM;
		info->id = *id;
		id = &info->id;
		err = snd_ctl_elem_info(ctl, info);
		if (err < 0)
			goto __err;
		if (id->numid == 0) {
			err = -ENOENT;
			goto __err;
		}
	}
	err = ctl->ops->element_tlv(ctl, op_flag, id->numid, tlv, tlv_size);
      __err:
      	if (info)
      		free(info);
	return err;
}

/**
 * \brief Read structured data from an element set to given buffer.
 * \param ctl A handle of backend module for control interface.
 * \param id ID of an element.
 * \param tlv An array with members of unsigned int type.
 * \param tlv_size The length of the array.
 * \return 0 on success otherwise a negative error code
 *
 * The format of an array of \a tlv argument is:
 *   tlv[0]:   Type. One of SND_CTL_TLVT_XXX.
 *   tlv[1]:   Length. The length of value in units of byte.
 *   tlv[2..]: Value. Depending on the type.
 *
 * Details are described in <sound/tlv.h>.
 */
int snd_ctl_elem_tlv_read(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id,
			  unsigned int *tlv, unsigned int tlv_size)
{
	int err;
	assert(ctl && id && (id->name[0] || id->numid) && tlv);
	if (tlv_size < 2 * sizeof(int))
		return -EINVAL;
	/* 1.0.12 driver doesn't return the error even if the user TLV
	 * is empty.  So, initialize TLV here with an invalid type
	 * and compare the returned value after ioctl for checking
	 * the validity of TLV.
	 */
	tlv[SNDRV_CTL_TLVO_TYPE] = -1;
	tlv[SNDRV_CTL_TLVO_LEN] = 0;
	err = snd_ctl_tlv_do(ctl, 0, id, tlv, tlv_size);
	if (err >= 0 && tlv[SNDRV_CTL_TLVO_TYPE] == (unsigned int)-1)
		err = -ENXIO;
	return err;
}

/**
 * \brief Write structured data from given buffer to an element set.
 * \param ctl A handle of backend module for control interface.
 * \param id ID of an element.
 * \param tlv An array with members of unsigned int type. The second member
 *	      must represent total bytes of the rest of array.
 * \retval 0 on success
 * \retval >0 on success when value was changed
 * \retval <0 a negative error code
 *
 * The format of an array of \a tlv argument is:
 *   tlv[0]:   Type. One of SND_CTL_TLVT_XXX.
 *   tlv[1]:   Length. The length of value in units of byte.
 *   tlv[2..]: Value. Depending on the type.
 *
 * Details are described in <sound/tlv.h>.
 */
int snd_ctl_elem_tlv_write(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id,
			   const unsigned int *tlv)
{
	assert(ctl && id && (id->name[0] || id->numid) && tlv);
	return snd_ctl_tlv_do(ctl, 1, id, (unsigned int *)tlv,
			tlv[SNDRV_CTL_TLVO_LEN] + 2 * sizeof(unsigned int));
}

/**
 * \brief Process structured data from given buffer for an element set.
 * \param ctl A handle of backend module for control interface.
 * \param id ID of an element.
 * \param tlv An array with members of unsigned int type. The second member
 *	      must represent total bytes of the rest of array.
 * \retval 0 on success
 * \retval >0 on success when value was changed
 * \retval <0 a negative error code
 *
 * The format of an array of \a tlv argument is:
 *   tlv[0]:   Type. One of SND_CTL_TLVT_XXX.
 *   tlv[1]:   Length. The length of value in units of byte.
 *   tlv[2..]: Value. Depending on the type.
 *
 * Details are described in <sound/tlv.h>.
 */
int snd_ctl_elem_tlv_command(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id,
			     const unsigned int *tlv)
{
	assert(ctl && id && (id->name[0] || id->numid) && tlv);
	return snd_ctl_tlv_do(ctl, -1, id, (unsigned int *)tlv,
			tlv[SNDRV_CTL_TLVO_LEN] + 2 * sizeof(unsigned int));
}

/**
 * \brief Lock CTL element
 * \param ctl CTL handle
 * \param id CTL element id pointer
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_elem_lock(snd_ctl_t *ctl, snd_ctl_elem_id_t *id)
{
	assert(ctl && id);
	return ctl->ops->element_lock(ctl, id);
}

/**
 * \brief Unlock CTL element
 * \param ctl CTL handle
 * \param id CTL element id pointer
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_elem_unlock(snd_ctl_t *ctl, snd_ctl_elem_id_t *id)
{
	assert(ctl && id);
	return ctl->ops->element_unlock(ctl, id);
}

/**
 * \brief Get next hardware dependent device number
 * \param ctl CTL handle
 * \param device current device on entry and next device on return
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_hwdep_next_device(snd_ctl_t *ctl, int *device)
{
	assert(ctl && device);
	return ctl->ops->hwdep_next_device(ctl, device);
}

/**
 * \brief Get info about a hardware dependent device
 * \param ctl CTL handle
 * \param info Hardware dependent device id/info pointer
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_hwdep_info(snd_ctl_t *ctl, snd_hwdep_info_t * info)
{
	assert(ctl && info);
	return ctl->ops->hwdep_info(ctl, info);
}

/**
 * \brief Get next PCM device number
 * \param ctl CTL handle
 * \param device current device on entry and next device on return
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_pcm_next_device(snd_ctl_t *ctl, int * device)
{
	assert(ctl && device);
	return ctl->ops->pcm_next_device(ctl, device);
}

/**
 * \brief Get info about a PCM device
 * \param ctl CTL handle
 * \param info PCM device id/info pointer
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_pcm_info(snd_ctl_t *ctl, snd_pcm_info_t * info)
{
	assert(ctl && info);
	return ctl->ops->pcm_info(ctl, info);
}

/**
 * \brief Set preferred PCM subdevice number of successive PCM open
 * \param ctl CTL handle
 * \param subdev Preferred PCM subdevice number
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_pcm_prefer_subdevice(snd_ctl_t *ctl, int subdev)
{
	assert(ctl);
	return ctl->ops->pcm_prefer_subdevice(ctl, subdev);
}

/**
 * \brief Get next RawMidi device number
 * \param ctl CTL handle
 * \param device current device on entry and next device on return
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_rawmidi_next_device(snd_ctl_t *ctl, int * device)
{
	assert(ctl && device);
	return ctl->ops->rawmidi_next_device(ctl, device);
}

/**
 * \brief Get info about a RawMidi device
 * \param ctl CTL handle
 * \param info RawMidi device id/info pointer
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_rawmidi_info(snd_ctl_t *ctl, snd_rawmidi_info_t * info)
{
	assert(ctl && info);
	return ctl->ops->rawmidi_info(ctl, info);
}

/**
 * \brief Set preferred RawMidi subdevice number of successive RawMidi open
 * \param ctl CTL handle
 * \param subdev Preferred RawMidi subdevice number
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_rawmidi_prefer_subdevice(snd_ctl_t *ctl, int subdev)
{
	assert(ctl);
	return ctl->ops->rawmidi_prefer_subdevice(ctl, subdev);
}

/**
 * \brief Get next UMP device number
 * \param ctl CTL handle
 * \param device current device on entry and next device on return
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_ump_next_device(snd_ctl_t *ctl, int *device)
{
	assert(ctl && device);
	if (ctl->ops->ump_next_device)
		return ctl->ops->ump_next_device(ctl, device);
	return -ENXIO;
}

/**
 * \brief Get UMP Endpoint info about a UMP RawMidi device
 * \param ctl CTL handle
 * \param info UMP Endpoint info pointer
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_ump_endpoint_info(snd_ctl_t *ctl, snd_ump_endpoint_info_t *info)
{
	assert(ctl && info);
	return ctl->ops->ump_endpoint_info(ctl, info);
}

/**
 * \brief Get UMP Block info about a UMP RawMidi device
 * \param ctl CTL handle
 * \param info UMP Block info pointer
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_ump_block_info(snd_ctl_t *ctl, snd_ump_block_info_t *info)
{
	assert(ctl && info);
	return ctl->ops->ump_block_info(ctl, info);
}

/**
 * \brief Set Power State to given SND_CTL_POWER_* value and do the power management
 * \param ctl CTL handle
 * \param state Desired Power State
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_set_power_state(snd_ctl_t *ctl, unsigned int state)
{
	assert(ctl);
	if (ctl->ops->set_power_state)
		return ctl->ops->set_power_state(ctl, state);
	return -ENXIO;
}

/**
 * \brief Get actual Power State
 * \param ctl CTL handle
 * \param state Destination value
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_get_power_state(snd_ctl_t *ctl, unsigned int *state)
{
	assert(ctl);
	if (ctl->ops->get_power_state)
		return ctl->ops->get_power_state(ctl, state);
	return -ENXIO;
}

/**
 * \brief Read an event
 * \param ctl CTL handle
 * \param event Event pointer
 * \return number of events read otherwise a negative error code on failure
 */
int snd_ctl_read(snd_ctl_t *ctl, snd_ctl_event_t *event)
{
	assert(ctl && event);
	return (ctl->ops->read)(ctl, event);
}

/**
 * \brief Wait for a CTL to become ready (i.e. at least one event pending)
 * \param ctl CTL handle
 * \param timeout maximum time in milliseconds to wait
 * \return 0 otherwise a negative error code on failure
 */
int snd_ctl_wait(snd_ctl_t *ctl, int timeout)
{
	struct pollfd *pfd;
	unsigned short revents;
	int npfds, err, err_poll;

	npfds = snd_ctl_poll_descriptors_count(ctl);
	if (npfds <= 0 || npfds >= 16) {
		SNDERR("Invalid poll_fds %d", npfds);
		return -EIO;
	}
	pfd = alloca(sizeof(*pfd) * npfds);
	err = snd_ctl_poll_descriptors(ctl, pfd, npfds);
	if (err < 0)
		return err;
	if (err != npfds) {
		SNDMSG("invalid poll descriptors %d", err);
		return -EIO;
	}
	for (;;) {
		err_poll = poll(pfd, npfds, timeout);
		if (err_poll < 0)
			return -errno;
		if (! err_poll)
			return 0;
		err = snd_ctl_poll_descriptors_revents(ctl, pfd, npfds, &revents);
		if (err < 0)
			return err;
		if (revents & (POLLERR | POLLNVAL))
			return -EIO;
		if (revents & (POLLIN | POLLOUT))
			return 1;
	}
}

/**
 * \brief Add an async handler for a CTL
 * \param handler Returned handler handle
 * \param ctl CTL handle
 * \param callback Callback function
 * \param private_data Callback private data
 * \return 0 otherwise a negative error code on failure
 */
int snd_async_add_ctl_handler(snd_async_handler_t **handler, snd_ctl_t *ctl, 
			      snd_async_callback_t callback, void *private_data)
{
	int err;
	int was_empty;
	snd_async_handler_t *h;
	err = snd_async_add_handler(&h, _snd_ctl_async_descriptor(ctl),
				    callback, private_data);
	if (err < 0)
		return err;
	h->type = SND_ASYNC_HANDLER_CTL;
	h->u.ctl = ctl;
	was_empty = list_empty(&ctl->async_handlers);
	list_add_tail(&h->hlist, &ctl->async_handlers);
	if (was_empty) {
		err = snd_ctl_async(ctl, snd_async_handler_get_signo(h), getpid());
		if (err < 0) {
			snd_async_del_handler(h);
			return err;
		}
	}
	*handler = h;
	return 0;
}

/**
 * \brief Return CTL handle related to an async handler
 * \param handler Async handler handle
 * \return CTL handle
 */
snd_ctl_t *snd_async_handler_get_ctl(snd_async_handler_t *handler)
{
	assert(handler->type == SND_ASYNC_HANDLER_CTL);
	return handler->u.ctl;
}

static const char *const build_in_ctls[] = {
	"hw", "empty", "remap", "shm", NULL
};

static int snd_ctl_open_conf(snd_ctl_t **ctlp, const char *name,
			     snd_config_t *ctl_root, snd_config_t *ctl_conf, int mode)
{
	const char *str;
	char *buf = NULL, *buf1 = NULL;
	int err;
	snd_config_t *conf, *type_conf = NULL;
	snd_config_iterator_t i, next;
	const char *lib = NULL, *open_name = NULL;
	const char *id;
	int (*open_func)(snd_ctl_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL;
#ifndef PIC
	extern void *snd_control_open_symbols(void);
#endif
	if (snd_config_get_type(ctl_conf) != SND_CONFIG_TYPE_COMPOUND) {
		if (name)
			SNDERR("Invalid type for CTL %s definition", name);
		else
			SNDERR("Invalid type for CTL definition");
		return -EINVAL;
	}
	err = snd_config_search(ctl_conf, "type", &conf);
	if (err < 0) {
		SNDERR("type is not defined");
		return err;
	}
	err = snd_config_get_id(conf, &id);
	if (err < 0) {
		SNDERR("unable to get id");
		return err;
	}
	err = snd_config_get_string(conf, &str);
	if (err < 0) {
		SNDERR("Invalid type for %s", id);
		return err;
	}
	err = snd_config_search_definition(ctl_root, "ctl_type", str, &type_conf);
	if (err >= 0) {
		if (snd_config_get_type(type_conf) != SND_CONFIG_TYPE_COMPOUND) {
			SNDERR("Invalid type for CTL type %s definition", str);
			err = -EINVAL;
			goto _err;
		}
		snd_config_for_each(i, next, type_conf) {
			snd_config_t *n = snd_config_iterator_entry(i);
			const char *id;
			if (snd_config_get_id(n, &id) < 0)
				continue;
			if (strcmp(id, "comment") == 0)
				continue;
			if (strcmp(id, "lib") == 0) {
				err = snd_config_get_string(n, &lib);
				if (err < 0) {
					SNDERR("Invalid type for %s", id);
					goto _err;
				}
				continue;
			}
			if (strcmp(id, "open") == 0) {
				err = snd_config_get_string(n, &open_name);
				if (err < 0) {
					SNDERR("Invalid type for %s", id);
					goto _err;
				}
				continue;
			}
			SNDERR("Unknown field %s", id);
			err = -EINVAL;
			goto _err;
		}
	}
	if (!open_name) {
		buf = malloc(strlen(str) + 32);
		if (buf == NULL) {
			err = -ENOMEM;
			goto _err;
		}
		open_name = buf;
		sprintf(buf, "_snd_ctl_%s_open", str);
	}
	if (!lib) {
		const char *const *build_in = build_in_ctls;
		while (*build_in) {
			if (!strcmp(*build_in, str))
				break;
			build_in++;
		}
		if (*build_in == NULL) {
			buf1 = malloc(strlen(str) + 32);
			if (buf1 == NULL) {
				err = -ENOMEM;
				goto _err;
			}
			lib = buf1;
			sprintf(buf1, "libasound_module_ctl_%s.so", str);
		}
	}
#ifndef PIC
	snd_control_open_symbols();
#endif
	open_func = snd_dlobj_cache_get(lib, open_name,
			SND_DLSYM_VERSION(SND_CONTROL_DLSYM_VERSION), 1);
	if (open_func) {
		err = open_func(ctlp, name, ctl_root, ctl_conf, mode);
		if (err >= 0) {
			(*ctlp)->open_func = open_func;
			err = 0;
		} else {
			snd_dlobj_cache_put(open_func);
		}
	} else {
		err = -ENXIO;
	}
       _err:
	if (type_conf)
		snd_config_delete(type_conf);
	free(buf);
	free(buf1);
	return err;
}

static int snd_ctl_open_noupdate(snd_ctl_t **ctlp, snd_config_t *root,
				 const char *name, int mode, int hop)
{
	int err;
	snd_config_t *ctl_conf;
	const char *str;

	err = snd_config_search_definition(root, "ctl", name, &ctl_conf);
	if (err < 0) {
		SNDERR("Invalid CTL %s", name);
		return err;
	}
	if (snd_config_get_string(ctl_conf, &str) >= 0)
		err = snd_ctl_open_noupdate(ctlp, root, str, mode, hop + 1);
	else {
		snd_config_set_hop(ctl_conf, hop);
		err = snd_ctl_open_conf(ctlp, name, root, ctl_conf, mode);
	}
	snd_config_delete(ctl_conf);
	return err;
}

#ifndef DOC_HIDDEN
int _snd_ctl_open_named_child(snd_ctl_t **pctl, const char *name,
			      snd_config_t *root, snd_config_t *conf,
			      int mode, snd_config_t *parent_conf)
{
	const char *str;
	int hop;

	if ((hop = snd_config_check_hop(parent_conf)) < 0)
		return hop;
	if (snd_config_get_string(conf, &str) >= 0)
		return snd_ctl_open_noupdate(pctl, root, str, mode, hop + 1);
	return snd_ctl_open_conf(pctl, name, root, conf, mode);
}
#endif

/**
 * \brief Opens a sound card.
 *
 * \param ctlp Returned CTL handle.
 * \param name A string identifying the card (See \ref control_cards_id).
 * \param mode Open mode (see #SND_CTL_NONBLOCK, #SND_CTL_ASYNC).
 *
 * \return 0 on success otherwise a negative error code.
 */
int snd_ctl_open(snd_ctl_t **ctlp, const char *name, int mode)
{
	snd_config_t *top;
	int err;

	assert(ctlp && name);
	if (_snd_is_ucm_device(name)) {
		name = uc_mgr_alibcfg_by_device(&top, name);
		if (name == NULL)
			return -ENODEV;
	} else {
		err = snd_config_update_ref(&top);
		if (err < 0)
			return err;
	}
	err = snd_ctl_open_noupdate(ctlp, top, name, mode, 0);
	snd_config_unref(top);
	return err;
}

/**
 * \brief Opens a CTL using local configuration
 * \param ctlp Returned CTL handle
 * \param name ASCII identifier of the CTL handle
 * \param mode Open mode (see #SND_CTL_NONBLOCK, #SND_CTL_ASYNC)
 * \param lconf Local configuration
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_open_lconf(snd_ctl_t **ctlp, const char *name,
		       int mode, snd_config_t *lconf)
{
	assert(ctlp && name && lconf);
	return snd_ctl_open_noupdate(ctlp, lconf, name, mode, 0);
}

/**
 * \brief Opens a fallback CTL
 * \param ctlp Returned CTL handle
 * \param root Configuration root
 * \param name ASCII identifier of the CTL handle used as fallback
 * \param orig_name The original ASCII name
 * \param mode Open mode (see #SND_CTL_NONBLOCK, #SND_CTL_ASYNC)
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_open_fallback(snd_ctl_t **ctlp, snd_config_t *root,
			  const char *name, const char *orig_name, int mode)
{
	int err;
	assert(ctlp && name && root);
	err = snd_ctl_open_noupdate(ctlp, root, name, mode, 0);
	if (err >= 0) {
		free((*ctlp)->name);
		(*ctlp)->name = orig_name ? strdup(orig_name) : NULL;
	}
	return err;
}

#ifndef DOC_HIDDEN
#define TYPE(v) [SND_CTL_ELEM_TYPE_##v] = #v
#define IFACE(v) [SND_CTL_ELEM_IFACE_##v] = #v
#define IFACE1(v, n) [SND_CTL_ELEM_IFACE_##v] = #n
#define EVENT(v) [SND_CTL_EVENT_##v] = #v

static const char *const snd_ctl_elem_type_names[] = {
	TYPE(NONE),
	TYPE(BOOLEAN),
	TYPE(INTEGER),
	TYPE(ENUMERATED),
	TYPE(BYTES),
	TYPE(IEC958),
	TYPE(INTEGER64),
};

static const char *const snd_ctl_elem_iface_names[] = {
	IFACE(CARD),
	IFACE(HWDEP),
	IFACE(MIXER),
	IFACE(PCM),
	IFACE(RAWMIDI),
	IFACE(TIMER),
	IFACE(SEQUENCER),
};

static const char *const snd_ctl_event_type_names[] = {
	EVENT(ELEM),
};
#endif

/**
 * \brief get name of a CTL element type
 * \param type CTL element type
 * \return ascii name of CTL element type
 */
const char *snd_ctl_elem_type_name(snd_ctl_elem_type_t type)
{
	assert(type <= SND_CTL_ELEM_TYPE_LAST);
	return snd_ctl_elem_type_names[type];
}

/**
 * \brief get name of a CTL element related interface
 * \param iface CTL element related interface
 * \return ascii name of CTL element related interface
 */
const char *snd_ctl_elem_iface_name(snd_ctl_elem_iface_t iface)
{
	assert(iface <= SND_CTL_ELEM_IFACE_LAST);
	return snd_ctl_elem_iface_names[iface];
}

/**
 * \brief get name of a CTL event type
 * \param type CTL event type
 * \return ascii name of CTL event type
 */
const char *snd_ctl_event_type_name(snd_ctl_event_type_t type)
{
	assert(type <= SND_CTL_EVENT_LAST);
	return snd_ctl_event_type_names[type];
}

/**
 * \brief allocate space for CTL element identifiers list
 *
 * The space can be released with snd_ctl_elem_list_free_space().
 *
 * \param obj CTL element identifiers list.
 * \param entries How many entries to allocate. See
 *        #snd_ctl_elem_list_t to learn how to obtain
 *        this number in advance.
 * \return 0 on success otherwise a negative error code.
 */
int snd_ctl_elem_list_alloc_space(snd_ctl_elem_list_t *obj, unsigned int entries)
{
	free(obj->pids);
	obj->pids = calloc(entries, sizeof(*obj->pids));
	if (!obj->pids) {
		obj->space = 0;
		return -ENOMEM;
	}
	obj->space = entries;
	return 0;
}  

/**
 * \brief free previously allocated space for CTL element identifiers list
 *
 * Releases space previously allocated using
 * snd_ctl_elem_list_alloc_space().
 *
 * \param obj CTL element identifiers list
 */
void snd_ctl_elem_list_free_space(snd_ctl_elem_list_t *obj)
{
	free(obj->pids);
	obj->pids = NULL;
	obj->space = 0;
}

/**
 * \brief Get event mask for an element related event
 * \param obj CTL event
 * \return event mask for element related event
 */
unsigned int snd_ctl_event_elem_get_mask(const snd_ctl_event_t *obj)
{
	assert(obj);
	assert(obj->type == SND_CTL_EVENT_ELEM);
	return obj->data.elem.mask;
}

/**
 * \brief Get CTL element identifier for an element related event
 * \param obj CTL event
 * \param ptr Pointer to returned CTL element identifier
 */
void snd_ctl_event_elem_get_id(const snd_ctl_event_t *obj, snd_ctl_elem_id_t *ptr)
{
	assert(obj && ptr);
	assert(obj->type == SND_CTL_EVENT_ELEM);
	*ptr = obj->data.elem.id;
}

/**
 * \brief Get element numeric identifier for an element related event
 * \param obj CTL event
 * \return element numeric identifier
 */
unsigned int snd_ctl_event_elem_get_numid(const snd_ctl_event_t *obj)
{
	assert(obj);
	assert(obj->type == SND_CTL_EVENT_ELEM);
	return obj->data.elem.id.numid;
}

/**
 * \brief Get interface part of CTL element identifier for an element related event
 * \param obj CTL event
 * \return interface part of element identifier
 */
snd_ctl_elem_iface_t snd_ctl_event_elem_get_interface(const snd_ctl_event_t *obj)
{
	assert(obj);
	assert(obj->type == SND_CTL_EVENT_ELEM);
	return obj->data.elem.id.iface;
}

/**
 * \brief Get device part of CTL element identifier for an element related event
 * \param obj CTL event
 * \return device part of element identifier
 */
unsigned int snd_ctl_event_elem_get_device(const snd_ctl_event_t *obj)
{
	assert(obj);
	assert(obj->type == SND_CTL_EVENT_ELEM);
	return obj->data.elem.id.device;
}

/**
 * \brief Get subdevice part of CTL element identifier for an element related event
 * \param obj CTL event
 * \return subdevice part of element identifier
 */
unsigned int snd_ctl_event_elem_get_subdevice(const snd_ctl_event_t *obj)
{
	assert(obj);
	assert(obj->type == SND_CTL_EVENT_ELEM);
	return obj->data.elem.id.subdevice;
}

/**
 * \brief Get name part of CTL element identifier for an element related event
 * \param obj CTL event
 * \return name part of element identifier
 */
const char *snd_ctl_event_elem_get_name(const snd_ctl_event_t *obj)
{
	assert(obj);
	assert(obj->type == SND_CTL_EVENT_ELEM);
	return (const char *)obj->data.elem.id.name;
}

/**
 * \brief Get index part of CTL element identifier for an element related event
 * \param obj CTL event
 * \return index part of element identifier
 */
unsigned int snd_ctl_event_elem_get_index(const snd_ctl_event_t *obj)
{
	assert(obj);
	assert(obj->type == SND_CTL_EVENT_ELEM);
	return obj->data.elem.id.index;
}

#ifndef DOC_HIDDEN
int _snd_ctl_poll_descriptor(snd_ctl_t *ctl)
{
	assert(ctl);
	return ctl->poll_fd;
}
#endif

/**
 * \brief get size of #snd_ctl_elem_id_t
 * \return size in bytes
 */
size_t snd_ctl_elem_id_sizeof()
{
	return sizeof(snd_ctl_elem_id_t);
}

/**
 * \brief allocate an invalid #snd_ctl_elem_id_t using standard malloc
 * \param ptr returned pointer
 * \return 0 on success otherwise negative error code
 */
int snd_ctl_elem_id_malloc(snd_ctl_elem_id_t **ptr)
{
	assert(ptr);
	*ptr = calloc(1, sizeof(snd_ctl_elem_id_t));
	if (!*ptr)
		return -ENOMEM;
	return 0;
}

/**
 * \brief frees a previously allocated #snd_ctl_elem_id_t
 * \param obj pointer to object to free
 */
void snd_ctl_elem_id_free(snd_ctl_elem_id_t *obj)
{
	free(obj);
}

/**
 * \brief clear given #snd_ctl_elem_id_t object
 * \param obj pointer to object to clear
 */
void snd_ctl_elem_id_clear(snd_ctl_elem_id_t *obj)
{
	memset(obj, 0, sizeof(snd_ctl_elem_id_t));
}

/**
 * \brief copy one #snd_ctl_elem_id_t to another
 * \param dst pointer to destination
 * \param src pointer to source
 */
void snd_ctl_elem_id_copy(snd_ctl_elem_id_t *dst, const snd_ctl_elem_id_t *src)
{
	assert(dst && src);
	*dst = *src;
}

/**
 * \brief compare one #snd_ctl_elem_id_t to another using numid
 * \param id1 pointer to first id
 * \param id2 pointer to second id
 * \retval zero when values are identical, other value on a difference (like strcmp)
 *
 * This comparison ignores the set of fields part.
 *
 * The return value can be used for sorting like qsort(). It gives persistent
 * results.
 */
int snd_ctl_elem_id_compare_numid(const snd_ctl_elem_id_t *id1, const snd_ctl_elem_id_t *id2)
{
	int64_t d;

	assert(id1 && id2);
	d = (int64_t)id1->numid - (int64_t)id2->numid;
	if (d & ((int64_t)INT_MAX + 1)) {	/* fast path */
		if (d > INT_MAX)
			d = INT_MAX;
		else if (d < INT_MIN)
			d = INT_MIN;
	}
	return d;
}

/**
 * \brief compare one #snd_ctl_elem_id_t to another
 * \param id1 pointer to first id
 * \param id2 pointer to second id
 * \retval zero when values are identical, other value on a difference (like strcmp)
 *
 * This comparison ignores the numid part. The numid comparison can be easily
 * implemented using snd_ctl_elem_id_get_numid() calls.
 *
 * The identifier set fields are compared in this order: interface, device,
 * subdevice, name, index.
 *
 * The return value can be used for sorting like qsort(). It gives persistent
 * results.
 */
int snd_ctl_elem_id_compare_set(const snd_ctl_elem_id_t *id1, const snd_ctl_elem_id_t *id2)
{
	int d;

	assert(id1 && id2);
	/* although those values are unsigned integer, practically, */
	/* the useable limit is really much lower */
	assert((id1->iface | id1->device | id1->subdevice | id1->index) <= INT_MAX);
	assert((id2->iface | id2->device | id2->subdevice | id1->index) <= INT_MAX);
	d = id1->iface - id2->iface;
	if (d != 0)
		return d;
	d = id1->device - id2->device;
	if (d != 0)
		return d;
	d = id1->subdevice - id2->subdevice;
	if (d != 0)
		return d;
	d = strcmp((const char *)id1->name, (const char *)id2->name);
	if (d != 0)
		return d;
	return id1->index - id2->index;
}

/**
 * \brief Get numeric identifier from a CTL element identifier
 * \param obj CTL element identifier
 * \return CTL element numeric identifier
 */
unsigned int snd_ctl_elem_id_get_numid(const snd_ctl_elem_id_t *obj)
{
	assert(obj);
	return obj->numid;
}

/**
 * \brief Get interface part of a CTL element identifier
 * \param obj CTL element identifier
 * \return CTL element related interface
 */
snd_ctl_elem_iface_t snd_ctl_elem_id_get_interface(const snd_ctl_elem_id_t *obj)
{
	assert(obj);
	return obj->iface;
}

/**
 * \brief Get device part of a CTL element identifier
 * \param obj CTL element identifier
 * \return CTL element related device
 */
unsigned int snd_ctl_elem_id_get_device(const snd_ctl_elem_id_t *obj)
{
	assert(obj);
	return obj->device;
}

/**
 * \brief Get subdevice part of a CTL element identifier
 * \param obj CTL element identifier
 * \return CTL element related subdevice
 */
unsigned int snd_ctl_elem_id_get_subdevice(const snd_ctl_elem_id_t *obj)
{
	assert(obj);
	return obj->subdevice;
}

/**
 * \brief Get name part of a CTL element identifier
 * \param obj CTL element identifier
 * \return CTL element name
 */
const char *snd_ctl_elem_id_get_name(const snd_ctl_elem_id_t *obj)
{
	assert(obj);
	return (const char *)obj->name;
}

/**
 * \brief Get index part of a CTL element identifier
 * \param obj CTL element identifier
 * \return CTL element index
 */
unsigned int snd_ctl_elem_id_get_index(const snd_ctl_elem_id_t *obj)
{
	assert(obj);
	return obj->index;
}

/**
 * \brief Set numeric identifier for a CTL element identifier
 * \param obj CTL element identifier
 * \param val CTL element numeric identifier
 */
void snd_ctl_elem_id_set_numid(snd_ctl_elem_id_t *obj, unsigned int val)
{
	assert(obj);
	obj->numid = val;
}

/**
 * \brief Set interface part for a CTL element identifier
 * \param obj CTL element identifier
 * \param val CTL element related interface
 */
void snd_ctl_elem_id_set_interface(snd_ctl_elem_id_t *obj, snd_ctl_elem_iface_t val)
{
	assert(obj);
	obj->iface = val;
}

/**
 * \brief Set device part for a CTL element identifier
 * \param obj CTL element identifier
 * \param val CTL element related device
 */
void snd_ctl_elem_id_set_device(snd_ctl_elem_id_t *obj, unsigned int val)
{
	assert(obj);
	obj->device = val;
}

/**
 * \brief Set subdevice part for a CTL element identifier
 * \param obj CTL element identifier
 * \param val CTL element related subdevice
 */
void snd_ctl_elem_id_set_subdevice(snd_ctl_elem_id_t *obj, unsigned int val)
{
	assert(obj);
	obj->subdevice = val;
}

/**
 * \brief Set name part for a CTL element identifier
 * \param obj CTL element identifier
 * \param val CTL element name
 */
void snd_ctl_elem_id_set_name(snd_ctl_elem_id_t *obj, const char *val)
{
	assert(obj);
	snd_strlcpy((char *)obj->name, val, sizeof(obj->name));
}

/**
 * \brief Set index part for a CTL element identifier
 * \param obj CTL element identifier
 * \param val CTL element index
 */
void snd_ctl_elem_id_set_index(snd_ctl_elem_id_t *obj, unsigned int val)
{
	assert(obj);
	obj->index = val;
}

/**
 * \brief get size of #snd_ctl_card_info_t.
 * \return Size in bytes.
 */
size_t snd_ctl_card_info_sizeof()
{
	return sizeof(snd_ctl_card_info_t);
}

/**
 * \brief Allocate an invalid #snd_ctl_card_info_t on the heap.
 *
 * Allocate space for a card info object on the heap. The allocated memory
 * must be freed using snd_ctl_card_info_free().
 *
 * See snd_ctl_card_info_t for details.
 *
 * \param ptr Pointer to a snd_ctl_card_info_t pointer. The address
 *            of the allocated space will be returned here.
 * \return 0 on success, otherwise a negative error code.
 */
int snd_ctl_card_info_malloc(snd_ctl_card_info_t **ptr)
{
	assert(ptr);
	*ptr = calloc(1, sizeof(snd_ctl_card_info_t));
	if (!*ptr)
		return -ENOMEM;
	return 0;
}

/**
 * \brief Free an #snd_ctl_card_info_t previously allocated using
 *        snd_ctl_card_info_malloc().
 *
 * \param obj Pointer to the snd_ctl_card_info_t.
 */
void snd_ctl_card_info_free(snd_ctl_card_info_t *obj)
{
	free(obj);
}

/**
 * \brief Clear given card info object.
 *
 * See snd_ctl_elem_value_t for details.
 *
 * \param obj Card info object.
 */
void snd_ctl_card_info_clear(snd_ctl_card_info_t *obj)
{
	memset(obj, 0, sizeof(snd_ctl_card_info_t));
}

/**
 * \brief Bitwise copy of a #snd_ctl_card_info_t object.
 *
 * \param dst Pointer to destination.
 * \param src Pointer to source.
 */
void snd_ctl_card_info_copy(snd_ctl_card_info_t *dst, const snd_ctl_card_info_t *src)
{
	assert(dst && src);
	*dst = *src;
}

/**
 * \brief Get the sound card index from the given info object.
 *
 * See snd_ctl_card_info_t for more details.
 *
 * \param obj The card info object.
 * \return Sound card index.
 */
int snd_ctl_card_info_get_card(const snd_ctl_card_info_t *obj)
{
	assert(obj);
	return obj->card;
}

/**
 * \brief Get the sound card ID from the given info object.
 *
 * See snd_ctl_card_info_t for more details.
 *
 * \param obj The card info object.
 * \return Sound card ID.
 */
const char *snd_ctl_card_info_get_id(const snd_ctl_card_info_t *obj)
{
	assert(obj);
	return (const char *)obj->id;
}

/**
 * \brief Get the sound card driver from the given info object.
 *
 * See snd_ctl_card_info_t for more details.
 *
 * \param obj The card info object.
 * \return The sound card driver.
 */
const char *snd_ctl_card_info_get_driver(const snd_ctl_card_info_t *obj)
{
	assert(obj);
	return (const char *)obj->driver;
}

/**
 * \brief Get the sound card name from the given info object.
 *
 * See snd_ctl_card_info_t for more details.
 *
 * \param obj The card info object.
 * \return Sound card name.
 */
const char *snd_ctl_card_info_get_name(const snd_ctl_card_info_t *obj)
{
	assert(obj);
	return (const char *)obj->name;
}

/**
 * \brief Get the sound cards long name from the given info object.
 *
 * See snd_ctl_card_info_t for more details.
 *
 * \param obj The card info object.
 * \return Sound cards long name.
 */
const char *snd_ctl_card_info_get_longname(const snd_ctl_card_info_t *obj)
{
	assert(obj);
	return (const char *)obj->longname;
}

/**
 * \brief Get the sound card mixer name from the given info object.
 *
 * See snd_ctl_card_info_t for more details.
 *
 * \param obj The card info object.
 * \return Sound card mixer name.
 */
const char *snd_ctl_card_info_get_mixername(const snd_ctl_card_info_t *obj)
{
	assert(obj);
	return (const char *)obj->mixername;
}

/**
 * \brief Get the sound cards "components" property from the given info object.
 *
 * See snd_ctl_card_info_t for more details.
 *
 * \param obj The card info object.
 * \return Sound cards "components" property.
 */
const char *snd_ctl_card_info_get_components(const snd_ctl_card_info_t *obj)
{
	assert(obj);
	return (const char *)obj->components;
}

/**
 * \brief get size of #snd_ctl_event_t
 * \return size in bytes
 */
size_t snd_ctl_event_sizeof()
{
	return sizeof(snd_ctl_event_t);
}

/**
 * \brief allocate an invalid #snd_ctl_event_t using standard malloc
 * \param ptr returned pointer
 * \return 0 on success otherwise negative error code
 */
int snd_ctl_event_malloc(snd_ctl_event_t **ptr)
{
	assert(ptr);
	*ptr = calloc(1, sizeof(snd_ctl_event_t));
	if (!*ptr)
		return -ENOMEM;
	return 0;
}

/**
 * \brief frees a previously allocated #snd_ctl_event_t
 * \param obj pointer to object to free
 */
void snd_ctl_event_free(snd_ctl_event_t *obj)
{
	free(obj);
}

/**
 * \brief clear given #snd_ctl_event_t object
 * \param obj pointer to object to clear
 */
void snd_ctl_event_clear(snd_ctl_event_t *obj)
{
	memset(obj, 0, sizeof(snd_ctl_event_t));
}

/**
 * \brief copy one #snd_ctl_event_t to another
 * \param dst pointer to destination
 * \param src pointer to source
 */
void snd_ctl_event_copy(snd_ctl_event_t *dst, const snd_ctl_event_t *src)
{
	assert(dst && src);
	*dst = *src;
}

/**
 * \brief Get type of a CTL event
 * \param obj CTL event
 * \return CTL event type
 */
snd_ctl_event_type_t snd_ctl_event_get_type(const snd_ctl_event_t *obj)
{
	assert(obj);
	return obj->type;
}

/**
 * \brief get size of #snd_ctl_elem_list_t.
 * \return size in bytes
 */
size_t snd_ctl_elem_list_sizeof()
{
	return sizeof(snd_ctl_elem_list_t);
}

/**
 * \brief allocate a #snd_ctl_elem_list_t using standard malloc.
 *
 * The memory can be released using snd_ctl_elem_list_free().
 *
 * \param ptr returned pointer
 * \return 0 on success otherwise negative error code
 */
int snd_ctl_elem_list_malloc(snd_ctl_elem_list_t **ptr)
{
	assert(ptr);
	*ptr = calloc(1, sizeof(snd_ctl_elem_list_t));
	if (!*ptr)
		return -ENOMEM;
	return 0;
}

/**
 * \brief frees a previously allocated #snd_ctl_elem_list_t.
 *
 * Release memory previously allocated using
 * snd_ctl_elem_list_malloc().
 *
 * If you used snd_ctl_elem_list_alloc_space() on the list, you must
 * use snd_ctl_elem_list_free_space() \em before calling this
 * function.
 *
 * \param obj pointer to object to free
 */
void snd_ctl_elem_list_free(snd_ctl_elem_list_t *obj)
{
	free(obj);
}

/**
 * \brief Clear given #snd_ctl_elem_list_t object.
 *
 * This will make the stored identifiers inaccessible without freeing
 * their space.
 *
 * \warning The element identifier space cannot be freed after calling
 *          this function. Therefore, snd_ctl_elem_list_free_space()
 *          must be called in advance.
 *
 * \param obj pointer to object to clear
 */
void snd_ctl_elem_list_clear(snd_ctl_elem_list_t *obj)
{
	memset(obj, 0, sizeof(snd_ctl_elem_list_t));
}

/**
 * \brief copy one #snd_ctl_elem_list_t to another.
 *
 * This performs a shallow copy. That means the both lists will share
 * the same space for the elements.  The elements will not be copied.
 *
 * \param dst pointer to destination
 * \param src pointer to source
 */
void snd_ctl_elem_list_copy(snd_ctl_elem_list_t *dst, const snd_ctl_elem_list_t *src)
{
	assert(dst && src);
	*dst = *src;
}

/**
 * \brief Set index of first wanted CTL element identifier in a CTL element identifiers list
 * \param obj CTL element identifiers list
 * \param val index of CTL element to put at position 0 of list
 */
void snd_ctl_elem_list_set_offset(snd_ctl_elem_list_t *obj, unsigned int val)
{
	assert(obj);
	obj->offset = val;
}

/**
 * \brief Get number of used entries in CTL element identifiers list
 *
 * This function returns how many entries are actually filled with
 * useful information.
 *
 * See also snd_ctl_elem_list_get_count().
 *
 * \param obj CTL element identifier list
 * \return number of used entries
 */
unsigned int snd_ctl_elem_list_get_used(const snd_ctl_elem_list_t *obj)
{
	assert(obj);
	return obj->used;
}

/**
 * \brief Get total count of elements present in CTL device
 *
 * This function returns how many entries were allocated using
 * snd_ctl_elem_list_alloc_space(). This information is present after
 * snd_ctl_elem_list() was called.
 *
 * See also snd_ctl_elem_list_get_used().
 *
 * \param obj CTL element identifier list
 * \return total number of elements
 */
unsigned int snd_ctl_elem_list_get_count(const snd_ctl_elem_list_t *obj)
{
	assert(obj);
	return obj->count;
}

/**
 * \brief Get CTL element identifier for an entry of a CTL element identifiers list
 * \param obj CTL element identifier list
 * \param idx Index of entry
 * \param ptr Pointer to returned CTL element identifier
 */
void snd_ctl_elem_list_get_id(const snd_ctl_elem_list_t *obj, unsigned int idx, snd_ctl_elem_id_t *ptr)
{
	assert(obj && ptr);
	assert(idx < obj->used);
	*ptr = obj->pids[idx];
}

/**
 * \brief Get CTL element numeric identifier for an entry of a CTL element identifiers list
 * \param obj CTL element identifier list
 * \param idx Index of entry
 * \return CTL element numeric identifier
 */
unsigned int snd_ctl_elem_list_get_numid(const snd_ctl_elem_list_t *obj, unsigned int idx)
{
	assert(obj);
	assert(idx < obj->used);
	return obj->pids[idx].numid;
}

/**
 * \brief Get interface part of CTL element identifier for an entry of a CTL element identifiers list
 * \param obj CTL element identifier list
 * \param idx Index of entry
 * \return CTL element related interface
 */
snd_ctl_elem_iface_t snd_ctl_elem_list_get_interface(const snd_ctl_elem_list_t *obj, unsigned int idx)
{
	assert(obj);
	assert(idx < obj->used);
	return obj->pids[idx].iface;
}

/**
 * \brief Get the device part of CTL element identifier for an entry of a CTL element identifiers list
 * \param obj CTL element identifier list
 * \param idx Index of entry
 * \return CTL element related device
 */
unsigned int snd_ctl_elem_list_get_device(const snd_ctl_elem_list_t *obj, unsigned int idx)
{
	assert(obj);
	assert(idx < obj->used);
	return obj->pids[idx].device;
}

/**
 * \brief Get subdevice part of CTL element identifier for an entry of a CTL element identifiers list
 * \param obj CTL element identifier list
 * \param idx Index of entry
 * \return CTL element related subdevice
 */
unsigned int snd_ctl_elem_list_get_subdevice(const snd_ctl_elem_list_t *obj, unsigned int idx)
{
	assert(obj);
	assert(idx < obj->used);
	return obj->pids[idx].subdevice;
}

/**
 * \brief Get name part of CTL element identifier for an entry of a CTL element identifiers list
 * \param obj CTL element identifier list
 * \param idx Index of entry
 * \return CTL element name
 */
const char *snd_ctl_elem_list_get_name(const snd_ctl_elem_list_t *obj, unsigned int idx)
{
	assert(obj);
	assert(idx < obj->used);
	return (const char *)obj->pids[idx].name;
}

/**
 * \brief Get index part of CTL element identifier for an entry of a CTL element identifiers list
 * \param obj CTL element identifier list
 * \param idx Index of entry
 * \return CTL element index
 */
unsigned int snd_ctl_elem_list_get_index(const snd_ctl_elem_list_t *obj, unsigned int idx)
{
	assert(obj);
	assert(idx < obj->used);
	return obj->pids[idx].index;
}

/**
 * \brief get size of #snd_ctl_elem_info_t
 * \return size in bytes
 */
size_t snd_ctl_elem_info_sizeof()
{
	return sizeof(snd_ctl_elem_info_t);
}

/**
 * \brief allocate an invalid #snd_ctl_elem_info_t using standard malloc
 * \param ptr returned pointer
 * \return 0 on success otherwise negative error code
 */
int snd_ctl_elem_info_malloc(snd_ctl_elem_info_t **ptr)
{
	assert(ptr);
	*ptr = calloc(1, sizeof(snd_ctl_elem_info_t));
	if (!*ptr)
		return -ENOMEM;
	return 0;
}

/**
 * \brief frees a previously allocated #snd_ctl_elem_info_t
 * \param obj pointer to object to free
 */
void snd_ctl_elem_info_free(snd_ctl_elem_info_t *obj)
{
	free(obj);
}

/**
 * \brief clear given #snd_ctl_elem_info_t object
 * \param obj pointer to object to clear
 */
void snd_ctl_elem_info_clear(snd_ctl_elem_info_t *obj)
{
	memset(obj, 0, sizeof(snd_ctl_elem_info_t));
}

/**
 * \brief copy one #snd_ctl_elem_info_t to another
 * \param dst pointer to destination
 * \param src pointer to source
 */
void snd_ctl_elem_info_copy(snd_ctl_elem_info_t *dst, const snd_ctl_elem_info_t *src)
{
	assert(dst && src);
	*dst = *src;
}

/**
 * \brief Get type from a CTL element id/info
 * \param obj CTL element id/info
 * \return CTL element content type
 */
snd_ctl_elem_type_t snd_ctl_elem_info_get_type(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	return obj->type;
}

/**
 * \brief Get info about readability from a CTL element id/info
 * \param obj CTL element id/info
 * \return 0 if element is not readable, 1 if element is readable
 */
int snd_ctl_elem_info_is_readable(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	return !!(obj->access & SNDRV_CTL_ELEM_ACCESS_READ);
}

/**
 * \brief Get info about writability from a CTL element id/info
 * \param obj CTL element id/info
 * \return 0 if element is not writable, 1 if element is not writable
 */
int snd_ctl_elem_info_is_writable(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	return !!(obj->access & SNDRV_CTL_ELEM_ACCESS_WRITE);
}

/**
 * \brief Get info about notification feasibility from a CTL element id/info
 * \param obj CTL element id/info
 * \return 0 if all element value changes are notified to subscribed applications, 1 otherwise
 */
int snd_ctl_elem_info_is_volatile(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	return !!(obj->access & SNDRV_CTL_ELEM_ACCESS_VOLATILE);
}

/**
 * \brief Get info about status from a CTL element id/info
 * \param obj CTL element id/info
 * \return 0 if element value is not active, 1 if is active
 */
int snd_ctl_elem_info_is_inactive(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	return !!(obj->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE);
}

/**
 * \brief Get info whether an element is locked
 * \param obj CTL element id/info
 * \return 0 if element value is currently changeable, 1 if it's locked by another application
 */
int snd_ctl_elem_info_is_locked(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	return !!(obj->access & SNDRV_CTL_ELEM_ACCESS_LOCK);
}

/**
 * \brief Get info if I own an element
 * \param obj CTL element id/info
 * \return 0 if element value is currently changeable, 1 if it's locked by another application
 */
int snd_ctl_elem_info_is_owner(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	return !!(obj->access & SNDRV_CTL_ELEM_ACCESS_OWNER);
}

/**
 * \brief Get info if it's a user element
 * \param obj CTL element id/info
 * \return 0 if element value is a system element, 1 if it's a user-created element
 */
int snd_ctl_elem_info_is_user(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	return !!(obj->access & SNDRV_CTL_ELEM_ACCESS_USER);
}

/**
 * \brief Get info about TLV readability from a CTL element id/info
 * \param obj CTL element id/info
 * \return 0 if element's TLV is not readable, 1 if element's TLV is readable
 */
int snd_ctl_elem_info_is_tlv_readable(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	return !!(obj->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ);
}

/**
 * \brief Get info about TLV writeability from a CTL element id/info
 * \param obj CTL element id/info
 * \return 0 if element's TLV is not writable, 1 if element's TLV is writable
 */
int snd_ctl_elem_info_is_tlv_writable(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	return !!(obj->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE);
}

/**
 * \brief Get info about TLV command possibility from a CTL element id/info
 * \param obj CTL element id/info
 * \return 0 if element's TLV command is not possible, 1 if element's TLV command is supported
 */
int snd_ctl_elem_info_is_tlv_commandable(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	return !!(obj->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND);
}

/**
 * \brief (DEPRECATED) Get info about values passing policy from a CTL element value
 * \param obj CTL element id/info
 * \return 0 if element value need to be passed by contents, 1 if need to be passed with a pointer
 */
int snd_ctl_elem_info_is_indirect(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	return 0;
}
link_warning(snd_ctl_elem_info_is_indirect, "Warning: snd_ctl_elem_info_is_indirect is deprecated, do not use it");

/**
 * \brief Get owner of a locked element
 * \param obj CTL element id/info
 * \return value entries count
 */
pid_t snd_ctl_elem_info_get_owner(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	return obj->owner;
}

/**
 * \brief Get number of value entries from a CTL element id/info
 * \param obj CTL element id/info
 * \return value entries count
 */
unsigned int snd_ctl_elem_info_get_count(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	return obj->count;
}

/**
 * \brief Get minimum value from a #SND_CTL_ELEM_TYPE_INTEGER CTL element id/info
 * \param obj CTL element id/info
 * \return Minimum value
 */
long snd_ctl_elem_info_get_min(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	assert(obj->type == SND_CTL_ELEM_TYPE_INTEGER);
	return obj->value.integer.min;
}

/**
 * \brief Get maximum value from a #SND_CTL_ELEM_TYPE_INTEGER CTL element id/info
 * \param obj CTL element id/info
 * \return Maximum value
 */
long snd_ctl_elem_info_get_max(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	assert(obj->type == SND_CTL_ELEM_TYPE_INTEGER);
	return obj->value.integer.max;
}

/**
 * \brief Get value step from a #SND_CTL_ELEM_TYPE_INTEGER CTL element id/info
 * \param obj CTL element id/info
 * \return Step
 */
long snd_ctl_elem_info_get_step(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	assert(obj->type == SND_CTL_ELEM_TYPE_INTEGER);
	return obj->value.integer.step;
}

/**
 * \brief Get minimum value from a #SND_CTL_ELEM_TYPE_INTEGER64 CTL element id/info
 * \param obj CTL element id/info
 * \return Minimum value
 */
long long snd_ctl_elem_info_get_min64(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	assert(obj->type == SND_CTL_ELEM_TYPE_INTEGER64);
	return obj->value.integer64.min;
}

/**
 * \brief Get maximum value from a #SND_CTL_ELEM_TYPE_INTEGER64 CTL element id/info
 * \param obj CTL element id/info
 * \return Maximum value
 */
long long snd_ctl_elem_info_get_max64(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	assert(obj->type == SND_CTL_ELEM_TYPE_INTEGER64);
	return obj->value.integer64.max;
}

/**
 * \brief Get value step from a #SND_CTL_ELEM_TYPE_INTEGER64 CTL element id/info
 * \param obj CTL element id/info
 * \return Step
 */
long long snd_ctl_elem_info_get_step64(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	assert(obj->type == SND_CTL_ELEM_TYPE_INTEGER64);
	return obj->value.integer64.step;
}

/**
 * \brief Get number of items available from a #SND_CTL_ELEM_TYPE_ENUMERATED CTL element id/info
 * \param obj CTL element id/info
 * \return items count
 */
unsigned int snd_ctl_elem_info_get_items(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	assert(obj->type == SND_CTL_ELEM_TYPE_ENUMERATED);
	return obj->value.enumerated.items;
}

/**
 * \brief Select item in a #SND_CTL_ELEM_TYPE_ENUMERATED CTL element id/info
 * \param obj CTL element id/info
 * \param val item number
 */
void snd_ctl_elem_info_set_item(snd_ctl_elem_info_t *obj, unsigned int val)
{
	assert(obj);
	obj->value.enumerated.item = val;
}

/**
 * \brief Get name for selected item in a #SND_CTL_ELEM_TYPE_ENUMERATED CTL element id/info
 * \param obj CTL element id/info
 * \return name of chosen item
 */
const char *snd_ctl_elem_info_get_item_name(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	assert(obj->type == SND_CTL_ELEM_TYPE_ENUMERATED);
	return obj->value.enumerated.name;
}

/**
 * \brief Get count of dimensions for given element
 * \param obj CTL element id/info
 * \return zero value if no dimensions are defined, otherwise positive value with count of dimensions
 *
 * \deprecated	Since 1.1.5
 * #snd_ctl_elem_info_get_dimensions is deprecated without any replacement.
 */
#ifndef DOXYGEN
EXPORT_SYMBOL int INTERNAL(snd_ctl_elem_info_get_dimensions)(const snd_ctl_elem_info_t *obj ATTRIBUTE_UNUSED)
#else
int snd_ctl_elem_info_get_dimensions(const snd_ctl_elem_info_t *obj)
#endif
{
#if 0 /* deprecated */
	int i;

	assert(obj);
	for (i = 3; i >= 0; i--)
		if (obj->dimen.d[i])
			break;
	return i + 1;
#else
	return -EINVAL;
#endif
}
use_default_symbol_version(__snd_ctl_elem_info_get_dimensions, snd_ctl_elem_info_get_dimensions, ALSA_0.9.3);

/**
 * \brief Get specified of dimension width for given element
 * \param obj CTL element id/info
 * \param idx The dimension index
 * \return zero value if no dimension width is defined, otherwise positive value with with of specified dimension
 *
 * \deprecated	Since 1.1.5
 * #snd_ctl_elem_info_get_dimension is deprecated without any replacement.
 */
#ifndef DOXYGEN
EXPORT_SYMBOL int INTERNAL(snd_ctl_elem_info_get_dimension)(const snd_ctl_elem_info_t *obj ATTRIBUTE_UNUSED, unsigned int idx ATTRIBUTE_UNUSED)
#else
int snd_ctl_elem_info_get_dimension(const snd_ctl_elem_info_t *obj, unsigned int idx)
#endif
{
#if 0 /* deprecated */
	assert(obj);
	if (idx > 3)
		return 0;
	return obj->dimen.d[idx];
#else /* deprecated */
	return -EINVAL;
#endif /* deprecated */
}
use_default_symbol_version(__snd_ctl_elem_info_get_dimension, snd_ctl_elem_info_get_dimension, ALSA_0.9.3);

/**
 * \brief Set width to a specified dimension level of given element information.
 * \param info Information of an element.
 * \param dimension Dimension width for each level by member unit.
 * \return Zero on success, otherwise a negative error code.
 *
 * \par Errors:
 * <dl>
 * <dt>-EINVAL
 * <dd>Invalid arguments are given as parameters.
 * </dl>
 *
 * \par Compatibility:
 * This function is added in version 1.1.2.
 *
 * \deprecated Since 1.1.5
 * #snd_ctl_elem_info_set_dimension is deprecated without any replacement.
 */
int snd_ctl_elem_info_set_dimension(snd_ctl_elem_info_t *info ATTRIBUTE_UNUSED,
				    const int dimension[4] ATTRIBUTE_UNUSED)
{
#if 0 /* deprecated */
	unsigned int i;

	if (info == NULL)
		return -EINVAL;

	for (i = 0; i < ARRAY_SIZE(info->dimen.d); i++) {
		if (dimension[i] < 0)
			return -EINVAL;

		info->dimen.d[i] = dimension[i];
	}

	return 0;
#else /* deprecated */
	return -EINVAL;
#endif /* deprecated */
}

/**
 * \brief Get CTL element identifier of a CTL element id/info
 * \param obj CTL element id/info
 * \param ptr Pointer to returned CTL element identifier
 */
void snd_ctl_elem_info_get_id(const snd_ctl_elem_info_t *obj, snd_ctl_elem_id_t *ptr)
{
	assert(obj && ptr);
	*ptr = obj->id;
}

/**
 * \brief Get element numeric identifier of a CTL element id/info
 * \param obj CTL element id/info
 * \return element numeric identifier
 */
unsigned int snd_ctl_elem_info_get_numid(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	return obj->id.numid;
}

/**
 * \brief Get interface part of CTL element identifier of a CTL element id/info
 * \param obj CTL element id/info
 * \return interface part of element identifier
 */
snd_ctl_elem_iface_t snd_ctl_elem_info_get_interface(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	return obj->id.iface;
}

/**
 * \brief Get device part of CTL element identifier of a CTL element id/info
 * \param obj CTL element id/info
 * \return device part of element identifier
 */
unsigned int snd_ctl_elem_info_get_device(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	return obj->id.device;
}

/**
 * \brief Get subdevice part of CTL element identifier of a CTL element id/info
 * \param obj CTL element id/info
 * \return subdevice part of element identifier
 */
unsigned int snd_ctl_elem_info_get_subdevice(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	return obj->id.subdevice;
}

/**
 * \brief Get name part of CTL element identifier of a CTL element id/info
 * \param obj CTL element id/info
 * \return name part of element identifier
 */
const char *snd_ctl_elem_info_get_name(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	return (const char *)obj->id.name;
}

/**
 * \brief Get index part of CTL element identifier of a CTL element id/info
 * \param obj CTL element id/info
 * \return index part of element identifier
 */
unsigned int snd_ctl_elem_info_get_index(const snd_ctl_elem_info_t *obj)
{
	assert(obj);
	return obj->id.index;
}

/**
 * \brief Set CTL element identifier of a CTL element id/info
 * \param obj CTL element id/info
 * \param ptr CTL element identifier
 */
void snd_ctl_elem_info_set_id(snd_ctl_elem_info_t *obj, const snd_ctl_elem_id_t *ptr)
{
	assert(obj && ptr);
	obj->id = *ptr;
}

/**
 * \brief Set element numeric identifier of a CTL element id/info
 * \param obj CTL element id/info
 * \param val element numeric identifier
 */
void snd_ctl_elem_info_set_numid(snd_ctl_elem_info_t *obj, unsigned int val)
{
	assert(obj);
	obj->id.numid = val;
}

/**
 * \brief Set interface part of CTL element identifier of a CTL element id/info
 * \param obj CTL element id/info
 * \param val interface part of element identifier
 */
void snd_ctl_elem_info_set_interface(snd_ctl_elem_info_t *obj, snd_ctl_elem_iface_t val)
{
	assert(obj);
	obj->id.iface = val;
}

/**
 * \brief Set device part of CTL element identifier of a CTL element id/info
 * \param obj CTL element id/info
 * \param val device part of element identifier
 */
void snd_ctl_elem_info_set_device(snd_ctl_elem_info_t *obj, unsigned int val)
{
	assert(obj);
	obj->id.device = val;
}

/**
 * \brief Set subdevice part of CTL element identifier of a CTL element id/info
 * \param obj CTL element id/info
 * \param val subdevice part of element identifier
 */
void snd_ctl_elem_info_set_subdevice(snd_ctl_elem_info_t *obj, unsigned int val)
{
	assert(obj);
	obj->id.subdevice = val;
}

/**
 * \brief Set name part of CTL element identifier of a CTL element id/info
 * \param obj CTL element id/info
 * \param val name part of element identifier
 */
void snd_ctl_elem_info_set_name(snd_ctl_elem_info_t *obj, const char *val)
{
	assert(obj);
	snd_strlcpy((char *)obj->id.name, val, sizeof(obj->id.name));
}

/**
 * \brief Set index part of CTL element identifier of a CTL element id/info
 * \param obj CTL element id/info
 * \param val index part of element identifier
 */
void snd_ctl_elem_info_set_index(snd_ctl_elem_info_t *obj, unsigned int val)
{
	assert(obj);
	obj->id.index = val;
}

/**
 * \brief Set readability/writeability parameter of a CTL element id/info
 * \param obj CTL element id/info
 * \param rval readability part of element identifier
 * \param wval writeability part of element identifier
 */
void snd_ctl_elem_info_set_read_write(snd_ctl_elem_info_t *obj, int rval, int wval)
{
	assert(obj);
	obj->access = (obj->access & ~SNDRV_CTL_ELEM_ACCESS_READWRITE) |
				(rval ? SNDRV_CTL_ELEM_ACCESS_READ : 0) |
				(wval ? SNDRV_CTL_ELEM_ACCESS_WRITE : 0);
}

/**
 * \brief Set TLV readability/writeability parameter of a CTL element id/info
 * \param obj CTL element id/info
 * \param rval TLV readability part of element identifier
 * \param wval TLV writeability part of element identifier
 */
void snd_ctl_elem_info_set_tlv_read_write(snd_ctl_elem_info_t *obj, int rval, int wval)
{
	assert(obj);
	obj->access = (obj->access & ~SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) |
				(rval ? SNDRV_CTL_ELEM_ACCESS_TLV_READ : 0) |
				(wval ? SNDRV_CTL_ELEM_ACCESS_TLV_WRITE : 0);
}

/**
 * \brief Set inactive parameter of a CTL element id/info
 * \param obj CTL element id/info
 * \param val inactive part of element identifier
 */
void snd_ctl_elem_info_set_inactive(snd_ctl_elem_info_t *obj, int val)
{
	assert(obj);
	obj->access = (obj->access & ~SNDRV_CTL_ELEM_ACCESS_INACTIVE) |
				(val ? SNDRV_CTL_ELEM_ACCESS_INACTIVE : 0);
}

/**
 * \brief Get size of data structure for an element.
 * \return Size in bytes.
 */
size_t snd_ctl_elem_value_sizeof()
{
	return sizeof(snd_ctl_elem_value_t);
}

/**
 * \brief Allocate an invalid #snd_ctl_elem_value_t on the heap.
 *
 * Allocate space for a value object on the heap. The allocated memory
 * must be freed using snd_ctl_elem_value_free().
 *
 * See snd_ctl_elem_value_t for details.
 *
 * \param ptr Pointer to a snd_ctl_elem_value_t pointer. The address
 *            of the allocated space will be returned here.
 * \return 0 on success, otherwise a negative error code.
 */
int snd_ctl_elem_value_malloc(snd_ctl_elem_value_t **ptr)
{
	assert(ptr);
	*ptr = calloc(1, sizeof(snd_ctl_elem_value_t));
	if (!*ptr)
		return -ENOMEM;
	return 0;
}

/**
 * \brief Free an #snd_ctl_elem_value_t previously allocated using
 *        snd_ctl_elem_value_malloc().
 *
 * \param obj Pointer to the snd_ctl_elem_value_t.
 */
void snd_ctl_elem_value_free(snd_ctl_elem_value_t *obj)
{
	free(obj);
}

/**
 * \brief Clear given data of an element.
 *
 * See snd_ctl_elem_value_t for details.
 *
 * \param obj Data of an element.
 */
void snd_ctl_elem_value_clear(snd_ctl_elem_value_t *obj)
{
	memset(obj, 0, sizeof(snd_ctl_elem_value_t));
}

/**
 * \brief Bitwise copy of a snd_ctl_elem_value_t value.
 * \param dst Pointer to destination.
 * \param src Pointer to source.
 */
void snd_ctl_elem_value_copy(snd_ctl_elem_value_t *dst,
			     const snd_ctl_elem_value_t *src)
{
	assert(dst && src);
	*dst = *src;
}

/**
 * \brief Compare two snd_ctl_elem_value_t values, bytewise.
 *
 * \param left First value.
 * \param right Second value.
 * \return 0 on match, less than or greater than otherwise, see memcmp(3).
 */
int snd_ctl_elem_value_compare(snd_ctl_elem_value_t *left,
			       const snd_ctl_elem_value_t *right)
{
	assert(left && right);
	return memcmp(left, right, sizeof(*left));
}

/**
 * \brief Get the element identifier from the given element value.
 *
 * See snd_ctl_elem_value_t for more details.
 *
 * \param obj The element value.
 * \param ptr Pointer to an identifier object. The identifier is
 *            stored there.
 */
void snd_ctl_elem_value_get_id(const snd_ctl_elem_value_t *obj, snd_ctl_elem_id_t *ptr)
{
	assert(obj && ptr);
	*ptr = obj->id;
}

/**
 * \brief Get the identifiers 'numid' part from the given element value.
 *
 * See snd_ctl_elem_value_t for more details.
 *
 * \param obj The element value.
 * \return The numid.
 */
unsigned int snd_ctl_elem_value_get_numid(const snd_ctl_elem_value_t *obj)
{
	assert(obj);
	return obj->id.numid;
}

/**
 * \brief Get the identifiers 'interface' part from the given element value.
 *
 * See snd_ctl_elem_value_t for more details.
 *
 * \param obj The element value.
 * \return The interface part of element identifier.
 */
snd_ctl_elem_iface_t snd_ctl_elem_value_get_interface(const snd_ctl_elem_value_t *obj)
{
	assert(obj);
	return obj->id.iface;
}

/**
 * \brief Get the identifiers 'device' part from the given element value.
 *
 * See snd_ctl_elem_value_t for more details.
 *
 * \param obj The element value.
 * \return The device part of element identifier.
 */
unsigned int snd_ctl_elem_value_get_device(const snd_ctl_elem_value_t *obj)
{
	assert(obj);
	return obj->id.device;
}

/**
 * \brief Get the identifiers 'subdevice' part from the given element value.
 *
 * See snd_ctl_elem_value_t for more details.
 *
 * \param obj The element value.
 * \return The subdevice part of element identifier.
 */
unsigned int snd_ctl_elem_value_get_subdevice(const snd_ctl_elem_value_t *obj)
{
	assert(obj);
	return obj->id.subdevice;
}

/**
 * \brief Get the identifiers 'name' part from the given element value.
 *
 * See snd_ctl_elem_value_t for more details.
 *
 * \param obj The element value.
 * \return The "name" part of element identifier.
 */
const char *snd_ctl_elem_value_get_name(const snd_ctl_elem_value_t *obj)
{
	assert(obj);
	return (const char *)obj->id.name;
}

/**
 * \brief Get the identifiers 'index' part from the given element value.
 *
 * See snd_ctl_elem_value_t for more details.
 *
 * \param obj The element value.
 * \return The index part of element identifier.
 */
unsigned int snd_ctl_elem_value_get_index(const snd_ctl_elem_value_t *obj)
{
	assert(obj);
	return obj->id.index;
}


/**
 * \brief Set the element identifier within the given element value.
 *
 * See snd_ctl_elem_value_t for more details.
 *
 * \param obj The element value.
 * \param ptr The new identifier.
 */
void snd_ctl_elem_value_set_id(snd_ctl_elem_value_t *obj, const snd_ctl_elem_id_t *ptr)
{
	assert(obj && ptr);
	obj->id = *ptr;
}

/**
 * \brief Set the identifiers 'numid' part within the given element value.
 *
 * See snd_ctl_elem_value_t for more details.
 *
 * \param obj The element value.
 * \param val The new numid.
 */
void snd_ctl_elem_value_set_numid(snd_ctl_elem_value_t *obj, unsigned int val)
{
	assert(obj);
	obj->id.numid = val;
}

/**
 * \brief Set the identifiers 'interface' part within the given element value.
 *
 * See snd_ctl_elem_value_t for more details.
 *
 * \param obj The element value.
 * \param val The new interface.
 */
void snd_ctl_elem_value_set_interface(snd_ctl_elem_value_t *obj, snd_ctl_elem_iface_t val)
{
	assert(obj);
	obj->id.iface = val;
}

/**
 * \brief Set the identifiers 'device' part within the given element value.
 *
 * See snd_ctl_elem_value_t for more details.
 *
 * \param obj The element value.
 * \param val The new device.
 */
void snd_ctl_elem_value_set_device(snd_ctl_elem_value_t *obj, unsigned int val)
{
	assert(obj);
	obj->id.device = val;
}

/**
 * \brief Set the identifiers 'subdevice' part within the given element value.
 *
 * See snd_ctl_elem_value_t for more details.
 *
 * \param obj The element value.
 * \param val The new subdevice.
 */
void snd_ctl_elem_value_set_subdevice(snd_ctl_elem_value_t *obj, unsigned int val)
{
	assert(obj);
	obj->id.subdevice = val;
}

/**
 * \brief Set the identifiers 'name' part within the given element value.
 *
 * See snd_ctl_elem_value_t for more details.
 *
 * \param obj The element value.
 * \param val The new name.
 */
void snd_ctl_elem_value_set_name(snd_ctl_elem_value_t *obj, const char *val)
{
	assert(obj);
	snd_strlcpy((char *)obj->id.name, val, sizeof(obj->id.name));
}

/**
 * \brief Set the identifiers 'index' part within the given element value.
 *
 * See snd_ctl_elem_value_t for more details.
 *
 * \param obj The element value.
 * \param val The new index.
 */
void snd_ctl_elem_value_set_index(snd_ctl_elem_value_t *obj, unsigned int val)
{
	assert(obj);
	obj->id.index = val;
}

/**
 * \brief Get an element members value.
 *
 * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_BOOLEAN. It
 * returns the value of one member. See \ref snd_ctl_elem_value_t and \ref
 * control for more details.
 *
 * \param obj The element value object
 * \param idx The index of the member.
 * \return The members value.
 */
int snd_ctl_elem_value_get_boolean(const snd_ctl_elem_value_t *obj, unsigned int idx)
{
	assert(obj);
	assert(idx < ARRAY_SIZE(obj->value.integer.value));
	return obj->value.integer.value[idx];
}

/**
 * \brief Get an element members value.
 *
 * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_INTEGER. It
 * returns the value of one member. See \ref snd_ctl_elem_value_t and \ref
 * control for more details.
 *
 * \param obj The element value object.
 * \param idx The index of the member.
 * \return The members value.
 */
long snd_ctl_elem_value_get_integer(const snd_ctl_elem_value_t *obj, unsigned int idx)
{
	assert(obj);
	assert(idx < ARRAY_SIZE(obj->value.integer.value));
	return obj->value.integer.value[idx];
}

/**
 * \brief Get an element members value.
 *
 * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_INTEGER64. It
 * returns the value of one member. See \ref snd_ctl_elem_value_t and \ref
 * control for more details.
 *
 * \param obj The element value object.
 * \param idx The index of the member.
 * \return The members value.
 */
long long snd_ctl_elem_value_get_integer64(const snd_ctl_elem_value_t *obj, unsigned int idx)
{
	assert(obj);
	assert(idx < ARRAY_SIZE(obj->value.integer64.value));
	return obj->value.integer64.value[idx];
}

/**
 * \brief Get an element members value.
 *
 * Use this function if the element is of type
 * SNDRV_CTL_ELEM_TYPE_ENUMERATED. It returns the index of the active item. See
 * \ref snd_ctl_elem_value_t and \ref control for more details.
 *
 * \param obj The element value object.
 * \param idx The index of the requested member.
 * \return The index of the active item.
 */
unsigned int snd_ctl_elem_value_get_enumerated(const snd_ctl_elem_value_t *obj, unsigned int idx)
{
	assert(obj);
	assert(idx < ARRAY_SIZE(obj->value.enumerated.item));
	return obj->value.enumerated.item[idx];
}

/**
 * \brief Get an element members value.
 *
 * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_BYTE. It
 * returns the value of one member. See \ref snd_ctl_elem_value_t and \ref
 * control for more details.
 *
 * \param obj The element value object.
 * \param idx The index of the member.
 * \return The members value.
 */
unsigned char snd_ctl_elem_value_get_byte(const snd_ctl_elem_value_t *obj, unsigned int idx)
{
	assert(obj);
	assert(idx < ARRAY_SIZE(obj->value.bytes.data));
	return obj->value.bytes.data[idx];
}

/**
 * \brief Set an element members value.
 *
 * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_BOOLEAN. It
 * sets the value of one member. See \ref snd_ctl_elem_value_t and \ref control
 * for more details.
 *
 * \param obj The element value object.
 * \param idx The index of the member.
 * \param val The new value.
 */
void snd_ctl_elem_value_set_boolean(snd_ctl_elem_value_t *obj, unsigned int idx, long val)
{
	assert(obj);
	assert(idx < ARRAY_SIZE(obj->value.integer.value));
	obj->value.integer.value[idx] = val;
}

/**
 * \brief Set an element members value.
 *
 * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_INTEGER. It
 * sets the value of one member. See \ref snd_ctl_elem_value_t and \ref control
 * for more details.
 *
 * \param obj The element value object.
 * \param idx The index of the member.
 * \param val The new value.
 */
void snd_ctl_elem_value_set_integer(snd_ctl_elem_value_t *obj, unsigned int idx, long val)
{
	assert(obj);
	assert(idx < ARRAY_SIZE(obj->value.integer.value));
	obj->value.integer.value[idx] = val;
}

/**
 * \brief Set an element members value.
 *
 * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_INTEGER64. It
 * sets the value of one member. See \ref snd_ctl_elem_value_t and \ref control
 * for more details.
 *
 * \param obj The element value object.
 * \param idx The index of the member.
 * \param val The new value.
 */
void snd_ctl_elem_value_set_integer64(snd_ctl_elem_value_t *obj, unsigned int idx, long long val)
{
	assert(obj);
	assert(idx < ARRAY_SIZE(obj->value.integer64.value));
	obj->value.integer64.value[idx] = val;
}

/**
 * \brief Set an element members value.
 *
 * Use this function if the element is of type
 * SNDRV_CTL_ELEM_TYPE_ENUMERATED. It activates the specified item. See \ref
 * snd_ctl_elem_value_t and \ref control for more details.
 *
 * \param obj The element value object.
 * \param idx The index of the requested member.
 * \param val The new index of the item to be activated.
 */
void snd_ctl_elem_value_set_enumerated(snd_ctl_elem_value_t *obj, unsigned int idx, unsigned int val)
{
	assert(obj);
	assert(idx < ARRAY_SIZE(obj->value.enumerated.item));
	obj->value.enumerated.item[idx] = val;
}

/**
 * \brief Set an element members value.
 *
 * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_BYTE. It
 * sets the value of one member. See \ref snd_ctl_elem_value_t and \ref control
 * for more details.
 *
 * \param obj The element value object.
 * \param idx The index of the member.
 * \param val The new value.
 */
void snd_ctl_elem_value_set_byte(snd_ctl_elem_value_t *obj, unsigned int idx, unsigned char val)
{
	assert(obj);
	assert(idx < ARRAY_SIZE(obj->value.bytes.data));
	obj->value.bytes.data[idx] = val;
}

/**
 * \brief Replace the data stored within the element.
 *
 * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_BYTES. It
 * replaces the data stored in the element. Note that "bytes" elements don't
 * have members. They have only one single block of data.
 *
 * See \ref snd_ctl_elem_value_t and \ref control for more details.
 *
 * \param obj The element value object.
 * \param data Pointer to the new data.
 * \param size The size of the new data, in bytes.
 */
void snd_ctl_elem_set_bytes(snd_ctl_elem_value_t *obj, void *data, size_t size)
{
	assert(obj);
	assert(size <= ARRAY_SIZE(obj->value.bytes.data));
	memcpy(obj->value.bytes.data, data, size);
}

/**
 * \brief Get the data stored within the element.
 *
 * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_BYTES. It
 * returns the data stored in the element. Note that "bytes" elements don't have
 * members. They have only one single block of data.
 *
 * See \ref snd_ctl_elem_value_t and \ref control for more details.
 *
 * \param obj The element value object.
 * \return Pointer to the elements data.
 */
const void * snd_ctl_elem_value_get_bytes(const snd_ctl_elem_value_t *obj)
{
	assert(obj);
	return obj->value.bytes.data;
}

/**
 * \brief Get an elements IEC958 data.
 *
 * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_IEC958.  Note that
 * "IEC958" elements don't have members. They have only one single
 * IEC958 information block.
 *
 * See \ref snd_ctl_elem_value_t and \ref control for more details.
 *
 * \param obj The element value object.
 * \param ptr Pointer to an IEC958 structure. The data is stored there.
 */
void snd_ctl_elem_value_get_iec958(const snd_ctl_elem_value_t *obj, snd_aes_iec958_t *ptr)
{
	assert(obj && ptr);
	memcpy(ptr, &obj->value.iec958, sizeof(*ptr));
}

/**
 * \brief Set an elements IEC958 data.
 *
 * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_IEC958.  Note
 * that "IEC958" elements don't have members. They have only one single IEC958
 * information block.
 *
 * See \ref snd_ctl_elem_value_t and \ref control for more details.
 *
 * \param obj The element value object.
 * \param ptr Pointer to the new IEC958 data.
 */
void snd_ctl_elem_value_set_iec958(snd_ctl_elem_value_t *obj, const snd_aes_iec958_t *ptr)
{
	assert(obj && ptr);
	memcpy(&obj->value.iec958, ptr, sizeof(obj->value.iec958));
}
