LCOV - code coverage report
Current view: top level - sound/core - seq_device.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 15 110 13.6 %
Date: 2022-04-01 14:35:51 Functions: 3 18 16.7 %
Branches: 4 42 9.5 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0-or-later
       2                 :            : /*
       3                 :            :  *  ALSA sequencer device management
       4                 :            :  *  Copyright (c) 1999 by Takashi Iwai <tiwai@suse.de>
       5                 :            :  *
       6                 :            :  *----------------------------------------------------------------
       7                 :            :  *
       8                 :            :  * This device handler separates the card driver module from sequencer
       9                 :            :  * stuff (sequencer core, synth drivers, etc), so that user can avoid
      10                 :            :  * to spend unnecessary resources e.g. if he needs only listening to
      11                 :            :  * MP3s.
      12                 :            :  *
      13                 :            :  * The card (or lowlevel) driver creates a sequencer device entry
      14                 :            :  * via snd_seq_device_new().  This is an entry pointer to communicate
      15                 :            :  * with the sequencer device "driver", which is involved with the
      16                 :            :  * actual part to communicate with the sequencer core.
      17                 :            :  * Each sequencer device entry has an id string and the corresponding
      18                 :            :  * driver with the same id is loaded when required.  For example,
      19                 :            :  * lowlevel codes to access emu8000 chip on sbawe card are included in
      20                 :            :  * emu8000-synth module.  To activate this module, the hardware
      21                 :            :  * resources like i/o port are passed via snd_seq_device argument.
      22                 :            :  */
      23                 :            : 
      24                 :            : #include <linux/device.h>
      25                 :            : #include <linux/init.h>
      26                 :            : #include <linux/module.h>
      27                 :            : #include <sound/core.h>
      28                 :            : #include <sound/info.h>
      29                 :            : #include <sound/seq_device.h>
      30                 :            : #include <sound/seq_kernel.h>
      31                 :            : #include <sound/initval.h>
      32                 :            : #include <linux/kmod.h>
      33                 :            : #include <linux/slab.h>
      34                 :            : #include <linux/mutex.h>
      35                 :            : 
      36                 :            : MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
      37                 :            : MODULE_DESCRIPTION("ALSA sequencer device management");
      38                 :            : MODULE_LICENSE("GPL");
      39                 :            : 
      40                 :            : /*
      41                 :            :  * bus definition
      42                 :            :  */
      43                 :          0 : static int snd_seq_bus_match(struct device *dev, struct device_driver *drv)
      44                 :            : {
      45                 :          0 :         struct snd_seq_device *sdev = to_seq_dev(dev);
      46                 :          0 :         struct snd_seq_driver *sdrv = to_seq_drv(drv);
      47                 :            : 
      48         [ #  # ]:          0 :         return strcmp(sdrv->id, sdev->id) == 0 &&
      49         [ #  # ]:          0 :                 sdrv->argsize == sdev->argsize;
      50                 :            : }
      51                 :            : 
      52                 :            : static struct bus_type snd_seq_bus_type = {
      53                 :            :         .name = "snd_seq",
      54                 :            :         .match = snd_seq_bus_match,
      55                 :            : };
      56                 :            : 
      57                 :            : /*
      58                 :            :  * proc interface -- just for compatibility
      59                 :            :  */
      60                 :            : #ifdef CONFIG_SND_PROC_FS
      61                 :            : static struct snd_info_entry *info_entry;
      62                 :            : 
      63                 :          0 : static int print_dev_info(struct device *dev, void *data)
      64                 :            : {
      65                 :          0 :         struct snd_seq_device *sdev = to_seq_dev(dev);
      66                 :          0 :         struct snd_info_buffer *buffer = data;
      67                 :            : 
      68         [ #  # ]:          0 :         snd_iprintf(buffer, "snd-%s,%s,%d\n", sdev->id,
      69                 :            :                     dev->driver ? "loaded" : "empty",
      70                 :            :                     dev->driver ? 1 : 0);
      71                 :          0 :         return 0;
      72                 :            : }
      73                 :            : 
      74                 :          0 : static void snd_seq_device_info(struct snd_info_entry *entry,
      75                 :            :                                 struct snd_info_buffer *buffer)
      76                 :            : {
      77                 :          0 :         bus_for_each_dev(&snd_seq_bus_type, NULL, buffer, print_dev_info);
      78                 :          0 : }
      79                 :            : #endif
      80                 :            : 
      81                 :            : /*
      82                 :            :  * load all registered drivers (called from seq_clientmgr.c)
      83                 :            :  */
      84                 :            : 
      85                 :            : #ifdef CONFIG_MODULES
      86                 :            : /* flag to block auto-loading */
      87                 :            : static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */
      88                 :            : 
      89                 :          0 : static int request_seq_drv(struct device *dev, void *data)
      90                 :            : {
      91                 :          0 :         struct snd_seq_device *sdev = to_seq_dev(dev);
      92                 :            : 
      93         [ #  # ]:          0 :         if (!dev->driver)
      94                 :          0 :                 request_module("snd-%s", sdev->id);
      95                 :          0 :         return 0;
      96                 :            : }
      97                 :            : 
      98                 :          0 : static void autoload_drivers(struct work_struct *work)
      99                 :            : {
     100                 :            :         /* avoid reentrance */
     101         [ #  # ]:          0 :         if (atomic_inc_return(&snd_seq_in_init) == 1)
     102                 :          0 :                 bus_for_each_dev(&snd_seq_bus_type, NULL, NULL,
     103                 :            :                                  request_seq_drv);
     104                 :          0 :         atomic_dec(&snd_seq_in_init);
     105                 :          0 : }
     106                 :            : 
     107                 :            : static DECLARE_WORK(autoload_work, autoload_drivers);
     108                 :            : 
     109                 :          0 : static void queue_autoload_drivers(void)
     110                 :            : {
     111                 :          0 :         schedule_work(&autoload_work);
     112                 :          0 : }
     113                 :            : 
     114                 :         21 : void snd_seq_autoload_init(void)
     115                 :            : {
     116                 :         21 :         atomic_dec(&snd_seq_in_init);
     117                 :            : #ifdef CONFIG_SND_SEQUENCER_MODULE
     118                 :            :         /* initial autoload only when snd-seq is a module */
     119                 :            :         queue_autoload_drivers();
     120                 :            : #endif
     121                 :         21 : }
     122                 :            : EXPORT_SYMBOL(snd_seq_autoload_init);
     123                 :            : 
     124                 :          0 : void snd_seq_autoload_exit(void)
     125                 :            : {
     126                 :          0 :         atomic_inc(&snd_seq_in_init);
     127                 :          0 : }
     128                 :            : EXPORT_SYMBOL(snd_seq_autoload_exit);
     129                 :            : 
     130                 :          0 : void snd_seq_device_load_drivers(void)
     131                 :            : {
     132                 :          0 :         queue_autoload_drivers();
     133                 :          0 :         flush_work(&autoload_work);
     134                 :          0 : }
     135                 :            : EXPORT_SYMBOL(snd_seq_device_load_drivers);
     136                 :            : #define cancel_autoload_drivers()       cancel_work_sync(&autoload_work)
     137                 :            : #else
     138                 :            : #define queue_autoload_drivers() /* NOP */
     139                 :            : #define cancel_autoload_drivers() /* NOP */
     140                 :            : #endif
     141                 :            : 
     142                 :            : /*
     143                 :            :  * device management
     144                 :            :  */
     145                 :          0 : static int snd_seq_device_dev_free(struct snd_device *device)
     146                 :            : {
     147                 :          0 :         struct snd_seq_device *dev = device->device_data;
     148                 :            : 
     149                 :          0 :         cancel_autoload_drivers();
     150                 :          0 :         put_device(&dev->dev);
     151                 :          0 :         return 0;
     152                 :            : }
     153                 :            : 
     154                 :          0 : static int snd_seq_device_dev_register(struct snd_device *device)
     155                 :            : {
     156                 :          0 :         struct snd_seq_device *dev = device->device_data;
     157                 :          0 :         int err;
     158                 :            : 
     159                 :          0 :         err = device_add(&dev->dev);
     160         [ #  # ]:          0 :         if (err < 0)
     161                 :            :                 return err;
     162         [ #  # ]:          0 :         if (!dev->dev.driver)
     163                 :          0 :                 queue_autoload_drivers();
     164                 :            :         return 0;
     165                 :            : }
     166                 :            : 
     167                 :          0 : static int snd_seq_device_dev_disconnect(struct snd_device *device)
     168                 :            : {
     169                 :          0 :         struct snd_seq_device *dev = device->device_data;
     170                 :            : 
     171                 :          0 :         device_del(&dev->dev);
     172                 :          0 :         return 0;
     173                 :            : }
     174                 :            : 
     175                 :          0 : static void snd_seq_dev_release(struct device *dev)
     176                 :            : {
     177                 :          0 :         struct snd_seq_device *sdev = to_seq_dev(dev);
     178                 :            : 
     179         [ #  # ]:          0 :         if (sdev->private_free)
     180                 :          0 :                 sdev->private_free(sdev);
     181                 :          0 :         kfree(sdev);
     182                 :          0 : }
     183                 :            : 
     184                 :            : /*
     185                 :            :  * register a sequencer device
     186                 :            :  * card = card info
     187                 :            :  * device = device number (if any)
     188                 :            :  * id = id of driver
     189                 :            :  * result = return pointer (NULL allowed if unnecessary)
     190                 :            :  */
     191                 :          0 : int snd_seq_device_new(struct snd_card *card, int device, const char *id,
     192                 :            :                        int argsize, struct snd_seq_device **result)
     193                 :            : {
     194                 :          0 :         struct snd_seq_device *dev;
     195                 :          0 :         int err;
     196                 :          0 :         static const struct snd_device_ops dops = {
     197                 :            :                 .dev_free = snd_seq_device_dev_free,
     198                 :            :                 .dev_register = snd_seq_device_dev_register,
     199                 :            :                 .dev_disconnect = snd_seq_device_dev_disconnect,
     200                 :            :         };
     201                 :            : 
     202         [ #  # ]:          0 :         if (result)
     203                 :          0 :                 *result = NULL;
     204                 :            : 
     205         [ #  # ]:          0 :         if (snd_BUG_ON(!id))
     206                 :            :                 return -EINVAL;
     207                 :            : 
     208                 :          0 :         dev = kzalloc(sizeof(*dev) + argsize, GFP_KERNEL);
     209         [ #  # ]:          0 :         if (!dev)
     210                 :            :                 return -ENOMEM;
     211                 :            : 
     212                 :            :         /* set up device info */
     213                 :          0 :         dev->card = card;
     214                 :          0 :         dev->device = device;
     215                 :          0 :         dev->id = id;
     216                 :          0 :         dev->argsize = argsize;
     217                 :            : 
     218                 :          0 :         device_initialize(&dev->dev);
     219                 :          0 :         dev->dev.parent = &card->card_dev;
     220                 :          0 :         dev->dev.bus = &snd_seq_bus_type;
     221                 :          0 :         dev->dev.release = snd_seq_dev_release;
     222                 :          0 :         dev_set_name(&dev->dev, "%s-%d-%d", dev->id, card->number, device);
     223                 :            : 
     224                 :            :         /* add this device to the list */
     225                 :          0 :         err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops);
     226         [ #  # ]:          0 :         if (err < 0) {
     227                 :          0 :                 put_device(&dev->dev);
     228                 :          0 :                 return err;
     229                 :            :         }
     230                 :            :         
     231         [ #  # ]:          0 :         if (result)
     232                 :          0 :                 *result = dev;
     233                 :            : 
     234                 :            :         return 0;
     235                 :            : }
     236                 :            : EXPORT_SYMBOL(snd_seq_device_new);
     237                 :            : 
     238                 :            : /*
     239                 :            :  * driver registration
     240                 :            :  */
     241                 :          0 : int __snd_seq_driver_register(struct snd_seq_driver *drv, struct module *mod)
     242                 :            : {
     243   [ #  #  #  #  :          0 :         if (WARN_ON(!drv->driver.name || !drv->id))
             #  #  #  # ]
     244                 :            :                 return -EINVAL;
     245                 :          0 :         drv->driver.bus = &snd_seq_bus_type;
     246                 :          0 :         drv->driver.owner = mod;
     247                 :          0 :         return driver_register(&drv->driver);
     248                 :            : }
     249                 :            : EXPORT_SYMBOL_GPL(__snd_seq_driver_register);
     250                 :            : 
     251                 :          0 : void snd_seq_driver_unregister(struct snd_seq_driver *drv)
     252                 :            : {
     253                 :          0 :         driver_unregister(&drv->driver);
     254                 :          0 : }
     255                 :            : EXPORT_SYMBOL_GPL(snd_seq_driver_unregister);
     256                 :            : 
     257                 :            : /*
     258                 :            :  * module part
     259                 :            :  */
     260                 :            : 
     261                 :         21 : static int __init seq_dev_proc_init(void)
     262                 :            : {
     263                 :            : #ifdef CONFIG_SND_PROC_FS
     264                 :         21 :         info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers",
     265                 :            :                                                   snd_seq_root);
     266         [ +  - ]:         21 :         if (info_entry == NULL)
     267                 :            :                 return -ENOMEM;
     268                 :         21 :         info_entry->content = SNDRV_INFO_CONTENT_TEXT;
     269                 :         21 :         info_entry->c.text.read = snd_seq_device_info;
     270         [ -  + ]:         21 :         if (snd_info_register(info_entry) < 0) {
     271                 :          0 :                 snd_info_free_entry(info_entry);
     272                 :          0 :                 return -ENOMEM;
     273                 :            :         }
     274                 :            : #endif
     275                 :            :         return 0;
     276                 :            : }
     277                 :            : 
     278                 :         21 : static int __init alsa_seq_device_init(void)
     279                 :            : {
     280                 :         21 :         int err;
     281                 :            : 
     282                 :         21 :         err = bus_register(&snd_seq_bus_type);
     283         [ +  - ]:         21 :         if (err < 0)
     284                 :            :                 return err;
     285                 :         21 :         err = seq_dev_proc_init();
     286         [ -  + ]:         21 :         if (err < 0)
     287                 :          0 :                 bus_unregister(&snd_seq_bus_type);
     288                 :            :         return err;
     289                 :            : }
     290                 :            : 
     291                 :          0 : static void __exit alsa_seq_device_exit(void)
     292                 :            : {
     293                 :            : #ifdef CONFIG_MODULES
     294                 :          0 :         cancel_work_sync(&autoload_work);
     295                 :            : #endif
     296                 :            : #ifdef CONFIG_SND_PROC_FS
     297                 :          0 :         snd_info_free_entry(info_entry);
     298                 :            : #endif
     299                 :          0 :         bus_unregister(&snd_seq_bus_type);
     300                 :          0 : }
     301                 :            : 
     302                 :            : subsys_initcall(alsa_seq_device_init)
     303                 :            : module_exit(alsa_seq_device_exit)

Generated by: LCOV version 1.14