"Fossies" - the Fresh Open Source Software Archive

Member "alsa-lib-1.1.9/src/pcm/pcm_hooks.c" (10 May 2019, 18983 Bytes) of package /linux/misc/alsa-lib-1.1.9.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "pcm_hooks.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.1.5_vs_1.1.6.

    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 
   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     .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  */
  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 
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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 
  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 
  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 
  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  */
  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