1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22#include <linux/init.h>
23#include <linux/slab.h>
24#include <linux/time.h>
25#include <linux/device.h>
26#include <linux/module.h>
27#include <sound/core.h>
28#include <sound/minors.h>
29#include <sound/info.h>
30#include <sound/control.h>
31#include <sound/initval.h>
32#include <linux/kmod.h>
33#include <linux/mutex.h>
34
35static int major = CONFIG_SND_MAJOR;
36int snd_major;
37EXPORT_SYMBOL(snd_major);
38
39static int cards_limit = 1;
40
41MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
42MODULE_DESCRIPTION("Advanced Linux Sound Architecture driver for soundcards.");
43MODULE_LICENSE("GPL");
44module_param(major, int, 0444);
45MODULE_PARM_DESC(major, "Major # for sound driver.");
46module_param(cards_limit, int, 0444);
47MODULE_PARM_DESC(cards_limit, "Count of auto-loadable soundcards.");
48MODULE_ALIAS_CHARDEV_MAJOR(CONFIG_SND_MAJOR);
49
50
51
52
53
54int snd_ecards_limit;
55EXPORT_SYMBOL(snd_ecards_limit);
56
57static struct snd_minor *snd_minors[SNDRV_OS_MINORS];
58static DEFINE_MUTEX(sound_mutex);
59
60#ifdef CONFIG_MODULES
61
62
63
64
65
66
67
68
69void snd_request_card(int card)
70{
71 if (snd_card_locked(card))
72 return;
73 if (card < 0 || card >= cards_limit)
74 return;
75 request_module("snd-card-%i", card);
76}
77
78EXPORT_SYMBOL(snd_request_card);
79
80static void snd_request_other(int minor)
81{
82 char *str;
83
84 switch (minor) {
85 case SNDRV_MINOR_SEQUENCER: str = "snd-seq"; break;
86 case SNDRV_MINOR_TIMER: str = "snd-timer"; break;
87 default: return;
88 }
89 request_module(str);
90}
91
92#endif
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109void *snd_lookup_minor_data(unsigned int minor, int type)
110{
111 struct snd_minor *mreg;
112 void *private_data;
113
114 if (minor >= ARRAY_SIZE(snd_minors))
115 return NULL;
116 mutex_lock(&sound_mutex);
117 mreg = snd_minors[minor];
118 if (mreg && mreg->type == type) {
119 private_data = mreg->private_data;
120 if (private_data && mreg->card_ptr)
121 atomic_inc(&mreg->card_ptr->refcount);
122 } else
123 private_data = NULL;
124 mutex_unlock(&sound_mutex);
125 return private_data;
126}
127
128EXPORT_SYMBOL(snd_lookup_minor_data);
129
130#ifdef CONFIG_MODULES
131static struct snd_minor *autoload_device(unsigned int minor)
132{
133 int dev;
134 mutex_unlock(&sound_mutex);
135 dev = SNDRV_MINOR_DEVICE(minor);
136 if (dev == SNDRV_MINOR_CONTROL) {
137
138 int card = SNDRV_MINOR_CARD(minor);
139 if (snd_cards[card] == NULL)
140 snd_request_card(card);
141 } else if (dev == SNDRV_MINOR_GLOBAL) {
142
143 snd_request_other(minor);
144 }
145 mutex_lock(&sound_mutex);
146 return snd_minors[minor];
147}
148#else
149#define autoload_device(minor) NULL
150#endif
151
152static int snd_open(struct inode *inode, struct file *file)
153{
154 unsigned int minor = iminor(inode);
155 struct snd_minor *mptr = NULL;
156 const struct file_operations *old_fops;
157 int err = 0;
158
159 if (minor >= ARRAY_SIZE(snd_minors))
160 return -ENODEV;
161 mutex_lock(&sound_mutex);
162 mptr = snd_minors[minor];
163 if (mptr == NULL) {
164 mptr = autoload_device(minor);
165 if (!mptr) {
166 mutex_unlock(&sound_mutex);
167 return -ENODEV;
168 }
169 }
170 old_fops = file->f_op;
171 file->f_op = fops_get(mptr->f_ops);
172 if (file->f_op == NULL) {
173 file->f_op = old_fops;
174 err = -ENODEV;
175 }
176 mutex_unlock(&sound_mutex);
177 if (err < 0)
178 return err;
179
180 if (file->f_op->open) {
181 err = file->f_op->open(inode, file);
182 if (err) {
183 fops_put(file->f_op);
184 file->f_op = fops_get(old_fops);
185 }
186 }
187 fops_put(old_fops);
188 return err;
189}
190
191static const struct file_operations snd_fops =
192{
193 .owner = THIS_MODULE,
194 .open = snd_open,
195 .llseek = noop_llseek,
196};
197
198#ifdef CONFIG_SND_DYNAMIC_MINORS
199static int snd_find_free_minor(int type)
200{
201 int minor;
202
203
204 if (type == SNDRV_DEVICE_TYPE_SEQUENCER)
205 return SNDRV_MINOR_SEQUENCER;
206 if (type == SNDRV_DEVICE_TYPE_TIMER)
207 return SNDRV_MINOR_TIMER;
208
209 for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) {
210
211 if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL)
212 continue;
213 if (minor == SNDRV_MINOR_SEQUENCER ||
214 minor == SNDRV_MINOR_TIMER)
215 continue;
216 if (!snd_minors[minor])
217 return minor;
218 }
219 return -EBUSY;
220}
221#else
222static int snd_kernel_minor(int type, struct snd_card *card, int dev)
223{
224 int minor;
225
226 switch (type) {
227 case SNDRV_DEVICE_TYPE_SEQUENCER:
228 case SNDRV_DEVICE_TYPE_TIMER:
229 minor = type;
230 break;
231 case SNDRV_DEVICE_TYPE_CONTROL:
232 if (snd_BUG_ON(!card))
233 return -EINVAL;
234 minor = SNDRV_MINOR(card->number, type);
235 break;
236 case SNDRV_DEVICE_TYPE_HWDEP:
237 case SNDRV_DEVICE_TYPE_RAWMIDI:
238 case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:
239 case SNDRV_DEVICE_TYPE_PCM_CAPTURE:
240 case SNDRV_DEVICE_TYPE_COMPRESS:
241 if (snd_BUG_ON(!card))
242 return -EINVAL;
243 minor = SNDRV_MINOR(card->number, type + dev);
244 break;
245 default:
246 return -EINVAL;
247 }
248 if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS))
249 return -EINVAL;
250 return minor;
251}
252#endif
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
270 const struct file_operations *f_ops,
271 void *private_data,
272 const char *name, struct device *device)
273{
274 int minor;
275 struct snd_minor *preg;
276
277 if (snd_BUG_ON(!name))
278 return -EINVAL;
279 preg = kmalloc(sizeof *preg, GFP_KERNEL);
280 if (preg == NULL)
281 return -ENOMEM;
282 preg->type = type;
283 preg->card = card ? card->number : -1;
284 preg->device = dev;
285 preg->f_ops = f_ops;
286 preg->private_data = private_data;
287 preg->card_ptr = card;
288 mutex_lock(&sound_mutex);
289#ifdef CONFIG_SND_DYNAMIC_MINORS
290 minor = snd_find_free_minor(type);
291#else
292 minor = snd_kernel_minor(type, card, dev);
293 if (minor >= 0 && snd_minors[minor])
294 minor = -EBUSY;
295#endif
296 if (minor < 0) {
297 mutex_unlock(&sound_mutex);
298 kfree(preg);
299 return minor;
300 }
301 snd_minors[minor] = preg;
302 preg->dev = device_create(sound_class, device, MKDEV(major, minor),
303 private_data, "%s", name);
304 if (IS_ERR(preg->dev)) {
305 snd_minors[minor] = NULL;
306 mutex_unlock(&sound_mutex);
307 minor = PTR_ERR(preg->dev);
308 kfree(preg);
309 return minor;
310 }
311
312 mutex_unlock(&sound_mutex);
313 return 0;
314}
315
316EXPORT_SYMBOL(snd_register_device_for_dev);
317
318
319
320
321static int find_snd_minor(int type, struct snd_card *card, int dev)
322{
323 int cardnum, minor;
324 struct snd_minor *mptr;
325
326 cardnum = card ? card->number : -1;
327 for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor)
328 if ((mptr = snd_minors[minor]) != NULL &&
329 mptr->type == type &&
330 mptr->card == cardnum &&
331 mptr->device == dev)
332 return minor;
333 return -1;
334}
335
336
337
338
339
340
341
342
343
344
345
346
347int snd_unregister_device(int type, struct snd_card *card, int dev)
348{
349 int minor;
350
351 mutex_lock(&sound_mutex);
352 minor = find_snd_minor(type, card, dev);
353 if (minor < 0) {
354 mutex_unlock(&sound_mutex);
355 return -EINVAL;
356 }
357
358 device_destroy(sound_class, MKDEV(major, minor));
359
360 kfree(snd_minors[minor]);
361 snd_minors[minor] = NULL;
362 mutex_unlock(&sound_mutex);
363 return 0;
364}
365
366EXPORT_SYMBOL(snd_unregister_device);
367
368int snd_add_device_sysfs_file(int type, struct snd_card *card, int dev,
369 struct device_attribute *attr)
370{
371 int minor, ret = -EINVAL;
372 struct device *d;
373
374 mutex_lock(&sound_mutex);
375 minor = find_snd_minor(type, card, dev);
376 if (minor >= 0 && (d = snd_minors[minor]->dev) != NULL)
377 ret = device_create_file(d, attr);
378 mutex_unlock(&sound_mutex);
379 return ret;
380
381}
382
383EXPORT_SYMBOL(snd_add_device_sysfs_file);
384
385#ifdef CONFIG_PROC_FS
386
387
388
389
390static struct snd_info_entry *snd_minor_info_entry;
391
392static const char *snd_device_type_name(int type)
393{
394 switch (type) {
395 case SNDRV_DEVICE_TYPE_CONTROL:
396 return "control";
397 case SNDRV_DEVICE_TYPE_HWDEP:
398 return "hardware dependent";
399 case SNDRV_DEVICE_TYPE_RAWMIDI:
400 return "raw midi";
401 case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:
402 return "digital audio playback";
403 case SNDRV_DEVICE_TYPE_PCM_CAPTURE:
404 return "digital audio capture";
405 case SNDRV_DEVICE_TYPE_SEQUENCER:
406 return "sequencer";
407 case SNDRV_DEVICE_TYPE_TIMER:
408 return "timer";
409 default:
410 return "?";
411 }
412}
413
414static void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
415{
416 int minor;
417 struct snd_minor *mptr;
418
419 mutex_lock(&sound_mutex);
420 for (minor = 0; minor < SNDRV_OS_MINORS; ++minor) {
421 if (!(mptr = snd_minors[minor]))
422 continue;
423 if (mptr->card >= 0) {
424 if (mptr->device >= 0)
425 snd_iprintf(buffer, "%3i: [%2i-%2i]: %s\n",
426 minor, mptr->card, mptr->device,
427 snd_device_type_name(mptr->type));
428 else
429 snd_iprintf(buffer, "%3i: [%2i] : %s\n",
430 minor, mptr->card,
431 snd_device_type_name(mptr->type));
432 } else
433 snd_iprintf(buffer, "%3i: : %s\n", minor,
434 snd_device_type_name(mptr->type));
435 }
436 mutex_unlock(&sound_mutex);
437}
438
439int __init snd_minor_info_init(void)
440{
441 struct snd_info_entry *entry;
442
443 entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL);
444 if (entry) {
445 entry->c.text.read = snd_minor_info_read;
446 if (snd_info_register(entry) < 0) {
447 snd_info_free_entry(entry);
448 entry = NULL;
449 }
450 }
451 snd_minor_info_entry = entry;
452 return 0;
453}
454
455int __exit snd_minor_info_done(void)
456{
457 snd_info_free_entry(snd_minor_info_entry);
458 return 0;
459}
460#endif
461
462
463
464
465
466static int __init alsa_sound_init(void)
467{
468 snd_major = major;
469 snd_ecards_limit = cards_limit;
470 if (register_chrdev(major, "alsa", &snd_fops)) {
471 snd_printk(KERN_ERR "unable to register native major device number %d\n", major);
472 return -EIO;
473 }
474 if (snd_info_init() < 0) {
475 unregister_chrdev(major, "alsa");
476 return -ENOMEM;
477 }
478 snd_info_minor_register();
479#ifndef MODULE
480 printk(KERN_INFO "Advanced Linux Sound Architecture Driver Initialized.\n");
481#endif
482 return 0;
483}
484
485static void __exit alsa_sound_exit(void)
486{
487 snd_info_minor_unregister();
488 snd_info_done();
489 unregister_chrdev(major, "alsa");
490}
491
492subsys_initcall(alsa_sound_init);
493module_exit(alsa_sound_exit);
494