diff options
author | Takashi Iwai <tiwai@suse.de> | 2015-03-23 08:14:02 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2015-03-23 08:14:02 -0400 |
commit | 3372dbdd8ca11f51be8c6a30b2bc79eb04c4a902 (patch) | |
tree | d4499bf5a5665b4820ffaf96bce55bf6b895195e /sound/core/seq/seq_device.c | |
parent | bc465aa9d045feb0e13b4a8f32cc33c1943f62d6 (diff) | |
parent | 967b1307b69b8ada8b331e01046ad1ef83742e99 (diff) |
Merge branch 'for-next' into topic/hda-core
Diffstat (limited to 'sound/core/seq/seq_device.c')
-rw-r--r-- | sound/core/seq/seq_device.c | 571 |
1 files changed, 135 insertions, 436 deletions
diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index 0631bdadd12b..d99f99d61983 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c | |||
@@ -36,6 +36,7 @@ | |||
36 | * | 36 | * |
37 | */ | 37 | */ |
38 | 38 | ||
39 | #include <linux/device.h> | ||
39 | #include <linux/init.h> | 40 | #include <linux/init.h> |
40 | #include <linux/module.h> | 41 | #include <linux/module.h> |
41 | #include <sound/core.h> | 42 | #include <sound/core.h> |
@@ -51,140 +52,78 @@ MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); | |||
51 | MODULE_DESCRIPTION("ALSA sequencer device management"); | 52 | MODULE_DESCRIPTION("ALSA sequencer device management"); |
52 | MODULE_LICENSE("GPL"); | 53 | MODULE_LICENSE("GPL"); |
53 | 54 | ||
54 | /* driver state */ | 55 | /* |
55 | #define DRIVER_EMPTY 0 | 56 | * bus definition |
56 | #define DRIVER_LOADED (1<<0) | 57 | */ |
57 | #define DRIVER_REQUESTED (1<<1) | 58 | static int snd_seq_bus_match(struct device *dev, struct device_driver *drv) |
58 | #define DRIVER_LOCKED (1<<2) | 59 | { |
59 | #define DRIVER_REQUESTING (1<<3) | 60 | struct snd_seq_device *sdev = to_seq_dev(dev); |
60 | 61 | struct snd_seq_driver *sdrv = to_seq_drv(drv); | |
61 | struct ops_list { | ||
62 | char id[ID_LEN]; /* driver id */ | ||
63 | int driver; /* driver state */ | ||
64 | int used; /* reference counter */ | ||
65 | int argsize; /* argument size */ | ||
66 | |||
67 | /* operators */ | ||
68 | struct snd_seq_dev_ops ops; | ||
69 | |||
70 | /* registered devices */ | ||
71 | struct list_head dev_list; /* list of devices */ | ||
72 | int num_devices; /* number of associated devices */ | ||
73 | int num_init_devices; /* number of initialized devices */ | ||
74 | struct mutex reg_mutex; | ||
75 | |||
76 | struct list_head list; /* next driver */ | ||
77 | }; | ||
78 | 62 | ||
63 | return strcmp(sdrv->id, sdev->id) == 0 && | ||
64 | sdrv->argsize == sdev->argsize; | ||
65 | } | ||
79 | 66 | ||
80 | static LIST_HEAD(opslist); | 67 | static struct bus_type snd_seq_bus_type = { |
81 | static int num_ops; | 68 | .name = "snd_seq", |
82 | static DEFINE_MUTEX(ops_mutex); | 69 | .match = snd_seq_bus_match, |
83 | #ifdef CONFIG_PROC_FS | 70 | }; |
84 | static struct snd_info_entry *info_entry; | ||
85 | #endif | ||
86 | 71 | ||
87 | /* | 72 | /* |
88 | * prototypes | 73 | * proc interface -- just for compatibility |
89 | */ | 74 | */ |
90 | static int snd_seq_device_free(struct snd_seq_device *dev); | 75 | #ifdef CONFIG_PROC_FS |
91 | static int snd_seq_device_dev_free(struct snd_device *device); | 76 | static struct snd_info_entry *info_entry; |
92 | static int snd_seq_device_dev_register(struct snd_device *device); | ||
93 | static int snd_seq_device_dev_disconnect(struct snd_device *device); | ||
94 | |||
95 | static int init_device(struct snd_seq_device *dev, struct ops_list *ops); | ||
96 | static int free_device(struct snd_seq_device *dev, struct ops_list *ops); | ||
97 | static struct ops_list *find_driver(char *id, int create_if_empty); | ||
98 | static struct ops_list *create_driver(char *id); | ||
99 | static void unlock_driver(struct ops_list *ops); | ||
100 | static void remove_drivers(void); | ||
101 | 77 | ||
102 | /* | 78 | static int print_dev_info(struct device *dev, void *data) |
103 | * show all drivers and their status | 79 | { |
104 | */ | 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 | } | ||
105 | 88 | ||
106 | #ifdef CONFIG_PROC_FS | ||
107 | static void snd_seq_device_info(struct snd_info_entry *entry, | 89 | static void snd_seq_device_info(struct snd_info_entry *entry, |
108 | struct snd_info_buffer *buffer) | 90 | struct snd_info_buffer *buffer) |
109 | { | 91 | { |
110 | struct ops_list *ops; | 92 | bus_for_each_dev(&snd_seq_bus_type, NULL, buffer, print_dev_info); |
111 | |||
112 | mutex_lock(&ops_mutex); | ||
113 | list_for_each_entry(ops, &opslist, list) { | ||
114 | snd_iprintf(buffer, "snd-%s%s%s%s,%d\n", | ||
115 | ops->id, | ||
116 | ops->driver & DRIVER_LOADED ? ",loaded" : (ops->driver == DRIVER_EMPTY ? ",empty" : ""), | ||
117 | ops->driver & DRIVER_REQUESTED ? ",requested" : "", | ||
118 | ops->driver & DRIVER_LOCKED ? ",locked" : "", | ||
119 | ops->num_devices); | ||
120 | } | ||
121 | mutex_unlock(&ops_mutex); | ||
122 | } | 93 | } |
123 | #endif | 94 | #endif |
124 | 95 | ||
125 | /* | 96 | /* |
126 | * load all registered drivers (called from seq_clientmgr.c) | 97 | * load all registered drivers (called from seq_clientmgr.c) |
127 | */ | 98 | */ |
128 | 99 | ||
129 | #ifdef CONFIG_MODULES | 100 | #ifdef CONFIG_MODULES |
130 | /* avoid auto-loading during module_init() */ | 101 | /* flag to block auto-loading */ |
131 | static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */ | 102 | static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */ |
132 | void snd_seq_autoload_lock(void) | ||
133 | { | ||
134 | atomic_inc(&snd_seq_in_init); | ||
135 | } | ||
136 | 103 | ||
137 | void snd_seq_autoload_unlock(void) | 104 | static int request_seq_drv(struct device *dev, void *data) |
138 | { | 105 | { |
139 | atomic_dec(&snd_seq_in_init); | 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; | ||
140 | } | 111 | } |
141 | 112 | ||
142 | static void autoload_drivers(void) | 113 | static void autoload_drivers(struct work_struct *work) |
143 | { | 114 | { |
144 | /* avoid reentrance */ | 115 | /* avoid reentrance */ |
145 | if (atomic_inc_return(&snd_seq_in_init) == 1) { | 116 | if (atomic_inc_return(&snd_seq_in_init) == 1) |
146 | struct ops_list *ops; | 117 | bus_for_each_dev(&snd_seq_bus_type, NULL, NULL, |
147 | 118 | request_seq_drv); | |
148 | mutex_lock(&ops_mutex); | ||
149 | list_for_each_entry(ops, &opslist, list) { | ||
150 | if ((ops->driver & DRIVER_REQUESTING) && | ||
151 | !(ops->driver & DRIVER_REQUESTED)) { | ||
152 | ops->used++; | ||
153 | mutex_unlock(&ops_mutex); | ||
154 | ops->driver |= DRIVER_REQUESTED; | ||
155 | request_module("snd-%s", ops->id); | ||
156 | mutex_lock(&ops_mutex); | ||
157 | ops->used--; | ||
158 | } | ||
159 | } | ||
160 | mutex_unlock(&ops_mutex); | ||
161 | } | ||
162 | atomic_dec(&snd_seq_in_init); | 119 | atomic_dec(&snd_seq_in_init); |
163 | } | 120 | } |
164 | 121 | ||
165 | static void call_autoload(struct work_struct *work) | 122 | static DECLARE_WORK(autoload_work, autoload_drivers); |
166 | { | ||
167 | autoload_drivers(); | ||
168 | } | ||
169 | |||
170 | static DECLARE_WORK(autoload_work, call_autoload); | ||
171 | |||
172 | static void try_autoload(struct ops_list *ops) | ||
173 | { | ||
174 | if (!ops->driver) { | ||
175 | ops->driver |= DRIVER_REQUESTING; | ||
176 | schedule_work(&autoload_work); | ||
177 | } | ||
178 | } | ||
179 | 123 | ||
180 | static void queue_autoload_drivers(void) | 124 | static void queue_autoload_drivers(void) |
181 | { | 125 | { |
182 | struct ops_list *ops; | 126 | schedule_work(&autoload_work); |
183 | |||
184 | mutex_lock(&ops_mutex); | ||
185 | list_for_each_entry(ops, &opslist, list) | ||
186 | try_autoload(ops); | ||
187 | mutex_unlock(&ops_mutex); | ||
188 | } | 127 | } |
189 | 128 | ||
190 | void snd_seq_autoload_init(void) | 129 | void snd_seq_autoload_init(void) |
@@ -195,384 +134,143 @@ void snd_seq_autoload_init(void) | |||
195 | queue_autoload_drivers(); | 134 | queue_autoload_drivers(); |
196 | #endif | 135 | #endif |
197 | } | 136 | } |
198 | #else | 137 | EXPORT_SYMBOL(snd_seq_autoload_init); |
199 | #define try_autoload(ops) /* NOP */ | ||
200 | #endif | ||
201 | 138 | ||
202 | void snd_seq_device_load_drivers(void) | 139 | void snd_seq_autoload_exit(void) |
203 | { | 140 | { |
204 | #ifdef CONFIG_MODULES | 141 | atomic_inc(&snd_seq_in_init); |
205 | queue_autoload_drivers(); | ||
206 | flush_work(&autoload_work); | ||
207 | #endif | ||
208 | } | 142 | } |
143 | EXPORT_SYMBOL(snd_seq_autoload_exit); | ||
209 | 144 | ||
210 | /* | 145 | void snd_seq_device_load_drivers(void) |
211 | * register a sequencer device | ||
212 | * card = card info | ||
213 | * device = device number (if any) | ||
214 | * id = id of driver | ||
215 | * result = return pointer (NULL allowed if unnecessary) | ||
216 | */ | ||
217 | int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, | ||
218 | struct snd_seq_device **result) | ||
219 | { | 146 | { |
220 | struct snd_seq_device *dev; | 147 | queue_autoload_drivers(); |
221 | struct ops_list *ops; | 148 | flush_work(&autoload_work); |
222 | int err; | ||
223 | static struct snd_device_ops dops = { | ||
224 | .dev_free = snd_seq_device_dev_free, | ||
225 | .dev_register = snd_seq_device_dev_register, | ||
226 | .dev_disconnect = snd_seq_device_dev_disconnect, | ||
227 | }; | ||
228 | |||
229 | if (result) | ||
230 | *result = NULL; | ||
231 | |||
232 | if (snd_BUG_ON(!id)) | ||
233 | return -EINVAL; | ||
234 | |||
235 | ops = find_driver(id, 1); | ||
236 | if (ops == NULL) | ||
237 | return -ENOMEM; | ||
238 | |||
239 | dev = kzalloc(sizeof(*dev)*2 + argsize, GFP_KERNEL); | ||
240 | if (dev == NULL) { | ||
241 | unlock_driver(ops); | ||
242 | return -ENOMEM; | ||
243 | } | ||
244 | |||
245 | /* set up device info */ | ||
246 | dev->card = card; | ||
247 | dev->device = device; | ||
248 | strlcpy(dev->id, id, sizeof(dev->id)); | ||
249 | dev->argsize = argsize; | ||
250 | dev->status = SNDRV_SEQ_DEVICE_FREE; | ||
251 | |||
252 | /* add this device to the list */ | ||
253 | mutex_lock(&ops->reg_mutex); | ||
254 | list_add_tail(&dev->list, &ops->dev_list); | ||
255 | ops->num_devices++; | ||
256 | mutex_unlock(&ops->reg_mutex); | ||
257 | |||
258 | if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) { | ||
259 | snd_seq_device_free(dev); | ||
260 | return err; | ||
261 | } | ||
262 | |||
263 | try_autoload(ops); | ||
264 | unlock_driver(ops); | ||
265 | |||
266 | if (result) | ||
267 | *result = dev; | ||
268 | |||
269 | return 0; | ||
270 | } | 149 | } |
150 | EXPORT_SYMBOL(snd_seq_device_load_drivers); | ||
151 | #else | ||
152 | #define queue_autoload_drivers() /* NOP */ | ||
153 | #endif | ||
271 | 154 | ||
272 | /* | 155 | /* |
273 | * free the existing device | 156 | * device management |
274 | */ | 157 | */ |
275 | static int snd_seq_device_free(struct snd_seq_device *dev) | ||
276 | { | ||
277 | struct ops_list *ops; | ||
278 | |||
279 | if (snd_BUG_ON(!dev)) | ||
280 | return -EINVAL; | ||
281 | |||
282 | ops = find_driver(dev->id, 0); | ||
283 | if (ops == NULL) | ||
284 | return -ENXIO; | ||
285 | |||
286 | /* remove the device from the list */ | ||
287 | mutex_lock(&ops->reg_mutex); | ||
288 | list_del(&dev->list); | ||
289 | ops->num_devices--; | ||
290 | mutex_unlock(&ops->reg_mutex); | ||
291 | |||
292 | free_device(dev, ops); | ||
293 | if (dev->private_free) | ||
294 | dev->private_free(dev); | ||
295 | kfree(dev); | ||
296 | |||
297 | unlock_driver(ops); | ||
298 | |||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | static int snd_seq_device_dev_free(struct snd_device *device) | 158 | static int snd_seq_device_dev_free(struct snd_device *device) |
303 | { | 159 | { |
304 | struct snd_seq_device *dev = device->device_data; | 160 | struct snd_seq_device *dev = device->device_data; |
305 | return snd_seq_device_free(dev); | 161 | |
162 | put_device(&dev->dev); | ||
163 | return 0; | ||
306 | } | 164 | } |
307 | 165 | ||
308 | /* | ||
309 | * register the device | ||
310 | */ | ||
311 | static int snd_seq_device_dev_register(struct snd_device *device) | 166 | static int snd_seq_device_dev_register(struct snd_device *device) |
312 | { | 167 | { |
313 | struct snd_seq_device *dev = device->device_data; | 168 | struct snd_seq_device *dev = device->device_data; |
314 | struct ops_list *ops; | 169 | int err; |
315 | |||
316 | ops = find_driver(dev->id, 0); | ||
317 | if (ops == NULL) | ||
318 | return -ENOENT; | ||
319 | |||
320 | /* initialize this device if the corresponding driver was | ||
321 | * already loaded | ||
322 | */ | ||
323 | if (ops->driver & DRIVER_LOADED) | ||
324 | init_device(dev, ops); | ||
325 | 170 | ||
326 | unlock_driver(ops); | 171 | err = device_add(&dev->dev); |
172 | if (err < 0) | ||
173 | return err; | ||
174 | if (!dev->dev.driver) | ||
175 | queue_autoload_drivers(); | ||
327 | return 0; | 176 | return 0; |
328 | } | 177 | } |
329 | 178 | ||
330 | /* | ||
331 | * disconnect the device | ||
332 | */ | ||
333 | static int snd_seq_device_dev_disconnect(struct snd_device *device) | 179 | static int snd_seq_device_dev_disconnect(struct snd_device *device) |
334 | { | 180 | { |
335 | struct snd_seq_device *dev = device->device_data; | 181 | struct snd_seq_device *dev = device->device_data; |
336 | struct ops_list *ops; | ||
337 | |||
338 | ops = find_driver(dev->id, 0); | ||
339 | if (ops == NULL) | ||
340 | return -ENOENT; | ||
341 | |||
342 | free_device(dev, ops); | ||
343 | 182 | ||
344 | unlock_driver(ops); | 183 | device_del(&dev->dev); |
345 | return 0; | 184 | return 0; |
346 | } | 185 | } |
347 | 186 | ||
348 | /* | 187 | static void snd_seq_dev_release(struct device *dev) |
349 | * register device driver | ||
350 | * id = driver id | ||
351 | * entry = driver operators - duplicated to each instance | ||
352 | */ | ||
353 | int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, | ||
354 | int argsize) | ||
355 | { | 188 | { |
356 | struct ops_list *ops; | 189 | struct snd_seq_device *sdev = to_seq_dev(dev); |
357 | struct snd_seq_device *dev; | ||
358 | 190 | ||
359 | if (id == NULL || entry == NULL || | 191 | if (sdev->private_free) |
360 | entry->init_device == NULL || entry->free_device == NULL) | 192 | sdev->private_free(sdev); |
361 | return -EINVAL; | 193 | kfree(sdev); |
362 | |||
363 | ops = find_driver(id, 1); | ||
364 | if (ops == NULL) | ||
365 | return -ENOMEM; | ||
366 | if (ops->driver & DRIVER_LOADED) { | ||
367 | pr_warn("ALSA: seq: driver_register: driver '%s' already exists\n", id); | ||
368 | unlock_driver(ops); | ||
369 | return -EBUSY; | ||
370 | } | ||
371 | |||
372 | mutex_lock(&ops->reg_mutex); | ||
373 | /* copy driver operators */ | ||
374 | ops->ops = *entry; | ||
375 | ops->driver |= DRIVER_LOADED; | ||
376 | ops->argsize = argsize; | ||
377 | |||
378 | /* initialize existing devices if necessary */ | ||
379 | list_for_each_entry(dev, &ops->dev_list, list) { | ||
380 | init_device(dev, ops); | ||
381 | } | ||
382 | mutex_unlock(&ops->reg_mutex); | ||
383 | |||
384 | unlock_driver(ops); | ||
385 | |||
386 | return 0; | ||
387 | } | 194 | } |
388 | 195 | ||
389 | |||
390 | /* | ||
391 | * create driver record | ||
392 | */ | ||
393 | static struct ops_list * create_driver(char *id) | ||
394 | { | ||
395 | struct ops_list *ops; | ||
396 | |||
397 | ops = kzalloc(sizeof(*ops), GFP_KERNEL); | ||
398 | if (ops == NULL) | ||
399 | return ops; | ||
400 | |||
401 | /* set up driver entry */ | ||
402 | strlcpy(ops->id, id, sizeof(ops->id)); | ||
403 | mutex_init(&ops->reg_mutex); | ||
404 | /* | ||
405 | * The ->reg_mutex locking rules are per-driver, so we create | ||
406 | * separate per-driver lock classes: | ||
407 | */ | ||
408 | lockdep_set_class(&ops->reg_mutex, (struct lock_class_key *)id); | ||
409 | |||
410 | ops->driver = DRIVER_EMPTY; | ||
411 | INIT_LIST_HEAD(&ops->dev_list); | ||
412 | /* lock this instance */ | ||
413 | ops->used = 1; | ||
414 | |||
415 | /* register driver entry */ | ||
416 | mutex_lock(&ops_mutex); | ||
417 | list_add_tail(&ops->list, &opslist); | ||
418 | num_ops++; | ||
419 | mutex_unlock(&ops_mutex); | ||
420 | |||
421 | return ops; | ||
422 | } | ||
423 | |||
424 | |||
425 | /* | 196 | /* |
426 | * unregister the specified driver | 197 | * register a sequencer device |
198 | * card = card info | ||
199 | * device = device number (if any) | ||
200 | * id = id of driver | ||
201 | * result = return pointer (NULL allowed if unnecessary) | ||
427 | */ | 202 | */ |
428 | int snd_seq_device_unregister_driver(char *id) | 203 | int snd_seq_device_new(struct snd_card *card, int device, const char *id, |
204 | int argsize, struct snd_seq_device **result) | ||
429 | { | 205 | { |
430 | struct ops_list *ops; | ||
431 | struct snd_seq_device *dev; | 206 | struct snd_seq_device *dev; |
207 | int err; | ||
208 | static struct snd_device_ops dops = { | ||
209 | .dev_free = snd_seq_device_dev_free, | ||
210 | .dev_register = snd_seq_device_dev_register, | ||
211 | .dev_disconnect = snd_seq_device_dev_disconnect, | ||
212 | }; | ||
432 | 213 | ||
433 | ops = find_driver(id, 0); | 214 | if (result) |
434 | if (ops == NULL) | 215 | *result = NULL; |
435 | return -ENXIO; | ||
436 | if (! (ops->driver & DRIVER_LOADED) || | ||
437 | (ops->driver & DRIVER_LOCKED)) { | ||
438 | pr_err("ALSA: seq: driver_unregister: cannot unload driver '%s': status=%x\n", | ||
439 | id, ops->driver); | ||
440 | unlock_driver(ops); | ||
441 | return -EBUSY; | ||
442 | } | ||
443 | |||
444 | /* close and release all devices associated with this driver */ | ||
445 | mutex_lock(&ops->reg_mutex); | ||
446 | ops->driver |= DRIVER_LOCKED; /* do not remove this driver recursively */ | ||
447 | list_for_each_entry(dev, &ops->dev_list, list) { | ||
448 | free_device(dev, ops); | ||
449 | } | ||
450 | |||
451 | ops->driver = 0; | ||
452 | if (ops->num_init_devices > 0) | ||
453 | pr_err("ALSA: seq: free_driver: init_devices > 0!! (%d)\n", | ||
454 | ops->num_init_devices); | ||
455 | mutex_unlock(&ops->reg_mutex); | ||
456 | |||
457 | unlock_driver(ops); | ||
458 | 216 | ||
459 | /* remove empty driver entries */ | 217 | if (snd_BUG_ON(!id)) |
460 | remove_drivers(); | 218 | return -EINVAL; |
461 | 219 | ||
462 | return 0; | 220 | dev = kzalloc(sizeof(*dev) + argsize, GFP_KERNEL); |
463 | } | 221 | if (!dev) |
222 | return -ENOMEM; | ||
464 | 223 | ||
224 | /* set up device info */ | ||
225 | dev->card = card; | ||
226 | dev->device = device; | ||
227 | dev->id = id; | ||
228 | dev->argsize = argsize; | ||
465 | 229 | ||
466 | /* | 230 | device_initialize(&dev->dev); |
467 | * remove empty driver entries | 231 | dev->dev.parent = &card->card_dev; |
468 | */ | 232 | dev->dev.bus = &snd_seq_bus_type; |
469 | static void remove_drivers(void) | 233 | dev->dev.release = snd_seq_dev_release; |
470 | { | 234 | dev_set_name(&dev->dev, "%s-%d-%d", dev->id, card->number, device); |
471 | struct list_head *head; | ||
472 | |||
473 | mutex_lock(&ops_mutex); | ||
474 | head = opslist.next; | ||
475 | while (head != &opslist) { | ||
476 | struct ops_list *ops = list_entry(head, struct ops_list, list); | ||
477 | if (! (ops->driver & DRIVER_LOADED) && | ||
478 | ops->used == 0 && ops->num_devices == 0) { | ||
479 | head = head->next; | ||
480 | list_del(&ops->list); | ||
481 | kfree(ops); | ||
482 | num_ops--; | ||
483 | } else | ||
484 | head = head->next; | ||
485 | } | ||
486 | mutex_unlock(&ops_mutex); | ||
487 | } | ||
488 | 235 | ||
489 | /* | 236 | /* add this device to the list */ |
490 | * initialize the device - call init_device operator | 237 | err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops); |
491 | */ | 238 | if (err < 0) { |
492 | static int init_device(struct snd_seq_device *dev, struct ops_list *ops) | 239 | put_device(&dev->dev); |
493 | { | 240 | return err; |
494 | if (! (ops->driver & DRIVER_LOADED)) | ||
495 | return 0; /* driver is not loaded yet */ | ||
496 | if (dev->status != SNDRV_SEQ_DEVICE_FREE) | ||
497 | return 0; /* already initialized */ | ||
498 | if (ops->argsize != dev->argsize) { | ||
499 | pr_err("ALSA: seq: incompatible device '%s' for plug-in '%s' (%d %d)\n", | ||
500 | dev->name, ops->id, ops->argsize, dev->argsize); | ||
501 | return -EINVAL; | ||
502 | } | ||
503 | if (ops->ops.init_device(dev) >= 0) { | ||
504 | dev->status = SNDRV_SEQ_DEVICE_REGISTERED; | ||
505 | ops->num_init_devices++; | ||
506 | } else { | ||
507 | pr_err("ALSA: seq: init_device failed: %s: %s\n", | ||
508 | dev->name, dev->id); | ||
509 | } | 241 | } |
242 | |||
243 | if (result) | ||
244 | *result = dev; | ||
510 | 245 | ||
511 | return 0; | 246 | return 0; |
512 | } | 247 | } |
248 | EXPORT_SYMBOL(snd_seq_device_new); | ||
513 | 249 | ||
514 | /* | 250 | /* |
515 | * release the device - call free_device operator | 251 | * driver registration |
516 | */ | 252 | */ |
517 | static int free_device(struct snd_seq_device *dev, struct ops_list *ops) | 253 | int __snd_seq_driver_register(struct snd_seq_driver *drv, struct module *mod) |
518 | { | 254 | { |
519 | int result; | 255 | if (WARN_ON(!drv->driver.name || !drv->id)) |
520 | |||
521 | if (! (ops->driver & DRIVER_LOADED)) | ||
522 | return 0; /* driver is not loaded yet */ | ||
523 | if (dev->status != SNDRV_SEQ_DEVICE_REGISTERED) | ||
524 | return 0; /* not registered */ | ||
525 | if (ops->argsize != dev->argsize) { | ||
526 | pr_err("ALSA: seq: incompatible device '%s' for plug-in '%s' (%d %d)\n", | ||
527 | dev->name, ops->id, ops->argsize, dev->argsize); | ||
528 | return -EINVAL; | 256 | return -EINVAL; |
529 | } | 257 | drv->driver.bus = &snd_seq_bus_type; |
530 | if ((result = ops->ops.free_device(dev)) >= 0 || result == -ENXIO) { | 258 | drv->driver.owner = mod; |
531 | dev->status = SNDRV_SEQ_DEVICE_FREE; | 259 | return driver_register(&drv->driver); |
532 | dev->driver_data = NULL; | ||
533 | ops->num_init_devices--; | ||
534 | } else { | ||
535 | pr_err("ALSA: seq: free_device failed: %s: %s\n", | ||
536 | dev->name, dev->id); | ||
537 | } | ||
538 | |||
539 | return 0; | ||
540 | } | 260 | } |
261 | EXPORT_SYMBOL_GPL(__snd_seq_driver_register); | ||
541 | 262 | ||
542 | /* | 263 | void snd_seq_driver_unregister(struct snd_seq_driver *drv) |
543 | * find the matching driver with given id | ||
544 | */ | ||
545 | static struct ops_list * find_driver(char *id, int create_if_empty) | ||
546 | { | 264 | { |
547 | struct ops_list *ops; | 265 | driver_unregister(&drv->driver); |
548 | |||
549 | mutex_lock(&ops_mutex); | ||
550 | list_for_each_entry(ops, &opslist, list) { | ||
551 | if (strcmp(ops->id, id) == 0) { | ||
552 | ops->used++; | ||
553 | mutex_unlock(&ops_mutex); | ||
554 | return ops; | ||
555 | } | ||
556 | } | ||
557 | mutex_unlock(&ops_mutex); | ||
558 | if (create_if_empty) | ||
559 | return create_driver(id); | ||
560 | return NULL; | ||
561 | } | 266 | } |
562 | 267 | EXPORT_SYMBOL_GPL(snd_seq_driver_unregister); | |
563 | static void unlock_driver(struct ops_list *ops) | ||
564 | { | ||
565 | mutex_lock(&ops_mutex); | ||
566 | ops->used--; | ||
567 | mutex_unlock(&ops_mutex); | ||
568 | } | ||
569 | |||
570 | 268 | ||
571 | /* | 269 | /* |
572 | * module part | 270 | * module part |
573 | */ | 271 | */ |
574 | 272 | ||
575 | static int __init alsa_seq_device_init(void) | 273 | static int __init seq_dev_proc_init(void) |
576 | { | 274 | { |
577 | #ifdef CONFIG_PROC_FS | 275 | #ifdef CONFIG_PROC_FS |
578 | info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers", | 276 | info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers", |
@@ -589,28 +287,29 @@ static int __init alsa_seq_device_init(void) | |||
589 | return 0; | 287 | return 0; |
590 | } | 288 | } |
591 | 289 | ||
290 | static int __init alsa_seq_device_init(void) | ||
291 | { | ||
292 | int err; | ||
293 | |||
294 | err = bus_register(&snd_seq_bus_type); | ||
295 | if (err < 0) | ||
296 | return err; | ||
297 | err = seq_dev_proc_init(); | ||
298 | if (err < 0) | ||
299 | bus_unregister(&snd_seq_bus_type); | ||
300 | return err; | ||
301 | } | ||
302 | |||
592 | static void __exit alsa_seq_device_exit(void) | 303 | static void __exit alsa_seq_device_exit(void) |
593 | { | 304 | { |
594 | #ifdef CONFIG_MODULES | 305 | #ifdef CONFIG_MODULES |
595 | cancel_work_sync(&autoload_work); | 306 | cancel_work_sync(&autoload_work); |
596 | #endif | 307 | #endif |
597 | remove_drivers(); | ||
598 | #ifdef CONFIG_PROC_FS | 308 | #ifdef CONFIG_PROC_FS |
599 | snd_info_free_entry(info_entry); | 309 | snd_info_free_entry(info_entry); |
600 | #endif | 310 | #endif |
601 | if (num_ops) | 311 | bus_unregister(&snd_seq_bus_type); |
602 | pr_err("ALSA: seq: drivers not released (%d)\n", num_ops); | ||
603 | } | 312 | } |
604 | 313 | ||
605 | module_init(alsa_seq_device_init) | 314 | subsys_initcall(alsa_seq_device_init) |
606 | module_exit(alsa_seq_device_exit) | 315 | module_exit(alsa_seq_device_exit) |
607 | |||
608 | EXPORT_SYMBOL(snd_seq_device_load_drivers); | ||
609 | EXPORT_SYMBOL(snd_seq_device_new); | ||
610 | EXPORT_SYMBOL(snd_seq_device_register_driver); | ||
611 | EXPORT_SYMBOL(snd_seq_device_unregister_driver); | ||
612 | #ifdef CONFIG_MODULES | ||
613 | EXPORT_SYMBOL(snd_seq_autoload_init); | ||
614 | EXPORT_SYMBOL(snd_seq_autoload_lock); | ||
615 | EXPORT_SYMBOL(snd_seq_autoload_unlock); | ||
616 | #endif | ||