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