| 1 | |
| 2 | |
| 3 | * Sound core. This file is composed of two parts. sound_class
|
| 4 | * which is common to both OSS and ALSA and OSS sound core which
|
| 5 | * is used OSS or emulation of it.
|
| 6 |
|
| 7 |
|
| 8 | |
| 9 | * First, the common part.
|
| 10 |
|
| 11 | #include <linux/module.h>
|
| 12 | #include <linux/device.h>
|
| 13 | #include <linux/err.h>
|
| 14 | #include <linux/kdev_t.h>
|
| 15 | #include <linux/major.h>
|
| 16 | #include <sound/core.h>
|
| 17 |
|
| 18 | #ifdef CONFIG_SOUND_OSS_CORE
|
| 19 | static int __init init_oss_soundcore(void);
|
| 20 | static void cleanup_oss_soundcore(void);
|
| 21 | #else
|
| 22 | static inline int init_oss_soundcore(void) { return 0; }
|
| 23 | static inline void cleanup_oss_soundcore(void) { }
|
| 24 | #endif
|
| 25 |
|
| 26 | MODULE_DESCRIPTION("Core sound module");
|
| 27 | MODULE_AUTHOR("Alan Cox");
|
| 28 | MODULE_LICENSE("GPL");
|
| 29 |
|
| 30 | static char *sound_devnode(const struct device *dev, umode_t *mode)
|
| 31 | {
|
| 32 | if (MAJOR(dev->devt) == SOUND_MAJOR)
|
| 33 | return NULL;
|
| 34 | return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev));
|
| 35 | }
|
| 36 |
|
| 37 | const struct class sound_class = {
|
| 38 | .name = "sound",
|
| 39 | .devnode = sound_devnode,
|
| 40 | };
|
| 41 | EXPORT_SYMBOL(sound_class);
|
| 42 |
|
| 43 | static int __init init_soundcore(void)
|
| 44 | {
|
| 45 | int rc;
|
| 46 |
|
| 47 | rc = init_oss_soundcore();
|
| 48 | if (rc)
|
| 49 | return rc;
|
| 50 |
|
| 51 | rc = class_register(&sound_class);
|
| 52 | if (rc) {
|
| 53 | cleanup_oss_soundcore();
|
| 54 | return rc;
|
| 55 | }
|
| 56 |
|
| 57 | return 0;
|
| 58 | }
|
| 59 |
|
| 60 | static void __exit cleanup_soundcore(void)
|
| 61 | {
|
| 62 | cleanup_oss_soundcore();
|
| 63 | class_unregister(&sound_class);
|
| 64 | }
|
| 65 |
|
| 66 | subsys_initcall(init_soundcore);
|
| 67 | module_exit(cleanup_soundcore);
|
| 68 |
|
| 69 |
|
| 70 | #ifdef CONFIG_SOUND_OSS_CORE
|
| 71 | |
| 72 | * OSS sound core handling. Breaks out sound functions to submodules
|
| 73 | *
|
| 74 | * Author: Alan Cox <[email protected]>
|
| 75 | *
|
| 76 | * Fixes:
|
| 77 | *
|
| 78 | * --------------------
|
| 79 | *
|
| 80 | * Top level handler for the sound subsystem. Various devices can
|
| 81 | * plug into this. The fact they don't all go via OSS doesn't mean
|
| 82 | * they don't have to implement the OSS API. There is a lot of logic
|
| 83 | * to keeping much of the OSS weight out of the code in a compatibility
|
| 84 | * module, but it's up to the driver to rember to load it...
|
| 85 | *
|
| 86 | * The code provides a set of functions for registration of devices
|
| 87 | * by type. This is done rather than providing a single call so that
|
| 88 | * we can hide any future changes in the internals (eg when we go to
|
| 89 | * 32bit dev_t) from the modules and their interface.
|
| 90 | *
|
| 91 | * Secondly we need to allocate the dsp, dsp16 and audio devices as
|
| 92 | * one. Thus we misuse the chains a bit to simplify this.
|
| 93 | *
|
| 94 | * Thirdly to make it more fun and for 2.3.x and above we do all
|
| 95 | * of this using fine grained locking.
|
| 96 | *
|
| 97 | * FIXME: we have to resolve modules and fine grained load/unload
|
| 98 | * locking at some point in 2.3.x.
|
| 99 |
|
| 100 |
|
| 101 | #include <linux/init.h>
|
| 102 | #include <linux/slab.h>
|
| 103 | #include <linux/types.h>
|
| 104 | #include <linux/kernel.h>
|
| 105 | #include <linux/sound.h>
|
| 106 | #include <linux/kmod.h>
|
| 107 |
|
| 108 | #define SOUND_STEP 16
|
| 109 |
|
| 110 | struct sound_unit
|
| 111 | {
|
| 112 | int unit_minor;
|
| 113 | const struct file_operations *unit_fops;
|
| 114 | struct sound_unit *next;
|
| 115 | char name[32];
|
| 116 | };
|
| 117 |
|
| 118 | |
| 119 | * By default, OSS sound_core claims full legacy minor range (0-255)
|
| 120 | * of SOUND_MAJOR to trap open attempts to any sound minor and
|
| 121 | * requests modules using custom sound-slot/service-* module aliases.
|
| 122 | * The only benefit of doing this is allowing use of custom module
|
| 123 | * aliases instead of the standard char-major-* ones. This behavior
|
| 124 | * prevents alternative OSS implementation and is scheduled to be
|
| 125 | * removed.
|
| 126 | *
|
| 127 | * CONFIG_SOUND_OSS_CORE_PRECLAIM and soundcore.preclaim_oss kernel
|
| 128 | * parameter are added to allow distros and developers to try and
|
| 129 | * switch to alternative implementations without needing to rebuild
|
| 130 | * the kernel in the meantime. If preclaim_oss is non-zero, the
|
| 131 | * kernel will behave the same as before. All SOUND_MAJOR minors are
|
| 132 | * preclaimed and the custom module aliases along with standard chrdev
|
| 133 | * ones are emitted if a missing device is opened. If preclaim_oss is
|
| 134 | * zero, sound_core only grabs what's actually in use and for missing
|
| 135 | * devices only the standard chrdev aliases are requested.
|
| 136 | *
|
| 137 | * All these clutters are scheduled to be removed along with
|
| 138 | * sound-slot/service-* module aliases.
|
| 139 |
|
| 140 | static int preclaim_oss = IS_ENABLED(CONFIG_SOUND_OSS_CORE_PRECLAIM);
|
| 141 |
|
| 142 | module_param(preclaim_oss, int, 0444);
|
| 143 |
|
| 144 | static int soundcore_open(struct inode *, struct file *);
|
| 145 |
|
| 146 | static const struct file_operations soundcore_fops =
|
| 147 | {
|
| 148 |
|
| 149 | .owner = THIS_MODULE,
|
| 150 | .open = soundcore_open,
|
| 151 | .llseek = noop_llseek,
|
| 152 | };
|
| 153 |
|
| 154 | |
| 155 | * Low level list operator. Scan the ordered list, find a hole and
|
| 156 | * join into it. Called with the lock asserted
|
| 157 |
|
| 158 |
|
| 159 | static int __sound_insert_unit(struct sound_unit * s, struct sound_unit **list, const struct file_operations *fops, int index, int low, int top)
|
| 160 | {
|
| 161 | int n=low;
|
| 162 |
|
| 163 | if (index < 0) {
|
| 164 |
|
| 165 | while (*list && (*list)->unit_minor<n)
|
| 166 | list=&((*list)->next);
|
| 167 |
|
| 168 | while(n<top)
|
| 169 | {
|
| 170 |
|
| 171 | if(*list==NULL || (*list)->unit_minor>n)
|
| 172 | break;
|
| 173 | list=&((*list)->next);
|
| 174 | n+=SOUND_STEP;
|
| 175 | }
|
| 176 |
|
| 177 | if(n>=top)
|
| 178 | return -ENOENT;
|
| 179 | } else {
|
| 180 | n = low+(index*16);
|
| 181 | while (*list) {
|
| 182 | if ((*list)->unit_minor==n)
|
| 183 | return -EBUSY;
|
| 184 | if ((*list)->unit_minor>n)
|
| 185 | break;
|
| 186 | list=&((*list)->next);
|
| 187 | }
|
| 188 | }
|
| 189 |
|
| 190 | |
| 191 | * Fill it in
|
| 192 |
|
| 193 |
|
| 194 | s->unit_minor=n;
|
| 195 | s->unit_fops=fops;
|
| 196 |
|
| 197 | |
| 198 | * Link it
|
| 199 |
|
| 200 |
|
| 201 | s->next=*list;
|
| 202 | *list=s;
|
| 203 |
|
| 204 |
|
| 205 | return n;
|
| 206 | }
|
| 207 |
|
| 208 | |
| 209 | * Remove a node from the chain. Called with the lock asserted
|
| 210 |
|
| 211 |
|
| 212 | static struct sound_unit *__sound_remove_unit(struct sound_unit **list, int unit)
|
| 213 | {
|
| 214 | while(*list)
|
| 215 | {
|
| 216 | struct sound_unit *p=*list;
|
| 217 | if(p->unit_minor==unit)
|
| 218 | {
|
| 219 | *list=p->next;
|
| 220 | return p;
|
| 221 | }
|
| 222 | list=&(p->next);
|
| 223 | }
|
| 224 | printk(KERN_ERR "Sound device %d went missing!\n", unit);
|
| 225 | return NULL;
|
| 226 | }
|
| 227 |
|
| 228 | |
| 229 | * This lock guards the sound loader list.
|
| 230 |
|
| 231 |
|
| 232 | static DEFINE_SPINLOCK(sound_loader_lock);
|
| 233 |
|
| 234 | |
| 235 | * Allocate the controlling structure and add it to the sound driver
|
| 236 | * list. Acquires locks as needed
|
| 237 |
|
| 238 |
|
| 239 | static int sound_insert_unit(struct sound_unit **list, const struct file_operations *fops, int index, int low, int top, const char *name, umode_t mode, struct device *dev)
|
| 240 | {
|
| 241 | struct sound_unit *s = kmalloc_obj(*s);
|
| 242 | int r;
|
| 243 |
|
| 244 | if (!s)
|
| 245 | return -ENOMEM;
|
| 246 |
|
| 247 | spin_lock(&sound_loader_lock);
|
| 248 | retry:
|
| 249 | r = __sound_insert_unit(s, list, fops, index, low, top);
|
| 250 | spin_unlock(&sound_loader_lock);
|
| 251 |
|
| 252 | if (r < 0)
|
| 253 | goto fail;
|
| 254 | else if (r < SOUND_STEP)
|
| 255 | sprintf(s->name, "sound/%s", name);
|
| 256 | else
|
| 257 | sprintf(s->name, "sound/%s%d", name, r / SOUND_STEP);
|
| 258 |
|
| 259 | if (!preclaim_oss) {
|
| 260 | |
| 261 | * Something else might have grabbed the minor. If
|
| 262 | * first free slot is requested, rescan with @low set
|
| 263 | * to the next unit; otherwise, -EBUSY.
|
| 264 |
|
| 265 | r = __register_chrdev(SOUND_MAJOR, s->unit_minor, 1, s->name,
|
| 266 | &soundcore_fops);
|
| 267 | if (r < 0) {
|
| 268 | spin_lock(&sound_loader_lock);
|
| 269 | __sound_remove_unit(list, s->unit_minor);
|
| 270 | if (index < 0) {
|
| 271 | low = s->unit_minor + SOUND_STEP;
|
| 272 | goto retry;
|
| 273 | }
|
| 274 | spin_unlock(&sound_loader_lock);
|
| 275 | r = -EBUSY;
|
| 276 | goto fail;
|
| 277 | }
|
| 278 | }
|
| 279 |
|
| 280 | device_create(&sound_class, dev, MKDEV(SOUND_MAJOR, s->unit_minor),
|
| 281 | NULL, "%s", s->name+6);
|
| 282 | return s->unit_minor;
|
| 283 |
|
| 284 | fail:
|
| 285 | kfree(s);
|
| 286 | return r;
|
| 287 | }
|
| 288 |
|
| 289 | |
| 290 | * Remove a unit. Acquires locks as needed. The drivers MUST have
|
| 291 | * completed the removal before their file operations become
|
| 292 | * invalid.
|
| 293 |
|
| 294 |
|
| 295 | static void sound_remove_unit(struct sound_unit **list, int unit)
|
| 296 | {
|
| 297 | struct sound_unit *p;
|
| 298 |
|
| 299 | spin_lock(&sound_loader_lock);
|
| 300 | p = __sound_remove_unit(list, unit);
|
| 301 | spin_unlock(&sound_loader_lock);
|
| 302 | if (p) {
|
| 303 | if (!preclaim_oss)
|
| 304 | __unregister_chrdev(SOUND_MAJOR, p->unit_minor, 1,
|
| 305 | p->name);
|
| 306 | device_destroy(&sound_class, MKDEV(SOUND_MAJOR, p->unit_minor));
|
| 307 | kfree(p);
|
| 308 | }
|
| 309 | }
|
| 310 |
|
| 311 | |
| 312 | * Allocations
|
| 313 | *
|
| 314 | * 0 *16 Mixers
|
| 315 | * 1 *8 Sequencers
|
| 316 | * 2 *16 Midi
|
| 317 | * 3 *16 DSP
|
| 318 | * 4 *16 SunDSP
|
| 319 | * 5 *16 DSP16
|
| 320 | * 6 -- sndstat (obsolete)
|
| 321 | * 7 *16 unused
|
| 322 | * 8 -- alternate sequencer (see above)
|
| 323 | * 9 *16 raw synthesizer access
|
| 324 | * 10 *16 unused
|
| 325 | * 11 *16 unused
|
| 326 | * 12 *16 unused
|
| 327 | * 13 *16 unused
|
| 328 | * 14 *16 unused
|
| 329 | * 15 *16 unused
|
| 330 |
|
| 331 |
|
| 332 | static struct sound_unit *chains[SOUND_STEP];
|
| 333 |
|
| 334 | |
| 335 | * register_sound_special_device - register a special sound node
|
| 336 | * @fops: File operations for the driver
|
| 337 | * @unit: Unit number to allocate
|
| 338 | * @dev: device pointer
|
| 339 | *
|
| 340 | * Allocate a special sound device by minor number from the sound
|
| 341 | * subsystem.
|
| 342 | *
|
| 343 | * Return: The allocated number is returned on success. On failure,
|
| 344 | * a negative error code is returned.
|
| 345 |
|
| 346 |
|
| 347 | int register_sound_special_device(const struct file_operations *fops, int unit,
|
| 348 | struct device *dev)
|
| 349 | {
|
| 350 | const int chain = unit % SOUND_STEP;
|
| 351 | int max_unit = 256;
|
| 352 | const char *name;
|
| 353 | char _name[16];
|
| 354 |
|
| 355 | switch (chain) {
|
| 356 | case 0:
|
| 357 | name = "mixer";
|
| 358 | break;
|
| 359 | case 1:
|
| 360 | name = "sequencer";
|
| 361 | if (unit >= SOUND_STEP)
|
| 362 | goto __unknown;
|
| 363 | max_unit = unit + 1;
|
| 364 | break;
|
| 365 | case 2:
|
| 366 | name = "midi";
|
| 367 | break;
|
| 368 | case 3:
|
| 369 | name = "dsp";
|
| 370 | break;
|
| 371 | case 4:
|
| 372 | name = "audio";
|
| 373 | break;
|
| 374 | case 5:
|
| 375 | name = "dspW";
|
| 376 | break;
|
| 377 | case 8:
|
| 378 | name = "sequencer2";
|
| 379 | if (unit >= SOUND_STEP)
|
| 380 | goto __unknown;
|
| 381 | max_unit = unit + 1;
|
| 382 | break;
|
| 383 | case 9:
|
| 384 | name = "dmmidi";
|
| 385 | break;
|
| 386 | case 10:
|
| 387 | name = "dmfm";
|
| 388 | break;
|
| 389 | case 12:
|
| 390 | name = "adsp";
|
| 391 | break;
|
| 392 | case 13:
|
| 393 | name = "amidi";
|
| 394 | break;
|
| 395 | case 14:
|
| 396 | name = "admmidi";
|
| 397 | break;
|
| 398 | default:
|
| 399 | {
|
| 400 | __unknown:
|
| 401 | sprintf(_name, "unknown%d", chain);
|
| 402 | if (unit >= SOUND_STEP)
|
| 403 | strcat(_name, "-");
|
| 404 | name = _name;
|
| 405 | }
|
| 406 | break;
|
| 407 | }
|
| 408 | return sound_insert_unit(&chains[chain], fops, -1, unit, max_unit,
|
| 409 | name, 0600, dev);
|
| 410 | }
|
| 411 |
|
| 412 | EXPORT_SYMBOL(register_sound_special_device);
|
| 413 |
|
| 414 | int register_sound_special(const struct file_operations *fops, int unit)
|
| 415 | {
|
| 416 | return register_sound_special_device(fops, unit, NULL);
|
| 417 | }
|
| 418 |
|
| 419 | EXPORT_SYMBOL(register_sound_special);
|
| 420 |
|
| 421 | |
| 422 | * register_sound_mixer - register a mixer device
|
| 423 | * @fops: File operations for the driver
|
| 424 | * @dev: Unit number to allocate
|
| 425 | *
|
| 426 | * Allocate a mixer device. Unit is the number of the mixer requested.
|
| 427 | * Pass -1 to request the next free mixer unit.
|
| 428 | *
|
| 429 | * Return: On success, the allocated number is returned. On failure,
|
| 430 | * a negative error code is returned.
|
| 431 |
|
| 432 |
|
| 433 | int register_sound_mixer(const struct file_operations *fops, int dev)
|
| 434 | {
|
| 435 | return sound_insert_unit(&chains[0], fops, dev, 0, 128,
|
| 436 | "mixer", 0600, NULL);
|
| 437 | }
|
| 438 |
|
| 439 | EXPORT_SYMBOL(register_sound_mixer);
|
| 440 |
|
| 441 | |
| 442 | * DSP's are registered as a triple. Register only one and cheat
|
| 443 | * in open - see below.
|
| 444 |
|
| 445 |
|
| 446 | |
| 447 | * register_sound_dsp - register a DSP device
|
| 448 | * @fops: File operations for the driver
|
| 449 | * @dev: Unit number to allocate
|
| 450 | *
|
| 451 | * Allocate a DSP device. Unit is the number of the DSP requested.
|
| 452 | * Pass -1 to request the next free DSP unit.
|
| 453 | *
|
| 454 | * This function allocates both the audio and dsp device entries together
|
| 455 | * and will always allocate them as a matching pair - eg dsp3/audio3
|
| 456 | *
|
| 457 | * Return: On success, the allocated number is returned. On failure,
|
| 458 | * a negative error code is returned.
|
| 459 |
|
| 460 |
|
| 461 | int register_sound_dsp(const struct file_operations *fops, int dev)
|
| 462 | {
|
| 463 | return sound_insert_unit(&chains[3], fops, dev, 3, 131,
|
| 464 | "dsp", 0600, NULL);
|
| 465 | }
|
| 466 |
|
| 467 | EXPORT_SYMBOL(register_sound_dsp);
|
| 468 |
|
| 469 | |
| 470 | * unregister_sound_special - unregister a special sound device
|
| 471 | * @unit: unit number to allocate
|
| 472 | *
|
| 473 | * Release a sound device that was allocated with
|
| 474 | * register_sound_special(). The unit passed is the return value from
|
| 475 | * the register function.
|
| 476 |
|
| 477 |
|
| 478 |
|
| 479 | void unregister_sound_special(int unit)
|
| 480 | {
|
| 481 | sound_remove_unit(&chains[unit % SOUND_STEP], unit);
|
| 482 | }
|
| 483 |
|
| 484 | EXPORT_SYMBOL(unregister_sound_special);
|
| 485 |
|
| 486 | |
| 487 | * unregister_sound_mixer - unregister a mixer
|
| 488 | * @unit: unit number to allocate
|
| 489 | *
|
| 490 | * Release a sound device that was allocated with register_sound_mixer().
|
| 491 | * The unit passed is the return value from the register function.
|
| 492 |
|
| 493 |
|
| 494 | void unregister_sound_mixer(int unit)
|
| 495 | {
|
| 496 | sound_remove_unit(&chains[0], unit);
|
| 497 | }
|
| 498 |
|
| 499 | EXPORT_SYMBOL(unregister_sound_mixer);
|
| 500 |
|
| 501 | |
| 502 | * unregister_sound_dsp - unregister a DSP device
|
| 503 | * @unit: unit number to allocate
|
| 504 | *
|
| 505 | * Release a sound device that was allocated with register_sound_dsp().
|
| 506 | * The unit passed is the return value from the register function.
|
| 507 | *
|
| 508 | * Both of the allocated units are released together automatically.
|
| 509 |
|
| 510 |
|
| 511 | void unregister_sound_dsp(int unit)
|
| 512 | {
|
| 513 | sound_remove_unit(&chains[3], unit);
|
| 514 | }
|
| 515 |
|
| 516 |
|
| 517 | EXPORT_SYMBOL(unregister_sound_dsp);
|
| 518 |
|
| 519 | static struct sound_unit *__look_for_unit(int chain, int unit)
|
| 520 | {
|
| 521 | struct sound_unit *s;
|
| 522 |
|
| 523 | s=chains[chain];
|
| 524 | while(s && s->unit_minor <= unit)
|
| 525 | {
|
| 526 | if(s->unit_minor==unit)
|
| 527 | return s;
|
| 528 | s=s->next;
|
| 529 | }
|
| 530 | return NULL;
|
| 531 | }
|
| 532 |
|
| 533 | static int soundcore_open(struct inode *inode, struct file *file)
|
| 534 | {
|
| 535 | int chain;
|
| 536 | int unit = iminor(inode);
|
| 537 | struct sound_unit *s;
|
| 538 | const struct file_operations *new_fops = NULL;
|
| 539 |
|
| 540 | chain=unit&0x0F;
|
| 541 | if(chain==4 || chain==5)
|
| 542 | {
|
| 543 | unit&=0xF0;
|
| 544 | unit|=3;
|
| 545 | chain=3;
|
| 546 | }
|
| 547 |
|
| 548 | spin_lock(&sound_loader_lock);
|
| 549 | s = __look_for_unit(chain, unit);
|
| 550 | if (s)
|
| 551 | new_fops = fops_get(s->unit_fops);
|
| 552 | if (preclaim_oss && !new_fops) {
|
| 553 | spin_unlock(&sound_loader_lock);
|
| 554 |
|
| 555 | |
| 556 | * Please, don't change this order or code.
|
| 557 | * For ALSA slot means soundcard and OSS emulation code
|
| 558 | * comes as add-on modules which aren't depend on
|
| 559 | * ALSA toplevel modules for soundcards, thus we need
|
| 560 | * load them at first. [Jaroslav Kysela <[email protected]>]
|
| 561 |
|
| 562 | request_module("sound-slot-%i", unit>>4);
|
| 563 | request_module("sound-service-%i-%i", unit>>4, chain);
|
| 564 |
|
| 565 | |
| 566 | * sound-slot/service-* module aliases are scheduled
|
| 567 | * for removal in favor of the standard char-major-*
|
| 568 | * module aliases. For the time being, generate both
|
| 569 | * the legacy and standard module aliases to ease
|
| 570 | * transition.
|
| 571 |
|
| 572 | if (request_module("char-major-%d-%d", SOUND_MAJOR, unit) > 0)
|
| 573 | request_module("char-major-%d", SOUND_MAJOR);
|
| 574 |
|
| 575 | spin_lock(&sound_loader_lock);
|
| 576 | s = __look_for_unit(chain, unit);
|
| 577 | if (s)
|
| 578 | new_fops = fops_get(s->unit_fops);
|
| 579 | }
|
| 580 | spin_unlock(&sound_loader_lock);
|
| 581 |
|
| 582 | if (!new_fops)
|
| 583 | return -ENODEV;
|
| 584 |
|
| 585 | |
| 586 | * We rely upon the fact that we can't be unloaded while the
|
| 587 | * subdriver is there.
|
| 588 |
|
| 589 | replace_fops(file, new_fops);
|
| 590 |
|
| 591 | if (!file->f_op->open)
|
| 592 | return -ENODEV;
|
| 593 |
|
| 594 | return file->f_op->open(inode, file);
|
| 595 | }
|
| 596 |
|
| 597 | MODULE_ALIAS_CHARDEV_MAJOR(SOUND_MAJOR);
|
| 598 |
|
| 599 | static void cleanup_oss_soundcore(void)
|
| 600 | {
|
| 601 | |
| 602 | empty
|
| 603 | unregister_chrdev(SOUND_MAJOR, "sound");
|
| 604 | }
|
| 605 |
|
| 606 | static int __init init_oss_soundcore(void)
|
| 607 | {
|
| 608 | if (preclaim_oss &&
|
| 609 | register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops) < 0) {
|
| 610 | printk(KERN_ERR "soundcore: sound device already in use.\n");
|
| 611 | return -EBUSY;
|
| 612 | }
|
| 613 |
|
| 614 | return 0;
|
| 615 | }
|
| 616 |
|
| 617 | #endif
|