Blame src/pcm/pcm_hooks.c

Packit Service db8eaa
/**
Packit Service db8eaa
 * \file pcm/pcm_hooks.c
Packit Service db8eaa
 * \ingroup PCM_Hook
Packit Service db8eaa
 * \brief PCM Hook Interface
Packit Service db8eaa
 * \author Abramo Bagnara <abramo@alsa-project.org>
Packit Service db8eaa
 * \author Jaroslav Kysela <perex@perex.cz>
Packit Service db8eaa
 * \date 2000-2001
Packit Service db8eaa
 */
Packit Service db8eaa
/*
Packit Service db8eaa
 *  PCM - Hook functions
Packit Service db8eaa
 *  Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org>
Packit Service db8eaa
 *
Packit Service db8eaa
 *
Packit Service db8eaa
 *   This library is free software; you can redistribute it and/or modify
Packit Service db8eaa
 *   it under the terms of the GNU Lesser General Public License as
Packit Service db8eaa
 *   published by the Free Software Foundation; either version 2.1 of
Packit Service db8eaa
 *   the License, or (at your option) any later version.
Packit Service db8eaa
 *
Packit Service db8eaa
 *   This program is distributed in the hope that it will be useful,
Packit Service db8eaa
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service db8eaa
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service db8eaa
 *   GNU Lesser General Public License for more details.
Packit Service db8eaa
 *
Packit Service db8eaa
 *   You should have received a copy of the GNU Lesser General Public
Packit Service db8eaa
 *   License along with this library; if not, write to the Free Software
Packit Service db8eaa
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit Service db8eaa
 *
Packit Service db8eaa
 */
Packit Service db8eaa
  
Packit Service db8eaa
#include "pcm_local.h"
Packit Service db8eaa
#include "pcm_generic.h"
Packit Service db8eaa
Packit Service db8eaa
#ifndef PIC
Packit Service db8eaa
/* entry for static linking */
Packit Service db8eaa
const char *_snd_module_pcm_hooks = "";
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
#ifndef DOC_HIDDEN
Packit Service db8eaa
struct _snd_pcm_hook {
Packit Service db8eaa
	snd_pcm_t *pcm;
Packit Service db8eaa
	snd_pcm_hook_func_t func;
Packit Service db8eaa
	void *private_data;
Packit Service db8eaa
	struct list_head list;
Packit Service db8eaa
};
Packit Service db8eaa
Packit Service db8eaa
struct snd_pcm_hook_dllist {
Packit Service db8eaa
	void *dlobj;
Packit Service db8eaa
	struct list_head list;
Packit Service db8eaa
};
Packit Service db8eaa
Packit Service db8eaa
typedef struct {
Packit Service db8eaa
	snd_pcm_generic_t gen;
Packit Service db8eaa
	struct list_head hooks[SND_PCM_HOOK_TYPE_LAST + 1];
Packit Service db8eaa
	struct list_head dllist;
Packit Service db8eaa
} snd_pcm_hooks_t;
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
static int hook_add_dlobj(snd_pcm_t *pcm, void *dlobj)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_hooks_t *h = pcm->private_data;
Packit Service db8eaa
	struct snd_pcm_hook_dllist *dl;
Packit Service db8eaa
Packit Service db8eaa
	dl = malloc(sizeof(*dl));
Packit Service db8eaa
	if (!dl)
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
Packit Service db8eaa
	dl->dlobj = dlobj;
Packit Service db8eaa
	list_add_tail(&dl->list, &h->dllist);
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static void hook_remove_dlobj(struct snd_pcm_hook_dllist *dl)
Packit Service db8eaa
{
Packit Service db8eaa
	list_del(&dl->list);
Packit Service db8eaa
	snd_dlclose(dl->dlobj);
Packit Service db8eaa
	free(dl);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_hooks_close(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_hooks_t *h = pcm->private_data;
Packit Service db8eaa
	struct list_head *pos, *next;
Packit Service db8eaa
	unsigned int k;
Packit Service db8eaa
	int res = 0, err;
Packit Service db8eaa
Packit Service db8eaa
	list_for_each_safe(pos, next, &h->hooks[SND_PCM_HOOK_TYPE_CLOSE]) {
Packit Service db8eaa
		snd_pcm_hook_t *hook = list_entry(pos, snd_pcm_hook_t, list);
Packit Service db8eaa
		err = hook->func(hook);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			res = err;
Packit Service db8eaa
	}
Packit Service db8eaa
	for (k = 0; k <= SND_PCM_HOOK_TYPE_LAST; ++k) {
Packit Service db8eaa
		struct list_head *hooks = &h->hooks[k];
Packit Service db8eaa
		while (!list_empty(hooks)) {
Packit Service db8eaa
			snd_pcm_hook_t *hook;
Packit Service db8eaa
			pos = hooks->next;
Packit Service db8eaa
			hook = list_entry(pos, snd_pcm_hook_t, list);
Packit Service db8eaa
			snd_pcm_hook_remove(hook);
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	while (!list_empty(&h->dllist)) {
Packit Service db8eaa
		pos = h->dllist.next;
Packit Service db8eaa
		hook_remove_dlobj(list_entry(pos, struct snd_pcm_hook_dllist, list));
Packit Service db8eaa
	}
Packit Service db8eaa
	err = snd_pcm_generic_close(pcm);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		res = err;
Packit Service db8eaa
	return res;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_hooks_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_hooks_t *h = pcm->private_data;
Packit Service db8eaa
	struct list_head *pos, *next;
Packit Service db8eaa
	int err = snd_pcm_generic_hw_params(pcm, params);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	list_for_each_safe(pos, next, &h->hooks[SND_PCM_HOOK_TYPE_HW_PARAMS]) {
Packit Service db8eaa
		snd_pcm_hook_t *hook = list_entry(pos, snd_pcm_hook_t, list);
Packit Service db8eaa
		err = hook->func(hook);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_hooks_hw_free(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_hooks_t *h = pcm->private_data;
Packit Service db8eaa
	struct list_head *pos, *next;
Packit Service db8eaa
	int err = snd_pcm_generic_hw_free(pcm);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	list_for_each_safe(pos, next, &h->hooks[SND_PCM_HOOK_TYPE_HW_FREE]) {
Packit Service db8eaa
		snd_pcm_hook_t *hook = list_entry(pos, snd_pcm_hook_t, list);
Packit Service db8eaa
		err = hook->func(hook);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static void snd_pcm_hooks_dump(snd_pcm_t *pcm, snd_output_t *out)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_hooks_t *h = pcm->private_data;
Packit Service db8eaa
	snd_output_printf(out, "Hooks PCM\n");
Packit Service db8eaa
	if (pcm->setup) {
Packit Service db8eaa
		snd_output_printf(out, "Its setup is:\n");
Packit Service db8eaa
		snd_pcm_dump_setup(pcm, out);
Packit Service db8eaa
	}
Packit Service db8eaa
	snd_output_printf(out, "Slave: ");
Packit Service db8eaa
	snd_pcm_dump(h->gen.slave, out);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static const snd_pcm_ops_t snd_pcm_hooks_ops = {
Packit Service db8eaa
	.close = snd_pcm_hooks_close,
Packit Service db8eaa
	.info = snd_pcm_generic_info,
Packit Service db8eaa
	.hw_refine = snd_pcm_generic_hw_refine,
Packit Service db8eaa
	.hw_params = snd_pcm_hooks_hw_params,
Packit Service db8eaa
	.hw_free = snd_pcm_hooks_hw_free,
Packit Service db8eaa
	.sw_params = snd_pcm_generic_sw_params,
Packit Service db8eaa
	.channel_info = snd_pcm_generic_channel_info,
Packit Service db8eaa
	.dump = snd_pcm_hooks_dump,
Packit Service db8eaa
	.nonblock = snd_pcm_generic_nonblock,
Packit Service db8eaa
	.async = snd_pcm_generic_async,
Packit Service db8eaa
	.mmap = snd_pcm_generic_mmap,
Packit Service db8eaa
	.munmap = snd_pcm_generic_munmap,
Packit Service db8eaa
	.query_chmaps = snd_pcm_generic_query_chmaps,
Packit Service db8eaa
	.get_chmap = snd_pcm_generic_get_chmap,
Packit Service db8eaa
	.set_chmap = snd_pcm_generic_set_chmap,
Packit Service db8eaa
};
Packit Service db8eaa
Packit Service db8eaa
static const snd_pcm_fast_ops_t snd_pcm_hooks_fast_ops = {
Packit Service db8eaa
	.status = snd_pcm_generic_status,
Packit Service db8eaa
	.state = snd_pcm_generic_state,
Packit Service db8eaa
	.hwsync = snd_pcm_generic_hwsync,
Packit Service db8eaa
	.delay = snd_pcm_generic_delay,
Packit Service db8eaa
	.prepare = snd_pcm_generic_prepare,
Packit Service db8eaa
	.reset = snd_pcm_generic_reset,
Packit Service db8eaa
	.start = snd_pcm_generic_start,
Packit Service db8eaa
	.drop = snd_pcm_generic_drop,
Packit Service db8eaa
	.drain = snd_pcm_generic_drain,
Packit Service db8eaa
	.pause = snd_pcm_generic_pause,
Packit Service db8eaa
	.rewindable = snd_pcm_generic_rewindable,
Packit Service db8eaa
	.rewind = snd_pcm_generic_rewind,
Packit Service db8eaa
	.forwardable = snd_pcm_generic_forwardable,
Packit Service db8eaa
	.forward = snd_pcm_generic_forward,
Packit Service db8eaa
	.resume = snd_pcm_generic_resume,
Packit Service db8eaa
	.link = snd_pcm_generic_link,
Packit Service db8eaa
	.link_slaves = snd_pcm_generic_link_slaves,
Packit Service db8eaa
	.unlink = snd_pcm_generic_unlink,
Packit Service db8eaa
	.writei = snd_pcm_generic_writei,
Packit Service db8eaa
	.writen = snd_pcm_generic_writen,
Packit Service db8eaa
	.readi = snd_pcm_generic_readi,
Packit Service db8eaa
	.readn = snd_pcm_generic_readn,
Packit Service db8eaa
	.avail_update = snd_pcm_generic_avail_update,
Packit Service db8eaa
	.mmap_commit = snd_pcm_generic_mmap_commit,
Packit Service db8eaa
	.htimestamp = snd_pcm_generic_htimestamp,
Packit Service db8eaa
	.poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
Packit Service db8eaa
	.poll_descriptors = snd_pcm_generic_poll_descriptors,
Packit Service db8eaa
	.poll_revents = snd_pcm_generic_poll_revents,
Packit Service db8eaa
	.may_wait_for_avail_min = snd_pcm_generic_may_wait_for_avail_min,
Packit Service db8eaa
};
Packit Service db8eaa
Packit Service db8eaa
/**
Packit Service db8eaa
 * \brief Creates a new hooks PCM
Packit Service db8eaa
 * \param pcmp Returns created PCM handle
Packit Service db8eaa
 * \param name Name of PCM
Packit Service db8eaa
 * \param slave Slave PCM
Packit Service db8eaa
 * \param close_slave If set, slave PCM handle is closed when hooks PCM is closed
Packit Service db8eaa
 * \retval zero on success otherwise a negative error code
Packit Service db8eaa
 * \warning Using of this function might be dangerous in the sense
Packit Service db8eaa
 *          of compatibility reasons. The prototype might be freely
Packit Service db8eaa
 *	    changed in future.
Packit Service db8eaa
 */
Packit Service db8eaa
int snd_pcm_hooks_open(snd_pcm_t **pcmp, const char *name, snd_pcm_t *slave, int close_slave)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_t *pcm;
Packit Service db8eaa
	snd_pcm_hooks_t *h;
Packit Service db8eaa
	unsigned int k;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	assert(pcmp && slave);
Packit Service db8eaa
	h = calloc(1, sizeof(snd_pcm_hooks_t));
Packit Service db8eaa
	if (!h)
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
	h->gen.slave = slave;
Packit Service db8eaa
	h->gen.close_slave = close_slave;
Packit Service db8eaa
	for (k = 0; k <= SND_PCM_HOOK_TYPE_LAST; ++k) {
Packit Service db8eaa
		INIT_LIST_HEAD(&h->hooks[k]);
Packit Service db8eaa
	}
Packit Service db8eaa
	INIT_LIST_HEAD(&h->dllist);
Packit Service db8eaa
	err = snd_pcm_new(&pcm, SND_PCM_TYPE_HOOKS, name, slave->stream, slave->mode);
Packit Service db8eaa
	if (err < 0) {
Packit Service db8eaa
		free(h);
Packit Service db8eaa
		return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	pcm->ops = &snd_pcm_hooks_ops;
Packit Service db8eaa
	pcm->fast_ops = &snd_pcm_hooks_fast_ops;
Packit Service db8eaa
	pcm->private_data = h;
Packit Service db8eaa
	pcm->poll_fd = slave->poll_fd;
Packit Service db8eaa
	pcm->poll_events = slave->poll_events;
Packit Service db8eaa
	pcm->mmap_shadow = 1;
Packit Service db8eaa
	pcm->tstamp_type = slave->tstamp_type;
Packit Service db8eaa
	snd_pcm_link_hw_ptr(pcm, slave);
Packit Service db8eaa
	snd_pcm_link_appl_ptr(pcm, slave);
Packit Service db8eaa
	*pcmp = pcm;
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*! \page pcm_plugins
Packit Service db8eaa
Packit Service db8eaa
\section pcm_plugins_hooks Plugin: hooks
Packit Service db8eaa
Packit Service db8eaa
This plugin is used to call some 'hook' function when this plugin is opened,
Packit Service db8eaa
modified or closed.
Packit Service db8eaa
Typically, it is used to change control values for a certain state
Packit Service db8eaa
specially for the PCM (see the example below).
Packit Service db8eaa
Packit Service db8eaa
\code
Packit Service db8eaa
# Hook arguments definition
Packit Service db8eaa
hook_args.NAME {
Packit Service db8eaa
	...			# Arbitrary arguments
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
# PCM hook type
Packit Service db8eaa
pcm_hook_type.NAME {
Packit Service db8eaa
	[lib STR]		# Library file (default libasound.so)
Packit Service db8eaa
	[install STR]		# Install function (default _snd_pcm_hook_NAME_install)
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
# PCM hook definition
Packit Service db8eaa
pcm_hook.NAME {
Packit Service db8eaa
	type STR		# PCM Hook type (see pcm_hook_type)
Packit Service db8eaa
	[args STR]		# Arguments for install function (see hook_args)
Packit Service db8eaa
	# or
Packit Service db8eaa
	[args { }]		# Arguments for install function
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
# PCM hook plugin
Packit Service db8eaa
pcm.NAME {
Packit Service db8eaa
	type hooks		# PCM with hooks
Packit Service db8eaa
	slave STR		# Slave name
Packit Service db8eaa
	# or
Packit Service db8eaa
	slave {			# Slave definition
Packit Service db8eaa
	  	pcm STR		# Slave PCM name
Packit Service db8eaa
		# or
Packit Service db8eaa
	  	pcm { }		# Slave PCM definition
Packit Service db8eaa
	}
Packit Service db8eaa
	hooks {
Packit Service db8eaa
		ID STR		# Hook name (see pcm_hook)
Packit Service db8eaa
		# or
Packit Service db8eaa
		ID { }		# Hook definition (see pcm_hook)
Packit Service db8eaa
	}
Packit Service db8eaa
}
Packit Service db8eaa
\endcode
Packit Service db8eaa
Packit Service db8eaa
Example:
Packit Service db8eaa
Packit Service db8eaa
\code
Packit Service db8eaa
	hooks.0 {
Packit Service db8eaa
		type ctl_elems
Packit Service db8eaa
		hook_args [
Packit Service db8eaa
			{
Packit Service db8eaa
				name "Wave Surround Playback Volume"
Packit Service db8eaa
				preserve true
Packit Service db8eaa
				lock true
Packit Service db8eaa
				optional true
Packit Service db8eaa
				value [ 0 0 ]
Packit Service db8eaa
			}
Packit Service db8eaa
			{
Packit Service db8eaa
				name "EMU10K1 PCM Send Volume"
Packit Service db8eaa
				index { @func private_pcm_subdevice }
Packit Service db8eaa
				lock true
Packit Service db8eaa
				value [ 0 0 0 0 0 0 255 0 0 0 0 255 ]
Packit Service db8eaa
			}
Packit Service db8eaa
		]
Packit Service db8eaa
	}
Packit Service db8eaa
\endcode
Packit Service db8eaa
Here, the controls "Wave Surround Playback Volume" and "EMU10K1 PCM Send Volume"
Packit Service db8eaa
are set to the given values when this pcm is accessed.  Since these controls
Packit Service db8eaa
take multi-dimensional values, the value field is written as
Packit Service db8eaa
an array.
Packit Service db8eaa
When preserve is true, the old values are saved and restored
Packit Service db8eaa
when the pcm is closed.  The lock means that the control is
Packit Service db8eaa
locked during this pcm is opened, and cannot be changed by others.
Packit Service db8eaa
When optional is set, no error is returned but ignored
Packit Service db8eaa
even if the specified control doesn't exist.
Packit Service db8eaa
Packit Service db8eaa
\subsection pcm_plugins_hooks_funcref Function reference
Packit Service db8eaa
Packit Service db8eaa
    Packit Service db8eaa
      
  • The function ctl_elems - _snd_pcm_hook_ctl_elems_install() - installs
  • Packit Service db8eaa
          CTL settings described by given configuration.
    Packit Service db8eaa
      
  • snd_pcm_hooks_open()
  • Packit Service db8eaa
      
  • _snd_pcm_hooks_open()
  • Packit Service db8eaa
    Packit Service db8eaa
    Packit Service db8eaa
    */
    Packit Service db8eaa
    Packit Service db8eaa
    static int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *root, snd_config_t *conf)
    Packit Service db8eaa
    {
    Packit Service db8eaa
    	int err;
    Packit Service db8eaa
    	char buf[256], errbuf[256];
    Packit Service db8eaa
    	const char *str, *id;
    Packit Service db8eaa
    	const char *lib = NULL, *install = NULL;
    Packit Service db8eaa
    	snd_config_t *type = NULL, *args = NULL;
    Packit Service db8eaa
    	snd_config_iterator_t i, next;
    Packit Service db8eaa
    	int (*install_func)(snd_pcm_t *pcm, snd_config_t *args) = NULL;
    Packit Service db8eaa
    	void *h = NULL;
    Packit Service db8eaa
    Packit Service db8eaa
    	if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) {
    Packit Service db8eaa
    		SNDERR("Invalid hook definition");
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	snd_config_for_each(i, next, conf) {
    Packit Service db8eaa
    		snd_config_t *n = snd_config_iterator_entry(i);
    Packit Service db8eaa
    		const char *id;
    Packit Service db8eaa
    		if (snd_config_get_id(n, &id) < 0)
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		if (strcmp(id, "comment") == 0)
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		if (strcmp(id, "type") == 0) {
    Packit Service db8eaa
    			type = n;
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		if (strcmp(id, "hook_args") == 0) {
    Packit Service db8eaa
    			args = n;
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		SNDERR("Unknown field %s", id);
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	if (!type) {
    Packit Service db8eaa
    		SNDERR("type is not defined");
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	err = snd_config_get_id(type, &id;;
    Packit Service db8eaa
    	if (err < 0) {
    Packit Service db8eaa
    		SNDERR("unable to get id");
    Packit Service db8eaa
    		return err;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	err = snd_config_get_string(type, &str);
    Packit Service db8eaa
    	if (err < 0) {
    Packit Service db8eaa
    		SNDERR("Invalid type for %s", id);
    Packit Service db8eaa
    		return err;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	err = snd_config_search_definition(root, "pcm_hook_type", str, &type);
    Packit Service db8eaa
    	if (err >= 0) {
    Packit Service db8eaa
    		if (snd_config_get_type(type) != SND_CONFIG_TYPE_COMPOUND) {
    Packit Service db8eaa
    			SNDERR("Invalid type for PCM type %s definition", str);
    Packit Service db8eaa
    			err = -EINVAL;
    Packit Service db8eaa
    			goto _err;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		snd_config_for_each(i, next, type) {
    Packit Service db8eaa
    			snd_config_t *n = snd_config_iterator_entry(i);
    Packit Service db8eaa
    			const char *id;
    Packit Service db8eaa
    			if (snd_config_get_id(n, &id) < 0)
    Packit Service db8eaa
    				continue;
    Packit Service db8eaa
    			if (strcmp(id, "comment") == 0)
    Packit Service db8eaa
    				continue;
    Packit Service db8eaa
    			if (strcmp(id, "lib") == 0) {
    Packit Service db8eaa
    				err = snd_config_get_string(n, &lib);
    Packit Service db8eaa
    				if (err < 0) {
    Packit Service db8eaa
    					SNDERR("Invalid type for %s", id);
    Packit Service db8eaa
    					goto _err;
    Packit Service db8eaa
    				}
    Packit Service db8eaa
    				continue;
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    			if (strcmp(id, "install") == 0) {
    Packit Service db8eaa
    				err = snd_config_get_string(n, &install);
    Packit Service db8eaa
    				if (err < 0) {
    Packit Service db8eaa
    					SNDERR("Invalid type for %s", id);
    Packit Service db8eaa
    					goto _err;
    Packit Service db8eaa
    				}
    Packit Service db8eaa
    				continue;
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    			SNDERR("Unknown field %s", id);
    Packit Service db8eaa
    			err = -EINVAL;
    Packit Service db8eaa
    			goto _err;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	if (!install) {
    Packit Service db8eaa
    		install = buf;
    Packit Service db8eaa
    		snprintf(buf, sizeof(buf), "_snd_pcm_hook_%s_install", str);
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	h = INTERNAL(snd_dlopen)(lib, RTLD_NOW, errbuf, sizeof(errbuf));
    Packit Service db8eaa
    	install_func = h ? snd_dlsym(h, install, SND_DLSYM_VERSION(SND_PCM_DLSYM_VERSION)) : NULL;
    Packit Service db8eaa
    	err = 0;
    Packit Service db8eaa
    	if (!h) {
    Packit Service db8eaa
    		SNDERR("Cannot open shared library %s (%s)",
    Packit Service db8eaa
    		       lib ? lib : "[builtin]", errbuf);
    Packit Service db8eaa
    		err = -ENOENT;
    Packit Service db8eaa
    	} else if (!install_func) {
    Packit Service db8eaa
    		SNDERR("symbol %s is not defined inside %s", install,
    Packit Service db8eaa
    		       lib ? lib : "[builtin]");
    Packit Service db8eaa
    		snd_dlclose(h);
    Packit Service db8eaa
    		err = -ENXIO;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
           _err:
    Packit Service db8eaa
    	if (type)
    Packit Service db8eaa
    		snd_config_delete(type);
    Packit Service db8eaa
    	if (err < 0)
    Packit Service db8eaa
    		return err;
    Packit Service db8eaa
    Packit Service db8eaa
    	if (args && snd_config_get_string(args, &str) >= 0) {
    Packit Service db8eaa
    		err = snd_config_search_definition(root, "hook_args", str, &args);
    Packit Service db8eaa
    		if (err < 0)
    Packit Service db8eaa
    			SNDERR("unknown hook_args %s", str);
    Packit Service db8eaa
    		else
    Packit Service db8eaa
    			err = install_func(pcm, args);
    Packit Service db8eaa
    		snd_config_delete(args);
    Packit Service db8eaa
    	} else
    Packit Service db8eaa
    		err = install_func(pcm, args);
    Packit Service db8eaa
    Packit Service db8eaa
    	if (err >= 0)
    Packit Service db8eaa
    		err = hook_add_dlobj(pcm, h);
    Packit Service db8eaa
    Packit Service db8eaa
    	if (err < 0) {
    Packit Service db8eaa
    		if(h)
    Packit Service db8eaa
    			snd_dlclose(h);
    Packit Service db8eaa
    		return err;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	return 0;
    Packit Service db8eaa
    }
    Packit Service db8eaa
    Packit Service db8eaa
    /**
    Packit Service db8eaa
     * \brief Creates a new hooks PCM
    Packit Service db8eaa
     * \param pcmp Returns created PCM handle
    Packit Service db8eaa
     * \param name Name of PCM
    Packit Service db8eaa
     * \param root Root configuration node
    Packit Service db8eaa
     * \param conf Configuration node with hooks PCM description
    Packit Service db8eaa
     * \param stream PCM Stream
    Packit Service db8eaa
     * \param mode PCM Mode
    Packit Service db8eaa
     * \retval zero on success otherwise a negative error code
    Packit Service db8eaa
     * \warning Using of this function might be dangerous in the sense
    Packit Service db8eaa
     *          of compatibility reasons. The prototype might be freely
    Packit Service db8eaa
     *	    changed in future.
    Packit Service db8eaa
     */
    Packit Service db8eaa
    int _snd_pcm_hooks_open(snd_pcm_t **pcmp, const char *name,
    Packit Service db8eaa
    			snd_config_t *root, snd_config_t *conf, 
    Packit Service db8eaa
    			snd_pcm_stream_t stream, int mode)
    Packit Service db8eaa
    {
    Packit Service db8eaa
    	snd_config_iterator_t i, next;
    Packit Service db8eaa
    	int err;
    Packit Service db8eaa
    	snd_pcm_t *rpcm = NULL, *spcm;
    Packit Service db8eaa
    	snd_config_t *slave = NULL, *sconf;
    Packit Service db8eaa
    	snd_config_t *hooks = NULL;
    Packit Service db8eaa
    	snd_config_for_each(i, next, conf) {
    Packit Service db8eaa
    		snd_config_t *n = snd_config_iterator_entry(i);
    Packit Service db8eaa
    		const char *id;
    Packit Service db8eaa
    		if (snd_config_get_id(n, &id) < 0)
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		if (snd_pcm_conf_generic_id(id))
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		if (strcmp(id, "slave") == 0) {
    Packit Service db8eaa
    			slave = n;
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		if (strcmp(id, "hooks") == 0) {
    Packit Service db8eaa
    			if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
    Packit Service db8eaa
    				SNDERR("Invalid type for %s", id);
    Packit Service db8eaa
    				return -EINVAL;
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    			hooks = n;
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		SNDERR("Unknown field %s", id);
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	if (!slave) {
    Packit Service db8eaa
    		SNDERR("slave is not defined");
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	err = snd_pcm_slave_conf(root, slave, &sconf, 0);
    Packit Service db8eaa
    	if (err < 0)
    Packit Service db8eaa
    		return err;
    Packit Service db8eaa
    	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
    Packit Service db8eaa
    	snd_config_delete(sconf);
    Packit Service db8eaa
    	if (err < 0)
    Packit Service db8eaa
    		return err;
    Packit Service db8eaa
    	err = snd_pcm_hooks_open(&rpcm, name, spcm, 1);
    Packit Service db8eaa
    	if (err < 0) {
    Packit Service db8eaa
    		snd_pcm_close(spcm);
    Packit Service db8eaa
    		return err;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	if (!hooks)
    Packit Service db8eaa
    		goto _done;
    Packit Service db8eaa
    	snd_config_for_each(i, next, hooks) {
    Packit Service db8eaa
    		snd_config_t *n = snd_config_iterator_entry(i);
    Packit Service db8eaa
    		const char *str;
    Packit Service db8eaa
    		if (snd_config_get_string(n, &str) >= 0) {
    Packit Service db8eaa
    			err = snd_config_search_definition(root, "pcm_hook", str, &n);
    Packit Service db8eaa
    			if (err < 0) {
    Packit Service db8eaa
    				SNDERR("unknown pcm_hook %s", str);
    Packit Service db8eaa
    			} else {
    Packit Service db8eaa
    				err = snd_pcm_hook_add_conf(rpcm, root, n);
    Packit Service db8eaa
    				snd_config_delete(n);
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    		} else
    Packit Service db8eaa
    			err = snd_pcm_hook_add_conf(rpcm, root, n);
    Packit Service db8eaa
    		if (err < 0) {
    Packit Service db8eaa
    			snd_pcm_close(rpcm);
    Packit Service db8eaa
    			return err;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    	}
    Packit Service db8eaa
     _done:
    Packit Service db8eaa
    	*pcmp = rpcm;
    Packit Service db8eaa
    	return 0;
    Packit Service db8eaa
    }
    Packit Service db8eaa
    #ifndef DOC_HIDDEN
    Packit Service db8eaa
    SND_DLSYM_BUILD_VERSION(_snd_pcm_hooks_open, SND_PCM_DLSYM_VERSION);
    Packit Service db8eaa
    #endif
    Packit Service db8eaa
    Packit Service db8eaa
    /**
    Packit Service db8eaa
     * \brief Get PCM handle for a PCM hook
    Packit Service db8eaa
     * \param hook PCM hook handle
    Packit Service db8eaa
     * \return PCM handle
    Packit Service db8eaa
     */
    Packit Service db8eaa
    snd_pcm_t *snd_pcm_hook_get_pcm(snd_pcm_hook_t *hook)
    Packit Service db8eaa
    {
    Packit Service db8eaa
    	assert(hook);
    Packit Service db8eaa
    	return hook->pcm;
    Packit Service db8eaa
    }
    Packit Service db8eaa
    Packit Service db8eaa
    /**
    Packit Service db8eaa
     * \brief Get callback function private data for a PCM hook
    Packit Service db8eaa
     * \param hook PCM hook handle
    Packit Service db8eaa
     * \return callback function private data
    Packit Service db8eaa
     */
    Packit Service db8eaa
    void *snd_pcm_hook_get_private(snd_pcm_hook_t *hook)
    Packit Service db8eaa
    {
    Packit Service db8eaa
    	assert(hook);
    Packit Service db8eaa
    	return hook->private_data;
    Packit Service db8eaa
    }
    Packit Service db8eaa
    Packit Service db8eaa
    /**
    Packit Service db8eaa
     * \brief Set callback function private data for a PCM hook
    Packit Service db8eaa
     * \param hook PCM hook handle
    Packit Service db8eaa
     * \param private_data The private data value
    Packit Service db8eaa
     */
    Packit Service db8eaa
    void snd_pcm_hook_set_private(snd_pcm_hook_t *hook, void *private_data)
    Packit Service db8eaa
    {
    Packit Service db8eaa
    	assert(hook);
    Packit Service db8eaa
    	hook->private_data = private_data;
    Packit Service db8eaa
    }
    Packit Service db8eaa
    Packit Service db8eaa
    /**
    Packit Service db8eaa
     * \brief Add a PCM hook at end of hooks chain
    Packit Service db8eaa
     * \param hookp Returned PCM hook handle
    Packit Service db8eaa
     * \param pcm PCM handle
    Packit Service db8eaa
     * \param type PCM hook type
    Packit Service db8eaa
     * \param func PCM hook callback function
    Packit Service db8eaa
     * \param private_data PCM hook private data
    Packit Service db8eaa
     * \return 0 on success otherwise a negative error code
    Packit Service db8eaa
     *
    Packit Service db8eaa
     * Warning: an hook callback function cannot remove an hook of the same type
    Packit Service db8eaa
     * different from itself
    Packit Service db8eaa
     */
    Packit Service db8eaa
    int snd_pcm_hook_add(snd_pcm_hook_t **hookp, snd_pcm_t *pcm,
    Packit Service db8eaa
    		     snd_pcm_hook_type_t type,
    Packit Service db8eaa
    		     snd_pcm_hook_func_t func, void *private_data)
    Packit Service db8eaa
    {
    Packit Service db8eaa
    	snd_pcm_hook_t *h;
    Packit Service db8eaa
    	snd_pcm_hooks_t *hooks;
    Packit Service db8eaa
    	assert(hookp && func);
    Packit Service db8eaa
    	assert(snd_pcm_type(pcm) == SND_PCM_TYPE_HOOKS);
    Packit Service db8eaa
    	h = calloc(1, sizeof(*h));
    Packit Service db8eaa
    	if (!h)
    Packit Service db8eaa
    		return -ENOMEM;
    Packit Service db8eaa
    	h->pcm = pcm;
    Packit Service db8eaa
    	h->func = func;
    Packit Service db8eaa
    	h->private_data = private_data;
    Packit Service db8eaa
    	hooks = pcm->private_data;
    Packit Service db8eaa
    	list_add_tail(&h->list, &hooks->hooks[type]);
    Packit Service db8eaa
    	*hookp = h;
    Packit Service db8eaa
    	return 0;
    Packit Service db8eaa
    }
    Packit Service db8eaa
    Packit Service db8eaa
    /**
    Packit Service db8eaa
     * \brief Remove a PCM hook
    Packit Service db8eaa
     * \param hook PCM hook handle
    Packit Service db8eaa
     * \return 0 on success otherwise a negative error code
    Packit Service db8eaa
     *
    Packit Service db8eaa
     * Warning: an hook callback cannot remove an hook of the same type
    Packit Service db8eaa
     * different from itself
    Packit Service db8eaa
     */
    Packit Service db8eaa
    int snd_pcm_hook_remove(snd_pcm_hook_t *hook)
    Packit Service db8eaa
    {
    Packit Service db8eaa
    	assert(hook);
    Packit Service db8eaa
    	list_del(&hook->list);
    Packit Service db8eaa
    	free(hook);
    Packit Service db8eaa
    	return 0;
    Packit Service db8eaa
    }
    Packit Service db8eaa
    Packit Service db8eaa
    /*
    Packit Service db8eaa
     *
    Packit Service db8eaa
     */
    Packit Service db8eaa
    Packit Service db8eaa
    static int snd_pcm_hook_ctl_elems_hw_params(snd_pcm_hook_t *hook)
    Packit Service db8eaa
    {
    Packit Service db8eaa
    	snd_sctl_t *h = snd_pcm_hook_get_private(hook);
    Packit Service db8eaa
    	return snd_sctl_install(h);
    Packit Service db8eaa
    }
    Packit Service db8eaa
    Packit Service db8eaa
    static int snd_pcm_hook_ctl_elems_hw_free(snd_pcm_hook_t *hook)
    Packit Service db8eaa
    {
    Packit Service db8eaa
    	snd_sctl_t *h = snd_pcm_hook_get_private(hook);
    Packit Service db8eaa
    	return snd_sctl_remove(h);
    Packit Service db8eaa
    }
    Packit Service db8eaa
    Packit Service db8eaa
    static int snd_pcm_hook_ctl_elems_close(snd_pcm_hook_t *hook)
    Packit Service db8eaa
    {
    Packit Service db8eaa
    	snd_sctl_t *h = snd_pcm_hook_get_private(hook);
    Packit Service db8eaa
    	int err = snd_sctl_free(h);
    Packit Service db8eaa
    	snd_pcm_hook_set_private(hook, NULL);
    Packit Service db8eaa
    	return err;
    Packit Service db8eaa
    }
    Packit Service db8eaa
    Packit Service db8eaa
    /**
    Packit Service db8eaa
     * \brief Install CTL settings using hardware associated with PCM handle
    Packit Service db8eaa
     * \param pcm PCM handle
    Packit Service db8eaa
     * \param conf Configuration node with CTL settings
    Packit Service db8eaa
     * \return zero on success otherwise a negative error code
    Packit Service db8eaa
     */
    Packit Service db8eaa
    int _snd_pcm_hook_ctl_elems_install(snd_pcm_t *pcm, snd_config_t *conf)
    Packit Service db8eaa
    {
    Packit Service db8eaa
    	int err;
    Packit Service db8eaa
    	int card;
    Packit Service db8eaa
    	snd_pcm_info_t info = {0};
    Packit Service db8eaa
    	char ctl_name[16];
    Packit Service db8eaa
    	snd_ctl_t *ctl;
    Packit Service db8eaa
    	snd_sctl_t *sctl = NULL;
    Packit Service db8eaa
    	snd_config_t *pcm_conf = NULL;
    Packit Service db8eaa
    	snd_pcm_hook_t *h_hw_params = NULL, *h_hw_free = NULL, *h_close = NULL;
    Packit Service db8eaa
    	assert(conf);
    Packit Service db8eaa
    	assert(snd_config_get_type(conf) == SND_CONFIG_TYPE_COMPOUND);
    Packit Service db8eaa
    Packit Service db8eaa
    	err = snd_pcm_info(pcm, &info;;
    Packit Service db8eaa
    	if (err < 0)
    Packit Service db8eaa
    		return err;
    Packit Service db8eaa
    	card = snd_pcm_info_get_card(&info;;
    Packit Service db8eaa
    	if (card < 0) {
    Packit Service db8eaa
    		SNDERR("No card for this PCM");
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	sprintf(ctl_name, "hw:%d", card);
    Packit Service db8eaa
    	err = snd_ctl_open(&ctl, ctl_name, 0);
    Packit Service db8eaa
    	if (err < 0) {
    Packit Service db8eaa
    		SNDERR("Cannot open CTL %s", ctl_name);
    Packit Service db8eaa
    		return err;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	err = snd_config_imake_pointer(&pcm_conf, "pcm_handle", pcm);
    Packit Service db8eaa
    	if (err < 0)
    Packit Service db8eaa
    		goto _err;
    Packit Service db8eaa
    	err = snd_sctl_build(&sctl, ctl, conf, pcm_conf, 0);
    Packit Service db8eaa
    	if (err < 0)
    Packit Service db8eaa
    		goto _err;
    Packit Service db8eaa
    	err = snd_pcm_hook_add(&h_hw_params, pcm, SND_PCM_HOOK_TYPE_HW_PARAMS,
    Packit Service db8eaa
    			       snd_pcm_hook_ctl_elems_hw_params, sctl);
    Packit Service db8eaa
    	if (err < 0)
    Packit Service db8eaa
    		goto _err;
    Packit Service db8eaa
    	err = snd_pcm_hook_add(&h_hw_free, pcm, SND_PCM_HOOK_TYPE_HW_FREE,
    Packit Service db8eaa
    			       snd_pcm_hook_ctl_elems_hw_free, sctl);
    Packit Service db8eaa
    	if (err < 0)
    Packit Service db8eaa
    		goto _err;
    Packit Service db8eaa
    	err = snd_pcm_hook_add(&h_close, pcm, SND_PCM_HOOK_TYPE_CLOSE,
    Packit Service db8eaa
    			       snd_pcm_hook_ctl_elems_close, sctl);
    Packit Service db8eaa
    	if (err < 0)
    Packit Service db8eaa
    		goto _err;
    Packit Service db8eaa
    	snd_config_delete(pcm_conf);
    Packit Service db8eaa
    	return 0;
    Packit Service db8eaa
     _err:
    Packit Service db8eaa
    	if (h_hw_params)
    Packit Service db8eaa
    		snd_pcm_hook_remove(h_hw_params);
    Packit Service db8eaa
    	if (h_hw_free)
    Packit Service db8eaa
    		snd_pcm_hook_remove(h_hw_free);
    Packit Service db8eaa
    	if (h_close)
    Packit Service db8eaa
    		snd_pcm_hook_remove(h_close);
    Packit Service db8eaa
    	if (sctl)
    Packit Service db8eaa
    		snd_sctl_free(sctl);
    Packit Service db8eaa
    	if (pcm_conf)
    Packit Service db8eaa
    		snd_config_delete(pcm_conf);
    Packit Service db8eaa
    	return err;
    Packit Service db8eaa
    }
    Packit Service db8eaa
    #ifndef DOC_HIDDEN
    Packit Service db8eaa
    SND_DLSYM_BUILD_VERSION(_snd_pcm_hook_ctl_elems_install, SND_PCM_DLSYM_VERSION);
    Packit Service db8eaa
    #endif