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