diff options
author | Takashi Iwai <tiwai@suse.de> | 2009-09-10 09:33:04 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-09-10 09:33:04 -0400 |
commit | 3827119e207823ff0f3e85271bef7a0dc953ee38 (patch) | |
tree | 66d2a24524628b3123b39e1364281886d2f9074f | |
parent | 9d416811f8cab11bf595b2880c557c33e3ae1ae9 (diff) | |
parent | 93fe4483e6fd3e71d17cd919de14b3b1f9eb3795 (diff) |
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()
-rw-r--r-- | Documentation/feature-removal-schedule.txt | 24 | ||||
-rw-r--r-- | fs/char_dev.c | 39 | ||||
-rw-r--r-- | include/linux/fs.h | 19 | ||||
-rw-r--r-- | sound/Kconfig | 28 | ||||
-rw-r--r-- | sound/sound_core.c | 100 |
5 files changed, 176 insertions, 34 deletions
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 09e031c55887..f0690bbbd73c 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt | |||
@@ -468,3 +468,27 @@ Why: cpu_policy_rwsem has a new cleaner definition making it local to | |||
468 | cpufreq core and contained inside cpufreq.c. Other dependent | 468 | cpufreq core and contained inside cpufreq.c. Other dependent |
469 | drivers should not use it in order to safely avoid lockdep issues. | 469 | drivers should not use it in order to safely avoid lockdep issues. |
470 | Who: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> | 470 | Who: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> |
471 | |||
472 | ---------------------------- | ||
473 | |||
474 | What: sound-slot/service-* module aliases and related clutters in | ||
475 | sound/sound_core.c | ||
476 | When: August 2010 | ||
477 | Why: OSS sound_core grabs all legacy minors (0-255) of SOUND_MAJOR | ||
478 | (14) and requests modules using custom sound-slot/service-* | ||
479 | module aliases. The only benefit of doing this is allowing | ||
480 | use of custom module aliases which might as well be considered | ||
481 | a bug at this point. This preemptive claiming prevents | ||
482 | alternative OSS implementations. | ||
483 | |||
484 | Till the feature is removed, the kernel will be requesting | ||
485 | both sound-slot/service-* and the standard char-major-* module | ||
486 | aliases and allow turning off the pre-claiming selectively via | ||
487 | CONFIG_SOUND_OSS_CORE_PRECLAIM and soundcore.preclaim_oss | ||
488 | kernel parameter. | ||
489 | |||
490 | After the transition phase is complete, both the custom module | ||
491 | aliases and switches to disable it will go away. This removal | ||
492 | will also allow making ALSA OSS emulation independent of | ||
493 | sound_core. The dependency will be broken then too. | ||
494 | Who: Tejun Heo <tj@kernel.org> | ||
diff --git a/fs/char_dev.c b/fs/char_dev.c index a173551e19d7..2f18c1e4e301 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c | |||
@@ -237,8 +237,10 @@ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, | |||
237 | } | 237 | } |
238 | 238 | ||
239 | /** | 239 | /** |
240 | * register_chrdev() - Register a major number for character devices. | 240 | * __register_chrdev() - create and register a cdev occupying a range of minors |
241 | * @major: major device number or 0 for dynamic allocation | 241 | * @major: major device number or 0 for dynamic allocation |
242 | * @baseminor: first of the requested range of minor numbers | ||
243 | * @count: the number of minor numbers required | ||
242 | * @name: name of this range of devices | 244 | * @name: name of this range of devices |
243 | * @fops: file operations associated with this devices | 245 | * @fops: file operations associated with this devices |
244 | * | 246 | * |
@@ -254,19 +256,17 @@ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, | |||
254 | * /dev. It only helps to keep track of the different owners of devices. If | 256 | * /dev. It only helps to keep track of the different owners of devices. If |
255 | * your module name has only one type of devices it's ok to use e.g. the name | 257 | * your module name has only one type of devices it's ok to use e.g. the name |
256 | * of the module here. | 258 | * of the module here. |
257 | * | ||
258 | * This function registers a range of 256 minor numbers. The first minor number | ||
259 | * is 0. | ||
260 | */ | 259 | */ |
261 | int register_chrdev(unsigned int major, const char *name, | 260 | int __register_chrdev(unsigned int major, unsigned int baseminor, |
262 | const struct file_operations *fops) | 261 | unsigned int count, const char *name, |
262 | const struct file_operations *fops) | ||
263 | { | 263 | { |
264 | struct char_device_struct *cd; | 264 | struct char_device_struct *cd; |
265 | struct cdev *cdev; | 265 | struct cdev *cdev; |
266 | char *s; | 266 | char *s; |
267 | int err = -ENOMEM; | 267 | int err = -ENOMEM; |
268 | 268 | ||
269 | cd = __register_chrdev_region(major, 0, 256, name); | 269 | cd = __register_chrdev_region(major, baseminor, count, name); |
270 | if (IS_ERR(cd)) | 270 | if (IS_ERR(cd)) |
271 | return PTR_ERR(cd); | 271 | return PTR_ERR(cd); |
272 | 272 | ||
@@ -280,7 +280,7 @@ int register_chrdev(unsigned int major, const char *name, | |||
280 | for (s = strchr(kobject_name(&cdev->kobj),'/'); s; s = strchr(s, '/')) | 280 | for (s = strchr(kobject_name(&cdev->kobj),'/'); s; s = strchr(s, '/')) |
281 | *s = '!'; | 281 | *s = '!'; |
282 | 282 | ||
283 | err = cdev_add(cdev, MKDEV(cd->major, 0), 256); | 283 | err = cdev_add(cdev, MKDEV(cd->major, baseminor), count); |
284 | if (err) | 284 | if (err) |
285 | goto out; | 285 | goto out; |
286 | 286 | ||
@@ -290,7 +290,7 @@ int register_chrdev(unsigned int major, const char *name, | |||
290 | out: | 290 | out: |
291 | kobject_put(&cdev->kobj); | 291 | kobject_put(&cdev->kobj); |
292 | out2: | 292 | out2: |
293 | kfree(__unregister_chrdev_region(cd->major, 0, 256)); | 293 | kfree(__unregister_chrdev_region(cd->major, baseminor, count)); |
294 | return err; | 294 | return err; |
295 | } | 295 | } |
296 | 296 | ||
@@ -316,10 +316,23 @@ void unregister_chrdev_region(dev_t from, unsigned count) | |||
316 | } | 316 | } |
317 | } | 317 | } |
318 | 318 | ||
319 | void unregister_chrdev(unsigned int major, const char *name) | 319 | /** |
320 | * __unregister_chrdev - unregister and destroy a cdev | ||
321 | * @major: major device number | ||
322 | * @baseminor: first of the range of minor numbers | ||
323 | * @count: the number of minor numbers this cdev is occupying | ||
324 | * @name: name of this range of devices | ||
325 | * | ||
326 | * Unregister and destroy the cdev occupying the region described by | ||
327 | * @major, @baseminor and @count. This function undoes what | ||
328 | * __register_chrdev() did. | ||
329 | */ | ||
330 | void __unregister_chrdev(unsigned int major, unsigned int baseminor, | ||
331 | unsigned int count, const char *name) | ||
320 | { | 332 | { |
321 | struct char_device_struct *cd; | 333 | struct char_device_struct *cd; |
322 | cd = __unregister_chrdev_region(major, 0, 256); | 334 | |
335 | cd = __unregister_chrdev_region(major, baseminor, count); | ||
323 | if (cd && cd->cdev) | 336 | if (cd && cd->cdev) |
324 | cdev_del(cd->cdev); | 337 | cdev_del(cd->cdev); |
325 | kfree(cd); | 338 | kfree(cd); |
@@ -568,6 +581,6 @@ EXPORT_SYMBOL(cdev_alloc); | |||
568 | EXPORT_SYMBOL(cdev_del); | 581 | EXPORT_SYMBOL(cdev_del); |
569 | EXPORT_SYMBOL(cdev_add); | 582 | EXPORT_SYMBOL(cdev_add); |
570 | EXPORT_SYMBOL(cdev_index); | 583 | EXPORT_SYMBOL(cdev_index); |
571 | EXPORT_SYMBOL(register_chrdev); | 584 | EXPORT_SYMBOL(__register_chrdev); |
572 | EXPORT_SYMBOL(unregister_chrdev); | 585 | EXPORT_SYMBOL(__unregister_chrdev); |
573 | EXPORT_SYMBOL(directly_mappable_cdev_bdi); | 586 | EXPORT_SYMBOL(directly_mappable_cdev_bdi); |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 73e9b643e455..3972ffb597c5 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -1998,12 +1998,25 @@ extern void bd_release_from_disk(struct block_device *, struct gendisk *); | |||
1998 | #define CHRDEV_MAJOR_HASH_SIZE 255 | 1998 | #define CHRDEV_MAJOR_HASH_SIZE 255 |
1999 | extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *); | 1999 | extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *); |
2000 | extern int register_chrdev_region(dev_t, unsigned, const char *); | 2000 | extern int register_chrdev_region(dev_t, unsigned, const char *); |
2001 | extern int register_chrdev(unsigned int, const char *, | 2001 | extern int __register_chrdev(unsigned int major, unsigned int baseminor, |
2002 | const struct file_operations *); | 2002 | unsigned int count, const char *name, |
2003 | extern void unregister_chrdev(unsigned int, const char *); | 2003 | const struct file_operations *fops); |
2004 | extern void __unregister_chrdev(unsigned int major, unsigned int baseminor, | ||
2005 | unsigned int count, const char *name); | ||
2004 | extern void unregister_chrdev_region(dev_t, unsigned); | 2006 | extern void unregister_chrdev_region(dev_t, unsigned); |
2005 | extern void chrdev_show(struct seq_file *,off_t); | 2007 | extern void chrdev_show(struct seq_file *,off_t); |
2006 | 2008 | ||
2009 | static inline int register_chrdev(unsigned int major, const char *name, | ||
2010 | const struct file_operations *fops) | ||
2011 | { | ||
2012 | return __register_chrdev(major, 0, 256, name, fops); | ||
2013 | } | ||
2014 | |||
2015 | static inline void unregister_chrdev(unsigned int major, const char *name) | ||
2016 | { | ||
2017 | __unregister_chrdev(major, 0, 256, name); | ||
2018 | } | ||
2019 | |||
2007 | /* fs/block_dev.c */ | 2020 | /* fs/block_dev.c */ |
2008 | #define BDEVNAME_SIZE 32 /* Largest string for a blockdev identifier */ | 2021 | #define BDEVNAME_SIZE 32 /* Largest string for a blockdev identifier */ |
2009 | #define BDEVT_SIZE 10 /* Largest string for MAJ:MIN for blkdev */ | 2022 | #define BDEVT_SIZE 10 /* Largest string for MAJ:MIN for blkdev */ |
diff --git a/sound/Kconfig b/sound/Kconfig index 1eceb85287c5..439e15c8faa3 100644 --- a/sound/Kconfig +++ b/sound/Kconfig | |||
@@ -32,6 +32,34 @@ config SOUND_OSS_CORE | |||
32 | bool | 32 | bool |
33 | default n | 33 | default n |
34 | 34 | ||
35 | config SOUND_OSS_CORE_PRECLAIM | ||
36 | bool "Preclaim OSS device numbers" | ||
37 | depends on SOUND_OSS_CORE | ||
38 | default y | ||
39 | help | ||
40 | With this option enabled, the kernel will claim all OSS device | ||
41 | numbers if any OSS support (native or emulation) is enabled | ||
42 | whether the respective module is loaded or not and try to load the | ||
43 | appropriate module using sound-slot/service-* and char-major-* | ||
44 | module aliases when one of the device numbers is opened. With | ||
45 | this option disabled, kernel will only claim actually in-use | ||
46 | device numbers and opening a missing device will generate only the | ||
47 | standard char-major-* aliases. | ||
48 | |||
49 | The only visible difference is use of additional module aliases | ||
50 | and whether OSS sound devices appear multiple times in | ||
51 | /proc/devices. sound-slot/service-* module aliases are scheduled | ||
52 | to be removed (ie. PRECLAIM won't be available) and this option is | ||
53 | to make the transition easier. This option can be overridden | ||
54 | during boot using the kernel parameter soundcore.preclaim_oss. | ||
55 | |||
56 | Disabling this allows alternative OSS implementations. | ||
57 | |||
58 | Please read Documentation/feature-removal-schedule.txt for | ||
59 | details. | ||
60 | |||
61 | If unusre, say Y. | ||
62 | |||
35 | source "sound/oss/dmasound/Kconfig" | 63 | source "sound/oss/dmasound/Kconfig" |
36 | 64 | ||
37 | if !M68K | 65 | if !M68K |
diff --git a/sound/sound_core.c b/sound/sound_core.c index a41f8b127f49..bb4b88e606bb 100644 --- a/sound/sound_core.c +++ b/sound/sound_core.c | |||
@@ -128,6 +128,46 @@ extern int msnd_pinnacle_init(void); | |||
128 | #endif | 128 | #endif |
129 | 129 | ||
130 | /* | 130 | /* |
131 | * By default, OSS sound_core claims full legacy minor range (0-255) | ||
132 | * of SOUND_MAJOR to trap open attempts to any sound minor and | ||
133 | * requests modules using custom sound-slot/service-* module aliases. | ||
134 | * The only benefit of doing this is allowing use of custom module | ||
135 | * aliases instead of the standard char-major-* ones. This behavior | ||
136 | * prevents alternative OSS implementation and is scheduled to be | ||
137 | * removed. | ||
138 | * | ||
139 | * CONFIG_SOUND_OSS_CORE_PRECLAIM and soundcore.preclaim_oss kernel | ||
140 | * parameter are added to allow distros and developers to try and | ||
141 | * switch to alternative implementations without needing to rebuild | ||
142 | * the kernel in the meantime. If preclaim_oss is non-zero, the | ||
143 | * kernel will behave the same as before. All SOUND_MAJOR minors are | ||
144 | * preclaimed and the custom module aliases along with standard chrdev | ||
145 | * ones are emitted if a missing device is opened. If preclaim_oss is | ||
146 | * zero, sound_core only grabs what's actually in use and for missing | ||
147 | * devices only the standard chrdev aliases are requested. | ||
148 | * | ||
149 | * All these clutters are scheduled to be removed along with | ||
150 | * sound-slot/service-* module aliases. Please take a look at | ||
151 | * feature-removal-schedule.txt for details. | ||
152 | */ | ||
153 | #ifdef CONFIG_SOUND_OSS_CORE_PRECLAIM | ||
154 | static int preclaim_oss = 1; | ||
155 | #else | ||
156 | static int preclaim_oss = 0; | ||
157 | #endif | ||
158 | |||
159 | module_param(preclaim_oss, int, 0444); | ||
160 | |||
161 | static int soundcore_open(struct inode *, struct file *); | ||
162 | |||
163 | static const struct file_operations soundcore_fops = | ||
164 | { | ||
165 | /* We must have an owner or the module locking fails */ | ||
166 | .owner = THIS_MODULE, | ||
167 | .open = soundcore_open, | ||
168 | }; | ||
169 | |||
170 | /* | ||
131 | * Low level list operator. Scan the ordered list, find a hole and | 171 | * Low level list operator. Scan the ordered list, find a hole and |
132 | * join into it. Called with the lock asserted | 172 | * join into it. Called with the lock asserted |
133 | */ | 173 | */ |
@@ -219,8 +259,9 @@ static int sound_insert_unit(struct sound_unit **list, const struct file_operati | |||
219 | 259 | ||
220 | if (!s) | 260 | if (!s) |
221 | return -ENOMEM; | 261 | return -ENOMEM; |
222 | 262 | ||
223 | spin_lock(&sound_loader_lock); | 263 | spin_lock(&sound_loader_lock); |
264 | retry: | ||
224 | r = __sound_insert_unit(s, list, fops, index, low, top); | 265 | r = __sound_insert_unit(s, list, fops, index, low, top); |
225 | spin_unlock(&sound_loader_lock); | 266 | spin_unlock(&sound_loader_lock); |
226 | 267 | ||
@@ -231,11 +272,31 @@ static int sound_insert_unit(struct sound_unit **list, const struct file_operati | |||
231 | else | 272 | else |
232 | sprintf(s->name, "sound/%s%d", name, r / SOUND_STEP); | 273 | sprintf(s->name, "sound/%s%d", name, r / SOUND_STEP); |
233 | 274 | ||
275 | if (!preclaim_oss) { | ||
276 | /* | ||
277 | * Something else might have grabbed the minor. If | ||
278 | * first free slot is requested, rescan with @low set | ||
279 | * to the next unit; otherwise, -EBUSY. | ||
280 | */ | ||
281 | r = __register_chrdev(SOUND_MAJOR, s->unit_minor, 1, s->name, | ||
282 | &soundcore_fops); | ||
283 | if (r < 0) { | ||
284 | spin_lock(&sound_loader_lock); | ||
285 | __sound_remove_unit(list, s->unit_minor); | ||
286 | if (index < 0) { | ||
287 | low = s->unit_minor + SOUND_STEP; | ||
288 | goto retry; | ||
289 | } | ||
290 | spin_unlock(&sound_loader_lock); | ||
291 | return -EBUSY; | ||
292 | } | ||
293 | } | ||
294 | |||
234 | device_create(sound_class, dev, MKDEV(SOUND_MAJOR, s->unit_minor), | 295 | device_create(sound_class, dev, MKDEV(SOUND_MAJOR, s->unit_minor), |
235 | NULL, s->name+6); | 296 | NULL, s->name+6); |
236 | return r; | 297 | return s->unit_minor; |
237 | 298 | ||
238 | fail: | 299 | fail: |
239 | kfree(s); | 300 | kfree(s); |
240 | return r; | 301 | return r; |
241 | } | 302 | } |
@@ -254,6 +315,9 @@ static void sound_remove_unit(struct sound_unit **list, int unit) | |||
254 | p = __sound_remove_unit(list, unit); | 315 | p = __sound_remove_unit(list, unit); |
255 | spin_unlock(&sound_loader_lock); | 316 | spin_unlock(&sound_loader_lock); |
256 | if (p) { | 317 | if (p) { |
318 | if (!preclaim_oss) | ||
319 | __unregister_chrdev(SOUND_MAJOR, p->unit_minor, 1, | ||
320 | p->name); | ||
257 | device_destroy(sound_class, MKDEV(SOUND_MAJOR, p->unit_minor)); | 321 | device_destroy(sound_class, MKDEV(SOUND_MAJOR, p->unit_minor)); |
258 | kfree(p); | 322 | kfree(p); |
259 | } | 323 | } |
@@ -491,19 +555,6 @@ void unregister_sound_dsp(int unit) | |||
491 | 555 | ||
492 | EXPORT_SYMBOL(unregister_sound_dsp); | 556 | EXPORT_SYMBOL(unregister_sound_dsp); |
493 | 557 | ||
494 | /* | ||
495 | * Now our file operations | ||
496 | */ | ||
497 | |||
498 | static int soundcore_open(struct inode *, struct file *); | ||
499 | |||
500 | static const struct file_operations soundcore_fops= | ||
501 | { | ||
502 | /* We must have an owner or the module locking fails */ | ||
503 | .owner = THIS_MODULE, | ||
504 | .open = soundcore_open, | ||
505 | }; | ||
506 | |||
507 | static struct sound_unit *__look_for_unit(int chain, int unit) | 558 | static struct sound_unit *__look_for_unit(int chain, int unit) |
508 | { | 559 | { |
509 | struct sound_unit *s; | 560 | struct sound_unit *s; |
@@ -539,8 +590,9 @@ static int soundcore_open(struct inode *inode, struct file *file) | |||
539 | s = __look_for_unit(chain, unit); | 590 | s = __look_for_unit(chain, unit); |
540 | if (s) | 591 | if (s) |
541 | new_fops = fops_get(s->unit_fops); | 592 | new_fops = fops_get(s->unit_fops); |
542 | if (!new_fops) { | 593 | if (preclaim_oss && !new_fops) { |
543 | spin_unlock(&sound_loader_lock); | 594 | spin_unlock(&sound_loader_lock); |
595 | |||
544 | /* | 596 | /* |
545 | * Please, don't change this order or code. | 597 | * Please, don't change this order or code. |
546 | * For ALSA slot means soundcard and OSS emulation code | 598 | * For ALSA slot means soundcard and OSS emulation code |
@@ -550,6 +602,17 @@ static int soundcore_open(struct inode *inode, struct file *file) | |||
550 | */ | 602 | */ |
551 | request_module("sound-slot-%i", unit>>4); | 603 | request_module("sound-slot-%i", unit>>4); |
552 | request_module("sound-service-%i-%i", unit>>4, chain); | 604 | request_module("sound-service-%i-%i", unit>>4, chain); |
605 | |||
606 | /* | ||
607 | * sound-slot/service-* module aliases are scheduled | ||
608 | * for removal in favor of the standard char-major-* | ||
609 | * module aliases. For the time being, generate both | ||
610 | * the legacy and standard module aliases to ease | ||
611 | * transition. | ||
612 | */ | ||
613 | if (request_module("char-major-%d-%d", SOUND_MAJOR, unit) > 0) | ||
614 | request_module("char-major-%d", SOUND_MAJOR); | ||
615 | |||
553 | spin_lock(&sound_loader_lock); | 616 | spin_lock(&sound_loader_lock); |
554 | s = __look_for_unit(chain, unit); | 617 | s = __look_for_unit(chain, unit); |
555 | if (s) | 618 | if (s) |
@@ -593,7 +656,8 @@ static void cleanup_oss_soundcore(void) | |||
593 | 656 | ||
594 | static int __init init_oss_soundcore(void) | 657 | static int __init init_oss_soundcore(void) |
595 | { | 658 | { |
596 | if (register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops)==-1) { | 659 | if (preclaim_oss && |
660 | register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops) == -1) { | ||
597 | printk(KERN_ERR "soundcore: sound device already in use.\n"); | 661 | printk(KERN_ERR "soundcore: sound device already in use.\n"); |
598 | return -EBUSY; | 662 | return -EBUSY; |
599 | } | 663 | } |