📄 dmasound_core.c
字号:
/* * linux/sound/oss/dmasound/dmasound_core.c * * * OSS/Free compatible Atari TT/Falcon and Amiga DMA sound driver for * Linux/m68k * Extended to support Power Macintosh for Linux/ppc by Paul Mackerras * * (c) 1995 by Michael Schlueter & Michael Marte * * Michael Schlueter (michael@duck.syd.de) did the basic structure of the VFS * interface and the u-law to signed byte conversion. * * Michael Marte (marte@informatik.uni-muenchen.de) did the sound queue, * /dev/mixer, /dev/sndstat and complemented the VFS interface. He would like * to thank: * - Michael Schlueter for initial ideas and documentation on the MFP and * the DMA sound hardware. * - Therapy? for their CD 'Troublegum' which really made me rock. * * /dev/sndstat is based on code by Hannu Savolainen, the author of the * VoxWare family of drivers. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive * for more details. * * History: * * 1995/8/25 First release * * 1995/9/02 Roman Hodek: * - Fixed atari_stram_alloc() call, the timer * programming and several race conditions * 1995/9/14 Roman Hodek: * - After some discussion with Michael Schlueter, * revised the interrupt disabling * - Slightly speeded up U8->S8 translation by using * long operations where possible * - Added 4:3 interpolation for /dev/audio * * 1995/9/20 Torsten Scherer: * - Fixed a bug in sq_write and changed /dev/audio * converting to play at 12517Hz instead of 6258Hz. * * 1995/9/23 Torsten Scherer: * - Changed sq_interrupt() and sq_play() to pre-program * the DMA for another frame while there's still one * running. This allows the IRQ response to be * arbitrarily delayed and playing will still continue. * * 1995/10/14 Guenther Kelleter, Torsten Scherer: * - Better support for Falcon audio (the Falcon doesn't * raise an IRQ at the end of a frame, but at the * beginning instead!). uses 'if (codec_dma)' in lots * of places to simply switch between Falcon and TT * code. * * 1995/11/06 Torsten Scherer: * - Started introducing a hardware abstraction scheme * (may perhaps also serve for Amigas?) * - Can now play samples at almost all frequencies by * means of a more generalized expand routine * - Takes a good deal of care to cut data only at * sample sizes * - Buffer size is now a kernel runtime option * - Implemented fsync() & several minor improvements * Guenther Kelleter: * - Useful hints and bug fixes * - Cross-checked it for Falcons * * 1996/3/9 Geert Uytterhoeven: * - Support added for Amiga, A-law, 16-bit little * endian. * - Unification to drivers/sound/dmasound.c. * * 1996/4/6 Martin Mitchell: * - Updated to 1.3 kernel. * * 1996/6/13 Topi Kanerva: * - Fixed things that were broken (mainly the amiga * 14-bit routines) * - /dev/sndstat shows now the real hardware frequency * - The lowpass filter is disabled by default now * * 1996/9/25 Geert Uytterhoeven: * - Modularization * * 1998/6/10 Andreas Schwab: * - Converted to use sound_core * * 1999/12/28 Richard Zidlicky: * - Added support for Q40 * * 2000/2/27 Geert Uytterhoeven: * - Clean up and split the code into 4 parts: * o dmasound_core: machine-independent code * o dmasound_atari: Atari TT and Falcon support * o dmasound_awacs: Apple PowerMac support * o dmasound_paula: Amiga support * * 2000/3/25 Geert Uytterhoeven: * - Integration of dmasound_q40 * - Small clean ups * * 2001/01/26 [1.0] Iain Sandoe * - make /dev/sndstat show revision & edition info. * - since dmasound.mach.sq_setup() can fail on pmac * its type has been changed to int and the returns * are checked. * [1.1] - stop missing translations from being called. * 2001/02/08 [1.2] - remove unused translation tables & move machine- * specific tables to low-level. * - return correct info. for SNDCTL_DSP_GETFMTS. * [1.3] - implement SNDCTL_DSP_GETCAPS fully. * [1.4] - make /dev/sndstat text length usage deterministic. * - make /dev/sndstat call to low-level * dmasound.mach.state_info() pass max space to ll driver. * - tidy startup banners and output info. * [1.5] - tidy up a little (removed some unused #defines in * dmasound.h) * - fix up HAS_RECORD conditionalisation. * - add record code in places it is missing... * - change buf-sizes to bytes to allow < 1kb for pmac * if user param entry is < 256 the value is taken to * be in kb > 256 is taken to be in bytes. * - make default buff/frag params conditional on * machine to allow smaller values for pmac. * - made the ioctls, read & write comply with the OSS * rules on setting params. * - added parsing of _setup() params for record. * 2001/04/04 [1.6] - fix bug where sample rates higher than maximum were * being reported as OK. * - fix open() to return -EBUSY as per OSS doc. when * audio is in use - this is independent of O_NOBLOCK. * - fix bug where SNDCTL_DSP_POST was blocking. */ /* Record capability notes 30/01/2001: * At present these observations apply only to pmac LL driver (the only one * that can do record, at present). However, if other LL drivers for machines * with record are added they may apply. * * The fragment parameters for the record and play channels are separate. * However, if the driver is opened O_RDWR there is no way (in the current OSS * API) to specify their values independently for the record and playback * channels. Since the only common factor between the input & output is the * sample rate (on pmac) it should be possible to open /dev/dspX O_WRONLY and * /dev/dspY O_RDONLY. The input & output channels could then have different * characteristics (other than the first that sets sample rate claiming the * right to set it for ever). As it stands, the format, channels, number of * bits & sample rate are assumed to be common. In the future perhaps these * should be the responsibility of the LL driver - and then if a card really * does not share items between record & playback they can be specified * separately.*//* Thread-safeness of shared_resources notes: 31/01/2001 * If the user opens O_RDWR and then splits record & play between two threads * both of which inherit the fd - and then starts changing things from both * - we will have difficulty telling. * * It's bad application coding - but ... * TODO: think about how to sort this out... without bogging everything down in * semaphores. * * Similarly, the OSS spec says "all changes to parameters must be between * open() and the first read() or write(). - and a bit later on (by * implication) "between SNDCTL_DSP_RESET and the first read() or write() after * it". If the app is multi-threaded and this rule is broken between threads * we will have trouble spotting it - and the fault will be rather obscure :-( * * We will try and put out at least a kmsg if we see it happen... but I think * it will be quite hard to trap it with an -EXXX return... because we can't * see the fault until after the damage is done.*/#include <linux/module.h>#include <linux/slab.h>#include <linux/sound.h>#include <linux/init.h>#include <linux/soundcard.h>#include <linux/poll.h>#include <linux/smp_lock.h>#include <asm/uaccess.h>#include "dmasound.h"#define DMASOUND_CORE_REVISION 1#define DMASOUND_CORE_EDITION 6 /* * Declarations */int dmasound_catchRadius = 0;MODULE_PARM(dmasound_catchRadius, "i");static unsigned int numWriteBufs = DEFAULT_N_BUFFERS;MODULE_PARM(numWriteBufs, "i");static unsigned int writeBufSize = DEFAULT_BUFF_SIZE ; /* in bytes */MODULE_PARM(writeBufSize, "i");#ifdef HAS_RECORDstatic unsigned int numReadBufs = DEFAULT_N_BUFFERS;MODULE_PARM(numReadBufs, "i");static unsigned int readBufSize = DEFAULT_BUFF_SIZE; /* in bytes */MODULE_PARM(readBufSize, "i");#endifMODULE_LICENSE("GPL");#ifdef MODULEstatic int sq_unit = -1;static int mixer_unit = -1;static int state_unit = -1;static int irq_installed;#endif /* MODULE *//* software implemented recording volume! */uint software_input_volume = SW_INPUT_VOLUME_SCALE * SW_INPUT_VOLUME_DEFAULT;EXPORT_SYMBOL(software_input_volume);/* control over who can modify resources shared between play/record */static mode_t shared_resource_owner;static int shared_resources_initialised; /* * Mid level stuff */struct sound_settings dmasound = { .lock = SPIN_LOCK_UNLOCKED };static inline void sound_silence(void){ dmasound.mach.silence(); /* _MUST_ stop DMA */}static inline int sound_set_format(int format){ return dmasound.mach.setFormat(format);}static int sound_set_speed(int speed){ if (speed < 0) return dmasound.soft.speed; /* trap out-of-range speed settings. at present we allow (arbitrarily) low rates - using soft up-conversion - but we can't allow > max because there is no soft down-conversion. */ if (dmasound.mach.max_dsp_speed && (speed > dmasound.mach.max_dsp_speed)) speed = dmasound.mach.max_dsp_speed ; dmasound.soft.speed = speed; if (dmasound.minDev == SND_DEV_DSP) dmasound.dsp.speed = dmasound.soft.speed; return dmasound.soft.speed;}static int sound_set_stereo(int stereo){ if (stereo < 0) return dmasound.soft.stereo; stereo = !!stereo; /* should be 0 or 1 now */ dmasound.soft.stereo = stereo; if (dmasound.minDev == SND_DEV_DSP) dmasound.dsp.stereo = stereo; return stereo;}static ssize_t sound_copy_translate(TRANS *trans, const u_char __user *userPtr, size_t userCount, u_char frame[], ssize_t *frameUsed, ssize_t frameLeft){ ssize_t (*ct_func)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t); switch (dmasound.soft.format) { case AFMT_MU_LAW: ct_func = trans->ct_ulaw; break; case AFMT_A_LAW: ct_func = trans->ct_alaw; break; case AFMT_S8: ct_func = trans->ct_s8; break; case AFMT_U8: ct_func = trans->ct_u8; break; case AFMT_S16_BE: ct_func = trans->ct_s16be; break; case AFMT_U16_BE: ct_func = trans->ct_u16be; break; case AFMT_S16_LE: ct_func = trans->ct_s16le; break; case AFMT_U16_LE: ct_func = trans->ct_u16le; break; default: return 0; } /* if the user has requested a non-existent translation don't try to call it but just return 0 bytes moved */ if (ct_func) return ct_func(userPtr, userCount, frame, frameUsed, frameLeft); return 0;} /* * /dev/mixer abstraction */static struct { int busy; int modify_counter;} mixer;static int mixer_open(struct inode *inode, struct file *file){ if (!try_module_get(dmasound.mach.owner)) return -ENODEV; mixer.busy = 1; return 0;}static int mixer_release(struct inode *inode, struct file *file){ lock_kernel(); mixer.busy = 0; module_put(dmasound.mach.owner); unlock_kernel(); return 0;}static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg){ if (_SIOC_DIR(cmd) & _SIOC_WRITE) mixer.modify_counter++; switch (cmd) { case OSS_GETVERSION: return IOCTL_OUT(arg, SOUND_VERSION); case SOUND_MIXER_INFO: { mixer_info info; memset(&info, 0, sizeof(info)); strlcpy(info.id, dmasound.mach.name2, sizeof(info.id)); strlcpy(info.name, dmasound.mach.name2, sizeof(info.name)); info.modify_counter = mixer.modify_counter; if (copy_to_user((void __user *)arg, &info, sizeof(info))) return -EFAULT; return 0; } } if (dmasound.mach.mixer_ioctl) return dmasound.mach.mixer_ioctl(cmd, arg); return -EINVAL;}static struct file_operations mixer_fops ={ .owner = THIS_MODULE, .llseek = no_llseek, .ioctl = mixer_ioctl, .open = mixer_open, .release = mixer_release,};static void mixer_init(void){#ifndef MODULE int mixer_unit;#endif mixer_unit = register_sound_mixer(&mixer_fops, -1); if (mixer_unit < 0) return; mixer.busy = 0; dmasound.treble = 0; dmasound.bass = 0; if (dmasound.mach.mixer_init) dmasound.mach.mixer_init();} /* * Sound queue stuff, the heart of the driver */struct sound_queue dmasound_write_sq;static void sq_reset_output(void) ;#ifdef HAS_RECORDstruct sound_queue dmasound_read_sq;static void sq_reset_input(void) ;#endifstatic int sq_allocate_buffers(struct sound_queue *sq, int num, int size){ int i; if (sq->buffers) return 0; sq->numBufs = num; sq->bufSize = size; sq->buffers = kmalloc (num * sizeof(char *), GFP_KERNEL); if (!sq->buffers) return -ENOMEM; for (i = 0; i < num; i++) { sq->buffers[i] = dmasound.mach.dma_alloc(size, GFP_KERNEL); if (!sq->buffers[i]) { while (i--) dmasound.mach.dma_free(sq->buffers[i], size); kfree(sq->buffers); sq->buffers = NULL; return -ENOMEM; } } return 0;}static void sq_release_buffers(struct sound_queue *sq){ int i; if (sq->buffers) { for (i = 0; i < sq->numBufs; i++) dmasound.mach.dma_free(sq->buffers[i], sq->bufSize); kfree(sq->buffers); sq->buffers = NULL; }}static int sq_setup(struct sound_queue *sq){ int (*setup_func)(void) = NULL; int hard_frame ; if (sq->locked) { /* are we already set? - and not changeable */#ifdef DEBUG_DMASOUNDprintk("dmasound_core: tried to sq_setup a locked queue\n") ;#endif return -EINVAL ; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -