/*
- * Sound core handling. Breaks out sound functions to submodules
+ * Sound core. This file is composed of two parts. sound_class
+ * which is common to both OSS and ALSA and OSS sound core which
+ * is used OSS or emulation of it.
+ */
+
+/*
+ * First, the common part.
+ */
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kdev_t.h>
+#include <linux/major.h>
+#include <sound/core.h>
+
+#ifdef CONFIG_SOUND_OSS_CORE
+static int __init init_oss_soundcore(void);
+static void cleanup_oss_soundcore(void);
+#else
+static inline int init_oss_soundcore(void) { return 0; }
+static inline void cleanup_oss_soundcore(void) { }
+#endif
+
+struct class *sound_class;
+EXPORT_SYMBOL(sound_class);
+
+MODULE_DESCRIPTION("Core sound module");
+MODULE_AUTHOR("Alan Cox");
+MODULE_LICENSE("GPL");
+
+static char *sound_devnode(struct device *dev, mode_t *mode)
+{
+ if (MAJOR(dev->devt) == SOUND_MAJOR)
+ return NULL;
+ return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev));
+}
+
+static int __init init_soundcore(void)
+{
+ int rc;
+
+ rc = init_oss_soundcore();
+ if (rc)
+ return rc;
+
+ sound_class = class_create(THIS_MODULE, "sound");
+ if (IS_ERR(sound_class)) {
+ cleanup_oss_soundcore();
+ return PTR_ERR(sound_class);
+ }
+
+ sound_class->devnode = sound_devnode;
+
+ return 0;
+}
+
+static void __exit cleanup_soundcore(void)
+{
+ cleanup_oss_soundcore();
+ class_destroy(sound_class);
+}
+
+module_init(init_soundcore);
+module_exit(cleanup_soundcore);
+
+
+#ifdef CONFIG_SOUND_OSS_CORE
+/*
+ * OSS sound core handling. Breaks out sound functions to submodules
*
- * Author: Alan Cox <alan.cox@linux.org>
+ * Author: Alan Cox <alan@lxorguk.ukuu.org.uk>
*
* Fixes:
*
* locking at some point in 2.3.x.
*/
-#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/smp_lock.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/fs.h>
#include <linux/sound.h>
-#include <linux/major.h>
#include <linux/kmod.h>
-#include <linux/device.h>
#define SOUND_STEP 16
-
struct sound_unit
{
int unit_minor;
extern int msnd_pinnacle_init(void);
#endif
-struct class *sound_class;
-EXPORT_SYMBOL(sound_class);
+/*
+ * By default, OSS sound_core claims full legacy minor range (0-255)
+ * of SOUND_MAJOR to trap open attempts to any sound minor and
+ * requests modules using custom sound-slot/service-* module aliases.
+ * The only benefit of doing this is allowing use of custom module
+ * aliases instead of the standard char-major-* ones. This behavior
+ * prevents alternative OSS implementation and is scheduled to be
+ * removed.
+ *
+ * CONFIG_SOUND_OSS_CORE_PRECLAIM and soundcore.preclaim_oss kernel
+ * parameter are added to allow distros and developers to try and
+ * switch to alternative implementations without needing to rebuild
+ * the kernel in the meantime. If preclaim_oss is non-zero, the
+ * kernel will behave the same as before. All SOUND_MAJOR minors are
+ * preclaimed and the custom module aliases along with standard chrdev
+ * ones are emitted if a missing device is opened. If preclaim_oss is
+ * zero, sound_core only grabs what's actually in use and for missing
+ * devices only the standard chrdev aliases are requested.
+ *
+ * All these clutters are scheduled to be removed along with
+ * sound-slot/service-* module aliases. Please take a look at
+ * feature-removal-schedule.txt for details.
+ */
+#ifdef CONFIG_SOUND_OSS_CORE_PRECLAIM
+static int preclaim_oss = 1;
+#else
+static int preclaim_oss = 0;
+#endif
+
+module_param(preclaim_oss, int, 0444);
+
+static int soundcore_open(struct inode *, struct file *);
+
+static const struct file_operations soundcore_fops =
+{
+ /* We must have an owner or the module locking fails */
+ .owner = THIS_MODULE,
+ .open = soundcore_open,
+};
/*
* Low level list operator. Scan the ordered list, find a hole and
if (!s)
return -ENOMEM;
-
+
spin_lock(&sound_loader_lock);
+retry:
r = __sound_insert_unit(s, list, fops, index, low, top);
spin_unlock(&sound_loader_lock);
else
sprintf(s->name, "sound/%s%d", name, r / SOUND_STEP);
+ if (!preclaim_oss) {
+ /*
+ * Something else might have grabbed the minor. If
+ * first free slot is requested, rescan with @low set
+ * to the next unit; otherwise, -EBUSY.
+ */
+ r = __register_chrdev(SOUND_MAJOR, s->unit_minor, 1, s->name,
+ &soundcore_fops);
+ if (r < 0) {
+ spin_lock(&sound_loader_lock);
+ __sound_remove_unit(list, s->unit_minor);
+ if (index < 0) {
+ low = s->unit_minor + SOUND_STEP;
+ goto retry;
+ }
+ spin_unlock(&sound_loader_lock);
+ return -EBUSY;
+ }
+ }
+
device_create(sound_class, dev, MKDEV(SOUND_MAJOR, s->unit_minor),
- s->name+6);
- return r;
+ NULL, s->name+6);
+ return s->unit_minor;
- fail:
+fail:
kfree(s);
return r;
}
p = __sound_remove_unit(list, unit);
spin_unlock(&sound_loader_lock);
if (p) {
+ if (!preclaim_oss)
+ __unregister_chrdev(SOUND_MAJOR, p->unit_minor, 1,
+ p->name);
device_destroy(sound_class, MKDEV(SOUND_MAJOR, p->unit_minor));
kfree(p);
}
* @dev: device pointer
*
* Allocate a special sound device by minor number from the sound
- * subsystem. The allocated number is returned on succes. On failure
+ * subsystem. The allocated number is returned on success. On failure
* a negative error code is returned.
*/
void unregister_sound_midi(int unit)
{
- return sound_remove_unit(&chains[2], unit);
+ sound_remove_unit(&chains[2], unit);
}
EXPORT_SYMBOL(unregister_sound_midi);
void unregister_sound_dsp(int unit)
{
- return sound_remove_unit(&chains[3], unit);
+ sound_remove_unit(&chains[3], unit);
}
EXPORT_SYMBOL(unregister_sound_dsp);
-/*
- * Now our file operations
- */
-
-static int soundcore_open(struct inode *, struct file *);
-
-static const struct file_operations soundcore_fops=
-{
- /* We must have an owner or the module locking fails */
- .owner = THIS_MODULE,
- .open = soundcore_open,
-};
-
static struct sound_unit *__look_for_unit(int chain, int unit)
{
struct sound_unit *s;
return NULL;
}
-int soundcore_open(struct inode *inode, struct file *file)
+static int soundcore_open(struct inode *inode, struct file *file)
{
int chain;
int unit = iminor(inode);
struct sound_unit *s;
const struct file_operations *new_fops = NULL;
+ lock_kernel ();
+
chain=unit&0x0F;
if(chain==4 || chain==5) /* dsp/audio/dsp16 */
{
s = __look_for_unit(chain, unit);
if (s)
new_fops = fops_get(s->unit_fops);
- if (!new_fops) {
+ if (preclaim_oss && !new_fops) {
spin_unlock(&sound_loader_lock);
+
/*
* Please, don't change this order or code.
* For ALSA slot means soundcard and OSS emulation code
*/
request_module("sound-slot-%i", unit>>4);
request_module("sound-service-%i-%i", unit>>4, chain);
+
+ /*
+ * sound-slot/service-* module aliases are scheduled
+ * for removal in favor of the standard char-major-*
+ * module aliases. For the time being, generate both
+ * the legacy and standard module aliases to ease
+ * transition.
+ */
+ if (request_module("char-major-%d-%d", SOUND_MAJOR, unit) > 0)
+ request_module("char-major-%d", SOUND_MAJOR);
+
spin_lock(&sound_loader_lock);
s = __look_for_unit(chain, unit);
if (s)
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
+ unlock_kernel();
return err;
}
spin_unlock(&sound_loader_lock);
+ unlock_kernel();
return -ENODEV;
}
-MODULE_DESCRIPTION("Core sound module");
-MODULE_AUTHOR("Alan Cox");
-MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(SOUND_MAJOR);
-static void __exit cleanup_soundcore(void)
+static void cleanup_oss_soundcore(void)
{
/* We have nothing to really do here - we know the lists must be
empty */
unregister_chrdev(SOUND_MAJOR, "sound");
- class_destroy(sound_class);
}
-static int __init init_soundcore(void)
+static int __init init_oss_soundcore(void)
{
- if (register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops)==-1) {
+ if (preclaim_oss &&
+ register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops) == -1) {
printk(KERN_ERR "soundcore: sound device already in use.\n");
return -EBUSY;
}
- sound_class = class_create(THIS_MODULE, "sound");
- if (IS_ERR(sound_class))
- return PTR_ERR(sound_class);
return 0;
}
-module_init(init_soundcore);
-module_exit(cleanup_soundcore);
+#endif /* CONFIG_SOUND_OSS_CORE */