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

📄 tm6000-video.c

📁 trident tm5600的linux驱动
💻 C
📖 第 1 页 / 共 3 页
字号:
/*   tm6000-video.c - driver for TM5600/TM6000 USB video capture devices   Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>   Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>	- Fixed module load/unload   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 version 2   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/module.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/fs.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/random.h>#include <linux/version.h>#include <linux/usb.h>#include "compat.h"#include <linux/videodev2.h>#include <media/v4l2-ioctl.h>#include <linux/interrupt.h>#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)#include <linux/kthread.h>#endif#include <linux/highmem.h>#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)#include <linux/freezer.h>#endif#include "tm6000-regs.h"#include "tm6000.h"#define BUFFER_TIMEOUT     msecs_to_jiffies(2000)  /* 2 seconds *//* Limits minimum and default number of buffers */#define TM6000_MIN_BUF 4#define TM6000_DEF_BUF 8#define TM6000_MAX_ISO_PACKETS	40	/* Max number of ISO packets *//* Declare static vars that will be used as parameters */static unsigned int vid_limit = 16;	/* Video memory limit, in Mb */static int video_nr = -1;		/* /dev/videoN, -1 for autodetect *//* Debug level */int tm6000_debug;/* supported controls */static struct v4l2_queryctrl tm6000_qctrl[] = {	{		.id            = V4L2_CID_BRIGHTNESS,		.type          = V4L2_CTRL_TYPE_INTEGER,		.name          = "Brightness",		.minimum       = 0,		.maximum       = 255,		.step          = 1,		.default_value = 54,		.flags         = 0,	}, {		.id            = V4L2_CID_CONTRAST,		.type          = V4L2_CTRL_TYPE_INTEGER,		.name          = "Contrast",		.minimum       = 0,		.maximum       = 255,		.step          = 0x1,		.default_value = 119,		.flags         = 0,	}, {		.id            = V4L2_CID_SATURATION,		.type          = V4L2_CTRL_TYPE_INTEGER,		.name          = "Saturation",		.minimum       = 0,		.maximum       = 255,		.step          = 0x1,		.default_value = 112,		.flags         = 0,	}, {		.id            = V4L2_CID_HUE,		.type          = V4L2_CTRL_TYPE_INTEGER,		.name          = "Hue",		.minimum       = -128,		.maximum       = 127,		.step          = 0x1,		.default_value = 0,		.flags         = 0,	}};static int qctl_regs[ARRAY_SIZE(tm6000_qctrl)];static struct tm6000_fmt format[] = {	{		.name     = "4:2:2, packed, YVY2",		.fourcc   = V4L2_PIX_FMT_YUYV,		.depth    = 16,	}, {		.name     = "4:2:2, packed, UYVY",		.fourcc   = V4L2_PIX_FMT_UYVY,		.depth    = 16,	}, {		.name     = "A/V + VBI mux packet",		.fourcc   = V4L2_PIX_FMT_TM6000,		.depth    = 16,	}};static LIST_HEAD(tm6000_corelist);/* ------------------------------------------------------------------	DMA and thread functions   ------------------------------------------------------------------*/#define norm_maxw(a) 720#define norm_maxh(a) 576#define norm_minw(a) norm_maxw(a)#define norm_minh(a) norm_maxh(a)/* * video-buf generic routine to get the next available buffer */static inline void get_next_buf(struct tm6000_dmaqueue *dma_q,			       struct tm6000_buffer   **buf){	struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);#if 1	char *outp;#endif	if (list_empty(&dma_q->active)) {		dprintk(dev, V4L2_DEBUG_QUEUE, "No active queue to serve\n");		return;	}	*buf = list_entry(dma_q->active.next,			struct tm6000_buffer, vb.queue);#if 1	if (!buf)		return;	/* Cleans up buffer - Usefull for testing for frame/URB loss */	outp = videobuf_to_vmalloc(&(*buf)->vb);	memset(outp, 0, (*buf)->vb.size);#endif	return;}/* * Announces that a buffer were filled and request the next */static inline void buffer_filled(struct tm6000_core *dev,				 struct tm6000_dmaqueue *dma_q,				 struct tm6000_buffer *buf){	/* Advice that buffer was filled */	dprintk(dev, V4L2_DEBUG_ISOC, "[%p/%d] wakeup\n", buf, buf->vb.i);	buf->vb.state = VIDEOBUF_DONE;	buf->vb.field_count++;	do_gettimeofday(&buf->vb.ts);	list_del(&buf->vb.queue);	wake_up(&buf->vb.done);}const char *tm6000_msg_type[] = {	"unknown(0)",   /* 0 */	"video",        /* 1 */	"audio",        /* 2 */	"vbi",          /* 3 */	"pts",          /* 4 */	"err",          /* 5 */	"unknown(6)",   /* 6 */	"unknown(7)",   /* 7 */};/* * Identify the tm5600/6000 buffer header type and properly handles */static int copy_packet(struct urb *urb, u32 header, u8 **ptr, u8 *endp,			u8 *out_p, struct tm6000_buffer **buf){	struct tm6000_dmaqueue  *dma_q = urb->context;	struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);	u8 c;	unsigned int cmd, cpysize, pktsize, size, field, block, line, pos = 0;	int rc = 0;	/* FIXME: move to tm6000-isoc */	static int last_line = -2, start_line = -2, last_field = -2;	/* FIXME: this is the hardcoded window size	 */	unsigned int linewidth = (*buf)->vb.width << 1;	if (!dev->isoc_ctl.cmd) {		c = (header >> 24) & 0xff;		/* split the header fields */		size  = (((header & 0x7e) << 1) -1) *4;		block = (header >> 7) & 0xf;		field = (header >> 11) & 0x1;		line  = (header >> 12) & 0x1ff;		cmd   = (header >> 21) & 0x7;		/* Validates header fields */		if(size > TM6000_URB_MSG_LEN)			size = TM6000_URB_MSG_LEN;		if (cmd == TM6000_URB_MSG_VIDEO) {			if ((block+1)*TM6000_URB_MSG_LEN>linewidth)				cmd = TM6000_URB_MSG_ERR;			/* FIXME: Mounts the image as field0+field1			 * It should, instead, check if the user selected			 * entrelaced or non-entrelaced mode			 */			pos= ((line<<1)+field)*linewidth +				block*TM6000_URB_MSG_LEN;			/* Don't allow to write out of the buffer */			if (pos+TM6000_URB_MSG_LEN > (*buf)->vb.size) {				dprintk(dev, V4L2_DEBUG_ISOC,					"ERR: size=%d, num=%d, line=%d, "					"field=%d\n",					size, block, line, field);				cmd = TM6000_URB_MSG_ERR;			}		} else {			pos=0;		}		/* Prints debug info */		dprintk(dev, V4L2_DEBUG_ISOC, "size=%d, num=%d, "				" line=%d, field=%d\n",				size, block, line, field);		if ((last_line!=line)&&(last_line+1!=line) &&		    (cmd != TM6000_URB_MSG_ERR) )  {			if (cmd != TM6000_URB_MSG_VIDEO)  {				dprintk(dev, V4L2_DEBUG_ISOC,  "cmd=%d, "					"size=%d, num=%d, line=%d, field=%d\n",					cmd, size, block, line, field);			}			if (start_line<0)				start_line=last_line;			/* Prints debug info */			dprintk(dev, V4L2_DEBUG_ISOC, "lines= %d-%d, "					"field=%d\n",					start_line, last_line, field);			if ((start_line<6 && last_line>200) &&				(last_field != field) ) {				dev->isoc_ctl.nfields++;				if (dev->isoc_ctl.nfields>=2) {					dev->isoc_ctl.nfields=0;					/* Announces that a new buffer were filled */					buffer_filled (dev, dma_q, *buf);					dprintk(dev, V4L2_DEBUG_ISOC,							"new buffer filled\n");					get_next_buf (dma_q, buf);					if (!*buf)						return rc;				}			}			start_line=line;			last_field=field;		}		last_line=line;		pktsize = TM6000_URB_MSG_LEN;	} else {		/* Continue the last copy */		cmd = dev->isoc_ctl.cmd;		size= dev->isoc_ctl.size;		pos = dev->isoc_ctl.pos;		pktsize = dev->isoc_ctl.pktsize;	}#if 0 /* This never happens at this point */	if (!buf)		cmd=TM6000_URB_MSG_ERR;#endif	cpysize = (endp-(*ptr) > size) ? size : endp - *ptr;	if (cpysize) {		/* handles each different URB message */		switch(cmd) {		case TM6000_URB_MSG_VIDEO:			/* Fills video buffer */			memcpy(&out_p[pos], *ptr, cpysize);			break;		case TM6000_URB_MSG_PTS:			break;		case TM6000_URB_MSG_AUDIO:/* Need some code to process audio */printk ("%ld: cmd=%s, size=%d\n", jiffies,				tm6000_msg_type[cmd],size);			break;		default:			dprintk (dev, V4L2_DEBUG_ISOC, "cmd=%s, size=%d\n",						tm6000_msg_type[cmd],size);		}	}	if (cpysize<size) {		/* End of URB packet, but cmd processing is not		 * complete. Preserve the state for a next packet		 */		dev->isoc_ctl.pos = pos+cpysize;		dev->isoc_ctl.size= size-cpysize;		dev->isoc_ctl.cmd = cmd;		dev->isoc_ctl.pktsize = pktsize-cpysize;		(*ptr)+=cpysize;	} else {		dev->isoc_ctl.cmd = 0;		(*ptr)+=pktsize;	}	return rc;}static int copy_streams(u8 *data, u8 *out_p, unsigned long len,			struct urb *urb, struct tm6000_buffer **buf){	struct tm6000_dmaqueue  *dma_q = urb->context;	struct tm6000_core *dev= container_of(dma_q,struct tm6000_core,vidq);	u8 *ptr=data, *endp=data+len;	unsigned long header=0;	int rc=0;	for (ptr=data; ptr<endp;) {		if (!dev->isoc_ctl.cmd) {			u8 *p=(u8 *)&dev->isoc_ctl.tmp_buf;			/* FIXME: This seems very complex			 * It just recovers up to 3 bytes of the header that			 * might be at the previous packet			 */			if (dev->isoc_ctl.tmp_buf_len) {				while (dev->isoc_ctl.tmp_buf_len) {					if ( *(ptr+3-dev->isoc_ctl.tmp_buf_len) == 0x47) {						break;					}					p++;					dev->isoc_ctl.tmp_buf_len--;				}				if (dev->isoc_ctl.tmp_buf_len) {					memcpy (&header,p,						dev->isoc_ctl.tmp_buf_len);					memcpy (((u8 *)header)+						dev->isoc_ctl.tmp_buf,						ptr,						4-dev->isoc_ctl.tmp_buf_len);					ptr+=4-dev->isoc_ctl.tmp_buf_len;					goto HEADER;				}			}			/* Seek for sync */			for (;ptr<endp-3;ptr++) {				if (*(ptr+3)==0x47)					break;			}			if (ptr+3>=endp) {				dev->isoc_ctl.tmp_buf_len=endp-ptr;				memcpy (&dev->isoc_ctl.tmp_buf,ptr,					dev->isoc_ctl.tmp_buf_len);				dev->isoc_ctl.cmd=0;				return rc;			}			/* Get message header */			header=*(unsigned long *)ptr;			ptr+=4;		}HEADER:		/* Copy or continue last copy */		rc=copy_packet(urb,header,&ptr,endp,out_p,buf);		if (rc<0) {			buf=NULL;			printk(KERN_ERR "tm6000: buffer underrun at %ld\n",					jiffies);			return rc;		}	}	return 0;}/* * Identify the tm5600/6000 buffer header type and properly handles */static int copy_multiplexed(u8 *ptr, u8 *out_p, unsigned long len,			struct urb *urb, struct tm6000_buffer **buf){	struct tm6000_dmaqueue  *dma_q = urb->context;	struct tm6000_core *dev= container_of(dma_q,struct tm6000_core,vidq);	unsigned int pos=dev->isoc_ctl.pos,cpysize;	int rc=1;	while (len>0) {		cpysize=min(len,(*buf)->vb.size-pos);//printk("Copying %d bytes (max=%lu) from %p to %p[%u]\n",cpysize,(*buf)->vb.size,ptr,out_p,pos);		memcpy(&out_p[pos], ptr, cpysize);		pos+=cpysize;		ptr+=cpysize;		len-=cpysize;		if (pos >= (*buf)->vb.size) {			pos=0;			/* Announces that a new buffer were filled */			buffer_filled (dev, dma_q, *buf);			dprintk(dev, V4L2_DEBUG_ISOC, "new buffer filled\n");			get_next_buf (dma_q, buf);			if (!*buf)				break;		}	}	dev->isoc_ctl.pos=pos;	return rc;}static void inline print_err_status (struct tm6000_core *dev,				     int packet, int status){	char *errmsg = "Unknown";	switch(status) {	case -ENOENT:		errmsg = "unlinked synchronuously";		break;	case -ECONNRESET:		errmsg = "unlinked asynchronuously";		break;	case -ENOSR:		errmsg = "Buffer error (overrun)";		break;	case -EPIPE:		errmsg = "Stalled (device not responding)";		break;	case -EOVERFLOW:		errmsg = "Babble (bad cable?)";		break;	case -EPROTO:		errmsg = "Bit-stuff error (bad cable?)";		break;	case -EILSEQ:		errmsg = "CRC/Timeout (could be anything)";		break;	case -ETIME:		errmsg = "Device does not respond";		break;	}	if (packet<0) {		dprintk(dev, V4L2_DEBUG_QUEUE, "URB status %d [%s].\n",			status, errmsg);	} else {		dprintk(dev, V4L2_DEBUG_QUEUE, "URB packet %d, status %d [%s].\n",			packet, status, errmsg);	}}/* * Controls the isoc copy of each urb packet */static inline int tm6000_isoc_copy(struct urb *urb, struct tm6000_buffer **buf){	struct tm6000_dmaqueue  *dma_q = urb->context;	struct tm6000_core *dev= container_of(dma_q,struct tm6000_core,vidq);	void *outp=videobuf_to_vmalloc (&((*buf)->vb));	int i, len=0, rc=1;	int size=(*buf)->vb.size;	char *p;	unsigned long copied;	copied=0;	if (urb->status<0) {		print_err_status (dev,-1,urb->status);		return 0;	}	for (i = 0; i < urb->number_of_packets; i++) {		int status = urb->iso_frame_desc[i].status;		if (status<0) {			print_err_status (dev,i,status);			continue;		}		len=urb->iso_frame_desc[i].actual_length;//		if (len>=TM6000_URB_MSG_LEN) {			p=urb->transfer_buffer + urb->iso_frame_desc[i].offset;			if (!urb->iso_frame_desc[i].status) {				if (((*buf)->fmt->fourcc)==V4L2_PIX_FMT_TM6000) {					rc=copy_multiplexed(p,outp,len,urb,buf);					if (rc<=0)						return rc;				} else {					copy_streams(p,outp,len,urb,buf);				}			}			copied += len;			if (copied>=size)				break;//		}	}	return rc;}/* ------------------------------------------------------------------	URB control   ------------------------------------------------------------------*//* * IRQ callback, called by URB callback */#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)static void tm6000_irq_callback(struct urb *urb, struct pt_regs *regs)#elsestatic void tm6000_irq_callback(struct urb *urb)#endif{	struct tm6000_buffer    *buf = NULL;	struct tm6000_dmaqueue  *dma_q = urb->context;	struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);	unsigned long flags;

⌨️ 快捷键说明

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