18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Sound core. This file is composed of two parts. sound_class 48c2ecf20Sopenharmony_ci * which is common to both OSS and ALSA and OSS sound core which 58c2ecf20Sopenharmony_ci * is used OSS or emulation of it. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* 98c2ecf20Sopenharmony_ci * First, the common part. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/kdev_t.h> 158c2ecf20Sopenharmony_ci#include <linux/major.h> 168c2ecf20Sopenharmony_ci#include <sound/core.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#ifdef CONFIG_SOUND_OSS_CORE 198c2ecf20Sopenharmony_cistatic int __init init_oss_soundcore(void); 208c2ecf20Sopenharmony_cistatic void cleanup_oss_soundcore(void); 218c2ecf20Sopenharmony_ci#else 228c2ecf20Sopenharmony_cistatic inline int init_oss_soundcore(void) { return 0; } 238c2ecf20Sopenharmony_cistatic inline void cleanup_oss_soundcore(void) { } 248c2ecf20Sopenharmony_ci#endif 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct class *sound_class; 278c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sound_class); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Core sound module"); 308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alan Cox"); 318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic char *sound_devnode(struct device *dev, umode_t *mode) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci if (MAJOR(dev->devt) == SOUND_MAJOR) 368c2ecf20Sopenharmony_ci return NULL; 378c2ecf20Sopenharmony_ci return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev)); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int __init init_soundcore(void) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci int rc; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci rc = init_oss_soundcore(); 458c2ecf20Sopenharmony_ci if (rc) 468c2ecf20Sopenharmony_ci return rc; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci sound_class = class_create(THIS_MODULE, "sound"); 498c2ecf20Sopenharmony_ci if (IS_ERR(sound_class)) { 508c2ecf20Sopenharmony_ci cleanup_oss_soundcore(); 518c2ecf20Sopenharmony_ci return PTR_ERR(sound_class); 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci sound_class->devnode = sound_devnode; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return 0; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic void __exit cleanup_soundcore(void) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci cleanup_oss_soundcore(); 628c2ecf20Sopenharmony_ci class_destroy(sound_class); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cisubsys_initcall(init_soundcore); 668c2ecf20Sopenharmony_cimodule_exit(cleanup_soundcore); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#ifdef CONFIG_SOUND_OSS_CORE 708c2ecf20Sopenharmony_ci/* 718c2ecf20Sopenharmony_ci * OSS sound core handling. Breaks out sound functions to submodules 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * Author: Alan Cox <alan@lxorguk.ukuu.org.uk> 748c2ecf20Sopenharmony_ci * 758c2ecf20Sopenharmony_ci * Fixes: 768c2ecf20Sopenharmony_ci * 778c2ecf20Sopenharmony_ci * -------------------- 788c2ecf20Sopenharmony_ci * 798c2ecf20Sopenharmony_ci * Top level handler for the sound subsystem. Various devices can 808c2ecf20Sopenharmony_ci * plug into this. The fact they don't all go via OSS doesn't mean 818c2ecf20Sopenharmony_ci * they don't have to implement the OSS API. There is a lot of logic 828c2ecf20Sopenharmony_ci * to keeping much of the OSS weight out of the code in a compatibility 838c2ecf20Sopenharmony_ci * module, but it's up to the driver to rember to load it... 848c2ecf20Sopenharmony_ci * 858c2ecf20Sopenharmony_ci * The code provides a set of functions for registration of devices 868c2ecf20Sopenharmony_ci * by type. This is done rather than providing a single call so that 878c2ecf20Sopenharmony_ci * we can hide any future changes in the internals (eg when we go to 888c2ecf20Sopenharmony_ci * 32bit dev_t) from the modules and their interface. 898c2ecf20Sopenharmony_ci * 908c2ecf20Sopenharmony_ci * Secondly we need to allocate the dsp, dsp16 and audio devices as 918c2ecf20Sopenharmony_ci * one. Thus we misuse the chains a bit to simplify this. 928c2ecf20Sopenharmony_ci * 938c2ecf20Sopenharmony_ci * Thirdly to make it more fun and for 2.3.x and above we do all 948c2ecf20Sopenharmony_ci * of this using fine grained locking. 958c2ecf20Sopenharmony_ci * 968c2ecf20Sopenharmony_ci * FIXME: we have to resolve modules and fine grained load/unload 978c2ecf20Sopenharmony_ci * locking at some point in 2.3.x. 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#include <linux/init.h> 1018c2ecf20Sopenharmony_ci#include <linux/slab.h> 1028c2ecf20Sopenharmony_ci#include <linux/types.h> 1038c2ecf20Sopenharmony_ci#include <linux/kernel.h> 1048c2ecf20Sopenharmony_ci#include <linux/sound.h> 1058c2ecf20Sopenharmony_ci#include <linux/kmod.h> 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#define SOUND_STEP 16 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistruct sound_unit 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci int unit_minor; 1128c2ecf20Sopenharmony_ci const struct file_operations *unit_fops; 1138c2ecf20Sopenharmony_ci struct sound_unit *next; 1148c2ecf20Sopenharmony_ci char name[32]; 1158c2ecf20Sopenharmony_ci}; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/* 1188c2ecf20Sopenharmony_ci * By default, OSS sound_core claims full legacy minor range (0-255) 1198c2ecf20Sopenharmony_ci * of SOUND_MAJOR to trap open attempts to any sound minor and 1208c2ecf20Sopenharmony_ci * requests modules using custom sound-slot/service-* module aliases. 1218c2ecf20Sopenharmony_ci * The only benefit of doing this is allowing use of custom module 1228c2ecf20Sopenharmony_ci * aliases instead of the standard char-major-* ones. This behavior 1238c2ecf20Sopenharmony_ci * prevents alternative OSS implementation and is scheduled to be 1248c2ecf20Sopenharmony_ci * removed. 1258c2ecf20Sopenharmony_ci * 1268c2ecf20Sopenharmony_ci * CONFIG_SOUND_OSS_CORE_PRECLAIM and soundcore.preclaim_oss kernel 1278c2ecf20Sopenharmony_ci * parameter are added to allow distros and developers to try and 1288c2ecf20Sopenharmony_ci * switch to alternative implementations without needing to rebuild 1298c2ecf20Sopenharmony_ci * the kernel in the meantime. If preclaim_oss is non-zero, the 1308c2ecf20Sopenharmony_ci * kernel will behave the same as before. All SOUND_MAJOR minors are 1318c2ecf20Sopenharmony_ci * preclaimed and the custom module aliases along with standard chrdev 1328c2ecf20Sopenharmony_ci * ones are emitted if a missing device is opened. If preclaim_oss is 1338c2ecf20Sopenharmony_ci * zero, sound_core only grabs what's actually in use and for missing 1348c2ecf20Sopenharmony_ci * devices only the standard chrdev aliases are requested. 1358c2ecf20Sopenharmony_ci * 1368c2ecf20Sopenharmony_ci * All these clutters are scheduled to be removed along with 1378c2ecf20Sopenharmony_ci * sound-slot/service-* module aliases. 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_ci#ifdef CONFIG_SOUND_OSS_CORE_PRECLAIM 1408c2ecf20Sopenharmony_cistatic int preclaim_oss = 1; 1418c2ecf20Sopenharmony_ci#else 1428c2ecf20Sopenharmony_cistatic int preclaim_oss = 0; 1438c2ecf20Sopenharmony_ci#endif 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cimodule_param(preclaim_oss, int, 0444); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int soundcore_open(struct inode *, struct file *); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic const struct file_operations soundcore_fops = 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci /* We must have an owner or the module locking fails */ 1528c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1538c2ecf20Sopenharmony_ci .open = soundcore_open, 1548c2ecf20Sopenharmony_ci .llseek = noop_llseek, 1558c2ecf20Sopenharmony_ci}; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci/* 1588c2ecf20Sopenharmony_ci * Low level list operator. Scan the ordered list, find a hole and 1598c2ecf20Sopenharmony_ci * join into it. Called with the lock asserted 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic int __sound_insert_unit(struct sound_unit * s, struct sound_unit **list, const struct file_operations *fops, int index, int low, int top) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci int n=low; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (index < 0) { /* first free */ 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci while (*list && (*list)->unit_minor<n) 1698c2ecf20Sopenharmony_ci list=&((*list)->next); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci while(n<top) 1728c2ecf20Sopenharmony_ci { 1738c2ecf20Sopenharmony_ci /* Found a hole ? */ 1748c2ecf20Sopenharmony_ci if(*list==NULL || (*list)->unit_minor>n) 1758c2ecf20Sopenharmony_ci break; 1768c2ecf20Sopenharmony_ci list=&((*list)->next); 1778c2ecf20Sopenharmony_ci n+=SOUND_STEP; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if(n>=top) 1818c2ecf20Sopenharmony_ci return -ENOENT; 1828c2ecf20Sopenharmony_ci } else { 1838c2ecf20Sopenharmony_ci n = low+(index*16); 1848c2ecf20Sopenharmony_ci while (*list) { 1858c2ecf20Sopenharmony_ci if ((*list)->unit_minor==n) 1868c2ecf20Sopenharmony_ci return -EBUSY; 1878c2ecf20Sopenharmony_ci if ((*list)->unit_minor>n) 1888c2ecf20Sopenharmony_ci break; 1898c2ecf20Sopenharmony_ci list=&((*list)->next); 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* 1948c2ecf20Sopenharmony_ci * Fill it in 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci s->unit_minor=n; 1988c2ecf20Sopenharmony_ci s->unit_fops=fops; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* 2018c2ecf20Sopenharmony_ci * Link it 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci s->next=*list; 2058c2ecf20Sopenharmony_ci *list=s; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return n; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* 2128c2ecf20Sopenharmony_ci * Remove a node from the chain. Called with the lock asserted 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic struct sound_unit *__sound_remove_unit(struct sound_unit **list, int unit) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci while(*list) 2188c2ecf20Sopenharmony_ci { 2198c2ecf20Sopenharmony_ci struct sound_unit *p=*list; 2208c2ecf20Sopenharmony_ci if(p->unit_minor==unit) 2218c2ecf20Sopenharmony_ci { 2228c2ecf20Sopenharmony_ci *list=p->next; 2238c2ecf20Sopenharmony_ci return p; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci list=&(p->next); 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci printk(KERN_ERR "Sound device %d went missing!\n", unit); 2288c2ecf20Sopenharmony_ci return NULL; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci/* 2328c2ecf20Sopenharmony_ci * This lock guards the sound loader list. 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(sound_loader_lock); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci/* 2388c2ecf20Sopenharmony_ci * Allocate the controlling structure and add it to the sound driver 2398c2ecf20Sopenharmony_ci * list. Acquires locks as needed 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_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) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct sound_unit *s = kmalloc(sizeof(*s), GFP_KERNEL); 2458c2ecf20Sopenharmony_ci int r; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (!s) 2488c2ecf20Sopenharmony_ci return -ENOMEM; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci spin_lock(&sound_loader_lock); 2518c2ecf20Sopenharmony_ciretry: 2528c2ecf20Sopenharmony_ci r = __sound_insert_unit(s, list, fops, index, low, top); 2538c2ecf20Sopenharmony_ci spin_unlock(&sound_loader_lock); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (r < 0) 2568c2ecf20Sopenharmony_ci goto fail; 2578c2ecf20Sopenharmony_ci else if (r < SOUND_STEP) 2588c2ecf20Sopenharmony_ci sprintf(s->name, "sound/%s", name); 2598c2ecf20Sopenharmony_ci else 2608c2ecf20Sopenharmony_ci sprintf(s->name, "sound/%s%d", name, r / SOUND_STEP); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (!preclaim_oss) { 2638c2ecf20Sopenharmony_ci /* 2648c2ecf20Sopenharmony_ci * Something else might have grabbed the minor. If 2658c2ecf20Sopenharmony_ci * first free slot is requested, rescan with @low set 2668c2ecf20Sopenharmony_ci * to the next unit; otherwise, -EBUSY. 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_ci r = __register_chrdev(SOUND_MAJOR, s->unit_minor, 1, s->name, 2698c2ecf20Sopenharmony_ci &soundcore_fops); 2708c2ecf20Sopenharmony_ci if (r < 0) { 2718c2ecf20Sopenharmony_ci spin_lock(&sound_loader_lock); 2728c2ecf20Sopenharmony_ci __sound_remove_unit(list, s->unit_minor); 2738c2ecf20Sopenharmony_ci if (index < 0) { 2748c2ecf20Sopenharmony_ci low = s->unit_minor + SOUND_STEP; 2758c2ecf20Sopenharmony_ci goto retry; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci spin_unlock(&sound_loader_lock); 2788c2ecf20Sopenharmony_ci r = -EBUSY; 2798c2ecf20Sopenharmony_ci goto fail; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci device_create(sound_class, dev, MKDEV(SOUND_MAJOR, s->unit_minor), 2848c2ecf20Sopenharmony_ci NULL, "%s", s->name+6); 2858c2ecf20Sopenharmony_ci return s->unit_minor; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cifail: 2888c2ecf20Sopenharmony_ci kfree(s); 2898c2ecf20Sopenharmony_ci return r; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci/* 2938c2ecf20Sopenharmony_ci * Remove a unit. Acquires locks as needed. The drivers MUST have 2948c2ecf20Sopenharmony_ci * completed the removal before their file operations become 2958c2ecf20Sopenharmony_ci * invalid. 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic void sound_remove_unit(struct sound_unit **list, int unit) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct sound_unit *p; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci spin_lock(&sound_loader_lock); 3038c2ecf20Sopenharmony_ci p = __sound_remove_unit(list, unit); 3048c2ecf20Sopenharmony_ci spin_unlock(&sound_loader_lock); 3058c2ecf20Sopenharmony_ci if (p) { 3068c2ecf20Sopenharmony_ci if (!preclaim_oss) 3078c2ecf20Sopenharmony_ci __unregister_chrdev(SOUND_MAJOR, p->unit_minor, 1, 3088c2ecf20Sopenharmony_ci p->name); 3098c2ecf20Sopenharmony_ci device_destroy(sound_class, MKDEV(SOUND_MAJOR, p->unit_minor)); 3108c2ecf20Sopenharmony_ci kfree(p); 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci/* 3158c2ecf20Sopenharmony_ci * Allocations 3168c2ecf20Sopenharmony_ci * 3178c2ecf20Sopenharmony_ci * 0 *16 Mixers 3188c2ecf20Sopenharmony_ci * 1 *8 Sequencers 3198c2ecf20Sopenharmony_ci * 2 *16 Midi 3208c2ecf20Sopenharmony_ci * 3 *16 DSP 3218c2ecf20Sopenharmony_ci * 4 *16 SunDSP 3228c2ecf20Sopenharmony_ci * 5 *16 DSP16 3238c2ecf20Sopenharmony_ci * 6 -- sndstat (obsolete) 3248c2ecf20Sopenharmony_ci * 7 *16 unused 3258c2ecf20Sopenharmony_ci * 8 -- alternate sequencer (see above) 3268c2ecf20Sopenharmony_ci * 9 *16 raw synthesizer access 3278c2ecf20Sopenharmony_ci * 10 *16 unused 3288c2ecf20Sopenharmony_ci * 11 *16 unused 3298c2ecf20Sopenharmony_ci * 12 *16 unused 3308c2ecf20Sopenharmony_ci * 13 *16 unused 3318c2ecf20Sopenharmony_ci * 14 *16 unused 3328c2ecf20Sopenharmony_ci * 15 *16 unused 3338c2ecf20Sopenharmony_ci */ 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic struct sound_unit *chains[SOUND_STEP]; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci/** 3388c2ecf20Sopenharmony_ci * register_sound_special_device - register a special sound node 3398c2ecf20Sopenharmony_ci * @fops: File operations for the driver 3408c2ecf20Sopenharmony_ci * @unit: Unit number to allocate 3418c2ecf20Sopenharmony_ci * @dev: device pointer 3428c2ecf20Sopenharmony_ci * 3438c2ecf20Sopenharmony_ci * Allocate a special sound device by minor number from the sound 3448c2ecf20Sopenharmony_ci * subsystem. 3458c2ecf20Sopenharmony_ci * 3468c2ecf20Sopenharmony_ci * Return: The allocated number is returned on success. On failure, 3478c2ecf20Sopenharmony_ci * a negative error code is returned. 3488c2ecf20Sopenharmony_ci */ 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ciint register_sound_special_device(const struct file_operations *fops, int unit, 3518c2ecf20Sopenharmony_ci struct device *dev) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci const int chain = unit % SOUND_STEP; 3548c2ecf20Sopenharmony_ci int max_unit = 256; 3558c2ecf20Sopenharmony_ci const char *name; 3568c2ecf20Sopenharmony_ci char _name[16]; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci switch (chain) { 3598c2ecf20Sopenharmony_ci case 0: 3608c2ecf20Sopenharmony_ci name = "mixer"; 3618c2ecf20Sopenharmony_ci break; 3628c2ecf20Sopenharmony_ci case 1: 3638c2ecf20Sopenharmony_ci name = "sequencer"; 3648c2ecf20Sopenharmony_ci if (unit >= SOUND_STEP) 3658c2ecf20Sopenharmony_ci goto __unknown; 3668c2ecf20Sopenharmony_ci max_unit = unit + 1; 3678c2ecf20Sopenharmony_ci break; 3688c2ecf20Sopenharmony_ci case 2: 3698c2ecf20Sopenharmony_ci name = "midi"; 3708c2ecf20Sopenharmony_ci break; 3718c2ecf20Sopenharmony_ci case 3: 3728c2ecf20Sopenharmony_ci name = "dsp"; 3738c2ecf20Sopenharmony_ci break; 3748c2ecf20Sopenharmony_ci case 4: 3758c2ecf20Sopenharmony_ci name = "audio"; 3768c2ecf20Sopenharmony_ci break; 3778c2ecf20Sopenharmony_ci case 5: 3788c2ecf20Sopenharmony_ci name = "dspW"; 3798c2ecf20Sopenharmony_ci break; 3808c2ecf20Sopenharmony_ci case 8: 3818c2ecf20Sopenharmony_ci name = "sequencer2"; 3828c2ecf20Sopenharmony_ci if (unit >= SOUND_STEP) 3838c2ecf20Sopenharmony_ci goto __unknown; 3848c2ecf20Sopenharmony_ci max_unit = unit + 1; 3858c2ecf20Sopenharmony_ci break; 3868c2ecf20Sopenharmony_ci case 9: 3878c2ecf20Sopenharmony_ci name = "dmmidi"; 3888c2ecf20Sopenharmony_ci break; 3898c2ecf20Sopenharmony_ci case 10: 3908c2ecf20Sopenharmony_ci name = "dmfm"; 3918c2ecf20Sopenharmony_ci break; 3928c2ecf20Sopenharmony_ci case 12: 3938c2ecf20Sopenharmony_ci name = "adsp"; 3948c2ecf20Sopenharmony_ci break; 3958c2ecf20Sopenharmony_ci case 13: 3968c2ecf20Sopenharmony_ci name = "amidi"; 3978c2ecf20Sopenharmony_ci break; 3988c2ecf20Sopenharmony_ci case 14: 3998c2ecf20Sopenharmony_ci name = "admmidi"; 4008c2ecf20Sopenharmony_ci break; 4018c2ecf20Sopenharmony_ci default: 4028c2ecf20Sopenharmony_ci { 4038c2ecf20Sopenharmony_ci __unknown: 4048c2ecf20Sopenharmony_ci sprintf(_name, "unknown%d", chain); 4058c2ecf20Sopenharmony_ci if (unit >= SOUND_STEP) 4068c2ecf20Sopenharmony_ci strcat(_name, "-"); 4078c2ecf20Sopenharmony_ci name = _name; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci break; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci return sound_insert_unit(&chains[chain], fops, -1, unit, max_unit, 4128c2ecf20Sopenharmony_ci name, 0600, dev); 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(register_sound_special_device); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ciint register_sound_special(const struct file_operations *fops, int unit) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci return register_sound_special_device(fops, unit, NULL); 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(register_sound_special); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci/** 4258c2ecf20Sopenharmony_ci * register_sound_mixer - register a mixer device 4268c2ecf20Sopenharmony_ci * @fops: File operations for the driver 4278c2ecf20Sopenharmony_ci * @dev: Unit number to allocate 4288c2ecf20Sopenharmony_ci * 4298c2ecf20Sopenharmony_ci * Allocate a mixer device. Unit is the number of the mixer requested. 4308c2ecf20Sopenharmony_ci * Pass -1 to request the next free mixer unit. 4318c2ecf20Sopenharmony_ci * 4328c2ecf20Sopenharmony_ci * Return: On success, the allocated number is returned. On failure, 4338c2ecf20Sopenharmony_ci * a negative error code is returned. 4348c2ecf20Sopenharmony_ci */ 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ciint register_sound_mixer(const struct file_operations *fops, int dev) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci return sound_insert_unit(&chains[0], fops, dev, 0, 128, 4398c2ecf20Sopenharmony_ci "mixer", 0600, NULL); 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ciEXPORT_SYMBOL(register_sound_mixer); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci/* 4458c2ecf20Sopenharmony_ci * DSP's are registered as a triple. Register only one and cheat 4468c2ecf20Sopenharmony_ci * in open - see below. 4478c2ecf20Sopenharmony_ci */ 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci/** 4508c2ecf20Sopenharmony_ci * register_sound_dsp - register a DSP device 4518c2ecf20Sopenharmony_ci * @fops: File operations for the driver 4528c2ecf20Sopenharmony_ci * @dev: Unit number to allocate 4538c2ecf20Sopenharmony_ci * 4548c2ecf20Sopenharmony_ci * Allocate a DSP device. Unit is the number of the DSP requested. 4558c2ecf20Sopenharmony_ci * Pass -1 to request the next free DSP unit. 4568c2ecf20Sopenharmony_ci * 4578c2ecf20Sopenharmony_ci * This function allocates both the audio and dsp device entries together 4588c2ecf20Sopenharmony_ci * and will always allocate them as a matching pair - eg dsp3/audio3 4598c2ecf20Sopenharmony_ci * 4608c2ecf20Sopenharmony_ci * Return: On success, the allocated number is returned. On failure, 4618c2ecf20Sopenharmony_ci * a negative error code is returned. 4628c2ecf20Sopenharmony_ci */ 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ciint register_sound_dsp(const struct file_operations *fops, int dev) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci return sound_insert_unit(&chains[3], fops, dev, 3, 131, 4678c2ecf20Sopenharmony_ci "dsp", 0600, NULL); 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(register_sound_dsp); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci/** 4738c2ecf20Sopenharmony_ci * unregister_sound_special - unregister a special sound device 4748c2ecf20Sopenharmony_ci * @unit: unit number to allocate 4758c2ecf20Sopenharmony_ci * 4768c2ecf20Sopenharmony_ci * Release a sound device that was allocated with 4778c2ecf20Sopenharmony_ci * register_sound_special(). The unit passed is the return value from 4788c2ecf20Sopenharmony_ci * the register function. 4798c2ecf20Sopenharmony_ci */ 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_civoid unregister_sound_special(int unit) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci sound_remove_unit(&chains[unit % SOUND_STEP], unit); 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(unregister_sound_special); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci/** 4908c2ecf20Sopenharmony_ci * unregister_sound_mixer - unregister a mixer 4918c2ecf20Sopenharmony_ci * @unit: unit number to allocate 4928c2ecf20Sopenharmony_ci * 4938c2ecf20Sopenharmony_ci * Release a sound device that was allocated with register_sound_mixer(). 4948c2ecf20Sopenharmony_ci * The unit passed is the return value from the register function. 4958c2ecf20Sopenharmony_ci */ 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_civoid unregister_sound_mixer(int unit) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci sound_remove_unit(&chains[0], unit); 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(unregister_sound_mixer); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci/** 5058c2ecf20Sopenharmony_ci * unregister_sound_dsp - unregister a DSP device 5068c2ecf20Sopenharmony_ci * @unit: unit number to allocate 5078c2ecf20Sopenharmony_ci * 5088c2ecf20Sopenharmony_ci * Release a sound device that was allocated with register_sound_dsp(). 5098c2ecf20Sopenharmony_ci * The unit passed is the return value from the register function. 5108c2ecf20Sopenharmony_ci * 5118c2ecf20Sopenharmony_ci * Both of the allocated units are released together automatically. 5128c2ecf20Sopenharmony_ci */ 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_civoid unregister_sound_dsp(int unit) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci sound_remove_unit(&chains[3], unit); 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ciEXPORT_SYMBOL(unregister_sound_dsp); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic struct sound_unit *__look_for_unit(int chain, int unit) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci struct sound_unit *s; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci s=chains[chain]; 5278c2ecf20Sopenharmony_ci while(s && s->unit_minor <= unit) 5288c2ecf20Sopenharmony_ci { 5298c2ecf20Sopenharmony_ci if(s->unit_minor==unit) 5308c2ecf20Sopenharmony_ci return s; 5318c2ecf20Sopenharmony_ci s=s->next; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci return NULL; 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic int soundcore_open(struct inode *inode, struct file *file) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci int chain; 5398c2ecf20Sopenharmony_ci int unit = iminor(inode); 5408c2ecf20Sopenharmony_ci struct sound_unit *s; 5418c2ecf20Sopenharmony_ci const struct file_operations *new_fops = NULL; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci chain=unit&0x0F; 5448c2ecf20Sopenharmony_ci if(chain==4 || chain==5) /* dsp/audio/dsp16 */ 5458c2ecf20Sopenharmony_ci { 5468c2ecf20Sopenharmony_ci unit&=0xF0; 5478c2ecf20Sopenharmony_ci unit|=3; 5488c2ecf20Sopenharmony_ci chain=3; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci spin_lock(&sound_loader_lock); 5528c2ecf20Sopenharmony_ci s = __look_for_unit(chain, unit); 5538c2ecf20Sopenharmony_ci if (s) 5548c2ecf20Sopenharmony_ci new_fops = fops_get(s->unit_fops); 5558c2ecf20Sopenharmony_ci if (preclaim_oss && !new_fops) { 5568c2ecf20Sopenharmony_ci spin_unlock(&sound_loader_lock); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci /* 5598c2ecf20Sopenharmony_ci * Please, don't change this order or code. 5608c2ecf20Sopenharmony_ci * For ALSA slot means soundcard and OSS emulation code 5618c2ecf20Sopenharmony_ci * comes as add-on modules which aren't depend on 5628c2ecf20Sopenharmony_ci * ALSA toplevel modules for soundcards, thus we need 5638c2ecf20Sopenharmony_ci * load them at first. [Jaroslav Kysela <perex@jcu.cz>] 5648c2ecf20Sopenharmony_ci */ 5658c2ecf20Sopenharmony_ci request_module("sound-slot-%i", unit>>4); 5668c2ecf20Sopenharmony_ci request_module("sound-service-%i-%i", unit>>4, chain); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci /* 5698c2ecf20Sopenharmony_ci * sound-slot/service-* module aliases are scheduled 5708c2ecf20Sopenharmony_ci * for removal in favor of the standard char-major-* 5718c2ecf20Sopenharmony_ci * module aliases. For the time being, generate both 5728c2ecf20Sopenharmony_ci * the legacy and standard module aliases to ease 5738c2ecf20Sopenharmony_ci * transition. 5748c2ecf20Sopenharmony_ci */ 5758c2ecf20Sopenharmony_ci if (request_module("char-major-%d-%d", SOUND_MAJOR, unit) > 0) 5768c2ecf20Sopenharmony_ci request_module("char-major-%d", SOUND_MAJOR); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci spin_lock(&sound_loader_lock); 5798c2ecf20Sopenharmony_ci s = __look_for_unit(chain, unit); 5808c2ecf20Sopenharmony_ci if (s) 5818c2ecf20Sopenharmony_ci new_fops = fops_get(s->unit_fops); 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci spin_unlock(&sound_loader_lock); 5848c2ecf20Sopenharmony_ci if (new_fops) { 5858c2ecf20Sopenharmony_ci /* 5868c2ecf20Sopenharmony_ci * We rely upon the fact that we can't be unloaded while the 5878c2ecf20Sopenharmony_ci * subdriver is there. 5888c2ecf20Sopenharmony_ci */ 5898c2ecf20Sopenharmony_ci int err = 0; 5908c2ecf20Sopenharmony_ci replace_fops(file, new_fops); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci if (file->f_op->open) 5938c2ecf20Sopenharmony_ci err = file->f_op->open(inode,file); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci return err; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci return -ENODEV; 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ciMODULE_ALIAS_CHARDEV_MAJOR(SOUND_MAJOR); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic void cleanup_oss_soundcore(void) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci /* We have nothing to really do here - we know the lists must be 6058c2ecf20Sopenharmony_ci empty */ 6068c2ecf20Sopenharmony_ci unregister_chrdev(SOUND_MAJOR, "sound"); 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic int __init init_oss_soundcore(void) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci if (preclaim_oss && 6128c2ecf20Sopenharmony_ci register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops) < 0) { 6138c2ecf20Sopenharmony_ci printk(KERN_ERR "soundcore: sound device already in use.\n"); 6148c2ecf20Sopenharmony_ci return -EBUSY; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci return 0; 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci#endif /* CONFIG_SOUND_OSS_CORE */ 621