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