Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | * ALSA sequencer device management |
| 3 | * Copyright (c) 1999 by Takashi Iwai <tiwai@suse.de> |
| 4 | * |
| 5 | * This program is free software; you can redistribute it and/or modify |
| 6 | * it under the terms of the GNU General Public License as published by |
| 7 | * the Free Software Foundation; either version 2 of the License, or |
| 8 | * (at your option) any later version. |
| 9 | * |
| 10 | * This program is distributed in the hope that it will be useful, |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | * GNU General Public License for more details. |
| 14 | * |
| 15 | * You should have received a copy of the GNU General Public License |
| 16 | * along with this program; if not, write to the Free Software |
| 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 18 | * |
| 19 | * |
| 20 | *---------------------------------------------------------------- |
| 21 | * |
| 22 | * This device handler separates the card driver module from sequencer |
| 23 | * stuff (sequencer core, synth drivers, etc), so that user can avoid |
| 24 | * to spend unnecessary resources e.g. if he needs only listening to |
| 25 | * MP3s. |
| 26 | * |
| 27 | * The card (or lowlevel) driver creates a sequencer device entry |
| 28 | * via snd_seq_device_new(). This is an entry pointer to communicate |
| 29 | * with the sequencer device "driver", which is involved with the |
| 30 | * actual part to communicate with the sequencer core. |
| 31 | * Each sequencer device entry has an id string and the corresponding |
| 32 | * driver with the same id is loaded when required. For example, |
| 33 | * lowlevel codes to access emu8000 chip on sbawe card are included in |
| 34 | * emu8000-synth module. To activate this module, the hardware |
| 35 | * resources like i/o port are passed via snd_seq_device argument. |
| 36 | * |
| 37 | */ |
| 38 | |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 39 | #include <linux/device.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 40 | #include <linux/init.h> |
Paul Gortmaker | da155d5 | 2011-07-15 12:38:28 -0400 | [diff] [blame] | 41 | #include <linux/module.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 42 | #include <sound/core.h> |
| 43 | #include <sound/info.h> |
| 44 | #include <sound/seq_device.h> |
| 45 | #include <sound/seq_kernel.h> |
| 46 | #include <sound/initval.h> |
| 47 | #include <linux/kmod.h> |
| 48 | #include <linux/slab.h> |
Ingo Molnar | 1a60d4c | 2006-01-16 16:29:08 +0100 | [diff] [blame] | 49 | #include <linux/mutex.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 50 | |
| 51 | MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); |
| 52 | MODULE_DESCRIPTION("ALSA sequencer device management"); |
| 53 | MODULE_LICENSE("GPL"); |
| 54 | |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 55 | struct snd_seq_driver { |
| 56 | struct device_driver driver; |
Takashi Iwai | af03c24 | 2015-02-12 13:40:50 +0100 | [diff] [blame^] | 57 | const char *id; |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 58 | int argsize; |
Takashi Iwai | c7e0b5b | 2005-11-17 14:04:02 +0100 | [diff] [blame] | 59 | struct snd_seq_dev_ops ops; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 60 | }; |
| 61 | |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 62 | #define to_seq_drv(_drv) \ |
| 63 | container_of(_drv, struct snd_seq_driver, driver) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 64 | |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 65 | /* |
| 66 | * bus definition |
| 67 | */ |
| 68 | static int snd_seq_bus_match(struct device *dev, struct device_driver *drv) |
| 69 | { |
| 70 | struct snd_seq_device *sdev = to_seq_dev(dev); |
| 71 | struct snd_seq_driver *sdrv = to_seq_drv(drv); |
| 72 | |
| 73 | return strcmp(sdrv->id, sdev->id) == 0 && |
| 74 | sdrv->argsize == sdev->argsize; |
| 75 | } |
| 76 | |
| 77 | static struct bus_type snd_seq_bus_type = { |
| 78 | .name = "snd_seq", |
| 79 | .match = snd_seq_bus_match, |
| 80 | }; |
| 81 | |
| 82 | /* |
| 83 | * proc interface -- just for compatibility |
| 84 | */ |
Takashi Iwai | 04f141a | 2005-12-01 10:43:51 +0100 | [diff] [blame] | 85 | #ifdef CONFIG_PROC_FS |
Takashi Iwai | 6581f4e | 2006-05-17 17:14:51 +0200 | [diff] [blame] | 86 | static struct snd_info_entry *info_entry; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 87 | |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 88 | static int print_dev_info(struct device *dev, void *data) |
| 89 | { |
| 90 | struct snd_seq_device *sdev = to_seq_dev(dev); |
| 91 | struct snd_info_buffer *buffer = data; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 92 | |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 93 | snd_iprintf(buffer, "snd-%s,%s,%d\n", sdev->id, |
| 94 | dev->driver ? "loaded" : "empty", |
| 95 | dev->driver ? 1 : 0); |
| 96 | return 0; |
| 97 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 98 | |
Takashi Iwai | c7e0b5b | 2005-11-17 14:04:02 +0100 | [diff] [blame] | 99 | static void snd_seq_device_info(struct snd_info_entry *entry, |
| 100 | struct snd_info_buffer *buffer) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 101 | { |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 102 | bus_for_each_dev(&snd_seq_bus_type, NULL, buffer, print_dev_info); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 103 | } |
Takashi Iwai | 04f141a | 2005-12-01 10:43:51 +0100 | [diff] [blame] | 104 | #endif |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 105 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 106 | /* |
| 107 | * load all registered drivers (called from seq_clientmgr.c) |
| 108 | */ |
| 109 | |
Johannes Berg | ee2da99 | 2008-07-09 10:28:41 +0200 | [diff] [blame] | 110 | #ifdef CONFIG_MODULES |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 111 | /* avoid auto-loading during module_init() */ |
Takashi Iwai | 68ab610 | 2014-10-15 14:06:25 +0200 | [diff] [blame] | 112 | static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */ |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 113 | void snd_seq_autoload_lock(void) |
| 114 | { |
Takashi Iwai | 54841a0 | 2014-10-15 14:00:16 +0200 | [diff] [blame] | 115 | atomic_inc(&snd_seq_in_init); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 116 | } |
Takashi Iwai | b6a4267 | 2015-02-11 22:37:16 +0100 | [diff] [blame] | 117 | EXPORT_SYMBOL(snd_seq_autoload_lock); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 118 | |
| 119 | void snd_seq_autoload_unlock(void) |
| 120 | { |
Takashi Iwai | 54841a0 | 2014-10-15 14:00:16 +0200 | [diff] [blame] | 121 | atomic_dec(&snd_seq_in_init); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 122 | } |
Takashi Iwai | b6a4267 | 2015-02-11 22:37:16 +0100 | [diff] [blame] | 123 | EXPORT_SYMBOL(snd_seq_autoload_unlock); |
Takashi Iwai | 68ab610 | 2014-10-15 14:06:25 +0200 | [diff] [blame] | 124 | |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 125 | static int request_seq_drv(struct device *dev, void *data) |
| 126 | { |
| 127 | struct snd_seq_device *sdev = to_seq_dev(dev); |
| 128 | |
| 129 | if (!dev->driver) |
| 130 | request_module("snd-%s", sdev->id); |
| 131 | return 0; |
| 132 | } |
| 133 | |
| 134 | static void autoload_drivers(struct work_struct *work) |
Takashi Iwai | 68ab610 | 2014-10-15 14:06:25 +0200 | [diff] [blame] | 135 | { |
| 136 | /* avoid reentrance */ |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 137 | if (atomic_inc_return(&snd_seq_in_init) == 1) |
| 138 | bus_for_each_dev(&snd_seq_bus_type, NULL, NULL, |
| 139 | request_seq_drv); |
Takashi Iwai | 68ab610 | 2014-10-15 14:06:25 +0200 | [diff] [blame] | 140 | atomic_dec(&snd_seq_in_init); |
| 141 | } |
| 142 | |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 143 | static DECLARE_WORK(autoload_work, autoload_drivers); |
Takashi Iwai | 68ab610 | 2014-10-15 14:06:25 +0200 | [diff] [blame] | 144 | |
| 145 | static void queue_autoload_drivers(void) |
| 146 | { |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 147 | schedule_work(&autoload_work); |
Takashi Iwai | 68ab610 | 2014-10-15 14:06:25 +0200 | [diff] [blame] | 148 | } |
| 149 | |
| 150 | void snd_seq_autoload_init(void) |
| 151 | { |
| 152 | atomic_dec(&snd_seq_in_init); |
| 153 | #ifdef CONFIG_SND_SEQUENCER_MODULE |
| 154 | /* initial autoload only when snd-seq is a module */ |
| 155 | queue_autoload_drivers(); |
| 156 | #endif |
| 157 | } |
Takashi Iwai | b6a4267 | 2015-02-11 22:37:16 +0100 | [diff] [blame] | 158 | EXPORT_SYMBOL(snd_seq_autoload_init); |
Takashi Iwai | b6a4267 | 2015-02-11 22:37:16 +0100 | [diff] [blame] | 159 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 160 | void snd_seq_device_load_drivers(void) |
| 161 | { |
Takashi Iwai | 68ab610 | 2014-10-15 14:06:25 +0200 | [diff] [blame] | 162 | queue_autoload_drivers(); |
| 163 | flush_work(&autoload_work); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 164 | } |
Takashi Iwai | b6a4267 | 2015-02-11 22:37:16 +0100 | [diff] [blame] | 165 | EXPORT_SYMBOL(snd_seq_device_load_drivers); |
Takashi Iwai | 72496ed | 2015-02-11 22:39:51 +0100 | [diff] [blame] | 166 | #else |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 167 | #define queue_autoload_drivers() /* NOP */ |
Takashi Iwai | 72496ed | 2015-02-11 22:39:51 +0100 | [diff] [blame] | 168 | #endif |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 169 | |
| 170 | /* |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 171 | * device management |
| 172 | */ |
| 173 | static int snd_seq_device_dev_free(struct snd_device *device) |
| 174 | { |
| 175 | struct snd_seq_device *dev = device->device_data; |
| 176 | |
| 177 | put_device(&dev->dev); |
| 178 | return 0; |
| 179 | } |
| 180 | |
| 181 | static int snd_seq_device_dev_register(struct snd_device *device) |
| 182 | { |
| 183 | struct snd_seq_device *dev = device->device_data; |
| 184 | int err; |
| 185 | |
| 186 | err = device_add(&dev->dev); |
| 187 | if (err < 0) |
| 188 | return err; |
| 189 | if (!dev->dev.driver) |
| 190 | queue_autoload_drivers(); |
| 191 | return 0; |
| 192 | } |
| 193 | |
| 194 | static int snd_seq_device_dev_disconnect(struct snd_device *device) |
| 195 | { |
| 196 | struct snd_seq_device *dev = device->device_data; |
| 197 | |
| 198 | device_del(&dev->dev); |
| 199 | return 0; |
| 200 | } |
| 201 | |
| 202 | static void snd_seq_dev_release(struct device *dev) |
| 203 | { |
| 204 | struct snd_seq_device *sdev = to_seq_dev(dev); |
| 205 | |
| 206 | if (sdev->private_free) |
| 207 | sdev->private_free(sdev); |
| 208 | kfree(sdev); |
| 209 | } |
| 210 | |
| 211 | /* |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 212 | * register a sequencer device |
Takashi Iwai | f2f9307 | 2014-02-04 18:21:03 +0100 | [diff] [blame] | 213 | * card = card info |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 214 | * device = device number (if any) |
| 215 | * id = id of driver |
| 216 | * result = return pointer (NULL allowed if unnecessary) |
| 217 | */ |
Takashi Iwai | af03c24 | 2015-02-12 13:40:50 +0100 | [diff] [blame^] | 218 | int snd_seq_device_new(struct snd_card *card, int device, const char *id, |
| 219 | int argsize, struct snd_seq_device **result) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 220 | { |
Takashi Iwai | c7e0b5b | 2005-11-17 14:04:02 +0100 | [diff] [blame] | 221 | struct snd_seq_device *dev; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 222 | int err; |
Takashi Iwai | c7e0b5b | 2005-11-17 14:04:02 +0100 | [diff] [blame] | 223 | static struct snd_device_ops dops = { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 224 | .dev_free = snd_seq_device_dev_free, |
| 225 | .dev_register = snd_seq_device_dev_register, |
| 226 | .dev_disconnect = snd_seq_device_dev_disconnect, |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 227 | }; |
| 228 | |
| 229 | if (result) |
| 230 | *result = NULL; |
| 231 | |
Takashi Iwai | 7eaa943 | 2008-08-08 17:09:09 +0200 | [diff] [blame] | 232 | if (snd_BUG_ON(!id)) |
| 233 | return -EINVAL; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 234 | |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 235 | dev = kzalloc(sizeof(*dev) + argsize, GFP_KERNEL); |
| 236 | if (!dev) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 237 | return -ENOMEM; |
| 238 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 239 | /* set up device info */ |
| 240 | dev->card = card; |
| 241 | dev->device = device; |
Takashi Iwai | af03c24 | 2015-02-12 13:40:50 +0100 | [diff] [blame^] | 242 | dev->id = id; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 243 | dev->argsize = argsize; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 244 | |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 245 | device_initialize(&dev->dev); |
| 246 | dev->dev.parent = &card->card_dev; |
| 247 | dev->dev.bus = &snd_seq_bus_type; |
| 248 | dev->dev.release = snd_seq_dev_release; |
| 249 | dev_set_name(&dev->dev, "%s-%d-%d", dev->id, card->number, device); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 250 | |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 251 | /* add this device to the list */ |
| 252 | err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops); |
| 253 | if (err < 0) { |
| 254 | put_device(&dev->dev); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 255 | return err; |
| 256 | } |
| 257 | |
| 258 | if (result) |
| 259 | *result = dev; |
| 260 | |
| 261 | return 0; |
| 262 | } |
Takashi Iwai | b6a4267 | 2015-02-11 22:37:16 +0100 | [diff] [blame] | 263 | EXPORT_SYMBOL(snd_seq_device_new); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 264 | |
| 265 | /* |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 266 | * driver binding - just pass to each driver callback |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 267 | */ |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 268 | static int snd_seq_drv_probe(struct device *dev) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 269 | { |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 270 | struct snd_seq_driver *sdrv = to_seq_drv(dev->driver); |
| 271 | struct snd_seq_device *sdev = to_seq_dev(dev); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 272 | |
Takashi Iwai | af03c24 | 2015-02-12 13:40:50 +0100 | [diff] [blame^] | 273 | return sdrv->ops.init_device(sdev); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 274 | } |
| 275 | |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 276 | static int snd_seq_drv_remove(struct device *dev) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 277 | { |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 278 | struct snd_seq_driver *sdrv = to_seq_drv(dev->driver); |
| 279 | struct snd_seq_device *sdev = to_seq_dev(dev); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 280 | |
Takashi Iwai | af03c24 | 2015-02-12 13:40:50 +0100 | [diff] [blame^] | 281 | return sdrv->ops.free_device(sdev); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 282 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 283 | |
| 284 | /* |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 285 | * register device driver |
| 286 | * id = driver id |
| 287 | * entry = driver operators - duplicated to each instance |
| 288 | */ |
Takashi Iwai | af03c24 | 2015-02-12 13:40:50 +0100 | [diff] [blame^] | 289 | int snd_seq_device_register_driver(const char *id, |
| 290 | struct snd_seq_dev_ops *entry, int argsize) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 291 | { |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 292 | struct snd_seq_driver *sdrv; |
| 293 | int err; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 294 | |
| 295 | if (id == NULL || entry == NULL || |
| 296 | entry->init_device == NULL || entry->free_device == NULL) |
| 297 | return -EINVAL; |
| 298 | |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 299 | sdrv = kzalloc(sizeof(*sdrv), GFP_KERNEL); |
| 300 | if (!sdrv) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 301 | return -ENOMEM; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 302 | |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 303 | sdrv->driver.name = id; |
| 304 | sdrv->driver.bus = &snd_seq_bus_type; |
| 305 | sdrv->driver.probe = snd_seq_drv_probe; |
| 306 | sdrv->driver.remove = snd_seq_drv_remove; |
Takashi Iwai | af03c24 | 2015-02-12 13:40:50 +0100 | [diff] [blame^] | 307 | sdrv->id = id; |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 308 | sdrv->argsize = argsize; |
| 309 | sdrv->ops = *entry; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 310 | |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 311 | err = driver_register(&sdrv->driver); |
| 312 | if (err < 0) |
| 313 | kfree(sdrv); |
| 314 | return err; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 315 | } |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 316 | EXPORT_SYMBOL(snd_seq_device_register_driver); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 317 | |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 318 | /* callback to find a specific driver; data is a pointer to the id string ptr. |
| 319 | * when the id matches, store the driver pointer in return and break the loop. |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 320 | */ |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 321 | static int find_drv(struct device_driver *drv, void *data) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 322 | { |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 323 | struct snd_seq_driver *sdrv = to_seq_drv(drv); |
| 324 | void **ptr = (void **)data; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 325 | |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 326 | if (strcmp(sdrv->id, *ptr)) |
| 327 | return 0; /* id don't match, continue the loop */ |
| 328 | *ptr = sdrv; |
| 329 | return 1; /* break the loop */ |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 330 | } |
| 331 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 332 | /* |
| 333 | * unregister the specified driver |
| 334 | */ |
Takashi Iwai | af03c24 | 2015-02-12 13:40:50 +0100 | [diff] [blame^] | 335 | int snd_seq_device_unregister_driver(const char *id) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 336 | { |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 337 | struct snd_seq_driver *sdrv = (struct snd_seq_driver *)id; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 338 | |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 339 | if (!bus_for_each_drv(&snd_seq_bus_type, NULL, &sdrv, find_drv)) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 340 | return -ENXIO; |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 341 | driver_unregister(&sdrv->driver); |
| 342 | kfree(sdrv); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 343 | return 0; |
| 344 | } |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 345 | EXPORT_SYMBOL(snd_seq_device_unregister_driver); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 346 | |
| 347 | /* |
| 348 | * module part |
| 349 | */ |
| 350 | |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 351 | static int __init seq_dev_proc_init(void) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 352 | { |
Takashi Iwai | 04f141a | 2005-12-01 10:43:51 +0100 | [diff] [blame] | 353 | #ifdef CONFIG_PROC_FS |
Takashi Iwai | c7e0b5b | 2005-11-17 14:04:02 +0100 | [diff] [blame] | 354 | info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers", |
| 355 | snd_seq_root); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 356 | if (info_entry == NULL) |
| 357 | return -ENOMEM; |
| 358 | info_entry->content = SNDRV_INFO_CONTENT_TEXT; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 359 | info_entry->c.text.read = snd_seq_device_info; |
| 360 | if (snd_info_register(info_entry) < 0) { |
| 361 | snd_info_free_entry(info_entry); |
| 362 | return -ENOMEM; |
| 363 | } |
Takashi Iwai | 04f141a | 2005-12-01 10:43:51 +0100 | [diff] [blame] | 364 | #endif |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 365 | return 0; |
| 366 | } |
| 367 | |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 368 | static int __init alsa_seq_device_init(void) |
| 369 | { |
| 370 | int err; |
| 371 | |
| 372 | err = bus_register(&snd_seq_bus_type); |
| 373 | if (err < 0) |
| 374 | return err; |
| 375 | err = seq_dev_proc_init(); |
| 376 | if (err < 0) |
| 377 | bus_unregister(&snd_seq_bus_type); |
| 378 | return err; |
| 379 | } |
| 380 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 381 | static void __exit alsa_seq_device_exit(void) |
| 382 | { |
Takashi Iwai | 68ab610 | 2014-10-15 14:06:25 +0200 | [diff] [blame] | 383 | #ifdef CONFIG_MODULES |
| 384 | cancel_work_sync(&autoload_work); |
| 385 | #endif |
Takashi Iwai | 04f141a | 2005-12-01 10:43:51 +0100 | [diff] [blame] | 386 | #ifdef CONFIG_PROC_FS |
Takashi Iwai | 746d4a0 | 2006-06-23 14:37:59 +0200 | [diff] [blame] | 387 | snd_info_free_entry(info_entry); |
Takashi Iwai | 04f141a | 2005-12-01 10:43:51 +0100 | [diff] [blame] | 388 | #endif |
Takashi Iwai | 7c37ae5 | 2015-02-12 10:51:59 +0100 | [diff] [blame] | 389 | bus_unregister(&snd_seq_bus_type); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 390 | } |
| 391 | |
| 392 | module_init(alsa_seq_device_init) |
| 393 | module_exit(alsa_seq_device_exit) |