// SPDX-License-Identifier: GPL-2.0-or-later1/*2* Sound core. This file is composed of two parts. sound_class3* which is common to both OSS and ALSA and OSS sound core which4* is used OSS or emulation of it.5*/67/*8* First, the common part.9*/10#include <linux/module.h>11#include <linux/device.h>12#include <linux/err.h>13#include <linux/kdev_t.h>14#include <linux/major.h>15#include <sound/core.h>1617#ifdef CONFIG_SOUND_OSS_CORE18static int __init init_oss_soundcore(void);19static void cleanup_oss_soundcore(void);20#else21static inline int init_oss_soundcore(void) { return 0; }22static inline void cleanup_oss_soundcore(void) { }23#endif2425MODULE_DESCRIPTION("Core sound module");26MODULE_AUTHOR("Alan Cox");27MODULE_LICENSE("GPL");2829static char *sound_devnode(const struct device *dev, umode_t *mode)30{31if (MAJOR(dev->devt) == SOUND_MAJOR)32return NULL;33return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev));34}3536const struct class sound_class = {37.name = "sound",38.devnode = sound_devnode,39};40EXPORT_SYMBOL(sound_class);4142static int __init init_soundcore(void)43{44int rc;4546rc = init_oss_soundcore();47if (rc)48return rc;4950rc = class_register(&sound_class);51if (rc) {52cleanup_oss_soundcore();53return rc;54}5556return 0;57}5859static void __exit cleanup_soundcore(void)60{61cleanup_oss_soundcore();62class_unregister(&sound_class);63}6465subsys_initcall(init_soundcore);66module_exit(cleanup_soundcore);676869#ifdef CONFIG_SOUND_OSS_CORE70/*71* OSS sound core handling. Breaks out sound functions to submodules72*73* Author: Alan Cox <[email protected]>74*75* Fixes:76*77* --------------------78*79* Top level handler for the sound subsystem. Various devices can80* plug into this. The fact they don't all go via OSS doesn't mean81* they don't have to implement the OSS API. There is a lot of logic82* to keeping much of the OSS weight out of the code in a compatibility83* module, but it's up to the driver to rember to load it...84*85* The code provides a set of functions for registration of devices86* by type. This is done rather than providing a single call so that87* we can hide any future changes in the internals (eg when we go to88* 32bit dev_t) from the modules and their interface.89*90* Secondly we need to allocate the dsp, dsp16 and audio devices as91* one. Thus we misuse the chains a bit to simplify this.92*93* Thirdly to make it more fun and for 2.3.x and above we do all94* of this using fine grained locking.95*96* FIXME: we have to resolve modules and fine grained load/unload97* locking at some point in 2.3.x.98*/99100#include <linux/init.h>101#include <linux/slab.h>102#include <linux/types.h>103#include <linux/kernel.h>104#include <linux/sound.h>105#include <linux/kmod.h>106107#define SOUND_STEP 16108109struct sound_unit110{111int unit_minor;112const struct file_operations *unit_fops;113struct sound_unit *next;114char name[32];115};116117/*118* By default, OSS sound_core claims full legacy minor range (0-255)119* of SOUND_MAJOR to trap open attempts to any sound minor and120* requests modules using custom sound-slot/service-* module aliases.121* The only benefit of doing this is allowing use of custom module122* aliases instead of the standard char-major-* ones. This behavior123* prevents alternative OSS implementation and is scheduled to be124* removed.125*126* CONFIG_SOUND_OSS_CORE_PRECLAIM and soundcore.preclaim_oss kernel127* parameter are added to allow distros and developers to try and128* switch to alternative implementations without needing to rebuild129* the kernel in the meantime. If preclaim_oss is non-zero, the130* kernel will behave the same as before. All SOUND_MAJOR minors are131* preclaimed and the custom module aliases along with standard chrdev132* ones are emitted if a missing device is opened. If preclaim_oss is133* zero, sound_core only grabs what's actually in use and for missing134* devices only the standard chrdev aliases are requested.135*136* All these clutters are scheduled to be removed along with137* sound-slot/service-* module aliases.138*/139static int preclaim_oss = IS_ENABLED(CONFIG_SOUND_OSS_CORE_PRECLAIM);140141module_param(preclaim_oss, int, 0444);142143static int soundcore_open(struct inode *, struct file *);144145static const struct file_operations soundcore_fops =146{147/* We must have an owner or the module locking fails */148.owner = THIS_MODULE,149.open = soundcore_open,150.llseek = noop_llseek,151};152153/*154* Low level list operator. Scan the ordered list, find a hole and155* join into it. Called with the lock asserted156*/157158static int __sound_insert_unit(struct sound_unit * s, struct sound_unit **list, const struct file_operations *fops, int index, int low, int top)159{160int n=low;161162if (index < 0) { /* first free */163164while (*list && (*list)->unit_minor<n)165list=&((*list)->next);166167while(n<top)168{169/* Found a hole ? */170if(*list==NULL || (*list)->unit_minor>n)171break;172list=&((*list)->next);173n+=SOUND_STEP;174}175176if(n>=top)177return -ENOENT;178} else {179n = low+(index*16);180while (*list) {181if ((*list)->unit_minor==n)182return -EBUSY;183if ((*list)->unit_minor>n)184break;185list=&((*list)->next);186}187}188189/*190* Fill it in191*/192193s->unit_minor=n;194s->unit_fops=fops;195196/*197* Link it198*/199200s->next=*list;201*list=s;202203204return n;205}206207/*208* Remove a node from the chain. Called with the lock asserted209*/210211static struct sound_unit *__sound_remove_unit(struct sound_unit **list, int unit)212{213while(*list)214{215struct sound_unit *p=*list;216if(p->unit_minor==unit)217{218*list=p->next;219return p;220}221list=&(p->next);222}223printk(KERN_ERR "Sound device %d went missing!\n", unit);224return NULL;225}226227/*228* This lock guards the sound loader list.229*/230231static DEFINE_SPINLOCK(sound_loader_lock);232233/*234* Allocate the controlling structure and add it to the sound driver235* list. Acquires locks as needed236*/237238static 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)239{240struct sound_unit *s = kmalloc(sizeof(*s), GFP_KERNEL);241int r;242243if (!s)244return -ENOMEM;245246spin_lock(&sound_loader_lock);247retry:248r = __sound_insert_unit(s, list, fops, index, low, top);249spin_unlock(&sound_loader_lock);250251if (r < 0)252goto fail;253else if (r < SOUND_STEP)254sprintf(s->name, "sound/%s", name);255else256sprintf(s->name, "sound/%s%d", name, r / SOUND_STEP);257258if (!preclaim_oss) {259/*260* Something else might have grabbed the minor. If261* first free slot is requested, rescan with @low set262* to the next unit; otherwise, -EBUSY.263*/264r = __register_chrdev(SOUND_MAJOR, s->unit_minor, 1, s->name,265&soundcore_fops);266if (r < 0) {267spin_lock(&sound_loader_lock);268__sound_remove_unit(list, s->unit_minor);269if (index < 0) {270low = s->unit_minor + SOUND_STEP;271goto retry;272}273spin_unlock(&sound_loader_lock);274r = -EBUSY;275goto fail;276}277}278279device_create(&sound_class, dev, MKDEV(SOUND_MAJOR, s->unit_minor),280NULL, "%s", s->name+6);281return s->unit_minor;282283fail:284kfree(s);285return r;286}287288/*289* Remove a unit. Acquires locks as needed. The drivers MUST have290* completed the removal before their file operations become291* invalid.292*/293294static void sound_remove_unit(struct sound_unit **list, int unit)295{296struct sound_unit *p;297298spin_lock(&sound_loader_lock);299p = __sound_remove_unit(list, unit);300spin_unlock(&sound_loader_lock);301if (p) {302if (!preclaim_oss)303__unregister_chrdev(SOUND_MAJOR, p->unit_minor, 1,304p->name);305device_destroy(&sound_class, MKDEV(SOUND_MAJOR, p->unit_minor));306kfree(p);307}308}309310/*311* Allocations312*313* 0 *16 Mixers314* 1 *8 Sequencers315* 2 *16 Midi316* 3 *16 DSP317* 4 *16 SunDSP318* 5 *16 DSP16319* 6 -- sndstat (obsolete)320* 7 *16 unused321* 8 -- alternate sequencer (see above)322* 9 *16 raw synthesizer access323* 10 *16 unused324* 11 *16 unused325* 12 *16 unused326* 13 *16 unused327* 14 *16 unused328* 15 *16 unused329*/330331static struct sound_unit *chains[SOUND_STEP];332333/**334* register_sound_special_device - register a special sound node335* @fops: File operations for the driver336* @unit: Unit number to allocate337* @dev: device pointer338*339* Allocate a special sound device by minor number from the sound340* subsystem.341*342* Return: The allocated number is returned on success. On failure,343* a negative error code is returned.344*/345346int register_sound_special_device(const struct file_operations *fops, int unit,347struct device *dev)348{349const int chain = unit % SOUND_STEP;350int max_unit = 256;351const char *name;352char _name[16];353354switch (chain) {355case 0:356name = "mixer";357break;358case 1:359name = "sequencer";360if (unit >= SOUND_STEP)361goto __unknown;362max_unit = unit + 1;363break;364case 2:365name = "midi";366break;367case 3:368name = "dsp";369break;370case 4:371name = "audio";372break;373case 5:374name = "dspW";375break;376case 8:377name = "sequencer2";378if (unit >= SOUND_STEP)379goto __unknown;380max_unit = unit + 1;381break;382case 9:383name = "dmmidi";384break;385case 10:386name = "dmfm";387break;388case 12:389name = "adsp";390break;391case 13:392name = "amidi";393break;394case 14:395name = "admmidi";396break;397default:398{399__unknown:400sprintf(_name, "unknown%d", chain);401if (unit >= SOUND_STEP)402strcat(_name, "-");403name = _name;404}405break;406}407return sound_insert_unit(&chains[chain], fops, -1, unit, max_unit,408name, 0600, dev);409}410411EXPORT_SYMBOL(register_sound_special_device);412413int register_sound_special(const struct file_operations *fops, int unit)414{415return register_sound_special_device(fops, unit, NULL);416}417418EXPORT_SYMBOL(register_sound_special);419420/**421* register_sound_mixer - register a mixer device422* @fops: File operations for the driver423* @dev: Unit number to allocate424*425* Allocate a mixer device. Unit is the number of the mixer requested.426* Pass -1 to request the next free mixer unit.427*428* Return: On success, the allocated number is returned. On failure,429* a negative error code is returned.430*/431432int register_sound_mixer(const struct file_operations *fops, int dev)433{434return sound_insert_unit(&chains[0], fops, dev, 0, 128,435"mixer", 0600, NULL);436}437438EXPORT_SYMBOL(register_sound_mixer);439440/*441* DSP's are registered as a triple. Register only one and cheat442* in open - see below.443*/444445/**446* register_sound_dsp - register a DSP device447* @fops: File operations for the driver448* @dev: Unit number to allocate449*450* Allocate a DSP device. Unit is the number of the DSP requested.451* Pass -1 to request the next free DSP unit.452*453* This function allocates both the audio and dsp device entries together454* and will always allocate them as a matching pair - eg dsp3/audio3455*456* Return: On success, the allocated number is returned. On failure,457* a negative error code is returned.458*/459460int register_sound_dsp(const struct file_operations *fops, int dev)461{462return sound_insert_unit(&chains[3], fops, dev, 3, 131,463"dsp", 0600, NULL);464}465466EXPORT_SYMBOL(register_sound_dsp);467468/**469* unregister_sound_special - unregister a special sound device470* @unit: unit number to allocate471*472* Release a sound device that was allocated with473* register_sound_special(). The unit passed is the return value from474* the register function.475*/476477478void unregister_sound_special(int unit)479{480sound_remove_unit(&chains[unit % SOUND_STEP], unit);481}482483EXPORT_SYMBOL(unregister_sound_special);484485/**486* unregister_sound_mixer - unregister a mixer487* @unit: unit number to allocate488*489* Release a sound device that was allocated with register_sound_mixer().490* The unit passed is the return value from the register function.491*/492493void unregister_sound_mixer(int unit)494{495sound_remove_unit(&chains[0], unit);496}497498EXPORT_SYMBOL(unregister_sound_mixer);499500/**501* unregister_sound_dsp - unregister a DSP device502* @unit: unit number to allocate503*504* Release a sound device that was allocated with register_sound_dsp().505* The unit passed is the return value from the register function.506*507* Both of the allocated units are released together automatically.508*/509510void unregister_sound_dsp(int unit)511{512sound_remove_unit(&chains[3], unit);513}514515516EXPORT_SYMBOL(unregister_sound_dsp);517518static struct sound_unit *__look_for_unit(int chain, int unit)519{520struct sound_unit *s;521522s=chains[chain];523while(s && s->unit_minor <= unit)524{525if(s->unit_minor==unit)526return s;527s=s->next;528}529return NULL;530}531532static int soundcore_open(struct inode *inode, struct file *file)533{534int chain;535int unit = iminor(inode);536struct sound_unit *s;537const struct file_operations *new_fops = NULL;538539chain=unit&0x0F;540if(chain==4 || chain==5) /* dsp/audio/dsp16 */541{542unit&=0xF0;543unit|=3;544chain=3;545}546547spin_lock(&sound_loader_lock);548s = __look_for_unit(chain, unit);549if (s)550new_fops = fops_get(s->unit_fops);551if (preclaim_oss && !new_fops) {552spin_unlock(&sound_loader_lock);553554/*555* Please, don't change this order or code.556* For ALSA slot means soundcard and OSS emulation code557* comes as add-on modules which aren't depend on558* ALSA toplevel modules for soundcards, thus we need559* load them at first. [Jaroslav Kysela <[email protected]>]560*/561request_module("sound-slot-%i", unit>>4);562request_module("sound-service-%i-%i", unit>>4, chain);563564/*565* sound-slot/service-* module aliases are scheduled566* for removal in favor of the standard char-major-*567* module aliases. For the time being, generate both568* the legacy and standard module aliases to ease569* transition.570*/571if (request_module("char-major-%d-%d", SOUND_MAJOR, unit) > 0)572request_module("char-major-%d", SOUND_MAJOR);573574spin_lock(&sound_loader_lock);575s = __look_for_unit(chain, unit);576if (s)577new_fops = fops_get(s->unit_fops);578}579spin_unlock(&sound_loader_lock);580581if (!new_fops)582return -ENODEV;583584/*585* We rely upon the fact that we can't be unloaded while the586* subdriver is there.587*/588replace_fops(file, new_fops);589590if (!file->f_op->open)591return -ENODEV;592593return file->f_op->open(inode, file);594}595596MODULE_ALIAS_CHARDEV_MAJOR(SOUND_MAJOR);597598static void cleanup_oss_soundcore(void)599{600/* We have nothing to really do here - we know the lists must be601empty */602unregister_chrdev(SOUND_MAJOR, "sound");603}604605static int __init init_oss_soundcore(void)606{607if (preclaim_oss &&608register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops) < 0) {609printk(KERN_ERR "soundcore: sound device already in use.\n");610return -EBUSY;611}612613return 0;614}615616#endif /* CONFIG_SOUND_OSS_CORE */617618619