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

📄 saa7134-oss.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * * device driver for philips saa7134 based TV cards * oss dsp interface * * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] *     2005 conversion to standalone module: *         Ricardo Cerqueira <v4l@cerqueira.org> * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License as published by *  the Free Software Foundation; either version 2 of the License, or *  (at your option) any later version. * *  This program is distributed in the hope that it will be useful, *  but WITHOUT ANY WARRANTY; without even the implied warranty of *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *  GNU General Public License for more details. * *  You should have received a copy of the GNU General Public License *  along with this program; if not, write to the Free Software *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/init.h>#include <linux/list.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/kernel.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/sound.h>#include <linux/soundcard.h>#include "saa7134-reg.h"#include "saa7134.h"/* ------------------------------------------------------------------ */static unsigned int debug  = 0;module_param(debug, int, 0644);MODULE_PARM_DESC(debug,"enable debug messages [oss]");static unsigned int rate  = 0;module_param(rate, int, 0444);MODULE_PARM_DESC(rate,"sample rate (valid are: 32000,48000)");static unsigned int dsp_nr[]   = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };MODULE_PARM_DESC(dsp_nr, "device numbers for SAA7134 capture interface(s).");module_param_array(dsp_nr,   int, NULL, 0444);static unsigned int mixer_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };MODULE_PARM_DESC(mixer_nr, "mixer numbers for SAA7134 capture interface(s).");module_param_array(mixer_nr, int, NULL, 0444);#define dprintk(fmt, arg...)	if (debug) \	printk(KERN_DEBUG "%s/oss: " fmt, dev->name , ## arg)/* ------------------------------------------------------------------ */static int dsp_buffer_conf(struct saa7134_dev *dev, int blksize, int blocks){	if (blksize < 0x100)		blksize = 0x100;	if (blksize > 0x10000)		blksize = 0x10000;	if (blocks < 2)		blocks = 2;	if ((blksize * blocks) > 1024*1024)		blocks = 1024*1024 / blksize;	dev->dmasound.blocks  = blocks;	dev->dmasound.blksize = blksize;	dev->dmasound.bufsize = blksize * blocks;	dprintk("buffer config: %d blocks / %d bytes, %d kB total\n",		blocks,blksize,blksize * blocks / 1024);	return 0;}static int dsp_buffer_init(struct saa7134_dev *dev){	int err;	if (!dev->dmasound.bufsize)		BUG();	videobuf_dma_init(&dev->dmasound.dma);	err = videobuf_dma_init_kernel(&dev->dmasound.dma, PCI_DMA_FROMDEVICE,				       (dev->dmasound.bufsize + PAGE_SIZE) >> PAGE_SHIFT);	if (0 != err)		return err;	return 0;}static int dsp_buffer_free(struct saa7134_dev *dev){	if (!dev->dmasound.blksize)		BUG();	videobuf_dma_free(&dev->dmasound.dma);	dev->dmasound.blocks  = 0;	dev->dmasound.blksize = 0;	dev->dmasound.bufsize = 0;	return 0;}static void dsp_dma_start(struct saa7134_dev *dev){	dev->dmasound.dma_blk     = 0;	dev->dmasound.dma_running = 1;	saa7134_set_dmabits(dev);}static void dsp_dma_stop(struct saa7134_dev *dev){	dev->dmasound.dma_blk     = -1;	dev->dmasound.dma_running = 0;	saa7134_set_dmabits(dev);}static int dsp_rec_start(struct saa7134_dev *dev){	int err, bswap, sign;	u32 fmt, control;	unsigned long flags;	/* prepare buffer */	if (0 != (err = videobuf_dma_pci_map(dev->pci,&dev->dmasound.dma)))		return err;	if (0 != (err = saa7134_pgtable_alloc(dev->pci,&dev->dmasound.pt)))		goto fail1;	if (0 != (err = saa7134_pgtable_build(dev->pci,&dev->dmasound.pt,					      dev->dmasound.dma.sglist,					      dev->dmasound.dma.sglen,					      0)))		goto fail2;	/* sample format */	switch (dev->dmasound.afmt) {	case AFMT_U8:	case AFMT_S8:     fmt = 0x00;  break;	case AFMT_U16_LE:	case AFMT_U16_BE:	case AFMT_S16_LE:	case AFMT_S16_BE: fmt = 0x01;  break;	default:		err = -EINVAL;		goto fail2;	}	switch (dev->dmasound.afmt) {	case AFMT_S8:	case AFMT_S16_LE:	case AFMT_S16_BE: sign = 1; break;	default:          sign = 0; break;	}	switch (dev->dmasound.afmt) {	case AFMT_U16_BE:	case AFMT_S16_BE: bswap = 1; break;	default:          bswap = 0; break;	}	switch (dev->pci->device) {	case PCI_DEVICE_ID_PHILIPS_SAA7134:		if (1 == dev->dmasound.channels)			fmt |= (1 << 3);		if (2 == dev->dmasound.channels)			fmt |= (3 << 3);		if (sign)			fmt |= 0x04;		fmt |= (TV == dev->dmasound.input) ? 0xc0 : 0x80;		saa_writeb(SAA7134_NUM_SAMPLES0, ((dev->dmasound.blksize - 1) & 0x0000ff));		saa_writeb(SAA7134_NUM_SAMPLES1, ((dev->dmasound.blksize - 1) & 0x00ff00) >>  8);		saa_writeb(SAA7134_NUM_SAMPLES2, ((dev->dmasound.blksize - 1) & 0xff0000) >> 16);		saa_writeb(SAA7134_AUDIO_FORMAT_CTRL, fmt);		break;	case PCI_DEVICE_ID_PHILIPS_SAA7133:	case PCI_DEVICE_ID_PHILIPS_SAA7135:		if (1 == dev->dmasound.channels)			fmt |= (1 << 4);		if (2 == dev->dmasound.channels)			fmt |= (2 << 4);		if (!sign)			fmt |= 0x04;		saa_writel(SAA7133_NUM_SAMPLES, dev->dmasound.blksize -4);		saa_writel(SAA7133_AUDIO_CHANNEL, 0x543210 | (fmt << 24));		break;	}	dprintk("rec_start: afmt=%d ch=%d  =>  fmt=0x%x swap=%c\n",		dev->dmasound.afmt, dev->dmasound.channels, fmt,		bswap ? 'b' : '-');	/* dma: setup channel 6 (= AUDIO) */	control = SAA7134_RS_CONTROL_BURST_16 |		SAA7134_RS_CONTROL_ME |		(dev->dmasound.pt.dma >> 12);	if (bswap)		control |= SAA7134_RS_CONTROL_BSWAP;	saa_writel(SAA7134_RS_BA1(6),0);	saa_writel(SAA7134_RS_BA2(6),dev->dmasound.blksize);	saa_writel(SAA7134_RS_PITCH(6),0);	saa_writel(SAA7134_RS_CONTROL(6),control);	/* start dma */	dev->dmasound.recording_on = 1;	spin_lock_irqsave(&dev->slock,flags);	dsp_dma_start(dev);	spin_unlock_irqrestore(&dev->slock,flags);	return 0; fail2:	saa7134_pgtable_free(dev->pci,&dev->dmasound.pt); fail1:	videobuf_dma_pci_unmap(dev->pci,&dev->dmasound.dma);	return err;}static int dsp_rec_stop(struct saa7134_dev *dev){	unsigned long flags;	dprintk("rec_stop dma_blk=%d\n",dev->dmasound.dma_blk);	/* stop dma */	dev->dmasound.recording_on = 0;	spin_lock_irqsave(&dev->slock,flags);	dsp_dma_stop(dev);	spin_unlock_irqrestore(&dev->slock,flags);	/* unlock buffer */	saa7134_pgtable_free(dev->pci,&dev->dmasound.pt);	videobuf_dma_pci_unmap(dev->pci,&dev->dmasound.dma);	return 0;}/* ------------------------------------------------------------------ */static int dsp_open(struct inode *inode, struct file *file){	int minor = iminor(inode);	struct saa7134_dev *h,*dev = NULL;	struct list_head *list;	int err;	list_for_each(list,&saa7134_devlist) {		h = list_entry(list, struct saa7134_dev, devlist);		if (h->dmasound.minor_dsp == minor)			dev = h;	}	if (NULL == dev)		return -ENODEV;	down(&dev->dmasound.lock);	err = -EBUSY;	if (dev->dmasound.users_dsp)		goto fail1;	dev->dmasound.users_dsp++;	file->private_data = dev;	dev->dmasound.afmt        = AFMT_U8;	dev->dmasound.channels    = 1;	dev->dmasound.read_count  = 0;	dev->dmasound.read_offset = 0;	dsp_buffer_conf(dev,PAGE_SIZE,64);	err = dsp_buffer_init(dev);	if (0 != err)		goto fail2;	up(&dev->dmasound.lock);	return 0; fail2:	dev->dmasound.users_dsp--; fail1:	up(&dev->dmasound.lock);	return err;}static int dsp_release(struct inode *inode, struct file *file){	struct saa7134_dev *dev = file->private_data;	down(&dev->dmasound.lock);	if (dev->dmasound.recording_on)		dsp_rec_stop(dev);	dsp_buffer_free(dev);	dev->dmasound.users_dsp--;	file->private_data = NULL;	up(&dev->dmasound.lock);	return 0;}static ssize_t dsp_read(struct file *file, char __user *buffer,			size_t count, loff_t *ppos){	struct saa7134_dev *dev = file->private_data;	DECLARE_WAITQUEUE(wait, current);	unsigned int bytes;	unsigned long flags;	int err,ret = 0;	add_wait_queue(&dev->dmasound.wq, &wait);	down(&dev->dmasound.lock);	while (count > 0) {		/* wait for data if needed */		if (0 == dev->dmasound.read_count) {			if (!dev->dmasound.recording_on) {				err = dsp_rec_start(dev);				if (err < 0) {					if (0 == ret)						ret = err;					break;				}			}			if (dev->dmasound.recording_on &&			    !dev->dmasound.dma_running) {				/* recover from overruns */				spin_lock_irqsave(&dev->slock,flags);				dsp_dma_start(dev);				spin_unlock_irqrestore(&dev->slock,flags);			}			if (file->f_flags & O_NONBLOCK) {				if (0 == ret)					ret = -EAGAIN;				break;			}			up(&dev->dmasound.lock);			set_current_state(TASK_INTERRUPTIBLE);			if (0 == dev->dmasound.read_count)				schedule();			set_current_state(TASK_RUNNING);			down(&dev->dmasound.lock);			if (signal_pending(current)) {				if (0 == ret)					ret = -EINTR;				break;			}		}		/* copy data to userspace */		bytes = count;		if (bytes > dev->dmasound.read_count)			bytes = dev->dmasound.read_count;		if (bytes > dev->dmasound.bufsize - dev->dmasound.read_offset)			bytes = dev->dmasound.bufsize - dev->dmasound.read_offset;		if (copy_to_user(buffer + ret,				 dev->dmasound.dma.vmalloc + dev->dmasound.read_offset,				 bytes)) {			if (0 == ret)				ret = -EFAULT;			break;		}		ret   += bytes;		count -= bytes;		dev->dmasound.read_count  -= bytes;		dev->dmasound.read_offset += bytes;		if (dev->dmasound.read_offset == dev->dmasound.bufsize)			dev->dmasound.read_offset = 0;	}	up(&dev->dmasound.lock);	remove_wait_queue(&dev->dmasound.wq, &wait);	return ret;}static ssize_t dsp_write(struct file *file, const char __user *buffer,			 size_t count, loff_t *ppos){	return -EINVAL;}static int dsp_ioctl(struct inode *inode, struct file *file,		     unsigned int cmd, unsigned long arg){	struct saa7134_dev *dev = file->private_data;	void __user *argp = (void __user *) arg;	int __user *p = argp;	int val = 0;	if (debug > 1)		saa7134_print_ioctl(dev->name,cmd);	switch (cmd) {	case OSS_GETVERSION:		return put_user(SOUND_VERSION, p);	case SNDCTL_DSP_GETCAPS:		return 0;	case SNDCTL_DSP_SPEED:		if (get_user(val, p))			return -EFAULT;		/* fall through */	case SOUND_PCM_READ_RATE:		return put_user(dev->dmasound.rate, p);	case SNDCTL_DSP_STEREO:		if (get_user(val, p))			return -EFAULT;		down(&dev->dmasound.lock);		dev->dmasound.channels = val ? 2 : 1;		if (dev->dmasound.recording_on) {			dsp_rec_stop(dev);			dsp_rec_start(dev);		}		up(&dev->dmasound.lock);		return put_user(dev->dmasound.channels-1, p);	case SNDCTL_DSP_CHANNELS:		if (get_user(val, p))			return -EFAULT;		if (val != 1 && val != 2)			return -EINVAL;		down(&dev->dmasound.lock);		dev->dmasound.channels = val;		if (dev->dmasound.recording_on) {			dsp_rec_stop(dev);			dsp_rec_start(dev);		}		up(&dev->dmasound.lock);		/* fall through */	case SOUND_PCM_READ_CHANNELS:		return put_user(dev->dmasound.channels, p);	case SNDCTL_DSP_GETFMTS: /* Returns a mask */		return put_user(AFMT_U8     | AFMT_S8     |				AFMT_U16_LE | AFMT_U16_BE |				AFMT_S16_LE | AFMT_S16_BE, p);	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt */		if (get_user(val, p))			return -EFAULT;		switch (val) {		case AFMT_QUERY:			/* nothing to do */			break;		case AFMT_U8:		case AFMT_S8:		case AFMT_U16_LE:		case AFMT_U16_BE:		case AFMT_S16_LE:		case AFMT_S16_BE:			down(&dev->dmasound.lock);			dev->dmasound.afmt = val;			if (dev->dmasound.recording_on) {				dsp_rec_stop(dev);				dsp_rec_start(dev);			}			up(&dev->dmasound.lock);			return put_user(dev->dmasound.afmt, p);		default:			return -EINVAL;		}	case SOUND_PCM_READ_BITS:		switch (dev->dmasound.afmt) {		case AFMT_U8:		case AFMT_S8:			return put_user(8, p);		case AFMT_U16_LE:		case AFMT_U16_BE:		case AFMT_S16_LE:		case AFMT_S16_BE:			return put_user(16, p);		default:			return -EINVAL;		}	case SNDCTL_DSP_NONBLOCK:		file->f_flags |= O_NONBLOCK;		return 0;	case SNDCTL_DSP_RESET:		down(&dev->dmasound.lock);		if (dev->dmasound.recording_on)			dsp_rec_stop(dev);		up(&dev->dmasound.lock);		return 0;	case SNDCTL_DSP_GETBLKSIZE:		return put_user(dev->dmasound.blksize, p);	case SNDCTL_DSP_SETFRAGMENT:		if (get_user(val, p))			return -EFAULT;		if (dev->dmasound.recording_on)			return -EBUSY;		dsp_buffer_free(dev);		/* used to be arg >> 16 instead of val >> 16; fixed */		dsp_buffer_conf(dev,1 << (val & 0xffff), (val >> 16) & 0xffff);		dsp_buffer_init(dev);		return 0;	case SNDCTL_DSP_SYNC:		/* NOP */		return 0;	case SNDCTL_DSP_GETISPACE:	{		audio_buf_info info;		info.fragsize   = dev->dmasound.blksize;		info.fragstotal = dev->dmasound.blocks;		info.bytes      = dev->dmasound.read_count;		info.fragments  = info.bytes / info.fragsize;		if (copy_to_user(argp, &info, sizeof(info)))			return -EFAULT;		return 0;	}	default:		return -EINVAL;	}}

⌨️ 快捷键说明

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