tizen 2.3.1 release
[external/alsa-lib.git] / src / pcm / pcm_hooks.c
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., 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 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
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
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
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
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
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
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 };
169
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,
199 };
200
201 /**
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
210  *          changed in future.
211  */
212 int snd_pcm_hooks_open(snd_pcm_t **pcmp, const char *name, snd_pcm_t *slave, int close_slave)
213 {
214         snd_pcm_t *pcm;
215         snd_pcm_hooks_t *h;
216         unsigned int k;
217         int err;
218         assert(pcmp && slave);
219         h = calloc(1, sizeof(snd_pcm_hooks_t));
220         if (!h)
221                 return -ENOMEM;
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]);
226         }
227         INIT_LIST_HEAD(&h->dllist);
228         err = snd_pcm_new(&pcm, SND_PCM_TYPE_HOOKS, name, slave->stream, slave->mode);
229         if (err < 0) {
230                 free(h);
231                 return err;
232         }
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);
242         *pcmp = pcm;
243
244         return 0;
245 }
246
247 /*! \page pcm_plugins
248
249 \section pcm_plugins_hooks Plugin: hooks
250
251 This plugin is used to call some 'hook' function when this plugin is opened,
252 modified or closed.
253 Typically, it is used to change control values for a certain state
254 specially for the PCM (see the example below).
255
256 \code
257 # Hook arguments definition
258 hook_args.NAME {
259         ...                     # Arbitrary arguments
260 }
261
262 # PCM hook type
263 pcm_hook_type.NAME {
264         [lib STR]               # Library file (default libasound.so)
265         [install STR]           # Install function (default _snd_pcm_hook_NAME_install)
266 }
267
268 # PCM hook definition
269 pcm_hook.NAME {
270         type STR                # PCM Hook type (see pcm_hook_type)
271         [args STR]              # Arguments for install function (see hook_args)
272         # or
273         [args { }]              # Arguments for install function
274 }
275
276 # PCM hook plugin
277 pcm.NAME {
278         type hooks              # PCM with hooks
279         slave STR               # Slave name
280         # or
281         slave {                 # Slave definition
282                 pcm STR         # Slave PCM name
283                 # or
284                 pcm { }         # Slave PCM definition
285         }
286         hooks {
287                 ID STR          # Hook name (see pcm_hook)
288                 # or
289                 ID { }          # Hook definition (see pcm_hook)
290         }
291 }
292 \endcode
293
294 Example:
295
296 \code
297         hooks.0 {
298                 type ctl_elems
299                 hook_args [
300                         {
301                                 name "Wave Surround Playback Volume"
302                                 preserve true
303                                 lock true
304                                 optional true
305                                 value [ 0 0 ]
306                         }
307                         {
308                                 name "EMU10K1 PCM Send Volume"
309                                 index { @func private_pcm_subdevice }
310                                 lock true
311                                 value [ 0 0 0 0 0 0 255 0 0 0 0 255 ]
312                         }
313                 ]
314         }
315 \endcode
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
319 an array.
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.
325
326 \subsection pcm_plugins_hooks_funcref Function reference
327
328 <UL>
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()
333 </UL>
334
335 */
336
337 static int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *root, snd_config_t *conf)
338 {
339         int err;
340         char buf[256];
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;
346         void *h = NULL;
347
348         if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) {
349                 SNDERR("Invalid hook definition");
350                 return -EINVAL;
351         }
352         snd_config_for_each(i, next, conf) {
353                 snd_config_t *n = snd_config_iterator_entry(i);
354                 const char *id;
355                 if (snd_config_get_id(n, &id) < 0)
356                         continue;
357                 if (strcmp(id, "comment") == 0)
358                         continue;
359                 if (strcmp(id, "type") == 0) {
360                         type = n;
361                         continue;
362                 }
363                 if (strcmp(id, "hook_args") == 0) {
364                         args = n;
365                         continue;
366                 }
367                 SNDERR("Unknown field %s", id);
368                 return -EINVAL;
369         }
370         if (!type) {
371                 SNDERR("type is not defined");
372                 return -EINVAL;
373         }
374         err = snd_config_get_id(type, &id);
375         if (err < 0) {
376                 SNDERR("unable to get id");
377                 return err;
378         }
379         err = snd_config_get_string(type, &str);
380         if (err < 0) {
381                 SNDERR("Invalid type for %s", id);
382                 return err;
383         }
384         err = snd_config_search_definition(root, "pcm_hook_type", str, &type);
385         if (err >= 0) {
386                 if (snd_config_get_type(type) != SND_CONFIG_TYPE_COMPOUND) {
387                         SNDERR("Invalid type for PCM type %s definition", str);
388                         err = -EINVAL;
389                         goto _err;
390                 }
391                 snd_config_for_each(i, next, type) {
392                         snd_config_t *n = snd_config_iterator_entry(i);
393                         const char *id;
394                         if (snd_config_get_id(n, &id) < 0)
395                                 continue;
396                         if (strcmp(id, "comment") == 0)
397                                 continue;
398                         if (strcmp(id, "lib") == 0) {
399                                 err = snd_config_get_string(n, &lib);
400                                 if (err < 0) {
401                                         SNDERR("Invalid type for %s", id);
402                                         goto _err;
403                                 }
404                                 continue;
405                         }
406                         if (strcmp(id, "install") == 0) {
407                                 err = snd_config_get_string(n, &install);
408                                 if (err < 0) {
409                                         SNDERR("Invalid type for %s", id);
410                                         goto _err;
411                                 }
412                                 continue;
413                         }
414                         SNDERR("Unknown field %s", id);
415                         err = -EINVAL;
416                         goto _err;
417                 }
418         }
419         if (!install) {
420                 install = buf;
421                 snprintf(buf, sizeof(buf), "_snd_pcm_hook_%s_install", str);
422         }
423         h = snd_dlopen(lib, RTLD_NOW);
424         install_func = h ? snd_dlsym(h, install, SND_DLSYM_VERSION(SND_PCM_DLSYM_VERSION)) : NULL;
425         err = 0;
426         if (!h) {
427                 SNDERR("Cannot open shared library %s",
428                        lib ? lib : "[builtin]");
429                 err = -ENOENT;
430         } else if (!install_func) {
431                 SNDERR("symbol %s is not defined inside %s", install,
432                        lib ? lib : "[builtin]");
433                 snd_dlclose(h);
434                 err = -ENXIO;
435         }
436        _err:
437         if (type)
438                 snd_config_delete(type);
439         if (err < 0)
440                 return err;
441
442         if (args && snd_config_get_string(args, &str) >= 0) {
443                 err = snd_config_search_definition(root, "hook_args", str, &args);
444                 if (err < 0)
445                         SNDERR("unknown hook_args %s", str);
446                 else
447                         err = install_func(pcm, args);
448                 snd_config_delete(args);
449         } else
450                 err = install_func(pcm, args);
451
452         if (err >= 0)
453                 err = hook_add_dlobj(pcm, h);
454
455         if (err < 0) {
456                 snd_dlclose(h);
457                 return err;
458         }
459         return 0;
460 }
461
462 /**
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
473  *          changed in future.
474  */
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)
478 {
479         snd_config_iterator_t i, next;
480         int err;
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);
486                 const char *id;
487                 if (snd_config_get_id(n, &id) < 0)
488                         continue;
489                 if (snd_pcm_conf_generic_id(id))
490                         continue;
491                 if (strcmp(id, "slave") == 0) {
492                         slave = n;
493                         continue;
494                 }
495                 if (strcmp(id, "hooks") == 0) {
496                         if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
497                                 SNDERR("Invalid type for %s", id);
498                                 return -EINVAL;
499                         }
500                         hooks = n;
501                         continue;
502                 }
503                 SNDERR("Unknown field %s", id);
504                 return -EINVAL;
505         }
506         if (!slave) {
507                 SNDERR("slave is not defined");
508                 return -EINVAL;
509         }
510         err = snd_pcm_slave_conf(root, slave, &sconf, 0);
511         if (err < 0)
512                 return err;
513         err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
514         snd_config_delete(sconf);
515         if (err < 0)
516                 return err;
517         err = snd_pcm_hooks_open(&rpcm, name, spcm, 1);
518         if (err < 0) {
519                 snd_pcm_close(spcm);
520                 return err;
521         }
522         if (!hooks)
523                 goto _done;
524         snd_config_for_each(i, next, hooks) {
525                 snd_config_t *n = snd_config_iterator_entry(i);
526                 const char *str;
527                 if (snd_config_get_string(n, &str) >= 0) {
528                         err = snd_config_search_definition(root, "pcm_hook", str, &n);
529                         if (err < 0) {
530                                 SNDERR("unknown pcm_hook %s", str);
531                         } else {
532                                 err = snd_pcm_hook_add_conf(rpcm, root, n);
533                                 snd_config_delete(n);
534                         }
535                 } else
536                         err = snd_pcm_hook_add_conf(rpcm, root, n);
537                 if (err < 0) {
538                         snd_pcm_close(rpcm);
539                         return err;
540                 }
541         }
542  _done:
543         *pcmp = rpcm;
544         return 0;
545 }
546 #ifndef DOC_HIDDEN
547 SND_DLSYM_BUILD_VERSION(_snd_pcm_hooks_open, SND_PCM_DLSYM_VERSION);
548 #endif
549
550 /**
551  * \brief Get PCM handle for a PCM hook
552  * \param hook PCM hook handle
553  * \return PCM handle
554  */
555 snd_pcm_t *snd_pcm_hook_get_pcm(snd_pcm_hook_t *hook)
556 {
557         assert(hook);
558         return hook->pcm;
559 }
560
561 /**
562  * \brief Get callback function private data for a PCM hook
563  * \param hook PCM hook handle
564  * \return callback function private data
565  */
566 void *snd_pcm_hook_get_private(snd_pcm_hook_t *hook)
567 {
568         assert(hook);
569         return hook->private_data;
570 }
571
572 /**
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
576  */
577 void snd_pcm_hook_set_private(snd_pcm_hook_t *hook, void *private_data)
578 {
579         assert(hook);
580         hook->private_data = private_data;
581 }
582
583 /**
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
591  *
592  * Warning: an hook callback function cannot remove an hook of the same type
593  * different from itself
594  */
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)
598 {
599         snd_pcm_hook_t *h;
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));
604         if (!h)
605                 return -ENOMEM;
606         h->pcm = pcm;
607         h->func = func;
608         h->private_data = private_data;
609         hooks = pcm->private_data;
610         list_add_tail(&h->list, &hooks->hooks[type]);
611         *hookp = h;
612         return 0;
613 }
614
615 /**
616  * \brief Remove a PCM hook
617  * \param hook PCM hook handle
618  * \return 0 on success otherwise a negative error code
619  *
620  * Warning: an hook callback cannot remove an hook of the same type
621  * different from itself
622  */
623 int snd_pcm_hook_remove(snd_pcm_hook_t *hook)
624 {
625         assert(hook);
626         list_del(&hook->list);
627         free(hook);
628         return 0;
629 }
630
631 /*
632  *
633  */
634
635 static int snd_pcm_hook_ctl_elems_hw_params(snd_pcm_hook_t *hook)
636 {
637         snd_sctl_t *h = snd_pcm_hook_get_private(hook);
638         return snd_sctl_install(h);
639 }
640
641 static int snd_pcm_hook_ctl_elems_hw_free(snd_pcm_hook_t *hook)
642 {
643         snd_sctl_t *h = snd_pcm_hook_get_private(hook);
644         return snd_sctl_remove(h);
645 }
646
647 static int snd_pcm_hook_ctl_elems_close(snd_pcm_hook_t *hook)
648 {
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);
652         return err;
653 }
654
655 /**
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
660  */
661 int _snd_pcm_hook_ctl_elems_install(snd_pcm_t *pcm, snd_config_t *conf)
662 {
663         int err;
664         int card;
665         snd_pcm_info_t *info;
666         char ctl_name[16];
667         snd_ctl_t *ctl;
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;
671         assert(conf);
672         assert(snd_config_get_type(conf) == SND_CONFIG_TYPE_COMPOUND);
673         snd_pcm_info_alloca(&info);
674         err = snd_pcm_info(pcm, info);
675         if (err < 0)
676                 return err;
677         card = snd_pcm_info_get_card(info);
678         if (card < 0) {
679                 SNDERR("No card for this PCM");
680                 return -EINVAL;
681         }
682         sprintf(ctl_name, "hw:%d", card);
683         err = snd_ctl_open(&ctl, ctl_name, 0);
684         if (err < 0) {
685                 SNDERR("Cannot open CTL %s", ctl_name);
686                 return err;
687         }
688         err = snd_config_imake_pointer(&pcm_conf, "pcm_handle", pcm);
689         if (err < 0)
690                 goto _err;
691         err = snd_sctl_build(&sctl, ctl, conf, pcm_conf, 0);
692         if (err < 0)
693                 goto _err;
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);
696         if (err < 0)
697                 goto _err;
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);
700         if (err < 0)
701                 goto _err;
702         err = snd_pcm_hook_add(&h_close, pcm, SND_PCM_HOOK_TYPE_CLOSE,
703                                snd_pcm_hook_ctl_elems_close, sctl);
704         if (err < 0)
705                 goto _err;
706         snd_config_delete(pcm_conf);
707         return 0;
708  _err:
709         if (h_hw_params)
710                 snd_pcm_hook_remove(h_hw_params);
711         if (h_hw_free)
712                 snd_pcm_hook_remove(h_hw_free);
713         if (h_close)
714                 snd_pcm_hook_remove(h_close);
715         if (sctl)
716                 snd_sctl_free(sctl);
717         if (pcm_conf)
718                 snd_config_delete(pcm_conf);
719         return err;
720 }
721 #ifndef DOC_HIDDEN
722 SND_DLSYM_BUILD_VERSION(_snd_pcm_hook_ctl_elems_install, SND_PCM_DLSYM_VERSION);
723 #endif