Linux Audio

Check our new training course

Loading...
v5.4
  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
 36MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
 37MODULE_DESCRIPTION("ALSA sequencer device management");
 38MODULE_LICENSE("GPL");
 39
 40/*
 41 * bus definition
 42 */
 43static int snd_seq_bus_match(struct device *dev, struct device_driver *drv)
 44{
 45	struct snd_seq_device *sdev = to_seq_dev(dev);
 46	struct snd_seq_driver *sdrv = to_seq_drv(drv);
 47
 48	return strcmp(sdrv->id, sdev->id) == 0 &&
 49		sdrv->argsize == sdev->argsize;
 50}
 51
 52static 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
 61static struct snd_info_entry *info_entry;
 62
 63static int print_dev_info(struct device *dev, void *data)
 64{
 65	struct snd_seq_device *sdev = to_seq_dev(dev);
 66	struct snd_info_buffer *buffer = data;
 67
 68	snd_iprintf(buffer, "snd-%s,%s,%d\n", sdev->id,
 69		    dev->driver ? "loaded" : "empty",
 70		    dev->driver ? 1 : 0);
 71	return 0;
 72}
 73
 74static void snd_seq_device_info(struct snd_info_entry *entry,
 75				struct snd_info_buffer *buffer)
 76{
 77	bus_for_each_dev(&snd_seq_bus_type, NULL, buffer, print_dev_info);
 78}
 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 */
 87static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */
 88
 89static int request_seq_drv(struct device *dev, void *data)
 90{
 91	struct snd_seq_device *sdev = to_seq_dev(dev);
 92
 93	if (!dev->driver)
 94		request_module("snd-%s", sdev->id);
 95	return 0;
 96}
 97
 98static void autoload_drivers(struct work_struct *work)
 99{
100	/* avoid reentrance */
101	if (atomic_inc_return(&snd_seq_in_init) == 1)
102		bus_for_each_dev(&snd_seq_bus_type, NULL, NULL,
103				 request_seq_drv);
104	atomic_dec(&snd_seq_in_init);
105}
106
107static DECLARE_WORK(autoload_work, autoload_drivers);
108
109static void queue_autoload_drivers(void)
110{
111	schedule_work(&autoload_work);
112}
113
114void snd_seq_autoload_init(void)
115{
116	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}
122EXPORT_SYMBOL(snd_seq_autoload_init);
123
124void snd_seq_autoload_exit(void)
125{
126	atomic_inc(&snd_seq_in_init);
127}
128EXPORT_SYMBOL(snd_seq_autoload_exit);
129
130void snd_seq_device_load_drivers(void)
131{
132	queue_autoload_drivers();
133	flush_work(&autoload_work);
134}
135EXPORT_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 */
145static int snd_seq_device_dev_free(struct snd_device *device)
146{
147	struct snd_seq_device *dev = device->device_data;
148
149	cancel_autoload_drivers();
150	put_device(&dev->dev);
151	return 0;
152}
153
154static int snd_seq_device_dev_register(struct snd_device *device)
155{
156	struct snd_seq_device *dev = device->device_data;
157	int err;
158
159	err = device_add(&dev->dev);
160	if (err < 0)
161		return err;
162	if (!dev->dev.driver)
163		queue_autoload_drivers();
164	return 0;
165}
166
167static int snd_seq_device_dev_disconnect(struct snd_device *device)
168{
169	struct snd_seq_device *dev = device->device_data;
170
171	device_del(&dev->dev);
172	return 0;
173}
174
175static void snd_seq_dev_release(struct device *dev)
176{
177	struct snd_seq_device *sdev = to_seq_dev(dev);
178
179	if (sdev->private_free)
180		sdev->private_free(sdev);
181	kfree(sdev);
182}
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 */
191int snd_seq_device_new(struct snd_card *card, int device, const char *id,
192		       int argsize, struct snd_seq_device **result)
193{
194	struct snd_seq_device *dev;
195	int err;
196	static 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	if (result)
203		*result = NULL;
204
205	if (snd_BUG_ON(!id))
206		return -EINVAL;
207
208	dev = kzalloc(sizeof(*dev) + argsize, GFP_KERNEL);
209	if (!dev)
210		return -ENOMEM;
211
212	/* set up device info */
213	dev->card = card;
214	dev->device = device;
215	dev->id = id;
216	dev->argsize = argsize;
217
218	device_initialize(&dev->dev);
219	dev->dev.parent = &card->card_dev;
220	dev->dev.bus = &snd_seq_bus_type;
221	dev->dev.release = snd_seq_dev_release;
222	dev_set_name(&dev->dev, "%s-%d-%d", dev->id, card->number, device);
223
224	/* add this device to the list */
225	err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops);
226	if (err < 0) {
227		put_device(&dev->dev);
228		return err;
229	}
230	
231	if (result)
232		*result = dev;
233
234	return 0;
235}
236EXPORT_SYMBOL(snd_seq_device_new);
237
238/*
239 * driver registration
240 */
241int __snd_seq_driver_register(struct snd_seq_driver *drv, struct module *mod)
242{
243	if (WARN_ON(!drv->driver.name || !drv->id))
244		return -EINVAL;
245	drv->driver.bus = &snd_seq_bus_type;
246	drv->driver.owner = mod;
247	return driver_register(&drv->driver);
248}
249EXPORT_SYMBOL_GPL(__snd_seq_driver_register);
250
251void snd_seq_driver_unregister(struct snd_seq_driver *drv)
252{
253	driver_unregister(&drv->driver);
254}
255EXPORT_SYMBOL_GPL(snd_seq_driver_unregister);
256
257/*
258 * module part
259 */
260
261static int __init seq_dev_proc_init(void)
262{
263#ifdef CONFIG_SND_PROC_FS
264	info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers",
265						  snd_seq_root);
266	if (info_entry == NULL)
267		return -ENOMEM;
268	info_entry->content = SNDRV_INFO_CONTENT_TEXT;
269	info_entry->c.text.read = snd_seq_device_info;
270	if (snd_info_register(info_entry) < 0) {
271		snd_info_free_entry(info_entry);
272		return -ENOMEM;
273	}
274#endif
275	return 0;
276}
277
278static int __init alsa_seq_device_init(void)
279{
280	int err;
281
282	err = bus_register(&snd_seq_bus_type);
283	if (err < 0)
284		return err;
285	err = seq_dev_proc_init();
286	if (err < 0)
287		bus_unregister(&snd_seq_bus_type);
288	return err;
289}
290
291static void __exit alsa_seq_device_exit(void)
292{
293#ifdef CONFIG_MODULES
294	cancel_work_sync(&autoload_work);
295#endif
296#ifdef CONFIG_SND_PROC_FS
297	snd_info_free_entry(info_entry);
298#endif
299	bus_unregister(&snd_seq_bus_type);
300}
301
302subsys_initcall(alsa_seq_device_init)
303module_exit(alsa_seq_device_exit)
v4.17
 
  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
 39#include <linux/device.h>
 40#include <linux/init.h>
 41#include <linux/module.h>
 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>
 49#include <linux/mutex.h>
 50
 51MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
 52MODULE_DESCRIPTION("ALSA sequencer device management");
 53MODULE_LICENSE("GPL");
 54
 55/*
 56 * bus definition
 57 */
 58static int snd_seq_bus_match(struct device *dev, struct device_driver *drv)
 59{
 60	struct snd_seq_device *sdev = to_seq_dev(dev);
 61	struct snd_seq_driver *sdrv = to_seq_drv(drv);
 62
 63	return strcmp(sdrv->id, sdev->id) == 0 &&
 64		sdrv->argsize == sdev->argsize;
 65}
 66
 67static struct bus_type snd_seq_bus_type = {
 68	.name = "snd_seq",
 69	.match = snd_seq_bus_match,
 70};
 71
 72/*
 73 * proc interface -- just for compatibility
 74 */
 75#ifdef CONFIG_SND_PROC_FS
 76static struct snd_info_entry *info_entry;
 77
 78static int print_dev_info(struct device *dev, void *data)
 79{
 80	struct snd_seq_device *sdev = to_seq_dev(dev);
 81	struct snd_info_buffer *buffer = data;
 82
 83	snd_iprintf(buffer, "snd-%s,%s,%d\n", sdev->id,
 84		    dev->driver ? "loaded" : "empty",
 85		    dev->driver ? 1 : 0);
 86	return 0;
 87}
 88
 89static void snd_seq_device_info(struct snd_info_entry *entry,
 90				struct snd_info_buffer *buffer)
 91{
 92	bus_for_each_dev(&snd_seq_bus_type, NULL, buffer, print_dev_info);
 93}
 94#endif
 95
 96/*
 97 * load all registered drivers (called from seq_clientmgr.c)
 98 */
 99
100#ifdef CONFIG_MODULES
101/* flag to block auto-loading */
102static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */
103
104static int request_seq_drv(struct device *dev, void *data)
105{
106	struct snd_seq_device *sdev = to_seq_dev(dev);
107
108	if (!dev->driver)
109		request_module("snd-%s", sdev->id);
110	return 0;
111}
112
113static void autoload_drivers(struct work_struct *work)
114{
115	/* avoid reentrance */
116	if (atomic_inc_return(&snd_seq_in_init) == 1)
117		bus_for_each_dev(&snd_seq_bus_type, NULL, NULL,
118				 request_seq_drv);
119	atomic_dec(&snd_seq_in_init);
120}
121
122static DECLARE_WORK(autoload_work, autoload_drivers);
123
124static void queue_autoload_drivers(void)
125{
126	schedule_work(&autoload_work);
127}
128
129void snd_seq_autoload_init(void)
130{
131	atomic_dec(&snd_seq_in_init);
132#ifdef CONFIG_SND_SEQUENCER_MODULE
133	/* initial autoload only when snd-seq is a module */
134	queue_autoload_drivers();
135#endif
136}
137EXPORT_SYMBOL(snd_seq_autoload_init);
138
139void snd_seq_autoload_exit(void)
140{
141	atomic_inc(&snd_seq_in_init);
142}
143EXPORT_SYMBOL(snd_seq_autoload_exit);
144
145void snd_seq_device_load_drivers(void)
146{
147	queue_autoload_drivers();
148	flush_work(&autoload_work);
149}
150EXPORT_SYMBOL(snd_seq_device_load_drivers);
151#define cancel_autoload_drivers()	cancel_work_sync(&autoload_work)
152#else
153#define queue_autoload_drivers() /* NOP */
154#define cancel_autoload_drivers() /* NOP */
155#endif
156
157/*
158 * device management
159 */
160static int snd_seq_device_dev_free(struct snd_device *device)
161{
162	struct snd_seq_device *dev = device->device_data;
163
164	cancel_autoload_drivers();
165	put_device(&dev->dev);
166	return 0;
167}
168
169static int snd_seq_device_dev_register(struct snd_device *device)
170{
171	struct snd_seq_device *dev = device->device_data;
172	int err;
173
174	err = device_add(&dev->dev);
175	if (err < 0)
176		return err;
177	if (!dev->dev.driver)
178		queue_autoload_drivers();
179	return 0;
180}
181
182static int snd_seq_device_dev_disconnect(struct snd_device *device)
183{
184	struct snd_seq_device *dev = device->device_data;
185
186	device_del(&dev->dev);
187	return 0;
188}
189
190static void snd_seq_dev_release(struct device *dev)
191{
192	struct snd_seq_device *sdev = to_seq_dev(dev);
193
194	if (sdev->private_free)
195		sdev->private_free(sdev);
196	kfree(sdev);
197}
198
199/*
200 * register a sequencer device
201 * card = card info
202 * device = device number (if any)
203 * id = id of driver
204 * result = return pointer (NULL allowed if unnecessary)
205 */
206int snd_seq_device_new(struct snd_card *card, int device, const char *id,
207		       int argsize, struct snd_seq_device **result)
208{
209	struct snd_seq_device *dev;
210	int err;
211	static struct snd_device_ops dops = {
212		.dev_free = snd_seq_device_dev_free,
213		.dev_register = snd_seq_device_dev_register,
214		.dev_disconnect = snd_seq_device_dev_disconnect,
215	};
216
217	if (result)
218		*result = NULL;
219
220	if (snd_BUG_ON(!id))
221		return -EINVAL;
222
223	dev = kzalloc(sizeof(*dev) + argsize, GFP_KERNEL);
224	if (!dev)
225		return -ENOMEM;
226
227	/* set up device info */
228	dev->card = card;
229	dev->device = device;
230	dev->id = id;
231	dev->argsize = argsize;
232
233	device_initialize(&dev->dev);
234	dev->dev.parent = &card->card_dev;
235	dev->dev.bus = &snd_seq_bus_type;
236	dev->dev.release = snd_seq_dev_release;
237	dev_set_name(&dev->dev, "%s-%d-%d", dev->id, card->number, device);
238
239	/* add this device to the list */
240	err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops);
241	if (err < 0) {
242		put_device(&dev->dev);
243		return err;
244	}
245	
246	if (result)
247		*result = dev;
248
249	return 0;
250}
251EXPORT_SYMBOL(snd_seq_device_new);
252
253/*
254 * driver registration
255 */
256int __snd_seq_driver_register(struct snd_seq_driver *drv, struct module *mod)
257{
258	if (WARN_ON(!drv->driver.name || !drv->id))
259		return -EINVAL;
260	drv->driver.bus = &snd_seq_bus_type;
261	drv->driver.owner = mod;
262	return driver_register(&drv->driver);
263}
264EXPORT_SYMBOL_GPL(__snd_seq_driver_register);
265
266void snd_seq_driver_unregister(struct snd_seq_driver *drv)
267{
268	driver_unregister(&drv->driver);
269}
270EXPORT_SYMBOL_GPL(snd_seq_driver_unregister);
271
272/*
273 * module part
274 */
275
276static int __init seq_dev_proc_init(void)
277{
278#ifdef CONFIG_SND_PROC_FS
279	info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers",
280						  snd_seq_root);
281	if (info_entry == NULL)
282		return -ENOMEM;
283	info_entry->content = SNDRV_INFO_CONTENT_TEXT;
284	info_entry->c.text.read = snd_seq_device_info;
285	if (snd_info_register(info_entry) < 0) {
286		snd_info_free_entry(info_entry);
287		return -ENOMEM;
288	}
289#endif
290	return 0;
291}
292
293static int __init alsa_seq_device_init(void)
294{
295	int err;
296
297	err = bus_register(&snd_seq_bus_type);
298	if (err < 0)
299		return err;
300	err = seq_dev_proc_init();
301	if (err < 0)
302		bus_unregister(&snd_seq_bus_type);
303	return err;
304}
305
306static void __exit alsa_seq_device_exit(void)
307{
308#ifdef CONFIG_MODULES
309	cancel_work_sync(&autoload_work);
310#endif
311#ifdef CONFIG_SND_PROC_FS
312	snd_info_free_entry(info_entry);
313#endif
314	bus_unregister(&snd_seq_bus_type);
315}
316
317subsys_initcall(alsa_seq_device_init)
318module_exit(alsa_seq_device_exit)