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

📄 em28xx-video.c

📁 trident tm5600的linux驱动
💻 C
📖 第 1 页 / 共 4 页
字号:
/*   em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB		    video capture devices   Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>		      Markus Rechberger <mrechberger@gmail.com>		      Mauro Carvalho Chehab <mchehab@infradead.org>		      Sascha Sommer <saschasommer@freenet.de>	Some parts based on SN9C10x PC Camera Controllers GPL driver made		by Luca Risolia <luca.risolia@studio.unibo.it>   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/bitmap.h>#include <linux/usb.h>#include <linux/i2c.h>#include <linux/version.h>#include <linux/mm.h>#include <linux/mutex.h>#include "em28xx.h"#include <media/v4l2-common.h>#include <media/v4l2-ioctl.h>#include <media/msp3400.h>#include <media/tuner.h>#define DRIVER_AUTHOR "Ludovico Cavedon <cavedon@sssup.it>, " \		      "Markus Rechberger <mrechberger@gmail.com>, " \		      "Mauro Carvalho Chehab <mchehab@infradead.org>, " \		      "Sascha Sommer <saschasommer@freenet.de>"#define DRIVER_NAME         "em28xx"#define DRIVER_DESC         "Empia em28xx based USB video device driver"#define EM28XX_VERSION_CODE  KERNEL_VERSION(0, 1, 0)#define em28xx_videodbg(fmt, arg...) do {\	if (video_debug) \		printk(KERN_INFO "%s %s :"fmt, \			 dev->name, __func__ , ##arg); } while (0)static unsigned int isoc_debug;module_param(isoc_debug, int, 0644);MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]");#define em28xx_isocdbg(fmt, arg...) \do {\	if (isoc_debug) { \		printk(KERN_INFO "%s %s :"fmt, \			 dev->name, __func__ , ##arg); \	} \  } while (0)MODULE_AUTHOR(DRIVER_AUTHOR);MODULE_DESCRIPTION(DRIVER_DESC);MODULE_LICENSE("GPL");static LIST_HEAD(em28xx_devlist);static unsigned int card[]     = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };static unsigned int vbi_nr[]   = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };module_param_array(card,  int, NULL, 0444);module_param_array(video_nr, int, NULL, 0444);module_param_array(vbi_nr, int, NULL, 0444);module_param_array(radio_nr, int, NULL, 0444);MODULE_PARM_DESC(card,     "card type");MODULE_PARM_DESC(video_nr, "video device numbers");MODULE_PARM_DESC(vbi_nr,   "vbi device numbers");MODULE_PARM_DESC(radio_nr, "radio device numbers");static unsigned int video_debug;module_param(video_debug, int, 0644);MODULE_PARM_DESC(video_debug, "enable debug messages [video]");/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */static unsigned long em28xx_devused;/* supported controls *//* Common to all boards */static struct v4l2_queryctrl em28xx_qctrl[] = {	{		.id = V4L2_CID_AUDIO_VOLUME,		.type = V4L2_CTRL_TYPE_INTEGER,		.name = "Volume",		.minimum = 0x0,		.maximum = 0x1f,		.step = 0x1,		.default_value = 0x1f,		.flags = 0,	}, {		.id = V4L2_CID_AUDIO_MUTE,		.type = V4L2_CTRL_TYPE_BOOLEAN,		.name = "Mute",		.minimum = 0,		.maximum = 1,		.step = 1,		.default_value = 1,		.flags = 0,	}};static struct usb_driver em28xx_usb_driver;/* ------------------------------------------------------------------	DMA and thread functions   ------------------------------------------------------------------*//* * Announces that a buffer were filled and request the next */static inline void buffer_filled(struct em28xx *dev,				  struct em28xx_dmaqueue *dma_q,				  struct em28xx_buffer *buf){	/* Advice that buffer was filled */	em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);	buf->vb.state = VIDEOBUF_DONE;	buf->vb.field_count++;	do_gettimeofday(&buf->vb.ts);	dev->isoc_ctl.buf = NULL;	list_del(&buf->vb.queue);	wake_up(&buf->vb.done);}/* * Identify the buffer header type and properly handles */static void em28xx_copy_video(struct em28xx *dev,			      struct em28xx_dmaqueue  *dma_q,			      struct em28xx_buffer *buf,			      unsigned char *p,			      unsigned char *outp, unsigned long len){	void *fieldstart, *startwrite, *startread;	int  linesdone, currlinedone, offset, lencopy, remain;	int bytesperline = dev->width << 1;	if (dma_q->pos + len > buf->vb.size)		len = buf->vb.size - dma_q->pos;	if (p[0] != 0x88 && p[0] != 0x22) {		em28xx_isocdbg("frame is not complete\n");		len += 4;	} else		p += 4;	startread = p;	remain = len;	/* Interlaces frame */	if (buf->top_field)		fieldstart = outp;	else		fieldstart = outp + bytesperline;	linesdone = dma_q->pos / bytesperline;	currlinedone = dma_q->pos % bytesperline;	offset = linesdone * bytesperline * 2 + currlinedone;	startwrite = fieldstart + offset;	lencopy = bytesperline - currlinedone;	lencopy = lencopy > remain ? remain : lencopy;	if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) {		em28xx_isocdbg("Overflow of %zi bytes past buffer end (1)\n",			       ((char *)startwrite + lencopy) -			       ((char *)outp + buf->vb.size));		lencopy = remain = (char *)outp + buf->vb.size - (char *)startwrite;	}	if (lencopy <= 0)		return;	memcpy(startwrite, startread, lencopy);	remain -= lencopy;	while (remain > 0) {		startwrite += lencopy + bytesperline;		startread += lencopy;		if (bytesperline > remain)			lencopy = remain;		else			lencopy = bytesperline;		if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) {			em28xx_isocdbg("Overflow of %zi bytes past buffer end (2)\n",				       ((char *)startwrite + lencopy) -				       ((char *)outp + buf->vb.size));			lencopy = remain = (char *)outp + buf->vb.size -					   (char *)startwrite;		}		if (lencopy <= 0)			break;		memcpy(startwrite, startread, lencopy);		remain -= lencopy;	}	dma_q->pos += len;}static inline void print_err_status(struct em28xx *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) {		em28xx_isocdbg("URB status %d [%s].\n",	status, errmsg);	} else {		em28xx_isocdbg("URB packet %d, status %d [%s].\n",			       packet, status, errmsg);	}}/* * video-buf generic routine to get the next available buffer */static inline void get_next_buf(struct em28xx_dmaqueue *dma_q,					  struct em28xx_buffer **buf){	struct em28xx *dev = container_of(dma_q, struct em28xx, vidq);#if 1	char *outp;#endif	if (list_empty(&dma_q->active)) {		em28xx_isocdbg("No active queue to serve\n");		dev->isoc_ctl.buf = NULL;		*buf = NULL;		return;	}	/* Get the next buffer */	*buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue);#if 1	/* Cleans up buffer - Usefull for testing for frame/URB loss */	outp = videobuf_to_vmalloc(&(*buf)->vb);	memset(outp, 0, (*buf)->vb.size);#endif	dev->isoc_ctl.buf = *buf;	return;}/* * Controls the isoc copy of each urb packet */static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb){	struct em28xx_buffer    *buf;	struct em28xx_dmaqueue  *dma_q = urb->context;	unsigned char *outp = NULL;	int i, len = 0, rc = 1;	unsigned char *p;	if (!dev)		return 0;	if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))		return 0;	if (urb->status < 0) {		print_err_status(dev, -1, urb->status);		if (urb->status == -ENOENT)			return 0;	}	buf = dev->isoc_ctl.buf;	if (buf != NULL)		outp = videobuf_to_vmalloc(&buf->vb);	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);			if (urb->iso_frame_desc[i].status != -EPROTO)				continue;		}		len = urb->iso_frame_desc[i].actual_length - 4;		if (urb->iso_frame_desc[i].actual_length <= 0) {			/* em28xx_isocdbg("packet %d is empty",i); - spammy */			continue;		}		if (urb->iso_frame_desc[i].actual_length >						dev->max_pkt_size) {			em28xx_isocdbg("packet bigger than packet size");			continue;		}		p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;		/* FIXME: incomplete buffer checks where removed to make		   logic simpler. Impacts of those changes should be evaluated		 */		if (p[0] == 0x33 && p[1] == 0x95 && p[2] == 0x00) {			em28xx_isocdbg("VBI HEADER!!!\n");			/* FIXME: Should add vbi copy */			continue;		}		if (p[0] == 0x22 && p[1] == 0x5a) {			em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2],				       len, (p[2] & 1)? "odd" : "even");			if (!(p[2] & 1)) {				if (buf != NULL)					buffer_filled(dev, dma_q, buf);				get_next_buf(dma_q, &buf);				if (buf == NULL)					outp = NULL;				else					outp = videobuf_to_vmalloc(&buf->vb);			}			if (buf != NULL) {				if (p[2] & 1)					buf->top_field = 0;				else					buf->top_field = 1;			}			dma_q->pos = 0;		}		if (buf != NULL)			em28xx_copy_video(dev, dma_q, buf, p, outp, len);	}	return rc;}/* ------------------------------------------------------------------	Videobuf operations   ------------------------------------------------------------------*/static intbuffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size){	struct em28xx_fh *fh = vq->priv_data;	struct em28xx        *dev = fh->dev;	struct v4l2_frequency f;	*size = 16 * fh->dev->width * fh->dev->height >> 3;	if (0 == *count)		*count = EM28XX_DEF_BUF;	if (*count < EM28XX_MIN_BUF)		*count = EM28XX_MIN_BUF;	/* Ask tuner to go to analog mode */	memset(&f, 0, sizeof(f));	f.frequency = dev->ctl_freq;	em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f);	return 0;}/* This is called *without* dev->slock held; please keep it that way */static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf){	struct em28xx_fh     *fh  = vq->priv_data;	struct em28xx        *dev = fh->dev;	unsigned long flags = 0;	if (in_interrupt())		BUG();	/* We used to wait for the buffer to finish here, but this didn't work	   because, as we were keeping the state as VIDEOBUF_QUEUED,	   videobuf_queue_cancel marked it as finished for us.	   (Also, it could wedge forever if the hardware was misconfigured.)	   This should be safe; by the time we get here, the buffer isn't	   queued anymore. If we ever start marking the buffers as	   VIDEOBUF_ACTIVE, it won't be, though.	*/	spin_lock_irqsave(&dev->slock, flags);	if (dev->isoc_ctl.buf == buf)		dev->isoc_ctl.buf = NULL;	spin_unlock_irqrestore(&dev->slock, flags);	videobuf_vmalloc_free(&buf->vb);	buf->vb.state = VIDEOBUF_NEEDS_INIT;}static intbuffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,						enum v4l2_field field){	struct em28xx_fh     *fh  = vq->priv_data;	struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb);	struct em28xx        *dev = fh->dev;	int                  rc = 0, urb_init = 0;	/* FIXME: It assumes depth = 16 */	/* The only currently supported format is 16 bits/pixel */	buf->vb.size = 16 * dev->width * dev->height >> 3;	if (0 != buf->vb.baddr  &&  buf->vb.bsize < buf->vb.size)		return -EINVAL;	buf->vb.width  = dev->width;	buf->vb.height = dev->height;	buf->vb.field  = field;	if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {		rc = videobuf_iolock(vq, &buf->vb, NULL);		if (rc < 0)			goto fail;	}	if (!dev->isoc_ctl.num_bufs)		urb_init = 1;	if (urb_init) {		rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS,				      EM28XX_NUM_BUFS, dev->max_pkt_size,				      em28xx_isoc_copy);		if (rc < 0)			goto fail;	}	buf->vb.state = VIDEOBUF_PREPARED;	return 0;fail:	free_buffer(vq, buf);	return rc;}static voidbuffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb){	struct em28xx_buffer    *buf     = container_of(vb, struct em28xx_buffer, vb);	struct em28xx_fh        *fh      = vq->priv_data;	struct em28xx           *dev     = fh->dev;	struct em28xx_dmaqueue  *vidq    = &dev->vidq;	buf->vb.state = VIDEOBUF_QUEUED;	list_add_tail(&buf->vb.queue, &vidq->active);}static void buffer_release(struct videobuf_queue *vq,				struct videobuf_buffer *vb){	struct em28xx_buffer   *buf  = container_of(vb, struct em28xx_buffer, vb);	struct em28xx_fh       *fh   = vq->priv_data;	struct em28xx          *dev  = (struct em28xx *)fh->dev;	em28xx_isocdbg("em28xx: called buffer_release\n");	free_buffer(vq, buf);}static struct videobuf_queue_ops em28xx_video_qops = {	.buf_setup      = buffer_setup,	.buf_prepare    = buffer_prepare,	.buf_queue      = buffer_queue,	.buf_release    = buffer_release,};/*********************  v4l2 interface  **************************************//* * em28xx_config() * inits registers with sane defaults */static int em28xx_config(struct em28xx *dev){	int retval;	/* Sets I2C speed to 100 KHz */	if (!dev->is_em2800) {		retval = em28xx_write_regs_req(dev, 0x00, 0x06, "\x40", 1);		if (retval < 0) {			em28xx_errdev("%s: em28xx_write_regs_req failed! retval [%d]\n",				__func__, retval);			return retval;		}	}#if 1	/* enable vbi capturing *//*	em28xx_write_regs_req(dev, 0x00, 0x0e, "\xC0", 1); audio register *//*	em28xx_write_regs_req(dev, 0x00, 0x0f, "\x80", 1); clk register */	em28xx_write_regs_req(dev, 0x00, 0x11, "\x51", 1);#endif	dev->mute = 1;		/* maybe not the right place... */	dev->volume = 0x1f;	em28xx_outfmt_set_yuv422(dev);	em28xx_colorlevels_set_default(dev);	em28xx_compression_disable(dev);	return 0;}/* * em28xx_config_i2c() * configure i2c attached devices */static void em28xx_config_i2c(struct em28xx *dev){	struct v4l2_routing route;	route.input = INPUT(dev->ctl_input)->vmux;	route.output = 0;	em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, NULL);	em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);	em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL);}static void video_mux(struct em28xx *dev, int index){	struct v4l2_routing route;	route.input = INPUT(index)->vmux;	route.output = 0;	dev->ctl_input = index;	dev->ctl_ainput = INPUT(index)->amux;	em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);	if (dev->has_msp34xx) {		if (dev->i2s_speed) {			em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ,				&dev->i2s_speed);		}		route.input = dev->ctl_ainput;		route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);		/* Note: this is msp3400 specific */		em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING,			&route);	}	em28xx_audio_analog_set(dev);}/* Usage lock check functions */static int res_get(struct em28xx_fh *fh){	struct em28xx    *dev = fh->dev;	int		 rc   = 0;	/* This instance already has stream_on */	if (fh->stream_on)		return rc;	if (dev->stream_on)		return -EINVAL;	mutex_lock(&dev->lock);

⌨️ 快捷键说明

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