⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dmasound_core.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *  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 + -