162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Sound core. This file is composed of two parts. sound_class 462306a36Sopenharmony_ci * which is common to both OSS and ALSA and OSS sound core which 562306a36Sopenharmony_ci * is used OSS or emulation of it. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci * First, the common part. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/err.h> 1462306a36Sopenharmony_ci#include <linux/kdev_t.h> 1562306a36Sopenharmony_ci#include <linux/major.h> 1662306a36Sopenharmony_ci#include <sound/core.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#ifdef CONFIG_SOUND_OSS_CORE 1962306a36Sopenharmony_cistatic int __init init_oss_soundcore(void); 2062306a36Sopenharmony_cistatic void cleanup_oss_soundcore(void); 2162306a36Sopenharmony_ci#else 2262306a36Sopenharmony_cistatic inline int init_oss_soundcore(void) { return 0; } 2362306a36Sopenharmony_cistatic inline void cleanup_oss_soundcore(void) { } 2462306a36Sopenharmony_ci#endif 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ciMODULE_DESCRIPTION("Core sound module"); 2762306a36Sopenharmony_ciMODULE_AUTHOR("Alan Cox"); 2862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic char *sound_devnode(const struct device *dev, umode_t *mode) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci if (MAJOR(dev->devt) == SOUND_MAJOR) 3362306a36Sopenharmony_ci return NULL; 3462306a36Sopenharmony_ci return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev)); 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ciconst struct class sound_class = { 3862306a36Sopenharmony_ci .name = "sound", 3962306a36Sopenharmony_ci .devnode = sound_devnode, 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ciEXPORT_SYMBOL(sound_class); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic int __init init_soundcore(void) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci int rc; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci rc = init_oss_soundcore(); 4862306a36Sopenharmony_ci if (rc) 4962306a36Sopenharmony_ci return rc; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci rc = class_register(&sound_class); 5262306a36Sopenharmony_ci if (rc) { 5362306a36Sopenharmony_ci cleanup_oss_soundcore(); 5462306a36Sopenharmony_ci return rc; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return 0; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic void __exit cleanup_soundcore(void) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci cleanup_oss_soundcore(); 6362306a36Sopenharmony_ci class_unregister(&sound_class); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cisubsys_initcall(init_soundcore); 6762306a36Sopenharmony_cimodule_exit(cleanup_soundcore); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#ifdef CONFIG_SOUND_OSS_CORE 7162306a36Sopenharmony_ci/* 7262306a36Sopenharmony_ci * OSS sound core handling. Breaks out sound functions to submodules 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * Author: Alan Cox <alan@lxorguk.ukuu.org.uk> 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci * Fixes: 7762306a36Sopenharmony_ci * 7862306a36Sopenharmony_ci * -------------------- 7962306a36Sopenharmony_ci * 8062306a36Sopenharmony_ci * Top level handler for the sound subsystem. Various devices can 8162306a36Sopenharmony_ci * plug into this. The fact they don't all go via OSS doesn't mean 8262306a36Sopenharmony_ci * they don't have to implement the OSS API. There is a lot of logic 8362306a36Sopenharmony_ci * to keeping much of the OSS weight out of the code in a compatibility 8462306a36Sopenharmony_ci * module, but it's up to the driver to rember to load it... 8562306a36Sopenharmony_ci * 8662306a36Sopenharmony_ci * The code provides a set of functions for registration of devices 8762306a36Sopenharmony_ci * by type. This is done rather than providing a single call so that 8862306a36Sopenharmony_ci * we can hide any future changes in the internals (eg when we go to 8962306a36Sopenharmony_ci * 32bit dev_t) from the modules and their interface. 9062306a36Sopenharmony_ci * 9162306a36Sopenharmony_ci * Secondly we need to allocate the dsp, dsp16 and audio devices as 9262306a36Sopenharmony_ci * one. Thus we misuse the chains a bit to simplify this. 9362306a36Sopenharmony_ci * 9462306a36Sopenharmony_ci * Thirdly to make it more fun and for 2.3.x and above we do all 9562306a36Sopenharmony_ci * of this using fine grained locking. 9662306a36Sopenharmony_ci * 9762306a36Sopenharmony_ci * FIXME: we have to resolve modules and fine grained load/unload 9862306a36Sopenharmony_ci * locking at some point in 2.3.x. 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci#include <linux/init.h> 10262306a36Sopenharmony_ci#include <linux/slab.h> 10362306a36Sopenharmony_ci#include <linux/types.h> 10462306a36Sopenharmony_ci#include <linux/kernel.h> 10562306a36Sopenharmony_ci#include <linux/sound.h> 10662306a36Sopenharmony_ci#include <linux/kmod.h> 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#define SOUND_STEP 16 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistruct sound_unit 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci int unit_minor; 11362306a36Sopenharmony_ci const struct file_operations *unit_fops; 11462306a36Sopenharmony_ci struct sound_unit *next; 11562306a36Sopenharmony_ci char name[32]; 11662306a36Sopenharmony_ci}; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* 11962306a36Sopenharmony_ci * By default, OSS sound_core claims full legacy minor range (0-255) 12062306a36Sopenharmony_ci * of SOUND_MAJOR to trap open attempts to any sound minor and 12162306a36Sopenharmony_ci * requests modules using custom sound-slot/service-* module aliases. 12262306a36Sopenharmony_ci * The only benefit of doing this is allowing use of custom module 12362306a36Sopenharmony_ci * aliases instead of the standard char-major-* ones. This behavior 12462306a36Sopenharmony_ci * prevents alternative OSS implementation and is scheduled to be 12562306a36Sopenharmony_ci * removed. 12662306a36Sopenharmony_ci * 12762306a36Sopenharmony_ci * CONFIG_SOUND_OSS_CORE_PRECLAIM and soundcore.preclaim_oss kernel 12862306a36Sopenharmony_ci * parameter are added to allow distros and developers to try and 12962306a36Sopenharmony_ci * switch to alternative implementations without needing to rebuild 13062306a36Sopenharmony_ci * the kernel in the meantime. If preclaim_oss is non-zero, the 13162306a36Sopenharmony_ci * kernel will behave the same as before. All SOUND_MAJOR minors are 13262306a36Sopenharmony_ci * preclaimed and the custom module aliases along with standard chrdev 13362306a36Sopenharmony_ci * ones are emitted if a missing device is opened. If preclaim_oss is 13462306a36Sopenharmony_ci * zero, sound_core only grabs what's actually in use and for missing 13562306a36Sopenharmony_ci * devices only the standard chrdev aliases are requested. 13662306a36Sopenharmony_ci * 13762306a36Sopenharmony_ci * All these clutters are scheduled to be removed along with 13862306a36Sopenharmony_ci * sound-slot/service-* module aliases. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_cistatic int preclaim_oss = IS_ENABLED(CONFIG_SOUND_OSS_CORE_PRECLAIM); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cimodule_param(preclaim_oss, int, 0444); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int soundcore_open(struct inode *, struct file *); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic const struct file_operations soundcore_fops = 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci /* We must have an owner or the module locking fails */ 14962306a36Sopenharmony_ci .owner = THIS_MODULE, 15062306a36Sopenharmony_ci .open = soundcore_open, 15162306a36Sopenharmony_ci .llseek = noop_llseek, 15262306a36Sopenharmony_ci}; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/* 15562306a36Sopenharmony_ci * Low level list operator. Scan the ordered list, find a hole and 15662306a36Sopenharmony_ci * join into it. Called with the lock asserted 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int __sound_insert_unit(struct sound_unit * s, struct sound_unit **list, const struct file_operations *fops, int index, int low, int top) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci int n=low; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (index < 0) { /* first free */ 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci while (*list && (*list)->unit_minor<n) 16662306a36Sopenharmony_ci list=&((*list)->next); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci while(n<top) 16962306a36Sopenharmony_ci { 17062306a36Sopenharmony_ci /* Found a hole ? */ 17162306a36Sopenharmony_ci if(*list==NULL || (*list)->unit_minor>n) 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci list=&((*list)->next); 17462306a36Sopenharmony_ci n+=SOUND_STEP; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if(n>=top) 17862306a36Sopenharmony_ci return -ENOENT; 17962306a36Sopenharmony_ci } else { 18062306a36Sopenharmony_ci n = low+(index*16); 18162306a36Sopenharmony_ci while (*list) { 18262306a36Sopenharmony_ci if ((*list)->unit_minor==n) 18362306a36Sopenharmony_ci return -EBUSY; 18462306a36Sopenharmony_ci if ((*list)->unit_minor>n) 18562306a36Sopenharmony_ci break; 18662306a36Sopenharmony_ci list=&((*list)->next); 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* 19162306a36Sopenharmony_ci * Fill it in 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci s->unit_minor=n; 19562306a36Sopenharmony_ci s->unit_fops=fops; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* 19862306a36Sopenharmony_ci * Link it 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci s->next=*list; 20262306a36Sopenharmony_ci *list=s; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return n; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci/* 20962306a36Sopenharmony_ci * Remove a node from the chain. Called with the lock asserted 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic struct sound_unit *__sound_remove_unit(struct sound_unit **list, int unit) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci while(*list) 21562306a36Sopenharmony_ci { 21662306a36Sopenharmony_ci struct sound_unit *p=*list; 21762306a36Sopenharmony_ci if(p->unit_minor==unit) 21862306a36Sopenharmony_ci { 21962306a36Sopenharmony_ci *list=p->next; 22062306a36Sopenharmony_ci return p; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci list=&(p->next); 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci printk(KERN_ERR "Sound device %d went missing!\n", unit); 22562306a36Sopenharmony_ci return NULL; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci/* 22962306a36Sopenharmony_ci * This lock guards the sound loader list. 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic DEFINE_SPINLOCK(sound_loader_lock); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci/* 23562306a36Sopenharmony_ci * Allocate the controlling structure and add it to the sound driver 23662306a36Sopenharmony_ci * list. Acquires locks as needed 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic 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) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct sound_unit *s = kmalloc(sizeof(*s), GFP_KERNEL); 24262306a36Sopenharmony_ci int r; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (!s) 24562306a36Sopenharmony_ci return -ENOMEM; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci spin_lock(&sound_loader_lock); 24862306a36Sopenharmony_ciretry: 24962306a36Sopenharmony_ci r = __sound_insert_unit(s, list, fops, index, low, top); 25062306a36Sopenharmony_ci spin_unlock(&sound_loader_lock); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (r < 0) 25362306a36Sopenharmony_ci goto fail; 25462306a36Sopenharmony_ci else if (r < SOUND_STEP) 25562306a36Sopenharmony_ci sprintf(s->name, "sound/%s", name); 25662306a36Sopenharmony_ci else 25762306a36Sopenharmony_ci sprintf(s->name, "sound/%s%d", name, r / SOUND_STEP); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (!preclaim_oss) { 26062306a36Sopenharmony_ci /* 26162306a36Sopenharmony_ci * Something else might have grabbed the minor. If 26262306a36Sopenharmony_ci * first free slot is requested, rescan with @low set 26362306a36Sopenharmony_ci * to the next unit; otherwise, -EBUSY. 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_ci r = __register_chrdev(SOUND_MAJOR, s->unit_minor, 1, s->name, 26662306a36Sopenharmony_ci &soundcore_fops); 26762306a36Sopenharmony_ci if (r < 0) { 26862306a36Sopenharmony_ci spin_lock(&sound_loader_lock); 26962306a36Sopenharmony_ci __sound_remove_unit(list, s->unit_minor); 27062306a36Sopenharmony_ci if (index < 0) { 27162306a36Sopenharmony_ci low = s->unit_minor + SOUND_STEP; 27262306a36Sopenharmony_ci goto retry; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci spin_unlock(&sound_loader_lock); 27562306a36Sopenharmony_ci r = -EBUSY; 27662306a36Sopenharmony_ci goto fail; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci device_create(&sound_class, dev, MKDEV(SOUND_MAJOR, s->unit_minor), 28162306a36Sopenharmony_ci NULL, "%s", s->name+6); 28262306a36Sopenharmony_ci return s->unit_minor; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cifail: 28562306a36Sopenharmony_ci kfree(s); 28662306a36Sopenharmony_ci return r; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci/* 29062306a36Sopenharmony_ci * Remove a unit. Acquires locks as needed. The drivers MUST have 29162306a36Sopenharmony_ci * completed the removal before their file operations become 29262306a36Sopenharmony_ci * invalid. 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic void sound_remove_unit(struct sound_unit **list, int unit) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct sound_unit *p; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci spin_lock(&sound_loader_lock); 30062306a36Sopenharmony_ci p = __sound_remove_unit(list, unit); 30162306a36Sopenharmony_ci spin_unlock(&sound_loader_lock); 30262306a36Sopenharmony_ci if (p) { 30362306a36Sopenharmony_ci if (!preclaim_oss) 30462306a36Sopenharmony_ci __unregister_chrdev(SOUND_MAJOR, p->unit_minor, 1, 30562306a36Sopenharmony_ci p->name); 30662306a36Sopenharmony_ci device_destroy(&sound_class, MKDEV(SOUND_MAJOR, p->unit_minor)); 30762306a36Sopenharmony_ci kfree(p); 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci/* 31262306a36Sopenharmony_ci * Allocations 31362306a36Sopenharmony_ci * 31462306a36Sopenharmony_ci * 0 *16 Mixers 31562306a36Sopenharmony_ci * 1 *8 Sequencers 31662306a36Sopenharmony_ci * 2 *16 Midi 31762306a36Sopenharmony_ci * 3 *16 DSP 31862306a36Sopenharmony_ci * 4 *16 SunDSP 31962306a36Sopenharmony_ci * 5 *16 DSP16 32062306a36Sopenharmony_ci * 6 -- sndstat (obsolete) 32162306a36Sopenharmony_ci * 7 *16 unused 32262306a36Sopenharmony_ci * 8 -- alternate sequencer (see above) 32362306a36Sopenharmony_ci * 9 *16 raw synthesizer access 32462306a36Sopenharmony_ci * 10 *16 unused 32562306a36Sopenharmony_ci * 11 *16 unused 32662306a36Sopenharmony_ci * 12 *16 unused 32762306a36Sopenharmony_ci * 13 *16 unused 32862306a36Sopenharmony_ci * 14 *16 unused 32962306a36Sopenharmony_ci * 15 *16 unused 33062306a36Sopenharmony_ci */ 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic struct sound_unit *chains[SOUND_STEP]; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci/** 33562306a36Sopenharmony_ci * register_sound_special_device - register a special sound node 33662306a36Sopenharmony_ci * @fops: File operations for the driver 33762306a36Sopenharmony_ci * @unit: Unit number to allocate 33862306a36Sopenharmony_ci * @dev: device pointer 33962306a36Sopenharmony_ci * 34062306a36Sopenharmony_ci * Allocate a special sound device by minor number from the sound 34162306a36Sopenharmony_ci * subsystem. 34262306a36Sopenharmony_ci * 34362306a36Sopenharmony_ci * Return: The allocated number is returned on success. On failure, 34462306a36Sopenharmony_ci * a negative error code is returned. 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ciint register_sound_special_device(const struct file_operations *fops, int unit, 34862306a36Sopenharmony_ci struct device *dev) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci const int chain = unit % SOUND_STEP; 35162306a36Sopenharmony_ci int max_unit = 256; 35262306a36Sopenharmony_ci const char *name; 35362306a36Sopenharmony_ci char _name[16]; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci switch (chain) { 35662306a36Sopenharmony_ci case 0: 35762306a36Sopenharmony_ci name = "mixer"; 35862306a36Sopenharmony_ci break; 35962306a36Sopenharmony_ci case 1: 36062306a36Sopenharmony_ci name = "sequencer"; 36162306a36Sopenharmony_ci if (unit >= SOUND_STEP) 36262306a36Sopenharmony_ci goto __unknown; 36362306a36Sopenharmony_ci max_unit = unit + 1; 36462306a36Sopenharmony_ci break; 36562306a36Sopenharmony_ci case 2: 36662306a36Sopenharmony_ci name = "midi"; 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci case 3: 36962306a36Sopenharmony_ci name = "dsp"; 37062306a36Sopenharmony_ci break; 37162306a36Sopenharmony_ci case 4: 37262306a36Sopenharmony_ci name = "audio"; 37362306a36Sopenharmony_ci break; 37462306a36Sopenharmony_ci case 5: 37562306a36Sopenharmony_ci name = "dspW"; 37662306a36Sopenharmony_ci break; 37762306a36Sopenharmony_ci case 8: 37862306a36Sopenharmony_ci name = "sequencer2"; 37962306a36Sopenharmony_ci if (unit >= SOUND_STEP) 38062306a36Sopenharmony_ci goto __unknown; 38162306a36Sopenharmony_ci max_unit = unit + 1; 38262306a36Sopenharmony_ci break; 38362306a36Sopenharmony_ci case 9: 38462306a36Sopenharmony_ci name = "dmmidi"; 38562306a36Sopenharmony_ci break; 38662306a36Sopenharmony_ci case 10: 38762306a36Sopenharmony_ci name = "dmfm"; 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci case 12: 39062306a36Sopenharmony_ci name = "adsp"; 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci case 13: 39362306a36Sopenharmony_ci name = "amidi"; 39462306a36Sopenharmony_ci break; 39562306a36Sopenharmony_ci case 14: 39662306a36Sopenharmony_ci name = "admmidi"; 39762306a36Sopenharmony_ci break; 39862306a36Sopenharmony_ci default: 39962306a36Sopenharmony_ci { 40062306a36Sopenharmony_ci __unknown: 40162306a36Sopenharmony_ci sprintf(_name, "unknown%d", chain); 40262306a36Sopenharmony_ci if (unit >= SOUND_STEP) 40362306a36Sopenharmony_ci strcat(_name, "-"); 40462306a36Sopenharmony_ci name = _name; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci break; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci return sound_insert_unit(&chains[chain], fops, -1, unit, max_unit, 40962306a36Sopenharmony_ci name, 0600, dev); 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ciEXPORT_SYMBOL(register_sound_special_device); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ciint register_sound_special(const struct file_operations *fops, int unit) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci return register_sound_special_device(fops, unit, NULL); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ciEXPORT_SYMBOL(register_sound_special); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci/** 42262306a36Sopenharmony_ci * register_sound_mixer - register a mixer device 42362306a36Sopenharmony_ci * @fops: File operations for the driver 42462306a36Sopenharmony_ci * @dev: Unit number to allocate 42562306a36Sopenharmony_ci * 42662306a36Sopenharmony_ci * Allocate a mixer device. Unit is the number of the mixer requested. 42762306a36Sopenharmony_ci * Pass -1 to request the next free mixer unit. 42862306a36Sopenharmony_ci * 42962306a36Sopenharmony_ci * Return: On success, the allocated number is returned. On failure, 43062306a36Sopenharmony_ci * a negative error code is returned. 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ciint register_sound_mixer(const struct file_operations *fops, int dev) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci return sound_insert_unit(&chains[0], fops, dev, 0, 128, 43662306a36Sopenharmony_ci "mixer", 0600, NULL); 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ciEXPORT_SYMBOL(register_sound_mixer); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci/* 44262306a36Sopenharmony_ci * DSP's are registered as a triple. Register only one and cheat 44362306a36Sopenharmony_ci * in open - see below. 44462306a36Sopenharmony_ci */ 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci/** 44762306a36Sopenharmony_ci * register_sound_dsp - register a DSP device 44862306a36Sopenharmony_ci * @fops: File operations for the driver 44962306a36Sopenharmony_ci * @dev: Unit number to allocate 45062306a36Sopenharmony_ci * 45162306a36Sopenharmony_ci * Allocate a DSP device. Unit is the number of the DSP requested. 45262306a36Sopenharmony_ci * Pass -1 to request the next free DSP unit. 45362306a36Sopenharmony_ci * 45462306a36Sopenharmony_ci * This function allocates both the audio and dsp device entries together 45562306a36Sopenharmony_ci * and will always allocate them as a matching pair - eg dsp3/audio3 45662306a36Sopenharmony_ci * 45762306a36Sopenharmony_ci * Return: On success, the allocated number is returned. On failure, 45862306a36Sopenharmony_ci * a negative error code is returned. 45962306a36Sopenharmony_ci */ 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ciint register_sound_dsp(const struct file_operations *fops, int dev) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci return sound_insert_unit(&chains[3], fops, dev, 3, 131, 46462306a36Sopenharmony_ci "dsp", 0600, NULL); 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ciEXPORT_SYMBOL(register_sound_dsp); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci/** 47062306a36Sopenharmony_ci * unregister_sound_special - unregister a special sound device 47162306a36Sopenharmony_ci * @unit: unit number to allocate 47262306a36Sopenharmony_ci * 47362306a36Sopenharmony_ci * Release a sound device that was allocated with 47462306a36Sopenharmony_ci * register_sound_special(). The unit passed is the return value from 47562306a36Sopenharmony_ci * the register function. 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_civoid unregister_sound_special(int unit) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci sound_remove_unit(&chains[unit % SOUND_STEP], unit); 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ciEXPORT_SYMBOL(unregister_sound_special); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci/** 48762306a36Sopenharmony_ci * unregister_sound_mixer - unregister a mixer 48862306a36Sopenharmony_ci * @unit: unit number to allocate 48962306a36Sopenharmony_ci * 49062306a36Sopenharmony_ci * Release a sound device that was allocated with register_sound_mixer(). 49162306a36Sopenharmony_ci * The unit passed is the return value from the register function. 49262306a36Sopenharmony_ci */ 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_civoid unregister_sound_mixer(int unit) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci sound_remove_unit(&chains[0], unit); 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ciEXPORT_SYMBOL(unregister_sound_mixer); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci/** 50262306a36Sopenharmony_ci * unregister_sound_dsp - unregister a DSP device 50362306a36Sopenharmony_ci * @unit: unit number to allocate 50462306a36Sopenharmony_ci * 50562306a36Sopenharmony_ci * Release a sound device that was allocated with register_sound_dsp(). 50662306a36Sopenharmony_ci * The unit passed is the return value from the register function. 50762306a36Sopenharmony_ci * 50862306a36Sopenharmony_ci * Both of the allocated units are released together automatically. 50962306a36Sopenharmony_ci */ 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_civoid unregister_sound_dsp(int unit) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci sound_remove_unit(&chains[3], unit); 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ciEXPORT_SYMBOL(unregister_sound_dsp); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic struct sound_unit *__look_for_unit(int chain, int unit) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct sound_unit *s; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci s=chains[chain]; 52462306a36Sopenharmony_ci while(s && s->unit_minor <= unit) 52562306a36Sopenharmony_ci { 52662306a36Sopenharmony_ci if(s->unit_minor==unit) 52762306a36Sopenharmony_ci return s; 52862306a36Sopenharmony_ci s=s->next; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci return NULL; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic int soundcore_open(struct inode *inode, struct file *file) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci int chain; 53662306a36Sopenharmony_ci int unit = iminor(inode); 53762306a36Sopenharmony_ci struct sound_unit *s; 53862306a36Sopenharmony_ci const struct file_operations *new_fops = NULL; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci chain=unit&0x0F; 54162306a36Sopenharmony_ci if(chain==4 || chain==5) /* dsp/audio/dsp16 */ 54262306a36Sopenharmony_ci { 54362306a36Sopenharmony_ci unit&=0xF0; 54462306a36Sopenharmony_ci unit|=3; 54562306a36Sopenharmony_ci chain=3; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci spin_lock(&sound_loader_lock); 54962306a36Sopenharmony_ci s = __look_for_unit(chain, unit); 55062306a36Sopenharmony_ci if (s) 55162306a36Sopenharmony_ci new_fops = fops_get(s->unit_fops); 55262306a36Sopenharmony_ci if (preclaim_oss && !new_fops) { 55362306a36Sopenharmony_ci spin_unlock(&sound_loader_lock); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* 55662306a36Sopenharmony_ci * Please, don't change this order or code. 55762306a36Sopenharmony_ci * For ALSA slot means soundcard and OSS emulation code 55862306a36Sopenharmony_ci * comes as add-on modules which aren't depend on 55962306a36Sopenharmony_ci * ALSA toplevel modules for soundcards, thus we need 56062306a36Sopenharmony_ci * load them at first. [Jaroslav Kysela <perex@jcu.cz>] 56162306a36Sopenharmony_ci */ 56262306a36Sopenharmony_ci request_module("sound-slot-%i", unit>>4); 56362306a36Sopenharmony_ci request_module("sound-service-%i-%i", unit>>4, chain); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci /* 56662306a36Sopenharmony_ci * sound-slot/service-* module aliases are scheduled 56762306a36Sopenharmony_ci * for removal in favor of the standard char-major-* 56862306a36Sopenharmony_ci * module aliases. For the time being, generate both 56962306a36Sopenharmony_ci * the legacy and standard module aliases to ease 57062306a36Sopenharmony_ci * transition. 57162306a36Sopenharmony_ci */ 57262306a36Sopenharmony_ci if (request_module("char-major-%d-%d", SOUND_MAJOR, unit) > 0) 57362306a36Sopenharmony_ci request_module("char-major-%d", SOUND_MAJOR); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci spin_lock(&sound_loader_lock); 57662306a36Sopenharmony_ci s = __look_for_unit(chain, unit); 57762306a36Sopenharmony_ci if (s) 57862306a36Sopenharmony_ci new_fops = fops_get(s->unit_fops); 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci spin_unlock(&sound_loader_lock); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (!new_fops) 58362306a36Sopenharmony_ci return -ENODEV; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* 58662306a36Sopenharmony_ci * We rely upon the fact that we can't be unloaded while the 58762306a36Sopenharmony_ci * subdriver is there. 58862306a36Sopenharmony_ci */ 58962306a36Sopenharmony_ci replace_fops(file, new_fops); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (!file->f_op->open) 59262306a36Sopenharmony_ci return -ENODEV; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci return file->f_op->open(inode, file); 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ciMODULE_ALIAS_CHARDEV_MAJOR(SOUND_MAJOR); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cistatic void cleanup_oss_soundcore(void) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci /* We have nothing to really do here - we know the lists must be 60262306a36Sopenharmony_ci empty */ 60362306a36Sopenharmony_ci unregister_chrdev(SOUND_MAJOR, "sound"); 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic int __init init_oss_soundcore(void) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci if (preclaim_oss && 60962306a36Sopenharmony_ci register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops) < 0) { 61062306a36Sopenharmony_ci printk(KERN_ERR "soundcore: sound device already in use.\n"); 61162306a36Sopenharmony_ci return -EBUSY; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci return 0; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci#endif /* CONFIG_SOUND_OSS_CORE */ 618