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

📄 saa7134-oss.c

📁 saa7134-0.2.9.tar.gz 电视卡驱动程序
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * device driver for philips saa7134 based TV cards * oss dsp interface * * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] * *  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/kernel.h>#include <linux/slab.h>#include <linux/soundcard.h>#include "saa7134-reg.h"#include "saa7134.h"/* ------------------------------------------------------------------ */static unsigned int oss_debug  = 0;MODULE_PARM(oss_debug,"i");MODULE_PARM_DESC(oss_debug,"enable debug messages [oss]");static unsigned int oss_rate  = 0;MODULE_PARM(oss_rate,"i");MODULE_PARM_DESC(oss_rate,"sample rate (valid are: 32000,48000)");#define dprintk(fmt, arg...)	if (oss_debug) \	printk(KERN_DEBUG "%s/oss: " fmt, dev->name , ## arg)/* ------------------------------------------------------------------ */static int dsp_buffer_conf(struct saa7134_dev *dev, int blksize, int blocks){	blksize &= ~0xff;	if (blksize < 0x100)		blksize = 0x100;	if (blksize > 0x10000)		blksize = 0x100;	if (blocks < 2)		blocks = 2;        while ((blksize * blocks) & ~PAGE_MASK)		blocks++;	if ((blksize * blocks) > 1024*1024)		blocks = 1024*1024 / blksize;	dev->oss.blocks  = blocks;	dev->oss.blksize = blksize;	dev->oss.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->oss.bufsize)		BUG();	err = videobuf_dma_init_kernel(&dev->oss.dma, PCI_DMA_FROMDEVICE,				       dev->oss.bufsize >> PAGE_SHIFT);	if (0 != err)		return err;	return 0;}static int dsp_buffer_free(struct saa7134_dev *dev){	if (!dev->oss.blksize)		BUG();	videobuf_dma_free(&dev->oss.dma);	dev->oss.blocks  = 0;	dev->oss.blksize = 0;	dev->oss.bufsize = 0;	return 0;}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->oss.dma)))		return err;	if (0 != (err = saa7134_pgtable_alloc(dev->pci,&dev->oss.pt)))		goto fail1;	if (0 != (err = saa7134_pgtable_build(dev->pci,&dev->oss.pt,					      dev->oss.dma.sglist,					      dev->oss.dma.sglen,					      0)))		goto fail2;	/* sample format */	switch (dev->oss.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->oss.afmt) {	case AFMT_S8:     	case AFMT_S16_LE:	case AFMT_S16_BE: sign = 1; break;	default:          sign = 0; break;	}	switch (dev->oss.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->oss.channels)			fmt |= (1 << 3);		if (2 == dev->oss.channels)			fmt |= (3 << 3);		if (sign)			fmt |= 0x04;		fmt |= (TV == dev->oss.input) ? 0xc0 : 0x80;				saa_writeb(SAA7134_NUM_SAMPLES0, (dev->oss.blksize & 0x0000ff));		saa_writeb(SAA7134_NUM_SAMPLES1, (dev->oss.blksize & 0x00ff00) >>  8);		saa_writeb(SAA7134_NUM_SAMPLES2, (dev->oss.blksize & 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->oss.channels)			fmt |= (1 << 4);		if (2 == dev->oss.channels)			fmt |= (2 << 4);		if (!sign)			fmt |= 0x04;		saa_writel(0x588 >> 2, dev->oss.blksize);		saa_writel(0x58c >> 2, 0x543210 | (fmt << 24));		break;	}	dprintk("rec_start: afmt=%d ch=%d  =>  fmt=0x%x swap=%c\n",		dev->oss.afmt, dev->oss.channels, fmt,		bswap ? 'b' : '-');	/* dma: setup channel 6 (= AUDIO) */	control = SAA7134_RS_CONTROL_BURST_16 |		SAA7134_RS_CONTROL_ME |		(dev->oss.pt.dma >> 12);	if (bswap)		control |= SAA7134_RS_CONTROL_BSWAP;	saa_writel(SAA7134_RS_BA1(6),0);	saa_writel(SAA7134_RS_BA2(6),dev->oss.blksize);	saa_writel(SAA7134_RS_PITCH(6),0);	saa_writel(SAA7134_RS_CONTROL(6),control);		/* start dma */	spin_lock_irqsave(&dev->slock,flags);	dev->oss.recording = 1;	dev->oss.dma_blk   = 0;	saa7134_set_dmabits(dev);	spin_unlock_irqrestore(&dev->slock,flags);	return 0; fail2:	saa7134_pgtable_free(dev->pci,&dev->oss.pt); fail1:	videobuf_dma_pci_unmap(dev->pci,&dev->oss.dma);	return err;}static int dsp_rec_stop(struct saa7134_dev *dev){	unsigned long flags;	dprintk("rec_stop dma_blk=%d\n",dev->oss.dma_blk);	/* stop dma */	spin_lock_irqsave(&dev->slock,flags);	dev->oss.dma_blk   = -1;	dev->oss.recording = 0;	saa7134_set_dmabits(dev);	spin_unlock_irqrestore(&dev->slock,flags);	/* unlock buffer */	saa7134_pgtable_free(dev->pci,&dev->oss.pt);	videobuf_dma_pci_unmap(dev->pci,&dev->oss.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->oss.minor_dsp == minor)			dev = h;	}	if (NULL == dev)		return -ENODEV;	down(&dev->oss.lock);	err = -EBUSY;	if (dev->oss.users_dsp)		goto fail1;	dev->oss.users_dsp++;	file->private_data = dev;	dev->oss.afmt        = AFMT_U8;	dev->oss.channels    = 1;	dev->oss.read_count  = 0;	dev->oss.read_offset = 0;	dsp_buffer_conf(dev,PAGE_SIZE,64);	err = dsp_buffer_init(dev);	if (0 != err)		goto fail2;	up(&dev->oss.lock);	return 0; fail2:	dev->oss.users_dsp--; fail1:	up(&dev->oss.lock);	return err;}static int dsp_release(struct inode *inode, struct file *file){	struct saa7134_dev *dev = file->private_data;	down(&dev->oss.lock);	if (dev->oss.recording)		dsp_rec_stop(dev);	dsp_buffer_free(dev);	dev->oss.users_dsp--;	file->private_data = NULL;	up(&dev->oss.lock);	return 0;}static ssize_t dsp_read(struct file *file, char *buffer,			size_t count, loff_t *ppos){	struct saa7134_dev *dev = file->private_data;	DECLARE_WAITQUEUE(wait, current);	unsigned int bytes;	int err,ret = 0;	add_wait_queue(&dev->oss.wq, &wait);	down(&dev->oss.lock);	while (count > 0) {		/* wait for data if needed */		if (0 == dev->oss.read_count) {			if (!dev->oss.recording) {				err = dsp_rec_start(dev);				if (err < 0) {					if (0 == ret)						ret = err;					break;				}			}			if (file->f_flags & O_NONBLOCK) {				if (0 == ret)					ret = -EAGAIN;				break;			}			up(&dev->oss.lock);			set_current_state(TASK_INTERRUPTIBLE);			if (0 == dev->oss.read_count)				schedule();			set_current_state(TASK_RUNNING);			down(&dev->oss.lock);			if (signal_pending(current)) {				if (0 == ret)					ret = -EINTR;				break;			}		}		/* copy data to userspace */		bytes = count;		if (bytes > dev->oss.read_count)			bytes = dev->oss.read_count;		if (bytes > dev->oss.bufsize - dev->oss.read_offset)			bytes = dev->oss.bufsize - dev->oss.read_offset;		if (copy_to_user(buffer + ret,				 dev->oss.dma.vmalloc + dev->oss.read_offset,				 bytes)) {			if (0 == ret)				ret = -EFAULT;			break;		}		ret   += bytes;		count -= bytes;		dev->oss.read_count  -= bytes;		dev->oss.read_offset += bytes;		if (dev->oss.read_offset == dev->oss.bufsize)			dev->oss.read_offset = 0;	}	up(&dev->oss.lock);	remove_wait_queue(&dev->oss.wq, &wait);	return ret;}static ssize_t dsp_write(struct file *file, const char *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;	int val = 0;		if (oss_debug > 1)		saa7134_print_ioctl(dev->name,cmd);        switch (cmd) {        case OSS_GETVERSION:                return put_user(SOUND_VERSION, (int *)arg);        case SNDCTL_DSP_GETCAPS:		return 0;        case SNDCTL_DSP_SPEED:		if (get_user(val, (int*)arg))			return -EFAULT;		/* fall through */        case SOUND_PCM_READ_RATE:		return put_user(dev->oss.rate, (int*)arg);        case SNDCTL_DSP_STEREO:		if (get_user(val, (int*)arg))			return -EFAULT;		down(&dev->oss.lock);		dev->oss.channels = val ? 2 : 1;		if (dev->oss.recording) {			dsp_rec_stop(dev);			dsp_rec_start(dev);		}		up(&dev->oss.lock);		return put_user(dev->oss.channels-1, (int *)arg);        case SNDCTL_DSP_CHANNELS:		if (get_user(val, (int*)arg))			return -EFAULT;		if (val != 1 && val != 2)			return -EINVAL;		down(&dev->oss.lock);		dev->oss.channels = val;		if (dev->oss.recording) {			dsp_rec_stop(dev);			dsp_rec_start(dev);		}		up(&dev->oss.lock);		/* fall through */        case SOUND_PCM_READ_CHANNELS:		return put_user(dev->oss.channels, (int *)arg);		        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, (int*)arg);        case SNDCTL_DSP_SETFMT: /* Selects ONE fmt */		if (get_user(val, (int*)arg))			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->oss.lock);			dev->oss.afmt = val;			if (dev->oss.recording) {				dsp_rec_stop(dev);				dsp_rec_start(dev);			}			up(&dev->oss.lock);

⌨️ 快捷键说明

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