blob: 49daf6e3a387a758957e0d15fc7b2a231448d353 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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 Iwai7c37ae52015-02-12 10:51:59 +010039#include <linux/device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#include <linux/init.h>
Paul Gortmakerda155d52011-07-15 12:38:28 -040041#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#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 Molnar1a60d4c2006-01-16 16:29:08 +010049#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070050
51MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
52MODULE_DESCRIPTION("ALSA sequencer device management");
53MODULE_LICENSE("GPL");
54
Takashi Iwai7c37ae52015-02-12 10:51:59 +010055struct snd_seq_driver {
56 struct device_driver driver;
Takashi Iwaiaf03c242015-02-12 13:40:50 +010057 const char *id;
Takashi Iwai7c37ae52015-02-12 10:51:59 +010058 int argsize;
Takashi Iwaic7e0b5b2005-11-17 14:04:02 +010059 struct snd_seq_dev_ops ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -070060};
61
Takashi Iwai7c37ae52015-02-12 10:51:59 +010062#define to_seq_drv(_drv) \
63 container_of(_drv, struct snd_seq_driver, driver)
Linus Torvalds1da177e2005-04-16 15:20:36 -070064
Takashi Iwai7c37ae52015-02-12 10:51:59 +010065/*
66 * bus definition
67 */
68static 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
77static 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 Iwai04f141a2005-12-01 10:43:51 +010085#ifdef CONFIG_PROC_FS
Takashi Iwai6581f4e2006-05-17 17:14:51 +020086static struct snd_info_entry *info_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -070087
Takashi Iwai7c37ae52015-02-12 10:51:59 +010088static 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 Torvalds1da177e2005-04-16 15:20:36 -070092
Takashi Iwai7c37ae52015-02-12 10:51:59 +010093 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 Torvalds1da177e2005-04-16 15:20:36 -070098
Takashi Iwaic7e0b5b2005-11-17 14:04:02 +010099static void snd_seq_device_info(struct snd_info_entry *entry,
100 struct snd_info_buffer *buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101{
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100102 bus_for_each_dev(&snd_seq_bus_type, NULL, buffer, print_dev_info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103}
Takashi Iwai04f141a2005-12-01 10:43:51 +0100104#endif
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100105
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106/*
107 * load all registered drivers (called from seq_clientmgr.c)
108 */
109
Johannes Bergee2da992008-07-09 10:28:41 +0200110#ifdef CONFIG_MODULES
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111/* avoid auto-loading during module_init() */
Takashi Iwai68ab6102014-10-15 14:06:25 +0200112static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113void snd_seq_autoload_lock(void)
114{
Takashi Iwai54841a02014-10-15 14:00:16 +0200115 atomic_inc(&snd_seq_in_init);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116}
Takashi Iwaib6a42672015-02-11 22:37:16 +0100117EXPORT_SYMBOL(snd_seq_autoload_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118
119void snd_seq_autoload_unlock(void)
120{
Takashi Iwai54841a02014-10-15 14:00:16 +0200121 atomic_dec(&snd_seq_in_init);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122}
Takashi Iwaib6a42672015-02-11 22:37:16 +0100123EXPORT_SYMBOL(snd_seq_autoload_unlock);
Takashi Iwai68ab6102014-10-15 14:06:25 +0200124
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100125static 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
134static void autoload_drivers(struct work_struct *work)
Takashi Iwai68ab6102014-10-15 14:06:25 +0200135{
136 /* avoid reentrance */
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100137 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 Iwai68ab6102014-10-15 14:06:25 +0200140 atomic_dec(&snd_seq_in_init);
141}
142
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100143static DECLARE_WORK(autoload_work, autoload_drivers);
Takashi Iwai68ab6102014-10-15 14:06:25 +0200144
145static void queue_autoload_drivers(void)
146{
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100147 schedule_work(&autoload_work);
Takashi Iwai68ab6102014-10-15 14:06:25 +0200148}
149
150void 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 Iwaib6a42672015-02-11 22:37:16 +0100158EXPORT_SYMBOL(snd_seq_autoload_init);
Takashi Iwaib6a42672015-02-11 22:37:16 +0100159
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160void snd_seq_device_load_drivers(void)
161{
Takashi Iwai68ab6102014-10-15 14:06:25 +0200162 queue_autoload_drivers();
163 flush_work(&autoload_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164}
Takashi Iwaib6a42672015-02-11 22:37:16 +0100165EXPORT_SYMBOL(snd_seq_device_load_drivers);
Takashi Iwai72496ed2015-02-11 22:39:51 +0100166#else
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100167#define queue_autoload_drivers() /* NOP */
Takashi Iwai72496ed2015-02-11 22:39:51 +0100168#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169
170/*
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100171 * device management
172 */
173static 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
181static 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
194static 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
202static 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 Torvalds1da177e2005-04-16 15:20:36 -0700212 * register a sequencer device
Takashi Iwaif2f93072014-02-04 18:21:03 +0100213 * card = card info
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 * device = device number (if any)
215 * id = id of driver
216 * result = return pointer (NULL allowed if unnecessary)
217 */
Takashi Iwaiaf03c242015-02-12 13:40:50 +0100218int snd_seq_device_new(struct snd_card *card, int device, const char *id,
219 int argsize, struct snd_seq_device **result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220{
Takashi Iwaic7e0b5b2005-11-17 14:04:02 +0100221 struct snd_seq_device *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 int err;
Takashi Iwaic7e0b5b2005-11-17 14:04:02 +0100223 static struct snd_device_ops dops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 .dev_free = snd_seq_device_dev_free,
225 .dev_register = snd_seq_device_dev_register,
226 .dev_disconnect = snd_seq_device_dev_disconnect,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 };
228
229 if (result)
230 *result = NULL;
231
Takashi Iwai7eaa9432008-08-08 17:09:09 +0200232 if (snd_BUG_ON(!id))
233 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100235 dev = kzalloc(sizeof(*dev) + argsize, GFP_KERNEL);
236 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 return -ENOMEM;
238
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 /* set up device info */
240 dev->card = card;
241 dev->device = device;
Takashi Iwaiaf03c242015-02-12 13:40:50 +0100242 dev->id = id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 dev->argsize = argsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100245 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 Torvalds1da177e2005-04-16 15:20:36 -0700250
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100251 /* 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 Torvalds1da177e2005-04-16 15:20:36 -0700255 return err;
256 }
257
258 if (result)
259 *result = dev;
260
261 return 0;
262}
Takashi Iwaib6a42672015-02-11 22:37:16 +0100263EXPORT_SYMBOL(snd_seq_device_new);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264
265/*
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100266 * driver binding - just pass to each driver callback
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 */
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100268static int snd_seq_drv_probe(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269{
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100270 struct snd_seq_driver *sdrv = to_seq_drv(dev->driver);
271 struct snd_seq_device *sdev = to_seq_dev(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272
Takashi Iwaiaf03c242015-02-12 13:40:50 +0100273 return sdrv->ops.init_device(sdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274}
275
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100276static int snd_seq_drv_remove(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277{
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100278 struct snd_seq_driver *sdrv = to_seq_drv(dev->driver);
279 struct snd_seq_device *sdev = to_seq_dev(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280
Takashi Iwaiaf03c242015-02-12 13:40:50 +0100281 return sdrv->ops.free_device(sdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283
284/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 * register device driver
286 * id = driver id
287 * entry = driver operators - duplicated to each instance
288 */
Takashi Iwaiaf03c242015-02-12 13:40:50 +0100289int snd_seq_device_register_driver(const char *id,
290 struct snd_seq_dev_ops *entry, int argsize)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291{
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100292 struct snd_seq_driver *sdrv;
293 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294
295 if (id == NULL || entry == NULL ||
296 entry->init_device == NULL || entry->free_device == NULL)
297 return -EINVAL;
298
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100299 sdrv = kzalloc(sizeof(*sdrv), GFP_KERNEL);
300 if (!sdrv)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100303 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 Iwaiaf03c242015-02-12 13:40:50 +0100307 sdrv->id = id;
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100308 sdrv->argsize = argsize;
309 sdrv->ops = *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100311 err = driver_register(&sdrv->driver);
312 if (err < 0)
313 kfree(sdrv);
314 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315}
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100316EXPORT_SYMBOL(snd_seq_device_register_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100318/* 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 Torvalds1da177e2005-04-16 15:20:36 -0700320 */
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100321static int find_drv(struct device_driver *drv, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322{
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100323 struct snd_seq_driver *sdrv = to_seq_drv(drv);
324 void **ptr = (void **)data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100326 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 Torvalds1da177e2005-04-16 15:20:36 -0700330}
331
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332/*
333 * unregister the specified driver
334 */
Takashi Iwaiaf03c242015-02-12 13:40:50 +0100335int snd_seq_device_unregister_driver(const char *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336{
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100337 struct snd_seq_driver *sdrv = (struct snd_seq_driver *)id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100339 if (!bus_for_each_drv(&snd_seq_bus_type, NULL, &sdrv, find_drv))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 return -ENXIO;
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100341 driver_unregister(&sdrv->driver);
342 kfree(sdrv);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 return 0;
344}
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100345EXPORT_SYMBOL(snd_seq_device_unregister_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346
347/*
348 * module part
349 */
350
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100351static int __init seq_dev_proc_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352{
Takashi Iwai04f141a2005-12-01 10:43:51 +0100353#ifdef CONFIG_PROC_FS
Takashi Iwaic7e0b5b2005-11-17 14:04:02 +0100354 info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers",
355 snd_seq_root);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 if (info_entry == NULL)
357 return -ENOMEM;
358 info_entry->content = SNDRV_INFO_CONTENT_TEXT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 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 Iwai04f141a2005-12-01 10:43:51 +0100364#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 return 0;
366}
367
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100368static 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 Torvalds1da177e2005-04-16 15:20:36 -0700381static void __exit alsa_seq_device_exit(void)
382{
Takashi Iwai68ab6102014-10-15 14:06:25 +0200383#ifdef CONFIG_MODULES
384 cancel_work_sync(&autoload_work);
385#endif
Takashi Iwai04f141a2005-12-01 10:43:51 +0100386#ifdef CONFIG_PROC_FS
Takashi Iwai746d4a02006-06-23 14:37:59 +0200387 snd_info_free_entry(info_entry);
Takashi Iwai04f141a2005-12-01 10:43:51 +0100388#endif
Takashi Iwai7c37ae52015-02-12 10:51:59 +0100389 bus_unregister(&snd_seq_bus_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390}
391
392module_init(alsa_seq_device_init)
393module_exit(alsa_seq_device_exit)