Home | History | Annotate | Download | only in pcm
      1 /**
      2  * \file pcm/pcm_hooks.c
      3  * \ingroup PCM_Hook
      4  * \brief PCM Hook Interface
      5  * \author Abramo Bagnara <abramo (at) alsa-project.org>
      6  * \author Jaroslav Kysela <perex (at) perex.cz>
      7  * \date 2000-2001
      8  */
      9 /*
     10  *  PCM - Hook functions
     11  *  Copyright (c) 2001 by Abramo Bagnara <abramo (at) alsa-project.org>
     12  *
     13  *
     14  *   This library is free software; you can redistribute it and/or modify
     15  *   it under the terms of the GNU Lesser General Public License as
     16  *   published by the Free Software Foundation; either version 2.1 of
     17  *   the License, or (at your option) any later version.
     18  *
     19  *   This program is distributed in the hope that it will be useful,
     20  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     21  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     22  *   GNU Lesser General Public License for more details.
     23  *
     24  *   You should have received a copy of the GNU Lesser General Public
     25  *   License along with this library; if not, write to the Free Software
     26  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
     27  *
     28  */
     29 
     30 #include "pcm_local.h"
     31 #include "pcm_generic.h"
     32 
     33 #ifndef PIC
     34 /* entry for static linking */
     35 const char *_snd_module_pcm_hooks = "";
     36 #endif
     37 
     38 #ifndef DOC_HIDDEN
     39 struct _snd_pcm_hook {
     40 	snd_pcm_t *pcm;
     41 	snd_pcm_hook_func_t func;
     42 	void *private_data;
     43 	struct list_head list;
     44 };
     45 
     46 typedef struct {
     47 	snd_pcm_generic_t gen;
     48 	struct list_head hooks[SND_PCM_HOOK_TYPE_LAST + 1];
     49 } snd_pcm_hooks_t;
     50 #endif
     51 
     52 static int snd_pcm_hooks_close(snd_pcm_t *pcm)
     53 {
     54 	snd_pcm_hooks_t *h = pcm->private_data;
     55 	struct list_head *pos, *next;
     56 	unsigned int k;
     57 	int res = 0, err;
     58 
     59 	list_for_each_safe(pos, next, &h->hooks[SND_PCM_HOOK_TYPE_CLOSE]) {
     60 		snd_pcm_hook_t *hook = list_entry(pos, snd_pcm_hook_t, list);
     61 		err = hook->func(hook);
     62 		if (err < 0)
     63 			res = err;
     64 	}
     65 	for (k = 0; k <= SND_PCM_HOOK_TYPE_LAST; ++k) {
     66 		struct list_head *hooks = &h->hooks[k];
     67 		while (!list_empty(hooks)) {
     68 			snd_pcm_hook_t *hook;
     69 			pos = hooks->next;
     70 			hook = list_entry(pos, snd_pcm_hook_t, list);
     71 			snd_pcm_hook_remove(hook);
     72 		}
     73 	}
     74 	err = snd_pcm_generic_close(pcm);
     75 	if (err < 0)
     76 		res = err;
     77 	return res;
     78 }
     79 
     80 static int snd_pcm_hooks_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
     81 {
     82 	snd_pcm_hooks_t *h = pcm->private_data;
     83 	struct list_head *pos, *next;
     84 	int err = snd_pcm_generic_hw_params(pcm, params);
     85 	if (err < 0)
     86 		return err;
     87 	list_for_each_safe(pos, next, &h->hooks[SND_PCM_HOOK_TYPE_HW_PARAMS]) {
     88 		snd_pcm_hook_t *hook = list_entry(pos, snd_pcm_hook_t, list);
     89 		err = hook->func(hook);
     90 		if (err < 0)
     91 			return err;
     92 	}
     93 	return 0;
     94 }
     95 
     96 static int snd_pcm_hooks_hw_free(snd_pcm_t *pcm)
     97 {
     98 	snd_pcm_hooks_t *h = pcm->private_data;
     99 	struct list_head *pos, *next;
    100 	int err = snd_pcm_generic_hw_free(pcm);
    101 	if (err < 0)
    102 		return err;
    103 	list_for_each_safe(pos, next, &h->hooks[SND_PCM_HOOK_TYPE_HW_FREE]) {
    104 		snd_pcm_hook_t *hook = list_entry(pos, snd_pcm_hook_t, list);
    105 		err = hook->func(hook);
    106 		if (err < 0)
    107 			return err;
    108 	}
    109 	return 0;
    110 }
    111 
    112 static void snd_pcm_hooks_dump(snd_pcm_t *pcm, snd_output_t *out)
    113 {
    114 	snd_pcm_hooks_t *h = pcm->private_data;
    115 	snd_output_printf(out, "Hooks PCM\n");
    116 	if (pcm->setup) {
    117 		snd_output_printf(out, "Its setup is:\n");
    118 		snd_pcm_dump_setup(pcm, out);
    119 	}
    120 	snd_output_printf(out, "Slave: ");
    121 	snd_pcm_dump(h->gen.slave, out);
    122 }
    123 
    124 static const snd_pcm_ops_t snd_pcm_hooks_ops = {
    125 	.close = snd_pcm_hooks_close,
    126 	.info = snd_pcm_generic_info,
    127 	.hw_refine = snd_pcm_generic_hw_refine,
    128 	.hw_params = snd_pcm_hooks_hw_params,
    129 	.hw_free = snd_pcm_hooks_hw_free,
    130 	.sw_params = snd_pcm_generic_sw_params,
    131 	.channel_info = snd_pcm_generic_channel_info,
    132 	.dump = snd_pcm_hooks_dump,
    133 	.nonblock = snd_pcm_generic_nonblock,
    134 	.async = snd_pcm_generic_async,
    135 	.mmap = snd_pcm_generic_mmap,
    136 	.munmap = snd_pcm_generic_munmap,
    137 };
    138 
    139 static const snd_pcm_fast_ops_t snd_pcm_hooks_fast_ops = {
    140 	.status = snd_pcm_generic_status,
    141 	.state = snd_pcm_generic_state,
    142 	.hwsync = snd_pcm_generic_hwsync,
    143 	.delay = snd_pcm_generic_delay,
    144 	.prepare = snd_pcm_generic_prepare,
    145 	.reset = snd_pcm_generic_reset,
    146 	.start = snd_pcm_generic_start,
    147 	.drop = snd_pcm_generic_drop,
    148 	.drain = snd_pcm_generic_drain,
    149 	.pause = snd_pcm_generic_pause,
    150 	.rewindable = snd_pcm_generic_rewindable,
    151 	.rewind = snd_pcm_generic_rewind,
    152 	.forwardable = snd_pcm_generic_forwardable,
    153 	.forward = snd_pcm_generic_forward,
    154 	.resume = snd_pcm_generic_resume,
    155 	.link = snd_pcm_generic_link,
    156 	.link_slaves = snd_pcm_generic_link_slaves,
    157 	.unlink = snd_pcm_generic_unlink,
    158 	.writei = snd_pcm_generic_writei,
    159 	.writen = snd_pcm_generic_writen,
    160 	.readi = snd_pcm_generic_readi,
    161 	.readn = snd_pcm_generic_readn,
    162 	.avail_update = snd_pcm_generic_avail_update,
    163 	.mmap_commit = snd_pcm_generic_mmap_commit,
    164 	.htimestamp = snd_pcm_generic_htimestamp,
    165 	.poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
    166 	.poll_descriptors = snd_pcm_generic_poll_descriptors,
    167 	.poll_revents = snd_pcm_generic_poll_revents,
    168 };
    169 
    170 /**
    171  * \brief Creates a new hooks PCM
    172  * \param pcmp Returns created PCM handle
    173  * \param name Name of PCM
    174  * \param slave Slave PCM
    175  * \param close_slave If set, slave PCM handle is closed when hooks PCM is closed
    176  * \retval zero on success otherwise a negative error code
    177  * \warning Using of this function might be dangerous in the sense
    178  *          of compatibility reasons. The prototype might be freely
    179  *	    changed in future.
    180  */
    181 int snd_pcm_hooks_open(snd_pcm_t **pcmp, const char *name, snd_pcm_t *slave, int close_slave)
    182 {
    183 	snd_pcm_t *pcm;
    184 	snd_pcm_hooks_t *h;
    185 	unsigned int k;
    186 	int err;
    187 	assert(pcmp && slave);
    188 	h = calloc(1, sizeof(snd_pcm_hooks_t));
    189 	if (!h)
    190 		return -ENOMEM;
    191 	h->gen.slave = slave;
    192 	h->gen.close_slave = close_slave;
    193 	for (k = 0; k <= SND_PCM_HOOK_TYPE_LAST; ++k) {
    194 		INIT_LIST_HEAD(&h->hooks[k]);
    195 	}
    196 	err = snd_pcm_new(&pcm, SND_PCM_TYPE_HOOKS, name, slave->stream, slave->mode);
    197 	if (err < 0) {
    198 		free(h);
    199 		return err;
    200 	}
    201 	pcm->ops = &snd_pcm_hooks_ops;
    202 	pcm->fast_ops = &snd_pcm_hooks_fast_ops;
    203 	pcm->private_data = h;
    204 	pcm->poll_fd = slave->poll_fd;
    205 	pcm->poll_events = slave->poll_events;
    206 	pcm->mmap_shadow = 1;
    207 	pcm->monotonic = slave->monotonic;
    208 	snd_pcm_link_hw_ptr(pcm, slave);
    209 	snd_pcm_link_appl_ptr(pcm, slave);
    210 	*pcmp = pcm;
    211 
    212 	return 0;
    213 }
    214 
    215 /*! \page pcm_plugins
    216 
    217 \section pcm_plugins_hooks Plugin: hooks
    218 
    219 This plugin is used to call some 'hook' function when this plugin is opened,
    220 modified or closed.
    221 Typically, it is used to change control values for a certain state
    222 specially for the PCM (see the example below).
    223 
    224 \code
    225 # Hook arguments definition
    226 hook_args.NAME {
    227 	...			# Arbitrary arguments
    228 }
    229 
    230 # PCM hook type
    231 pcm_hook_type.NAME {
    232 	[lib STR]		# Library file (default libasound.so)
    233 	[install STR]		# Install function (default _snd_pcm_hook_NAME_install)
    234 }
    235 
    236 # PCM hook definition
    237 pcm_hook.NAME {
    238 	type STR		# PCM Hook type (see pcm_hook_type)
    239 	[args STR]		# Arguments for install function (see hook_args)
    240 	# or
    241 	[args { }]		# Arguments for install function
    242 }
    243 
    244 # PCM hook plugin
    245 pcm.NAME {
    246 	type hooks		# PCM with hooks
    247 	slave STR		# Slave name
    248 	# or
    249 	slave {			# Slave definition
    250 	  	pcm STR		# Slave PCM name
    251 		# or
    252 	  	pcm { }		# Slave PCM definition
    253 	}
    254 	hooks {
    255 		ID STR		# Hook name (see pcm_hook)
    256 		# or
    257 		ID { }		# Hook definition (see pcm_hook)
    258 	}
    259 }
    260 \endcode
    261 
    262 Example:
    263 
    264 \code
    265 	hooks.0 {
    266 		type ctl_elems
    267 		hook_args [
    268 			{
    269 				name "Wave Surround Playback Volume"
    270 				preserve true
    271 				lock true
    272 				optional true
    273 				value [ 0 0 ]
    274 			}
    275 			{
    276 				name "EMU10K1 PCM Send Volume"
    277 				index { @func private_pcm_subdevice }
    278 				lock true
    279 				value [ 0 0 0 0 0 0 255 0 0 0 0 255 ]
    280 			}
    281 		]
    282 	}
    283 \endcode
    284 Here, the controls "Wave Surround Playback Volume" and "EMU10K1 PCM Send Volume"
    285 are set to the given values when this pcm is accessed.  Since these controls
    286 take multi-dimensional values, the <code>value</code> field is written as
    287 an array.
    288 When <code>preserve</code> is true, the old values are saved and restored
    289 when the pcm is closed.  The <code>lock</code> means that the control is
    290 locked during this pcm is opened, and cannot be changed by others.
    291 When <code>optional</code> is set, no error is returned but ignored
    292 even if the specified control doesn't exist.
    293 
    294 \subsection pcm_plugins_hooks_funcref Function reference
    295 
    296 <UL>
    297   <LI>The function ctl_elems - _snd_pcm_hook_ctl_elems_install() - installs
    298       CTL settings described by given configuration.
    299   <LI>snd_pcm_hooks_open()
    300   <LI>_snd_pcm_hooks_open()
    301 </UL>
    302 
    303 */
    304 
    305 static int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *root, snd_config_t *conf)
    306 {
    307 	int err;
    308 	char buf[256];
    309 	const char *str, *id;
    310 	const char *lib = NULL, *install = NULL;
    311 	snd_config_t *type = NULL, *args = NULL;
    312 	snd_config_iterator_t i, next;
    313 	int (*install_func)(snd_pcm_t *pcm, snd_config_t *args) = NULL;
    314 	void *h = NULL;
    315 	if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) {
    316 		SNDERR("Invalid hook definition");
    317 		return -EINVAL;
    318 	}
    319 	snd_config_for_each(i, next, conf) {
    320 		snd_config_t *n = snd_config_iterator_entry(i);
    321 		const char *id;
    322 		if (snd_config_get_id(n, &id) < 0)
    323 			continue;
    324 		if (strcmp(id, "comment") == 0)
    325 			continue;
    326 		if (strcmp(id, "type") == 0) {
    327 			type = n;
    328 			continue;
    329 		}
    330 		if (strcmp(id, "hook_args") == 0) {
    331 			args = n;
    332 			continue;
    333 		}
    334 		SNDERR("Unknown field %s", id);
    335 		return -EINVAL;
    336 	}
    337 	if (!type) {
    338 		SNDERR("type is not defined");
    339 		return -EINVAL;
    340 	}
    341 	err = snd_config_get_id(type, &id);
    342 	if (err < 0) {
    343 		SNDERR("unable to get id");
    344 		return err;
    345 	}
    346 	err = snd_config_get_string(type, &str);
    347 	if (err < 0) {
    348 		SNDERR("Invalid type for %s", id);
    349 		return err;
    350 	}
    351 	err = snd_config_search_definition(root, "pcm_hook_type", str, &type);
    352 	if (err >= 0) {
    353 		if (snd_config_get_type(type) != SND_CONFIG_TYPE_COMPOUND) {
    354 			SNDERR("Invalid type for PCM type %s definition", str);
    355 			goto _err;
    356 		}
    357 		snd_config_for_each(i, next, type) {
    358 			snd_config_t *n = snd_config_iterator_entry(i);
    359 			const char *id;
    360 			if (snd_config_get_id(n, &id) < 0)
    361 				continue;
    362 			if (strcmp(id, "comment") == 0)
    363 				continue;
    364 			if (strcmp(id, "lib") == 0) {
    365 				err = snd_config_get_string(n, &lib);
    366 				if (err < 0) {
    367 					SNDERR("Invalid type for %s", id);
    368 					goto _err;
    369 				}
    370 				continue;
    371 			}
    372 			if (strcmp(id, "install") == 0) {
    373 				err = snd_config_get_string(n, &install);
    374 				if (err < 0) {
    375 					SNDERR("Invalid type for %s", id);
    376 					goto _err;
    377 				}
    378 				continue;
    379 			}
    380 			SNDERR("Unknown field %s", id);
    381 			err = -EINVAL;
    382 			goto _err;
    383 		}
    384 	}
    385 	if (!install) {
    386 		install = buf;
    387 		snprintf(buf, sizeof(buf), "_snd_pcm_hook_%s_install", str);
    388 	}
    389 	h = snd_dlopen(lib, RTLD_NOW);
    390 	install_func = h ? snd_dlsym(h, install, SND_DLSYM_VERSION(SND_PCM_DLSYM_VERSION)) : NULL;
    391 	err = 0;
    392 	if (!h) {
    393 		SNDERR("Cannot open shared library %s",
    394 		       lib ? lib : "[builtin]");
    395 		err = -ENOENT;
    396 	} else if (!install_func) {
    397 		SNDERR("symbol %s is not defined inside %s", install,
    398 		       lib ? lib : "[builtin]");
    399 		snd_dlclose(h);
    400 		err = -ENXIO;
    401 	}
    402        _err:
    403 	if (type)
    404 		snd_config_delete(type);
    405 	if (err >= 0) {
    406 		if (args && snd_config_get_string(args, &str) >= 0) {
    407 			err = snd_config_search_definition(root, "hook_args", str, &args);
    408 			if (err < 0)
    409 				SNDERR("unknown hook_args %s", str);
    410 			else
    411 				err = install_func(pcm, args);
    412 			snd_config_delete(args);
    413 		} else
    414 			err = install_func(pcm, args);
    415 		snd_dlclose(h);
    416 	}
    417 	if (err < 0)
    418 		return err;
    419 	return 0;
    420 }
    421 
    422 /**
    423  * \brief Creates a new hooks PCM
    424  * \param pcmp Returns created PCM handle
    425  * \param name Name of PCM
    426  * \param root Root configuration node
    427  * \param conf Configuration node with hooks PCM description
    428  * \param stream PCM Stream
    429  * \param mode PCM Mode
    430  * \retval zero on success otherwise a negative error code
    431  * \warning Using of this function might be dangerous in the sense
    432  *          of compatibility reasons. The prototype might be freely
    433  *	    changed in future.
    434  */
    435 int _snd_pcm_hooks_open(snd_pcm_t **pcmp, const char *name,
    436 			snd_config_t *root, snd_config_t *conf,
    437 			snd_pcm_stream_t stream, int mode)
    438 {
    439 	snd_config_iterator_t i, next;
    440 	int err;
    441 	snd_pcm_t *rpcm = NULL, *spcm;
    442 	snd_config_t *slave = NULL, *sconf;
    443 	snd_config_t *hooks = NULL;
    444 	snd_config_for_each(i, next, conf) {
    445 		snd_config_t *n = snd_config_iterator_entry(i);
    446 		const char *id;
    447 		if (snd_config_get_id(n, &id) < 0)
    448 			continue;
    449 		if (snd_pcm_conf_generic_id(id))
    450 			continue;
    451 		if (strcmp(id, "slave") == 0) {
    452 			slave = n;
    453 			continue;
    454 		}
    455 		if (strcmp(id, "hooks") == 0) {
    456 			if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
    457 				SNDERR("Invalid type for %s", id);
    458 				return -EINVAL;
    459 			}
    460 			hooks = n;
    461 			continue;
    462 		}
    463 		SNDERR("Unknown field %s", id);
    464 		return -EINVAL;
    465 	}
    466 	if (!slave) {
    467 		SNDERR("slave is not defined");
    468 		return -EINVAL;
    469 	}
    470 	err = snd_pcm_slave_conf(root, slave, &sconf, 0);
    471 	if (err < 0)
    472 		return err;
    473 	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
    474 	snd_config_delete(sconf);
    475 	if (err < 0)
    476 		return err;
    477 	err = snd_pcm_hooks_open(&rpcm, name, spcm, 1);
    478 	if (err < 0) {
    479 		snd_pcm_close(spcm);
    480 		return err;
    481 	}
    482 	if (!hooks)
    483 		goto _done;
    484 	snd_config_for_each(i, next, hooks) {
    485 		snd_config_t *n = snd_config_iterator_entry(i);
    486 		const char *str;
    487 		if (snd_config_get_string(n, &str) >= 0) {
    488 			err = snd_config_search_definition(root, "pcm_hook", str, &n);
    489 			if (err < 0) {
    490 				SNDERR("unknown pcm_hook %s", str);
    491 			} else {
    492 				err = snd_pcm_hook_add_conf(rpcm, root, n);
    493 				snd_config_delete(n);
    494 			}
    495 		} else
    496 			err = snd_pcm_hook_add_conf(rpcm, root, n);
    497 		if (err < 0) {
    498 			snd_pcm_close(rpcm);
    499 			return err;
    500 		}
    501 	}
    502  _done:
    503 	*pcmp = rpcm;
    504 	return 0;
    505 }
    506 #ifndef DOC_HIDDEN
    507 SND_DLSYM_BUILD_VERSION(_snd_pcm_hooks_open, SND_PCM_DLSYM_VERSION);
    508 #endif
    509 
    510 /**
    511  * \brief Get PCM handle for a PCM hook
    512  * \param hook PCM hook handle
    513  * \return PCM handle
    514  */
    515 snd_pcm_t *snd_pcm_hook_get_pcm(snd_pcm_hook_t *hook)
    516 {
    517 	assert(hook);
    518 	return hook->pcm;
    519 }
    520 
    521 /**
    522  * \brief Get callback function private data for a PCM hook
    523  * \param hook PCM hook handle
    524  * \return callback function private data
    525  */
    526 void *snd_pcm_hook_get_private(snd_pcm_hook_t *hook)
    527 {
    528 	assert(hook);
    529 	return hook->private_data;
    530 }
    531 
    532 /**
    533  * \brief Set callback function private data for a PCM hook
    534  * \param hook PCM hook handle
    535  * \param private_data The private data value
    536  */
    537 void snd_pcm_hook_set_private(snd_pcm_hook_t *hook, void *private_data)
    538 {
    539 	assert(hook);
    540 	hook->private_data = private_data;
    541 }
    542 
    543 /**
    544  * \brief Add a PCM hook at end of hooks chain
    545  * \param hookp Returned PCM hook handle
    546  * \param pcm PCM handle
    547  * \param type PCM hook type
    548  * \param func PCM hook callback function
    549  * \param private_data PCM hook private data
    550  * \return 0 on success otherwise a negative error code
    551  *
    552  * Warning: an hook callback function cannot remove an hook of the same type
    553  * different from itself
    554  */
    555 int snd_pcm_hook_add(snd_pcm_hook_t **hookp, snd_pcm_t *pcm,
    556 		     snd_pcm_hook_type_t type,
    557 		     snd_pcm_hook_func_t func, void *private_data)
    558 {
    559 	snd_pcm_hook_t *h;
    560 	snd_pcm_hooks_t *hooks;
    561 	assert(hookp && func);
    562 	assert(snd_pcm_type(pcm) == SND_PCM_TYPE_HOOKS);
    563 	h = calloc(1, sizeof(*h));
    564 	if (!h)
    565 		return -ENOMEM;
    566 	h->pcm = pcm;
    567 	h->func = func;
    568 	h->private_data = private_data;
    569 	hooks = pcm->private_data;
    570 	list_add_tail(&h->list, &hooks->hooks[type]);
    571 	*hookp = h;
    572 	return 0;
    573 }
    574 
    575 /**
    576  * \brief Remove a PCM hook
    577  * \param hook PCM hook handle
    578  * \return 0 on success otherwise a negative error code
    579  *
    580  * Warning: an hook callback cannot remove an hook of the same type
    581  * different from itself
    582  */
    583 int snd_pcm_hook_remove(snd_pcm_hook_t *hook)
    584 {
    585 	assert(hook);
    586 	list_del(&hook->list);
    587 	free(hook);
    588 	return 0;
    589 }
    590 
    591 /*
    592  *
    593  */
    594 
    595 static int snd_pcm_hook_ctl_elems_hw_params(snd_pcm_hook_t *hook)
    596 {
    597 	snd_sctl_t *h = snd_pcm_hook_get_private(hook);
    598 	return snd_sctl_install(h);
    599 }
    600 
    601 static int snd_pcm_hook_ctl_elems_hw_free(snd_pcm_hook_t *hook)
    602 {
    603 	snd_sctl_t *h = snd_pcm_hook_get_private(hook);
    604 	return snd_sctl_remove(h);
    605 }
    606 
    607 static int snd_pcm_hook_ctl_elems_close(snd_pcm_hook_t *hook)
    608 {
    609 	snd_sctl_t *h = snd_pcm_hook_get_private(hook);
    610 	int err = snd_sctl_free(h);
    611 	snd_pcm_hook_set_private(hook, NULL);
    612 	return err;
    613 }
    614 
    615 /**
    616  * \brief Install CTL settings using hardware associated with PCM handle
    617  * \param pcm PCM handle
    618  * \param conf Configuration node with CTL settings
    619  * \return zero on success otherwise a negative error code
    620  */
    621 int _snd_pcm_hook_ctl_elems_install(snd_pcm_t *pcm, snd_config_t *conf)
    622 {
    623 	int err;
    624 	int card;
    625 	snd_pcm_info_t *info;
    626 	char ctl_name[16];
    627 	snd_ctl_t *ctl;
    628 	snd_sctl_t *sctl = NULL;
    629 	snd_config_t *pcm_conf = NULL;
    630 	snd_pcm_hook_t *h_hw_params = NULL, *h_hw_free = NULL, *h_close = NULL;
    631 	assert(conf);
    632 	assert(snd_config_get_type(conf) == SND_CONFIG_TYPE_COMPOUND);
    633 	snd_pcm_info_alloca(&info);
    634 	err = snd_pcm_info(pcm, info);
    635 	if (err < 0)
    636 		return err;
    637 	card = snd_pcm_info_get_card(info);
    638 	if (card < 0) {
    639 		SNDERR("No card for this PCM");
    640 		return -EINVAL;
    641 	}
    642 	sprintf(ctl_name, "hw:%d", card);
    643 	err = snd_ctl_open(&ctl, ctl_name, 0);
    644 	if (err < 0) {
    645 		SNDERR("Cannot open CTL %s", ctl_name);
    646 		return err;
    647 	}
    648 	err = snd_config_imake_pointer(&pcm_conf, "pcm_handle", pcm);
    649 	if (err < 0)
    650 		goto _err;
    651 	err = snd_sctl_build(&sctl, ctl, conf, pcm_conf, 0);
    652 	if (err < 0)
    653 		goto _err;
    654 	err = snd_pcm_hook_add(&h_hw_params, pcm, SND_PCM_HOOK_TYPE_HW_PARAMS,
    655 			       snd_pcm_hook_ctl_elems_hw_params, sctl);
    656 	if (err < 0)
    657 		goto _err;
    658 	err = snd_pcm_hook_add(&h_hw_free, pcm, SND_PCM_HOOK_TYPE_HW_FREE,
    659 			       snd_pcm_hook_ctl_elems_hw_free, sctl);
    660 	if (err < 0)
    661 		goto _err;
    662 	err = snd_pcm_hook_add(&h_close, pcm, SND_PCM_HOOK_TYPE_CLOSE,
    663 			       snd_pcm_hook_ctl_elems_close, sctl);
    664 	if (err < 0)
    665 		goto _err;
    666 	snd_config_delete(pcm_conf);
    667 	return 0;
    668  _err:
    669 	if (h_hw_params)
    670 		snd_pcm_hook_remove(h_hw_params);
    671 	if (h_hw_free)
    672 		snd_pcm_hook_remove(h_hw_free);
    673 	if (h_close)
    674 		snd_pcm_hook_remove(h_close);
    675 	if (sctl)
    676 		snd_sctl_free(sctl);
    677 	if (pcm_conf)
    678 		snd_config_delete(pcm_conf);
    679 	return err;
    680 }
    681 #ifndef DOC_HIDDEN
    682 SND_DLSYM_BUILD_VERSION(_snd_pcm_hook_ctl_elems_install, SND_PCM_DLSYM_VERSION);
    683 #endif
    684