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 : 28 : void snd_seq_autoload_init(void)
115 : : {
116 : 28 : 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 : 28 : }
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 : 28 : static int __init seq_dev_proc_init(void)
262 : : {
263 : : #ifdef CONFIG_SND_PROC_FS
264 : 28 : info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers",
265 : : snd_seq_root);
266 [ + - ]: 28 : if (info_entry == NULL)
267 : : return -ENOMEM;
268 : 28 : info_entry->content = SNDRV_INFO_CONTENT_TEXT;
269 : 28 : info_entry->c.text.read = snd_seq_device_info;
270 [ - + ]: 28 : 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 : 28 : static int __init alsa_seq_device_init(void)
279 : : {
280 : 28 : int err;
281 : :
282 : 28 : err = bus_register(&snd_seq_bus_type);
283 [ + - ]: 28 : if (err < 0)
284 : : return err;
285 : 28 : err = seq_dev_proc_init();
286 [ - + ]: 28 : 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)
|