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

📄 dmasound_core.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
	    setup_func = dmasound.mach.write_sq_setup;	}	if (setup_func)	    return setup_func();	return 0 ;}static inline void sq_play(void){	dmasound.mach.play();}static ssize_t sq_write(struct file *file, const char __user *src, size_t uLeft,			loff_t *ppos){	ssize_t uWritten = 0;	u_char *dest;	ssize_t uUsed = 0, bUsed, bLeft;	unsigned long flags ;	/* ++TeSche: Is something like this necessary?	 * Hey, that's an honest question! Or does any other part of the	 * filesystem already checks this situation? I really don't know.	 */	if (uLeft == 0)		return 0;	/* implement any changes we have made to the soft/hard params.	   this is not satisfactory really, all we have done up to now is to	   say what we would like - there hasn't been any real checking of capability	*/	if (shared_resources_initialised == 0) {		dmasound.mach.init() ;		shared_resources_initialised = 1 ;	}	/* set up the sq if it is not already done. This may seem a dumb place	   to do it - but it is what OSS requires.  It means that write() can	   return memory allocation errors.  To avoid this possibility use the	   GETBLKSIZE or GETOSPACE ioctls (after you've fiddled with all the	   params you want to change) - these ioctls also force the setup.	*/	if (write_sq.locked == 0) {		if ((uWritten = sq_setup(&write_sq)) < 0) return uWritten ;		uWritten = 0 ;	}/* FIXME: I think that this may be the wrong behaviour when we get strapped	for time and the cpu is close to being (or actually) behind in sending data.	- because we've lost the time that the N samples, already in the buffer,	would have given us to get here with the next lot from the user.*/	/* The interrupt doesn't start to play the last, incomplete frame.	 * Thus we can append to it without disabling the interrupts! (Note	 * also that write_sq.rear isn't affected by the interrupt.)	 */	/* as of 1.6 this behaviour changes if SNDCTL_DSP_POST has been issued:	   this will mimic the behaviour of syncing and allow the sq_play() to	   queue a partial fragment.  Since sq_play() may/will be called from	   the IRQ handler - at least on Pmac we have to deal with it.	   The strategy - possibly not optimum - is to kill _POST status if we	   get here.  This seems, at least, reasonable - in the sense that POST	   is supposed to indicate that we might not write before the queue	   is drained - and if we get here in time then it does not apply.	*/	spin_lock_irqsave(&dmasound.lock, flags);	write_sq.syncing &= ~2 ; /* take out POST status */	spin_unlock_irqrestore(&dmasound.lock, flags);	if (write_sq.count > 0 &&	    (bLeft = write_sq.block_size-write_sq.rear_size) > 0) {		dest = write_sq.buffers[write_sq.rear];		bUsed = write_sq.rear_size;		uUsed = sound_copy_translate(dmasound.trans_write, src, uLeft,					     dest, &bUsed, bLeft);		if (uUsed <= 0)			return uUsed;		src += uUsed;		uWritten += uUsed;		uLeft = (uUsed <= uLeft) ? (uLeft - uUsed) : 0 ; /* paranoia */		write_sq.rear_size = bUsed;	}	while (uLeft) {		while (write_sq.count >= write_sq.max_active) {			sq_play();			if (write_sq.open_mode & O_NONBLOCK)				return uWritten > 0 ? uWritten : -EAGAIN;			SLEEP(write_sq.action_queue);			if (signal_pending(current))				return uWritten > 0 ? uWritten : -EINTR;		}		/* Here, we can avoid disabling the interrupt by first		 * copying and translating the data, and then updating		 * the write_sq variables. Until this is done, the interrupt		 * won't see the new frame and we can work on it		 * undisturbed.		 */		dest = write_sq.buffers[(write_sq.rear+1) % write_sq.max_count];		bUsed = 0;		bLeft = write_sq.block_size;		uUsed = sound_copy_translate(dmasound.trans_write, src, uLeft,					     dest, &bUsed, bLeft);		if (uUsed <= 0)			break;		src += uUsed;		uWritten += uUsed;		uLeft = (uUsed <= uLeft) ? (uLeft - uUsed) : 0 ; /* paranoia */		if (bUsed) {			write_sq.rear = (write_sq.rear+1) % write_sq.max_count;			write_sq.rear_size = bUsed;			write_sq.count++;		}	} /* uUsed may have been 0 */	sq_play();	return uUsed < 0? uUsed: uWritten;}static unsigned int sq_poll(struct file *file, struct poll_table_struct *wait){	unsigned int mask = 0;	int retVal;		if (write_sq.locked == 0) {		if ((retVal = sq_setup(&write_sq)) < 0)			return retVal;		return 0;	}	if (file->f_mode & FMODE_WRITE )		poll_wait(file, &write_sq.action_queue, wait);	if (file->f_mode & FMODE_WRITE)		if (write_sq.count < write_sq.max_active || write_sq.block_size - write_sq.rear_size > 0)			mask |= POLLOUT | POLLWRNORM;	return mask;}static inline void sq_init_waitqueue(struct sound_queue *sq){	init_waitqueue_head(&sq->action_queue);	init_waitqueue_head(&sq->open_queue);	init_waitqueue_head(&sq->sync_queue);	sq->busy = 0;}#if 0 /* blocking open() */static inline void sq_wake_up(struct sound_queue *sq, struct file *file,			      mode_t mode){	if (file->f_mode & mode) {		sq->busy = 0; /* CHECK: IS THIS OK??? */		WAKE_UP(sq->open_queue);	}}#endifstatic int sq_open2(struct sound_queue *sq, struct file *file, mode_t mode,		    int numbufs, int bufsize){	int rc = 0;	if (file->f_mode & mode) {		if (sq->busy) {#if 0 /* blocking open() */			rc = -EBUSY;			if (file->f_flags & O_NONBLOCK)				return rc;			rc = -EINTR;			while (sq->busy) {				SLEEP(sq->open_queue);				if (signal_pending(current))					return rc;			}			rc = 0;#else			/* OSS manual says we will return EBUSY regardless			   of O_NOBLOCK.			*/			return -EBUSY ;#endif		}		sq->busy = 1; /* Let's play spot-the-race-condition */		/* allocate the default number & size of buffers.		   (i.e. specified in _setup() or as module params)		   can't be changed at the moment - but _could_ be perhaps		   in the setfragments ioctl.		*/		if (( rc = sq_allocate_buffers(sq, numbufs, bufsize))) {#if 0 /* blocking open() */			sq_wake_up(sq, file, mode);#else			sq->busy = 0 ;#endif			return rc;		}		sq->open_mode = file->f_mode;	}	return rc;}#define write_sq_init_waitqueue()	sq_init_waitqueue(&write_sq)#if 0 /* blocking open() */#define write_sq_wake_up(file)		sq_wake_up(&write_sq, file, FMODE_WRITE)#endif#define write_sq_release_buffers()	sq_release_buffers(&write_sq)#define write_sq_open(file)	\	sq_open2(&write_sq, file, FMODE_WRITE, numWriteBufs, writeBufSize )static int sq_open(struct inode *inode, struct file *file){	int rc;	if (!try_module_get(dmasound.mach.owner))		return -ENODEV;	rc = write_sq_open(file); /* checks the f_mode */	if (rc)		goto out;	if (file->f_mode & FMODE_READ) {		/* TODO: if O_RDWR, release any resources grabbed by write part */		rc = -ENXIO ; /* I think this is what is required by open(2) */		goto out;	}	if (dmasound.mach.sq_open)	    dmasound.mach.sq_open(file->f_mode);	/* CHECK whether this is sensible - in the case that dsp0 could be opened	  O_RDONLY and dsp1 could be opened O_WRONLY	*/	dmasound.minDev = iminor(inode) & 0x0f;	/* OK. - we should make some attempt at consistency. At least the H'ware	   options should be set with a valid mode.  We will make it that the LL	   driver must supply defaults for hard & soft params.	*/	if (shared_resource_owner == 0) {		/* you can make this AFMT_U8/mono/8K if you want to mimic old		   OSS behaviour - while we still have soft translations ;-) */		dmasound.soft = dmasound.mach.default_soft ;		dmasound.dsp = dmasound.mach.default_soft ;		dmasound.hard = dmasound.mach.default_hard ;	}#ifndef DMASOUND_STRICT_OSS_COMPLIANCE	/* none of the current LL drivers can actually do this "native" at the moment	   OSS does not really require us to supply /dev/audio if we can't do it.	*/	if (dmasound.minDev == SND_DEV_AUDIO) {		sound_set_speed(8000);		sound_set_stereo(0);		sound_set_format(AFMT_MU_LAW);	}#endif	return 0; out:	module_put(dmasound.mach.owner);	return rc;}static void sq_reset_output(void){	sound_silence(); /* this _must_ stop DMA, we might be about to lose the buffers */	write_sq.active = 0;	write_sq.count = 0;	write_sq.rear_size = 0;	/* write_sq.front = (write_sq.rear+1) % write_sq.max_count;*/	write_sq.front = 0 ;	write_sq.rear = -1 ; /* same as for set-up */	/* OK - we can unlock the parameters and fragment settings */	write_sq.locked = 0 ;	write_sq.user_frags = 0 ;	write_sq.user_frag_size = 0 ;}static void sq_reset(void){	sq_reset_output() ;	/* we could consider resetting the shared_resources_owner here... but I	   think it is probably still rather non-obvious to application writer	*/	/* we release everything else though */	shared_resources_initialised = 0 ;}static int sq_fsync(struct file *filp, struct dentry *dentry){	int rc = 0;	int timeout = 5;	write_sq.syncing |= 1;	sq_play();	/* there may be an incomplete frame waiting */	while (write_sq.active) {		SLEEP(write_sq.sync_queue);		if (signal_pending(current)) {			/* While waiting for audio output to drain, an			 * interrupt occurred.  Stop audio output immediately			 * and clear the queue. */			sq_reset_output();			rc = -EINTR;			break;		}		if (!--timeout) {			printk(KERN_WARNING "dmasound: Timeout draining output\n");			sq_reset_output();			rc = -EIO;			break;		}	}	/* flag no sync regardless of whether we had a DSP_POST or not */	write_sq.syncing = 0 ;	return rc;}static int sq_release(struct inode *inode, struct file *file){	int rc = 0;	lock_kernel();	if (file->f_mode & FMODE_WRITE) {		if (write_sq.busy)			rc = sq_fsync(file, file->f_path.dentry);		sq_reset_output() ; /* make sure dma is stopped and all is quiet */		write_sq_release_buffers();		write_sq.busy = 0;	}	if (file->f_mode & shared_resource_owner) { /* it's us that has them */		shared_resource_owner = 0 ;		shared_resources_initialised = 0 ;		dmasound.hard = dmasound.mach.default_hard ;	}	module_put(dmasound.mach.owner);#if 0 /* blocking open() */	/* Wake up a process waiting for the queue being released.	 * Note: There may be several processes waiting for a call	 * to open() returning. */	/* Iain: hmm I don't understand this next comment ... */	/* There is probably a DOS atack here. They change the mode flag. */	/* XXX add check here,*/	read_sq_wake_up(file); /* checks f_mode */	write_sq_wake_up(file); /* checks f_mode */#endif /* blocking open() */	unlock_kernel();	return rc;}/* here we see if we have a right to modify format, channels, size and so on   if no-one else has claimed it already then we do...   TODO: We might change this to mask O_RDWR such that only one or the other channel   is the owner - if we have problems.*/static int shared_resources_are_mine(mode_t md){	if (shared_resource_owner)		return (shared_resource_owner & md ) ;	else {		shared_resource_owner = md ;		return 1 ;	}}/* if either queue is locked we must deny the right to change shared params*/static int queues_are_quiescent(void){	if (write_sq.locked)		return 0 ;	return 1 ;}/* check and set a queue's fragments per user's wishes...   we will check against the pre-defined literals and the actual sizes.   This is a bit fraught - because soft translations can mess with our   buffer requirements *after* this call - OSS says "call setfrags first"*//* It is possible to replace all the -EINVAL returns with an override that   just puts the allowable value in.  This may be what many OSS apps require*/static int set_queue_frags(struct sound_queue *sq, int bufs, int size){	if (sq->locked) {#ifdef DEBUG_DMASOUNDprintk("dmasound_core: tried to set_queue_frags on a locked queue\n") ;#endif		return -EINVAL ;	}	if ((size < MIN_FRAG_SIZE) || (size > MAX_FRAG_SIZE))		return -EINVAL ;	size = (1<<size) ; /* now in bytes */	if (size > sq->bufSize)		return -EINVAL ; /* this might still not work */	if (bufs <= 0)		return -EINVAL ;	if (bufs > sq->numBufs) /* the user is allowed say "don't care" with 0x7fff */		bufs = sq->numBufs ;	/* there is, currently, no way to specify max_active separately	   from max_count.  This could be a LL driver issue - I guess	   if there is a requirement for these values to be different then	  we will have to pass that info. up to this level.	*/	sq->user_frags =	sq->max_active = bufs ;	sq->user_frag_size = size ;	return 0 ;}static int sq_ioctl(struct inode *inode, struct file *file, u_int cmd,		    u_long arg){	int val, result;	u_long fmt;	int data;	int size, nbufs;	audio_buf_info info;	switch (cmd) {	case SNDCTL_DSP_RESET:		sq_reset();		return 0;		break ;	case SNDCTL_DSP_GETFMTS:		fmt = dmasound.mach.hardware_afmts ; /* this is what OSS says.. */		return IOCTL_OUT(arg, fmt);		break ;	case SNDCTL_DSP_GETBLKSIZE:		/* this should tell the caller about bytes that the app can		   read/write - the app doesn't care about our internal buffers.		   We force sq_setup() here as per OSS 1.1 (which should		   compute the values necessary).		   Since there is no mechanism to specify read/write separately, for		   fds opened O_RDWR, the write_sq values will, arbitrarily, overwrite		   the read_sq ones.		*/		size = 0 ;		if (file->f_mode & FMODE_WRITE) {			if ( !write_sq.locked )				sq_setup(&write_sq) ;			size = write_sq.user_frag_size ;		}		return IOCTL_OUT(arg, size);		break ;	case SNDCTL_DSP_POST:		/* all we are going to do is to tell the LL that any		   partial frags can be queued for output.		   The LL will have to clear this flag when last output		   is queued.		*/		write_sq.syncing |= 0x2 ;		sq_play() ;		return 0 ;	case SNDCTL_DSP_SYNC:		/* This call, effectively, has the same behaviour as SNDCTL_DSP_RESET		   except that it waits for output to finish before resetting		   everything - read, however, is killed imediately.		*/		result = 0 ;		if (file->f_mode & FMODE_WRITE) {			result = sq_fsync(file, file->f_path.dentry);			sq_reset_output() ;		}		/* if we are the shared resource owner then release them */		if (file->f_mode & shared_resource_owner)			shared_resources_initialised = 0 ;		return result ;		break ;	case SOUND_PCM_READ_RATE:		return IOCTL_OUT(arg, dmasound.soft.speed);	case SNDCTL_DSP_SPEED:		/* changing this on the fly will have weird effects on the sound.		   Where there are rate conversions implemented in soft form - it		   will cause the _ctx_xxx() functions to be substituted.		   However, there doesn't appear to be any reason to dis-allow it from		   a driver pov.		*/		if (shared_resources_are_mine(file->f_mode)) {			IOCTL_IN(arg, data);			data = sound_set_speed(data) ;			shared_resources_initialised = 0 ;			return IOCTL_OUT(arg, data);		} else			return -EINVAL ;		break ;	/* OSS says these next 4 actions are undefined when the device is	   busy/active - we will just return -EINVAL.	   To be allowed to change one - (a) you have to own the right

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -