Merge branch 'topic/soundcore-preclaim' into for-linus
* topic/soundcore-preclaim: sound: make OSS device number claiming optional and schedule its removal sound: request char-major-* module aliases for missing OSS devices chrdev: implement __[un]register_chrdev()
This commit is contained in:
commit
3827119e20
|
@ -468,3 +468,27 @@ Why: cpu_policy_rwsem has a new cleaner definition making it local to
|
||||||
cpufreq core and contained inside cpufreq.c. Other dependent
|
cpufreq core and contained inside cpufreq.c. Other dependent
|
||||||
drivers should not use it in order to safely avoid lockdep issues.
|
drivers should not use it in order to safely avoid lockdep issues.
|
||||||
Who: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
|
Who: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
|
||||||
|
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
What: sound-slot/service-* module aliases and related clutters in
|
||||||
|
sound/sound_core.c
|
||||||
|
When: August 2010
|
||||||
|
Why: OSS sound_core grabs all legacy minors (0-255) of SOUND_MAJOR
|
||||||
|
(14) and requests modules using custom sound-slot/service-*
|
||||||
|
module aliases. The only benefit of doing this is allowing
|
||||||
|
use of custom module aliases which might as well be considered
|
||||||
|
a bug at this point. This preemptive claiming prevents
|
||||||
|
alternative OSS implementations.
|
||||||
|
|
||||||
|
Till the feature is removed, the kernel will be requesting
|
||||||
|
both sound-slot/service-* and the standard char-major-* module
|
||||||
|
aliases and allow turning off the pre-claiming selectively via
|
||||||
|
CONFIG_SOUND_OSS_CORE_PRECLAIM and soundcore.preclaim_oss
|
||||||
|
kernel parameter.
|
||||||
|
|
||||||
|
After the transition phase is complete, both the custom module
|
||||||
|
aliases and switches to disable it will go away. This removal
|
||||||
|
will also allow making ALSA OSS emulation independent of
|
||||||
|
sound_core. The dependency will be broken then too.
|
||||||
|
Who: Tejun Heo <tj@kernel.org>
|
||||||
|
|
|
@ -237,8 +237,10 @@ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* register_chrdev() - Register a major number for character devices.
|
* __register_chrdev() - create and register a cdev occupying a range of minors
|
||||||
* @major: major device number or 0 for dynamic allocation
|
* @major: major device number or 0 for dynamic allocation
|
||||||
|
* @baseminor: first of the requested range of minor numbers
|
||||||
|
* @count: the number of minor numbers required
|
||||||
* @name: name of this range of devices
|
* @name: name of this range of devices
|
||||||
* @fops: file operations associated with this devices
|
* @fops: file operations associated with this devices
|
||||||
*
|
*
|
||||||
|
@ -254,11 +256,9 @@ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
|
||||||
* /dev. It only helps to keep track of the different owners of devices. If
|
* /dev. It only helps to keep track of the different owners of devices. If
|
||||||
* your module name has only one type of devices it's ok to use e.g. the name
|
* your module name has only one type of devices it's ok to use e.g. the name
|
||||||
* of the module here.
|
* of the module here.
|
||||||
*
|
|
||||||
* This function registers a range of 256 minor numbers. The first minor number
|
|
||||||
* is 0.
|
|
||||||
*/
|
*/
|
||||||
int register_chrdev(unsigned int major, const char *name,
|
int __register_chrdev(unsigned int major, unsigned int baseminor,
|
||||||
|
unsigned int count, const char *name,
|
||||||
const struct file_operations *fops)
|
const struct file_operations *fops)
|
||||||
{
|
{
|
||||||
struct char_device_struct *cd;
|
struct char_device_struct *cd;
|
||||||
|
@ -266,7 +266,7 @@ int register_chrdev(unsigned int major, const char *name,
|
||||||
char *s;
|
char *s;
|
||||||
int err = -ENOMEM;
|
int err = -ENOMEM;
|
||||||
|
|
||||||
cd = __register_chrdev_region(major, 0, 256, name);
|
cd = __register_chrdev_region(major, baseminor, count, name);
|
||||||
if (IS_ERR(cd))
|
if (IS_ERR(cd))
|
||||||
return PTR_ERR(cd);
|
return PTR_ERR(cd);
|
||||||
|
|
||||||
|
@ -280,7 +280,7 @@ int register_chrdev(unsigned int major, const char *name,
|
||||||
for (s = strchr(kobject_name(&cdev->kobj),'/'); s; s = strchr(s, '/'))
|
for (s = strchr(kobject_name(&cdev->kobj),'/'); s; s = strchr(s, '/'))
|
||||||
*s = '!';
|
*s = '!';
|
||||||
|
|
||||||
err = cdev_add(cdev, MKDEV(cd->major, 0), 256);
|
err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -290,7 +290,7 @@ int register_chrdev(unsigned int major, const char *name,
|
||||||
out:
|
out:
|
||||||
kobject_put(&cdev->kobj);
|
kobject_put(&cdev->kobj);
|
||||||
out2:
|
out2:
|
||||||
kfree(__unregister_chrdev_region(cd->major, 0, 256));
|
kfree(__unregister_chrdev_region(cd->major, baseminor, count));
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,10 +316,23 @@ void unregister_chrdev_region(dev_t from, unsigned count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void unregister_chrdev(unsigned int major, const char *name)
|
/**
|
||||||
|
* __unregister_chrdev - unregister and destroy a cdev
|
||||||
|
* @major: major device number
|
||||||
|
* @baseminor: first of the range of minor numbers
|
||||||
|
* @count: the number of minor numbers this cdev is occupying
|
||||||
|
* @name: name of this range of devices
|
||||||
|
*
|
||||||
|
* Unregister and destroy the cdev occupying the region described by
|
||||||
|
* @major, @baseminor and @count. This function undoes what
|
||||||
|
* __register_chrdev() did.
|
||||||
|
*/
|
||||||
|
void __unregister_chrdev(unsigned int major, unsigned int baseminor,
|
||||||
|
unsigned int count, const char *name)
|
||||||
{
|
{
|
||||||
struct char_device_struct *cd;
|
struct char_device_struct *cd;
|
||||||
cd = __unregister_chrdev_region(major, 0, 256);
|
|
||||||
|
cd = __unregister_chrdev_region(major, baseminor, count);
|
||||||
if (cd && cd->cdev)
|
if (cd && cd->cdev)
|
||||||
cdev_del(cd->cdev);
|
cdev_del(cd->cdev);
|
||||||
kfree(cd);
|
kfree(cd);
|
||||||
|
@ -568,6 +581,6 @@ EXPORT_SYMBOL(cdev_alloc);
|
||||||
EXPORT_SYMBOL(cdev_del);
|
EXPORT_SYMBOL(cdev_del);
|
||||||
EXPORT_SYMBOL(cdev_add);
|
EXPORT_SYMBOL(cdev_add);
|
||||||
EXPORT_SYMBOL(cdev_index);
|
EXPORT_SYMBOL(cdev_index);
|
||||||
EXPORT_SYMBOL(register_chrdev);
|
EXPORT_SYMBOL(__register_chrdev);
|
||||||
EXPORT_SYMBOL(unregister_chrdev);
|
EXPORT_SYMBOL(__unregister_chrdev);
|
||||||
EXPORT_SYMBOL(directly_mappable_cdev_bdi);
|
EXPORT_SYMBOL(directly_mappable_cdev_bdi);
|
||||||
|
|
|
@ -1998,12 +1998,25 @@ extern void bd_release_from_disk(struct block_device *, struct gendisk *);
|
||||||
#define CHRDEV_MAJOR_HASH_SIZE 255
|
#define CHRDEV_MAJOR_HASH_SIZE 255
|
||||||
extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
|
extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
|
||||||
extern int register_chrdev_region(dev_t, unsigned, const char *);
|
extern int register_chrdev_region(dev_t, unsigned, const char *);
|
||||||
extern int register_chrdev(unsigned int, const char *,
|
extern int __register_chrdev(unsigned int major, unsigned int baseminor,
|
||||||
const struct file_operations *);
|
unsigned int count, const char *name,
|
||||||
extern void unregister_chrdev(unsigned int, const char *);
|
const struct file_operations *fops);
|
||||||
|
extern void __unregister_chrdev(unsigned int major, unsigned int baseminor,
|
||||||
|
unsigned int count, const char *name);
|
||||||
extern void unregister_chrdev_region(dev_t, unsigned);
|
extern void unregister_chrdev_region(dev_t, unsigned);
|
||||||
extern void chrdev_show(struct seq_file *,off_t);
|
extern void chrdev_show(struct seq_file *,off_t);
|
||||||
|
|
||||||
|
static inline int register_chrdev(unsigned int major, const char *name,
|
||||||
|
const struct file_operations *fops)
|
||||||
|
{
|
||||||
|
return __register_chrdev(major, 0, 256, name, fops);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void unregister_chrdev(unsigned int major, const char *name)
|
||||||
|
{
|
||||||
|
__unregister_chrdev(major, 0, 256, name);
|
||||||
|
}
|
||||||
|
|
||||||
/* fs/block_dev.c */
|
/* fs/block_dev.c */
|
||||||
#define BDEVNAME_SIZE 32 /* Largest string for a blockdev identifier */
|
#define BDEVNAME_SIZE 32 /* Largest string for a blockdev identifier */
|
||||||
#define BDEVT_SIZE 10 /* Largest string for MAJ:MIN for blkdev */
|
#define BDEVT_SIZE 10 /* Largest string for MAJ:MIN for blkdev */
|
||||||
|
|
|
@ -32,6 +32,34 @@ config SOUND_OSS_CORE
|
||||||
bool
|
bool
|
||||||
default n
|
default n
|
||||||
|
|
||||||
|
config SOUND_OSS_CORE_PRECLAIM
|
||||||
|
bool "Preclaim OSS device numbers"
|
||||||
|
depends on SOUND_OSS_CORE
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
With this option enabled, the kernel will claim all OSS device
|
||||||
|
numbers if any OSS support (native or emulation) is enabled
|
||||||
|
whether the respective module is loaded or not and try to load the
|
||||||
|
appropriate module using sound-slot/service-* and char-major-*
|
||||||
|
module aliases when one of the device numbers is opened. With
|
||||||
|
this option disabled, kernel will only claim actually in-use
|
||||||
|
device numbers and opening a missing device will generate only the
|
||||||
|
standard char-major-* aliases.
|
||||||
|
|
||||||
|
The only visible difference is use of additional module aliases
|
||||||
|
and whether OSS sound devices appear multiple times in
|
||||||
|
/proc/devices. sound-slot/service-* module aliases are scheduled
|
||||||
|
to be removed (ie. PRECLAIM won't be available) and this option is
|
||||||
|
to make the transition easier. This option can be overridden
|
||||||
|
during boot using the kernel parameter soundcore.preclaim_oss.
|
||||||
|
|
||||||
|
Disabling this allows alternative OSS implementations.
|
||||||
|
|
||||||
|
Please read Documentation/feature-removal-schedule.txt for
|
||||||
|
details.
|
||||||
|
|
||||||
|
If unusre, say Y.
|
||||||
|
|
||||||
source "sound/oss/dmasound/Kconfig"
|
source "sound/oss/dmasound/Kconfig"
|
||||||
|
|
||||||
if !M68K
|
if !M68K
|
||||||
|
|
|
@ -127,6 +127,46 @@ extern int msnd_classic_init(void);
|
||||||
extern int msnd_pinnacle_init(void);
|
extern int msnd_pinnacle_init(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
* Low level list operator. Scan the ordered list, find a hole and
|
||||||
* join into it. Called with the lock asserted
|
* join into it. Called with the lock asserted
|
||||||
|
@ -221,6 +261,7 @@ static int sound_insert_unit(struct sound_unit **list, const struct file_operati
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
spin_lock(&sound_loader_lock);
|
spin_lock(&sound_loader_lock);
|
||||||
|
retry:
|
||||||
r = __sound_insert_unit(s, list, fops, index, low, top);
|
r = __sound_insert_unit(s, list, fops, index, low, top);
|
||||||
spin_unlock(&sound_loader_lock);
|
spin_unlock(&sound_loader_lock);
|
||||||
|
|
||||||
|
@ -231,11 +272,31 @@ static int sound_insert_unit(struct sound_unit **list, const struct file_operati
|
||||||
else
|
else
|
||||||
sprintf(s->name, "sound/%s%d", name, r / SOUND_STEP);
|
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),
|
device_create(sound_class, dev, MKDEV(SOUND_MAJOR, s->unit_minor),
|
||||||
NULL, s->name+6);
|
NULL, s->name+6);
|
||||||
return r;
|
return s->unit_minor;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
kfree(s);
|
kfree(s);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -254,6 +315,9 @@ static void sound_remove_unit(struct sound_unit **list, int unit)
|
||||||
p = __sound_remove_unit(list, unit);
|
p = __sound_remove_unit(list, unit);
|
||||||
spin_unlock(&sound_loader_lock);
|
spin_unlock(&sound_loader_lock);
|
||||||
if (p) {
|
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));
|
device_destroy(sound_class, MKDEV(SOUND_MAJOR, p->unit_minor));
|
||||||
kfree(p);
|
kfree(p);
|
||||||
}
|
}
|
||||||
|
@ -491,19 +555,6 @@ void unregister_sound_dsp(int unit)
|
||||||
|
|
||||||
EXPORT_SYMBOL(unregister_sound_dsp);
|
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)
|
static struct sound_unit *__look_for_unit(int chain, int unit)
|
||||||
{
|
{
|
||||||
struct sound_unit *s;
|
struct sound_unit *s;
|
||||||
|
@ -539,8 +590,9 @@ static int soundcore_open(struct inode *inode, struct file *file)
|
||||||
s = __look_for_unit(chain, unit);
|
s = __look_for_unit(chain, unit);
|
||||||
if (s)
|
if (s)
|
||||||
new_fops = fops_get(s->unit_fops);
|
new_fops = fops_get(s->unit_fops);
|
||||||
if (!new_fops) {
|
if (preclaim_oss && !new_fops) {
|
||||||
spin_unlock(&sound_loader_lock);
|
spin_unlock(&sound_loader_lock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Please, don't change this order or code.
|
* Please, don't change this order or code.
|
||||||
* For ALSA slot means soundcard and OSS emulation code
|
* For ALSA slot means soundcard and OSS emulation code
|
||||||
|
@ -550,6 +602,17 @@ static int soundcore_open(struct inode *inode, struct file *file)
|
||||||
*/
|
*/
|
||||||
request_module("sound-slot-%i", unit>>4);
|
request_module("sound-slot-%i", unit>>4);
|
||||||
request_module("sound-service-%i-%i", unit>>4, chain);
|
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);
|
spin_lock(&sound_loader_lock);
|
||||||
s = __look_for_unit(chain, unit);
|
s = __look_for_unit(chain, unit);
|
||||||
if (s)
|
if (s)
|
||||||
|
@ -593,7 +656,8 @@ static void cleanup_oss_soundcore(void)
|
||||||
|
|
||||||
static int __init init_oss_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");
|
printk(KERN_ERR "soundcore: sound device already in use.\n");
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue