/*1* Sound core. This file is composed of two parts. sound_class2* which is common to both OSS and ALSA and OSS sound core which3* is used OSS or emulation of it.4*/56/*7* First, the common part.8*/9#include <linux/module.h>10#include <linux/device.h>11#include <linux/err.h>12#include <linux/kdev_t.h>13#include <linux/major.h>14#include <sound/core.h>1516#ifdef CONFIG_SOUND_OSS_CORE17static int __init init_oss_soundcore(void);18static void cleanup_oss_soundcore(void);19#else20static inline int init_oss_soundcore(void) { return 0; }21static inline void cleanup_oss_soundcore(void) { }22#endif2324struct class *sound_class;25EXPORT_SYMBOL(sound_class);2627MODULE_DESCRIPTION("Core sound module");28MODULE_AUTHOR("Alan Cox");29MODULE_LICENSE("GPL");3031static char *sound_devnode(struct device *dev, mode_t *mode)32{33if (MAJOR(dev->devt) == SOUND_MAJOR)34return NULL;35return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev));36}3738static int __init init_soundcore(void)39{40int rc;4142rc = init_oss_soundcore();43if (rc)44return rc;4546sound_class = class_create(THIS_MODULE, "sound");47if (IS_ERR(sound_class)) {48cleanup_oss_soundcore();49return PTR_ERR(sound_class);50}5152sound_class->devnode = sound_devnode;5354return 0;55}5657static void __exit cleanup_soundcore(void)58{59cleanup_oss_soundcore();60class_destroy(sound_class);61}6263subsys_initcall(init_soundcore);64module_exit(cleanup_soundcore);656667#ifdef CONFIG_SOUND_OSS_CORE68/*69* OSS sound core handling. Breaks out sound functions to submodules70*71* Author: Alan Cox <[email protected]>72*73* Fixes:74*75*76* This program is free software; you can redistribute it and/or77* modify it under the terms of the GNU General Public License78* as published by the Free Software Foundation; either version79* 2 of the License, or (at your option) any later version.80*81* --------------------82*83* Top level handler for the sound subsystem. Various devices can84* plug into this. The fact they don't all go via OSS doesn't mean85* they don't have to implement the OSS API. There is a lot of logic86* to keeping much of the OSS weight out of the code in a compatibility87* module, but it's up to the driver to rember to load it...88*89* The code provides a set of functions for registration of devices90* by type. This is done rather than providing a single call so that91* we can hide any future changes in the internals (eg when we go to92* 32bit dev_t) from the modules and their interface.93*94* Secondly we need to allocate the dsp, dsp16 and audio devices as95* one. Thus we misuse the chains a bit to simplify this.96*97* Thirdly to make it more fun and for 2.3.x and above we do all98* of this using fine grained locking.99*100* FIXME: we have to resolve modules and fine grained load/unload101* locking at some point in 2.3.x.102*/103104#include <linux/init.h>105#include <linux/slab.h>106#include <linux/types.h>107#include <linux/kernel.h>108#include <linux/sound.h>109#include <linux/kmod.h>110111#define SOUND_STEP 16112113struct sound_unit114{115int unit_minor;116const struct file_operations *unit_fops;117struct sound_unit *next;118char name[32];119};120121#ifdef CONFIG_SOUND_MSNDCLAS122extern int msnd_classic_init(void);123#endif124#ifdef CONFIG_SOUND_MSNDPIN125extern int msnd_pinnacle_init(void);126#endif127128/*129* By default, OSS sound_core claims full legacy minor range (0-255)130* of SOUND_MAJOR to trap open attempts to any sound minor and131* requests modules using custom sound-slot/service-* module aliases.132* The only benefit of doing this is allowing use of custom module133* aliases instead of the standard char-major-* ones. This behavior134* prevents alternative OSS implementation and is scheduled to be135* removed.136*137* CONFIG_SOUND_OSS_CORE_PRECLAIM and soundcore.preclaim_oss kernel138* parameter are added to allow distros and developers to try and139* switch to alternative implementations without needing to rebuild140* the kernel in the meantime. If preclaim_oss is non-zero, the141* kernel will behave the same as before. All SOUND_MAJOR minors are142* preclaimed and the custom module aliases along with standard chrdev143* ones are emitted if a missing device is opened. If preclaim_oss is144* zero, sound_core only grabs what's actually in use and for missing145* devices only the standard chrdev aliases are requested.146*147* All these clutters are scheduled to be removed along with148* sound-slot/service-* module aliases. Please take a look at149* feature-removal-schedule.txt for details.150*/151#ifdef CONFIG_SOUND_OSS_CORE_PRECLAIM152static int preclaim_oss = 1;153#else154static int preclaim_oss = 0;155#endif156157module_param(preclaim_oss, int, 0444);158159static int soundcore_open(struct inode *, struct file *);160161static const struct file_operations soundcore_fops =162{163/* We must have an owner or the module locking fails */164.owner = THIS_MODULE,165.open = soundcore_open,166.llseek = noop_llseek,167};168169/*170* Low level list operator. Scan the ordered list, find a hole and171* join into it. Called with the lock asserted172*/173174static int __sound_insert_unit(struct sound_unit * s, struct sound_unit **list, const struct file_operations *fops, int index, int low, int top)175{176int n=low;177178if (index < 0) { /* first free */179180while (*list && (*list)->unit_minor<n)181list=&((*list)->next);182183while(n<top)184{185/* Found a hole ? */186if(*list==NULL || (*list)->unit_minor>n)187break;188list=&((*list)->next);189n+=SOUND_STEP;190}191192if(n>=top)193return -ENOENT;194} else {195n = low+(index*16);196while (*list) {197if ((*list)->unit_minor==n)198return -EBUSY;199if ((*list)->unit_minor>n)200break;201list=&((*list)->next);202}203}204205/*206* Fill it in207*/208209s->unit_minor=n;210s->unit_fops=fops;211212/*213* Link it214*/215216s->next=*list;217*list=s;218219220return n;221}222223/*224* Remove a node from the chain. Called with the lock asserted225*/226227static struct sound_unit *__sound_remove_unit(struct sound_unit **list, int unit)228{229while(*list)230{231struct sound_unit *p=*list;232if(p->unit_minor==unit)233{234*list=p->next;235return p;236}237list=&(p->next);238}239printk(KERN_ERR "Sound device %d went missing!\n", unit);240return NULL;241}242243/*244* This lock guards the sound loader list.245*/246247static DEFINE_SPINLOCK(sound_loader_lock);248249/*250* Allocate the controlling structure and add it to the sound driver251* list. Acquires locks as needed252*/253254static 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)255{256struct sound_unit *s = kmalloc(sizeof(*s), GFP_KERNEL);257int r;258259if (!s)260return -ENOMEM;261262spin_lock(&sound_loader_lock);263retry:264r = __sound_insert_unit(s, list, fops, index, low, top);265spin_unlock(&sound_loader_lock);266267if (r < 0)268goto fail;269else if (r < SOUND_STEP)270sprintf(s->name, "sound/%s", name);271else272sprintf(s->name, "sound/%s%d", name, r / SOUND_STEP);273274if (!preclaim_oss) {275/*276* Something else might have grabbed the minor. If277* first free slot is requested, rescan with @low set278* to the next unit; otherwise, -EBUSY.279*/280r = __register_chrdev(SOUND_MAJOR, s->unit_minor, 1, s->name,281&soundcore_fops);282if (r < 0) {283spin_lock(&sound_loader_lock);284__sound_remove_unit(list, s->unit_minor);285if (index < 0) {286low = s->unit_minor + SOUND_STEP;287goto retry;288}289spin_unlock(&sound_loader_lock);290return -EBUSY;291}292}293294device_create(sound_class, dev, MKDEV(SOUND_MAJOR, s->unit_minor),295NULL, s->name+6);296return s->unit_minor;297298fail:299kfree(s);300return r;301}302303/*304* Remove a unit. Acquires locks as needed. The drivers MUST have305* completed the removal before their file operations become306* invalid.307*/308309static void sound_remove_unit(struct sound_unit **list, int unit)310{311struct sound_unit *p;312313spin_lock(&sound_loader_lock);314p = __sound_remove_unit(list, unit);315spin_unlock(&sound_loader_lock);316if (p) {317if (!preclaim_oss)318__unregister_chrdev(SOUND_MAJOR, p->unit_minor, 1,319p->name);320device_destroy(sound_class, MKDEV(SOUND_MAJOR, p->unit_minor));321kfree(p);322}323}324325/*326* Allocations327*328* 0 *16 Mixers329* 1 *8 Sequencers330* 2 *16 Midi331* 3 *16 DSP332* 4 *16 SunDSP333* 5 *16 DSP16334* 6 -- sndstat (obsolete)335* 7 *16 unused336* 8 -- alternate sequencer (see above)337* 9 *16 raw synthesizer access338* 10 *16 unused339* 11 *16 unused340* 12 *16 unused341* 13 *16 unused342* 14 *16 unused343* 15 *16 unused344*/345346static struct sound_unit *chains[SOUND_STEP];347348/**349* register_sound_special_device - register a special sound node350* @fops: File operations for the driver351* @unit: Unit number to allocate352* @dev: device pointer353*354* Allocate a special sound device by minor number from the sound355* subsystem. The allocated number is returned on success. On failure356* a negative error code is returned.357*/358359int register_sound_special_device(const struct file_operations *fops, int unit,360struct device *dev)361{362const int chain = unit % SOUND_STEP;363int max_unit = 128 + chain;364const char *name;365char _name[16];366367switch (chain) {368case 0:369name = "mixer";370break;371case 1:372name = "sequencer";373if (unit >= SOUND_STEP)374goto __unknown;375max_unit = unit + 1;376break;377case 2:378name = "midi";379break;380case 3:381name = "dsp";382break;383case 4:384name = "audio";385break;386case 5:387name = "dspW";388break;389case 8:390name = "sequencer2";391if (unit >= SOUND_STEP)392goto __unknown;393max_unit = unit + 1;394break;395case 9:396name = "dmmidi";397break;398case 10:399name = "dmfm";400break;401case 12:402name = "adsp";403break;404case 13:405name = "amidi";406break;407case 14:408name = "admmidi";409break;410default:411{412__unknown:413sprintf(_name, "unknown%d", chain);414if (unit >= SOUND_STEP)415strcat(_name, "-");416name = _name;417}418break;419}420return sound_insert_unit(&chains[chain], fops, -1, unit, max_unit,421name, S_IRUSR | S_IWUSR, dev);422}423424EXPORT_SYMBOL(register_sound_special_device);425426int register_sound_special(const struct file_operations *fops, int unit)427{428return register_sound_special_device(fops, unit, NULL);429}430431EXPORT_SYMBOL(register_sound_special);432433/**434* register_sound_mixer - register a mixer device435* @fops: File operations for the driver436* @dev: Unit number to allocate437*438* Allocate a mixer device. Unit is the number of the mixer requested.439* Pass -1 to request the next free mixer unit. On success the allocated440* number is returned, on failure a negative error code is returned.441*/442443int register_sound_mixer(const struct file_operations *fops, int dev)444{445return sound_insert_unit(&chains[0], fops, dev, 0, 128,446"mixer", S_IRUSR | S_IWUSR, NULL);447}448449EXPORT_SYMBOL(register_sound_mixer);450451/**452* register_sound_midi - register a midi device453* @fops: File operations for the driver454* @dev: Unit number to allocate455*456* Allocate a midi device. Unit is the number of the midi device requested.457* Pass -1 to request the next free midi unit. On success the allocated458* number is returned, on failure a negative error code is returned.459*/460461int register_sound_midi(const struct file_operations *fops, int dev)462{463return sound_insert_unit(&chains[2], fops, dev, 2, 130,464"midi", S_IRUSR | S_IWUSR, NULL);465}466467EXPORT_SYMBOL(register_sound_midi);468469/*470* DSP's are registered as a triple. Register only one and cheat471* in open - see below.472*/473474/**475* register_sound_dsp - register a DSP device476* @fops: File operations for the driver477* @dev: Unit number to allocate478*479* Allocate a DSP device. Unit is the number of the DSP requested.480* Pass -1 to request the next free DSP unit. On success the allocated481* number is returned, on failure a negative error code is returned.482*483* This function allocates both the audio and dsp device entries together484* and will always allocate them as a matching pair - eg dsp3/audio3485*/486487int register_sound_dsp(const struct file_operations *fops, int dev)488{489return sound_insert_unit(&chains[3], fops, dev, 3, 131,490"dsp", S_IWUSR | S_IRUSR, NULL);491}492493EXPORT_SYMBOL(register_sound_dsp);494495/**496* unregister_sound_special - unregister a special sound device497* @unit: unit number to allocate498*499* Release a sound device that was allocated with500* register_sound_special(). The unit passed is the return value from501* the register function.502*/503504505void unregister_sound_special(int unit)506{507sound_remove_unit(&chains[unit % SOUND_STEP], unit);508}509510EXPORT_SYMBOL(unregister_sound_special);511512/**513* unregister_sound_mixer - unregister a mixer514* @unit: unit number to allocate515*516* Release a sound device that was allocated with register_sound_mixer().517* The unit passed is the return value from the register function.518*/519520void unregister_sound_mixer(int unit)521{522sound_remove_unit(&chains[0], unit);523}524525EXPORT_SYMBOL(unregister_sound_mixer);526527/**528* unregister_sound_midi - unregister a midi device529* @unit: unit number to allocate530*531* Release a sound device that was allocated with register_sound_midi().532* The unit passed is the return value from the register function.533*/534535void unregister_sound_midi(int unit)536{537sound_remove_unit(&chains[2], unit);538}539540EXPORT_SYMBOL(unregister_sound_midi);541542/**543* unregister_sound_dsp - unregister a DSP device544* @unit: unit number to allocate545*546* Release a sound device that was allocated with register_sound_dsp().547* The unit passed is the return value from the register function.548*549* Both of the allocated units are released together automatically.550*/551552void unregister_sound_dsp(int unit)553{554sound_remove_unit(&chains[3], unit);555}556557558EXPORT_SYMBOL(unregister_sound_dsp);559560static struct sound_unit *__look_for_unit(int chain, int unit)561{562struct sound_unit *s;563564s=chains[chain];565while(s && s->unit_minor <= unit)566{567if(s->unit_minor==unit)568return s;569s=s->next;570}571return NULL;572}573574static int soundcore_open(struct inode *inode, struct file *file)575{576int chain;577int unit = iminor(inode);578struct sound_unit *s;579const struct file_operations *new_fops = NULL;580581chain=unit&0x0F;582if(chain==4 || chain==5) /* dsp/audio/dsp16 */583{584unit&=0xF0;585unit|=3;586chain=3;587}588589spin_lock(&sound_loader_lock);590s = __look_for_unit(chain, unit);591if (s)592new_fops = fops_get(s->unit_fops);593if (preclaim_oss && !new_fops) {594spin_unlock(&sound_loader_lock);595596/*597* Please, don't change this order or code.598* For ALSA slot means soundcard and OSS emulation code599* comes as add-on modules which aren't depend on600* ALSA toplevel modules for soundcards, thus we need601* load them at first. [Jaroslav Kysela <[email protected]>]602*/603request_module("sound-slot-%i", unit>>4);604request_module("sound-service-%i-%i", unit>>4, chain);605606/*607* sound-slot/service-* module aliases are scheduled608* for removal in favor of the standard char-major-*609* module aliases. For the time being, generate both610* the legacy and standard module aliases to ease611* transition.612*/613if (request_module("char-major-%d-%d", SOUND_MAJOR, unit) > 0)614request_module("char-major-%d", SOUND_MAJOR);615616spin_lock(&sound_loader_lock);617s = __look_for_unit(chain, unit);618if (s)619new_fops = fops_get(s->unit_fops);620}621if (new_fops) {622/*623* We rely upon the fact that we can't be unloaded while the624* subdriver is there, so if ->open() is successful we can625* safely drop the reference counter and if it is not we can626* revert to old ->f_op. Ugly, indeed, but that's the cost of627* switching ->f_op in the first place.628*/629int err = 0;630const struct file_operations *old_fops = file->f_op;631file->f_op = new_fops;632spin_unlock(&sound_loader_lock);633634if (file->f_op->open)635err = file->f_op->open(inode,file);636637if (err) {638fops_put(file->f_op);639file->f_op = fops_get(old_fops);640}641642fops_put(old_fops);643return err;644}645spin_unlock(&sound_loader_lock);646return -ENODEV;647}648649MODULE_ALIAS_CHARDEV_MAJOR(SOUND_MAJOR);650651static void cleanup_oss_soundcore(void)652{653/* We have nothing to really do here - we know the lists must be654empty */655unregister_chrdev(SOUND_MAJOR, "sound");656}657658static int __init init_oss_soundcore(void)659{660if (preclaim_oss &&661register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops) == -1) {662printk(KERN_ERR "soundcore: sound device already in use.\n");663return -EBUSY;664}665666return 0;667}668669#endif /* CONFIG_SOUND_OSS_CORE */670671672