2 * \file pcm/pcm_hooks.c
4 * \brief PCM Hook Interface
5 * \author Abramo Bagnara <abramo@alsa-project.org>
6 * \author Jaroslav Kysela <perex@perex.cz>
10 * PCM - Hook functions
11 * Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org>
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.
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.
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
30 #include "pcm_local.h"
31 #include "pcm_generic.h"
34 /* entry for static linking */
35 const char *_snd_module_pcm_hooks = "";
39 struct _snd_pcm_hook {
41 snd_pcm_hook_func_t func;
43 struct list_head list;
46 struct snd_pcm_hook_dllist {
48 struct list_head list;
52 snd_pcm_generic_t gen;
53 struct list_head hooks[SND_PCM_HOOK_TYPE_LAST + 1];
54 struct list_head dllist;
58 static int hook_add_dlobj(snd_pcm_t *pcm, void *dlobj)
60 snd_pcm_hooks_t *h = pcm->private_data;
61 struct snd_pcm_hook_dllist *dl;
63 dl = malloc(sizeof(*dl));
68 list_add_tail(&dl->list, &h->dllist);
72 static void hook_remove_dlobj(struct snd_pcm_hook_dllist *dl)
75 snd_dlclose(dl->dlobj);
79 static int snd_pcm_hooks_close(snd_pcm_t *pcm)
81 snd_pcm_hooks_t *h = pcm->private_data;
82 struct list_head *pos, *next;
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);
92 for (k = 0; k <= SND_PCM_HOOK_TYPE_LAST; ++k) {
93 struct list_head *hooks = &h->hooks[k];
94 while (!list_empty(hooks)) {
97 hook = list_entry(pos, snd_pcm_hook_t, list);
98 snd_pcm_hook_remove(hook);
101 while (!list_empty(&h->dllist)) {
102 pos = h->dllist.next;
103 hook_remove_dlobj(list_entry(pos, struct snd_pcm_hook_dllist, list));
105 err = snd_pcm_generic_close(pcm);
111 static int snd_pcm_hooks_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
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);
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);
127 static int snd_pcm_hooks_hw_free(snd_pcm_t *pcm)
129 snd_pcm_hooks_t *h = pcm->private_data;
130 struct list_head *pos, *next;
131 int err = snd_pcm_generic_hw_free(pcm);
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);
143 static void snd_pcm_hooks_dump(snd_pcm_t *pcm, snd_output_t *out)
145 snd_pcm_hooks_t *h = pcm->private_data;
146 snd_output_printf(out, "Hooks PCM\n");
148 snd_output_printf(out, "Its setup is:\n");
149 snd_pcm_dump_setup(pcm, out);
151 snd_output_printf(out, "Slave: ");
152 snd_pcm_dump(h->gen.slave, out);
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,
170 static const snd_pcm_fast_ops_t snd_pcm_hooks_fast_ops = {
171 .status = snd_pcm_generic_status,
172 .state = snd_pcm_generic_state,
173 .hwsync = snd_pcm_generic_hwsync,
174 .delay = snd_pcm_generic_delay,
175 .prepare = snd_pcm_generic_prepare,
176 .reset = snd_pcm_generic_reset,
177 .start = snd_pcm_generic_start,
178 .drop = snd_pcm_generic_drop,
179 .drain = snd_pcm_generic_drain,
180 .pause = snd_pcm_generic_pause,
181 .rewindable = snd_pcm_generic_rewindable,
182 .rewind = snd_pcm_generic_rewind,
183 .forwardable = snd_pcm_generic_forwardable,
184 .forward = snd_pcm_generic_forward,
185 .resume = snd_pcm_generic_resume,
186 .link = snd_pcm_generic_link,
187 .link_slaves = snd_pcm_generic_link_slaves,
188 .unlink = snd_pcm_generic_unlink,
189 .writei = snd_pcm_generic_writei,
190 .writen = snd_pcm_generic_writen,
191 .readi = snd_pcm_generic_readi,
192 .readn = snd_pcm_generic_readn,
193 .avail_update = snd_pcm_generic_avail_update,
194 .mmap_commit = snd_pcm_generic_mmap_commit,
195 .htimestamp = snd_pcm_generic_htimestamp,
196 .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
197 .poll_descriptors = snd_pcm_generic_poll_descriptors,
198 .poll_revents = snd_pcm_generic_poll_revents,
202 * \brief Creates a new hooks PCM
203 * \param pcmp Returns created PCM handle
204 * \param name Name of PCM
205 * \param slave Slave PCM
206 * \param close_slave If set, slave PCM handle is closed when hooks PCM is closed
207 * \retval zero on success otherwise a negative error code
208 * \warning Using of this function might be dangerous in the sense
209 * of compatibility reasons. The prototype might be freely
212 int snd_pcm_hooks_open(snd_pcm_t **pcmp, const char *name, snd_pcm_t *slave, int close_slave)
218 assert(pcmp && slave);
219 h = calloc(1, sizeof(snd_pcm_hooks_t));
222 h->gen.slave = slave;
223 h->gen.close_slave = close_slave;
224 for (k = 0; k <= SND_PCM_HOOK_TYPE_LAST; ++k) {
225 INIT_LIST_HEAD(&h->hooks[k]);
227 INIT_LIST_HEAD(&h->dllist);
228 err = snd_pcm_new(&pcm, SND_PCM_TYPE_HOOKS, name, slave->stream, slave->mode);
233 pcm->ops = &snd_pcm_hooks_ops;
234 pcm->fast_ops = &snd_pcm_hooks_fast_ops;
235 pcm->private_data = h;
236 pcm->poll_fd = slave->poll_fd;
237 pcm->poll_events = slave->poll_events;
238 pcm->mmap_shadow = 1;
239 pcm->monotonic = slave->monotonic;
240 snd_pcm_link_hw_ptr(pcm, slave);
241 snd_pcm_link_appl_ptr(pcm, slave);
247 /*! \page pcm_plugins
249 \section pcm_plugins_hooks Plugin: hooks
251 This plugin is used to call some 'hook' function when this plugin is opened,
253 Typically, it is used to change control values for a certain state
254 specially for the PCM (see the example below).
257 # Hook arguments definition
259 ... # Arbitrary arguments
264 [lib STR] # Library file (default libasound.so)
265 [install STR] # Install function (default _snd_pcm_hook_NAME_install)
268 # PCM hook definition
270 type STR # PCM Hook type (see pcm_hook_type)
271 [args STR] # Arguments for install function (see hook_args)
273 [args { }] # Arguments for install function
278 type hooks # PCM with hooks
279 slave STR # Slave name
281 slave { # Slave definition
282 pcm STR # Slave PCM name
284 pcm { } # Slave PCM definition
287 ID STR # Hook name (see pcm_hook)
289 ID { } # Hook definition (see pcm_hook)
301 name "Wave Surround Playback Volume"
308 name "EMU10K1 PCM Send Volume"
309 index { @func private_pcm_subdevice }
311 value [ 0 0 0 0 0 0 255 0 0 0 0 255 ]
316 Here, the controls "Wave Surround Playback Volume" and "EMU10K1 PCM Send Volume"
317 are set to the given values when this pcm is accessed. Since these controls
318 take multi-dimensional values, the <code>value</code> field is written as
320 When <code>preserve</code> is true, the old values are saved and restored
321 when the pcm is closed. The <code>lock</code> means that the control is
322 locked during this pcm is opened, and cannot be changed by others.
323 When <code>optional</code> is set, no error is returned but ignored
324 even if the specified control doesn't exist.
326 \subsection pcm_plugins_hooks_funcref Function reference
329 <LI>The function ctl_elems - _snd_pcm_hook_ctl_elems_install() - installs
330 CTL settings described by given configuration.
331 <LI>snd_pcm_hooks_open()
332 <LI>_snd_pcm_hooks_open()
337 static int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *root, snd_config_t *conf)
341 const char *str, *id;
342 const char *lib = NULL, *install = NULL;
343 snd_config_t *type = NULL, *args = NULL;
344 snd_config_iterator_t i, next;
345 int (*install_func)(snd_pcm_t *pcm, snd_config_t *args) = NULL;
348 if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) {
349 SNDERR("Invalid hook definition");
352 snd_config_for_each(i, next, conf) {
353 snd_config_t *n = snd_config_iterator_entry(i);
355 if (snd_config_get_id(n, &id) < 0)
357 if (strcmp(id, "comment") == 0)
359 if (strcmp(id, "type") == 0) {
363 if (strcmp(id, "hook_args") == 0) {
367 SNDERR("Unknown field %s", id);
371 SNDERR("type is not defined");
374 err = snd_config_get_id(type, &id);
376 SNDERR("unable to get id");
379 err = snd_config_get_string(type, &str);
381 SNDERR("Invalid type for %s", id);
384 err = snd_config_search_definition(root, "pcm_hook_type", str, &type);
386 if (snd_config_get_type(type) != SND_CONFIG_TYPE_COMPOUND) {
387 SNDERR("Invalid type for PCM type %s definition", str);
391 snd_config_for_each(i, next, type) {
392 snd_config_t *n = snd_config_iterator_entry(i);
394 if (snd_config_get_id(n, &id) < 0)
396 if (strcmp(id, "comment") == 0)
398 if (strcmp(id, "lib") == 0) {
399 err = snd_config_get_string(n, &lib);
401 SNDERR("Invalid type for %s", id);
406 if (strcmp(id, "install") == 0) {
407 err = snd_config_get_string(n, &install);
409 SNDERR("Invalid type for %s", id);
414 SNDERR("Unknown field %s", id);
421 snprintf(buf, sizeof(buf), "_snd_pcm_hook_%s_install", str);
423 h = snd_dlopen(lib, RTLD_NOW);
424 install_func = h ? snd_dlsym(h, install, SND_DLSYM_VERSION(SND_PCM_DLSYM_VERSION)) : NULL;
427 SNDERR("Cannot open shared library %s",
428 lib ? lib : "[builtin]");
430 } else if (!install_func) {
431 SNDERR("symbol %s is not defined inside %s", install,
432 lib ? lib : "[builtin]");
438 snd_config_delete(type);
442 if (args && snd_config_get_string(args, &str) >= 0) {
443 err = snd_config_search_definition(root, "hook_args", str, &args);
445 SNDERR("unknown hook_args %s", str);
447 err = install_func(pcm, args);
448 snd_config_delete(args);
450 err = install_func(pcm, args);
453 err = hook_add_dlobj(pcm, h);
463 * \brief Creates a new hooks PCM
464 * \param pcmp Returns created PCM handle
465 * \param name Name of PCM
466 * \param root Root configuration node
467 * \param conf Configuration node with hooks PCM description
468 * \param stream PCM Stream
469 * \param mode PCM Mode
470 * \retval zero on success otherwise a negative error code
471 * \warning Using of this function might be dangerous in the sense
472 * of compatibility reasons. The prototype might be freely
475 int _snd_pcm_hooks_open(snd_pcm_t **pcmp, const char *name,
476 snd_config_t *root, snd_config_t *conf,
477 snd_pcm_stream_t stream, int mode)
479 snd_config_iterator_t i, next;
481 snd_pcm_t *rpcm = NULL, *spcm;
482 snd_config_t *slave = NULL, *sconf;
483 snd_config_t *hooks = NULL;
484 snd_config_for_each(i, next, conf) {
485 snd_config_t *n = snd_config_iterator_entry(i);
487 if (snd_config_get_id(n, &id) < 0)
489 if (snd_pcm_conf_generic_id(id))
491 if (strcmp(id, "slave") == 0) {
495 if (strcmp(id, "hooks") == 0) {
496 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
497 SNDERR("Invalid type for %s", id);
503 SNDERR("Unknown field %s", id);
507 SNDERR("slave is not defined");
510 err = snd_pcm_slave_conf(root, slave, &sconf, 0);
513 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
514 snd_config_delete(sconf);
517 err = snd_pcm_hooks_open(&rpcm, name, spcm, 1);
524 snd_config_for_each(i, next, hooks) {
525 snd_config_t *n = snd_config_iterator_entry(i);
527 if (snd_config_get_string(n, &str) >= 0) {
528 err = snd_config_search_definition(root, "pcm_hook", str, &n);
530 SNDERR("unknown pcm_hook %s", str);
532 err = snd_pcm_hook_add_conf(rpcm, root, n);
533 snd_config_delete(n);
536 err = snd_pcm_hook_add_conf(rpcm, root, n);
547 SND_DLSYM_BUILD_VERSION(_snd_pcm_hooks_open, SND_PCM_DLSYM_VERSION);
551 * \brief Get PCM handle for a PCM hook
552 * \param hook PCM hook handle
555 snd_pcm_t *snd_pcm_hook_get_pcm(snd_pcm_hook_t *hook)
562 * \brief Get callback function private data for a PCM hook
563 * \param hook PCM hook handle
564 * \return callback function private data
566 void *snd_pcm_hook_get_private(snd_pcm_hook_t *hook)
569 return hook->private_data;
573 * \brief Set callback function private data for a PCM hook
574 * \param hook PCM hook handle
575 * \param private_data The private data value
577 void snd_pcm_hook_set_private(snd_pcm_hook_t *hook, void *private_data)
580 hook->private_data = private_data;
584 * \brief Add a PCM hook at end of hooks chain
585 * \param hookp Returned PCM hook handle
586 * \param pcm PCM handle
587 * \param type PCM hook type
588 * \param func PCM hook callback function
589 * \param private_data PCM hook private data
590 * \return 0 on success otherwise a negative error code
592 * Warning: an hook callback function cannot remove an hook of the same type
593 * different from itself
595 int snd_pcm_hook_add(snd_pcm_hook_t **hookp, snd_pcm_t *pcm,
596 snd_pcm_hook_type_t type,
597 snd_pcm_hook_func_t func, void *private_data)
600 snd_pcm_hooks_t *hooks;
601 assert(hookp && func);
602 assert(snd_pcm_type(pcm) == SND_PCM_TYPE_HOOKS);
603 h = calloc(1, sizeof(*h));
608 h->private_data = private_data;
609 hooks = pcm->private_data;
610 list_add_tail(&h->list, &hooks->hooks[type]);
616 * \brief Remove a PCM hook
617 * \param hook PCM hook handle
618 * \return 0 on success otherwise a negative error code
620 * Warning: an hook callback cannot remove an hook of the same type
621 * different from itself
623 int snd_pcm_hook_remove(snd_pcm_hook_t *hook)
626 list_del(&hook->list);
635 static int snd_pcm_hook_ctl_elems_hw_params(snd_pcm_hook_t *hook)
637 snd_sctl_t *h = snd_pcm_hook_get_private(hook);
638 return snd_sctl_install(h);
641 static int snd_pcm_hook_ctl_elems_hw_free(snd_pcm_hook_t *hook)
643 snd_sctl_t *h = snd_pcm_hook_get_private(hook);
644 return snd_sctl_remove(h);
647 static int snd_pcm_hook_ctl_elems_close(snd_pcm_hook_t *hook)
649 snd_sctl_t *h = snd_pcm_hook_get_private(hook);
650 int err = snd_sctl_free(h);
651 snd_pcm_hook_set_private(hook, NULL);
656 * \brief Install CTL settings using hardware associated with PCM handle
657 * \param pcm PCM handle
658 * \param conf Configuration node with CTL settings
659 * \return zero on success otherwise a negative error code
661 int _snd_pcm_hook_ctl_elems_install(snd_pcm_t *pcm, snd_config_t *conf)
665 snd_pcm_info_t *info;
668 snd_sctl_t *sctl = NULL;
669 snd_config_t *pcm_conf = NULL;
670 snd_pcm_hook_t *h_hw_params = NULL, *h_hw_free = NULL, *h_close = NULL;
672 assert(snd_config_get_type(conf) == SND_CONFIG_TYPE_COMPOUND);
673 snd_pcm_info_alloca(&info);
674 err = snd_pcm_info(pcm, info);
677 card = snd_pcm_info_get_card(info);
679 SNDERR("No card for this PCM");
682 sprintf(ctl_name, "hw:%d", card);
683 err = snd_ctl_open(&ctl, ctl_name, 0);
685 SNDERR("Cannot open CTL %s", ctl_name);
688 err = snd_config_imake_pointer(&pcm_conf, "pcm_handle", pcm);
691 err = snd_sctl_build(&sctl, ctl, conf, pcm_conf, 0);
694 err = snd_pcm_hook_add(&h_hw_params, pcm, SND_PCM_HOOK_TYPE_HW_PARAMS,
695 snd_pcm_hook_ctl_elems_hw_params, sctl);
698 err = snd_pcm_hook_add(&h_hw_free, pcm, SND_PCM_HOOK_TYPE_HW_FREE,
699 snd_pcm_hook_ctl_elems_hw_free, sctl);
702 err = snd_pcm_hook_add(&h_close, pcm, SND_PCM_HOOK_TYPE_CLOSE,
703 snd_pcm_hook_ctl_elems_close, sctl);
706 snd_config_delete(pcm_conf);
710 snd_pcm_hook_remove(h_hw_params);
712 snd_pcm_hook_remove(h_hw_free);
714 snd_pcm_hook_remove(h_close);
718 snd_config_delete(pcm_conf);
722 SND_DLSYM_BUILD_VERSION(_snd_pcm_hook_ctl_elems_install, SND_PCM_DLSYM_VERSION);