📄 soundcard.c
字号:
/* * linux/drivers/sound/soundcard.c * * Sound card driver for Linux *//* * Copyright (C) by Hannu Savolainen 1993-1997 * * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) * Version 2 (June 1991). See the "COPYING" file distributed with this software * for more info. *//* * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) * integrated sound_switch.c * Stefan Reinauer : integrated /proc/sound (equals to /dev/sndstat, * which should disappear in the near future) * * Rob Riggs Added persistent DMA buffers support (1998/10/17) */#include <linux/config.h>#include "sound_config.h"#include <linux/types.h>#include <linux/errno.h>#include <linux/signal.h>#include <linux/fcntl.h>#include <linux/ctype.h>#include <linux/stddef.h>#include <linux/kmod.h>#ifdef __KERNEL__#include <asm/dma.h>#include <asm/io.h>#include <asm/segment.h>#include <linux/wait.h>#include <linux/malloc.h>#include <linux/ioport.h>#endif /* __KERNEL__ */#include <linux/delay.h>#include <linux/proc_fs.h>#include "soundmodule.h"struct notifier_block *sound_locker=(struct notifier_block *)0;static int lock_depth = 0;#ifdef MODULE#define modular 1#else#define modular 0#endif/* * This ought to be moved into include/asm/dma.h */#ifndef valid_dma#define valid_dma(n) ((n) >= 0 && (n) < MAX_DMA_CHANNELS && (n) != 4)#endifstatic int chrdev_registered = 0;static int is_unloading = 0;/* * Table for permanently allocated memory (used when unloading the module) */caddr_t sound_mem_blocks[1024];int sound_nblocks = 0;/* Persistent DMA buffers */#ifdef CONFIG_SOUND_DMAPint sound_dmap_flag = 1;#elseint sound_dmap_flag = 0;#endifstatic int soundcard_configured = 0;static char dma_alloc_map[MAX_DMA_CHANNELS] ={0};#define DMA_MAP_UNAVAIL 0#define DMA_MAP_FREE 1#define DMA_MAP_BUSY 2static int in_use = 0; /* Total # of open devices */unsigned long seq_time = 0; /* Time for /dev/sequencer *//* * Table for configurable mixer volume handling */static mixer_vol_table mixer_vols[MAX_MIXER_DEV];static int num_mixer_volumes = 0;int *load_mixer_volumes(char *name, int *levels, int present){ int i, n; for (i = 0; i < num_mixer_volumes; i++) { if (strcmp(name, mixer_vols[i].name) == 0) { if (present) mixer_vols[i].num = i; return mixer_vols[i].levels; } } if (num_mixer_volumes >= MAX_MIXER_DEV) { printk(KERN_ERR "Sound: Too many mixers (%s)\n", name); return levels; } n = num_mixer_volumes++; strcpy(mixer_vols[n].name, name); if (present) mixer_vols[n].num = n; else mixer_vols[n].num = -1; for (i = 0; i < 32; i++) mixer_vols[n].levels[i] = levels[i]; return mixer_vols[n].levels;}static int set_mixer_levels(caddr_t arg){ /* mixer_vol_table is 174 bytes, so IMHO no reason to not allocate it on the stack */ mixer_vol_table buf; if (__copy_from_user(&buf, arg, sizeof(buf))) return -EFAULT; load_mixer_volumes(buf.name, buf.levels, 0); if (__copy_to_user(arg, &buf, sizeof(buf))) return -EFAULT; return 0;}static int get_mixer_levels(caddr_t arg){ int n; if (__get_user(n, (int *)(&(((mixer_vol_table *)arg)->num)))) return -EFAULT; if (n < 0 || n >= num_mixer_volumes) return -EINVAL; if (__copy_to_user(arg, &mixer_vols[n], sizeof(mixer_vol_table))) return -EFAULT; return 0;}static int sound_proc_get_info(char *buffer, char **start, off_t offset, int length, int inout){ int len, i, drv; off_t pos = 0; off_t begin = 0;#ifdef MODULE#define MODULEPROCSTRING "Driver loaded as a module"#else#define MODULEPROCSTRING "Driver compiled into kernel"#endif#ifdef OSKIT len = sprintf(buffer, "OSS/Free:" SOUND_VERSION_STRING "\n" "Load type: " MODULEPROCSTRING "\n" "Config options: %x\n\nInstalled drivers: \n", SELECTED_SOUND_OPTIONS);#else down(&uts_sem); len = sprintf(buffer, "OSS/Free:" SOUND_VERSION_STRING "\n" "Load type: " MODULEPROCSTRING "\n" "Kernel: %s %s %s %s %s\n" "Config options: %x\n\nInstalled drivers: \n", system_utsname.sysname, system_utsname.nodename, system_utsname.release, system_utsname.version, system_utsname.machine, SELECTED_SOUND_OPTIONS); up(&uts_sem);#endif for (i = 0; (i < num_sound_drivers) && (pos <= offset + length); i++) { if (!sound_drivers[i].card_type) continue; len += sprintf(buffer + len, "Type %d: %s\n", sound_drivers[i].card_type, sound_drivers[i].name); pos = begin + len; if (pos < offset) { len = 0; begin = pos; } } len += sprintf(buffer + len, "\nCard config: \n"); for (i = 0; (i < num_sound_cards) && (pos <= offset + length); i++) { if (!snd_installed_cards[i].card_type) continue; if (!snd_installed_cards[i].enabled) len += sprintf(buffer + len, "("); if ((drv = snd_find_driver(snd_installed_cards[i].card_type)) != -1) len += sprintf(buffer + len, "%s", sound_drivers[drv].name); if (snd_installed_cards[i].config.io_base) len += sprintf(buffer + len, " at 0x%x", snd_installed_cards[i].config.io_base); if (snd_installed_cards[i].config.irq != 0) len += sprintf(buffer + len, " irq %d", abs(snd_installed_cards[i].config.irq)); if (snd_installed_cards[i].config.dma != -1) { len += sprintf(buffer + len, " drq %d", snd_installed_cards[i].config.dma); if (snd_installed_cards[i].config.dma2 != -1) len += sprintf(buffer + len, ",%d", snd_installed_cards[i].config.dma2); } if (!snd_installed_cards[i].enabled) len += sprintf(buffer + len, ")"); len += sprintf(buffer + len, "\n"); pos = begin + len; if (pos < offset) { len = 0; begin = pos; } } if (!sound_started) len += sprintf(buffer + len, "\n\n***** Sound driver not started *****\n\n");#ifndef CONFIG_AUDIO len += sprintf(buffer + len, "\nAudio devices: NOT ENABLED IN CONFIG\n");#else len += sprintf(buffer + len, "\nAudio devices:\n"); for (i = 0; (i < num_audiodevs) && (pos <= offset + length); i++) { if (audio_devs[i] == NULL) continue; len += sprintf(buffer + len, "%d: %s%s\n", i, audio_devs[i]->name, audio_devs[i]->flags & DMA_DUPLEX ? " (DUPLEX)" : ""); pos = begin + len; if (pos < offset) { len = 0; begin = pos; } }#endif#ifndef CONFIG_SEQUENCER len += sprintf(buffer + len, "\nSynth devices: NOT ENABLED IN CONFIG\n");#else len += sprintf(buffer + len, "\nSynth devices:\n"); for (i = 0; (i < num_synths) && (pos <= offset + length); i++) { if (synth_devs[i] == NULL) continue; len += sprintf(buffer + len, "%d: %s\n", i, synth_devs[i]->info->name); pos = begin + len; if (pos < offset) { len = 0; begin = pos; } }#endif#ifndef CONFIG_MIDI len += sprintf(buffer + len, "\nMidi devices: NOT ENABLED IN CONFIG\n");#else len += sprintf(buffer + len, "\nMidi devices:\n"); for (i = 0; (i < num_midis) && (pos <= offset + length); i++) { if (midi_devs[i] == NULL) continue; len += sprintf(buffer + len, "%d: %s\n", i, midi_devs[i]->info.name); pos = begin + len; if (pos < offset) { len = 0; begin = pos; } }#endif#ifdef CONFIG_SEQUENCER len += sprintf(buffer + len, "\nTimers:\n"); for (i = 0; (i < num_sound_timers) && (pos <= offset + length); i++) { if (sound_timer_devs[i] == NULL) continue; len += sprintf(buffer + len, "%d: %s\n", i, sound_timer_devs[i]->info.name); pos = begin + len; if (pos < offset) { len = 0; begin = pos; } }#endif len += sprintf(buffer + len, "\nMixers:\n"); for (i = 0; (i < num_mixers) && (pos <= offset + length); i++) { if (mixer_devs[i] == NULL) continue; len += sprintf(buffer + len, "%d: %s\n", i, mixer_devs[i]->name); pos = begin + len; if (pos < offset) { len = 0; begin = pos; } } *start = buffer + (offset - begin); len -= (offset - begin); if (len > length) len = length; return len;}#ifdef CONFIG_PROC_FSstatic struct proc_dir_entry proc_root_sound = { PROC_SOUND, 5, "sound", S_IFREG | S_IRUGO, 1, 0, 0, 0, NULL, sound_proc_get_info};#endif#ifndef MIN#define MIN(a,b) (((a) < (b)) ? (a) : (b))#endif/* 4K page size but our output routines use some slack for overruns */#define PROC_BLOCK_SIZE (3*1024)/* * basically copied from fs/proc/generic.c:proc_file_read * should be removed sometime in the future together with /dev/sndstat * (a symlink /dev/sndstat -> /proc/sound will do as well) */static ssize_t sndstat_file_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos){ char *page; ssize_t retval=0; int eof=0; ssize_t n, count; char *start; if (!(page = (char*) __get_free_page(GFP_KERNEL))) return -ENOMEM; while ((nbytes > 0) && !eof) { count = MIN(PROC_BLOCK_SIZE, nbytes); start = NULL; n = sound_proc_get_info(page, &start, *ppos, count, 0); if (n < count) eof = 1; if (!start) { /* * For proc files that are less than 4k */ start = page + *ppos; n -= *ppos; if (n <= 0) break; if (n > count) n = count; } if (n == 0) break; /* End of file */ if (n < 0) { if (retval == 0) retval = n; break; } n -= copy_to_user(buf, start, n); /* BUG ??? */ if (n == 0) { if (retval == 0) retval = -EFAULT; break; } *ppos += n; /* Move down the file */ nbytes -= n; buf += n; retval += n; } free_page((unsigned long) page); return retval;}static ssize_t sound_read(struct file *file, char *buf, size_t count, loff_t *ppos){ int dev = MINOR(file->f_dentry->d_inode->i_rdev); DEB(printk("sound_read(dev=%d, count=%d)\n", dev, count)); switch (dev & 0x0f) { case SND_DEV_STATUS: return sndstat_file_read(file, buf, count, ppos);#ifdef CONFIG_AUDIO case SND_DEV_DSP: case SND_DEV_DSP16: case SND_DEV_AUDIO: return audio_read(dev, file, buf, count);#endif#ifdef CONFIG_SEQUENCER case SND_DEV_SEQ: case SND_DEV_SEQ2: return sequencer_read(dev, file, buf, count);#endif#ifdef CONFIG_MIDI case SND_DEV_MIDIN: return MIDIbuf_read(dev, file, buf, count);#endif default:; } return -EINVAL;}static ssize_t sound_write(struct file *file, const char *buf, size_t count, loff_t *ppos){ int dev = MINOR(file->f_dentry->d_inode->i_rdev); DEB(printk("sound_write(dev=%d, count=%d)\n", dev, count)); switch (dev & 0x0f) {#ifdef CONFIG_SEQUENCER case SND_DEV_SEQ: case SND_DEV_SEQ2: return sequencer_write(dev, file, buf, count);#endif#ifdef CONFIG_AUDIO case SND_DEV_DSP: case SND_DEV_DSP16: case SND_DEV_AUDIO: return audio_write(dev, file, buf, count);#endif#ifdef CONFIG_MIDI case SND_DEV_MIDIN: return MIDIbuf_write(dev, file, buf, count);#endif } return -EINVAL;}static long long sound_lseek(struct file *file, long long offset, int orig){ return -ESPIPE;}static int sound_open(struct inode *inode, struct file *file){ int dev, retval; if (is_unloading) { /* printk(KERN_ERR "Sound: Driver partially removed. Can't open device\n");*/ return -EBUSY; } dev = MINOR(inode->i_rdev); if (!soundcard_configured && dev != SND_DEV_CTL && dev != SND_DEV_STATUS) { /* printk("SoundCard Error: The sound system has not been configured\n");*/ return -ENXIO; } DEB(printk("sound_open(dev=%d)\n", dev)); if ((dev >= SND_NDEVS) || (dev < 0)) { /* printk(KERN_ERR "Invalid minor device %d\n", dev);*/ return -ENXIO; } switch (dev & 0x0f) { case SND_DEV_STATUS: break; case SND_DEV_CTL: dev >>= 4; if (dev >= 0 && dev < MAX_MIXER_DEV && mixer_devs[dev] == NULL) { char modname[20]; sprintf(modname, "mixer%d", dev); request_module(modname); } if (dev && (dev >= num_mixers || mixer_devs[dev] == NULL)) return -ENXIO; break;#ifdef CONFIG_SEQUENCER case SND_DEV_SEQ: case SND_DEV_SEQ2: if ((retval = sequencer_open(dev, file)) < 0) return retval; break;#endif#ifdef CONFIG_MIDI case SND_DEV_MIDIN: if ((retval = MIDIbuf_open(dev, file)) < 0) return retval; break;#endif#ifdef CONFIG_AUDIO case SND_DEV_DSP: case SND_DEV_DSP16: case SND_DEV_AUDIO: if ((retval = audio_open(dev, file)) < 0) return retval; break;#endif default: printk(KERN_ERR "Invalid minor device %d\n", dev); return -ENXIO; } in_use++;#ifdef CONFIG_MODULES notifier_call_chain(&sound_locker, 1, 0); lock_depth++;#endif return 0;}static int sound_release(struct inode *inode, struct file *file){ int dev = MINOR(inode->i_rdev); DEB(printk("sound_release(dev=%d)\n", dev)); switch (dev & 0x0f) { case SND_DEV_STATUS: case SND_DEV_CTL: break; #ifdef CONFIG_SEQUENCER case SND_DEV_SEQ: case SND_DEV_SEQ2: sequencer_release(dev, file); break;#endif#ifdef CONFIG_MIDI case SND_DEV_MIDIN: MIDIbuf_release(dev, file); break;#endif#ifdef CONFIG_AUDIO case SND_DEV_DSP: case SND_DEV_DSP16: case SND_DEV_AUDIO: audio_release(dev, file); break;#endif default: printk(KERN_ERR "Sound error: Releasing unknown device 0x%02x\n", dev); } in_use--;#ifdef CONFIG_MODULES notifier_call_chain(&sound_locker, 0, 0); lock_depth--;#endif return 0;}static int get_mixer_info(int dev, caddr_t arg){ mixer_info info; strncpy(info.id, mixer_devs[dev]->id, sizeof(info.id));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -