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

📄 gspca.c

📁 trident tm5600的linux驱动
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * Main USB camera driver * * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> * * 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. */#define MODULE_NAME "gspca"#include <linux/init.h>#include <linux/version.h>#include <linux/fs.h>#include <linux/vmalloc.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/string.h>#include <linux/pagemap.h>#include <linux/io.h>#include <linux/kref.h>#include <asm/page.h>#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)#include <asm/uaccess.h>#else#include <linux/uaccess.h>#endif#include <linux/jiffies.h>#include <media/v4l2-ioctl.h>#include "gspca.h"/* global values */#define DEF_NURBS 2		/* default number of URBs */MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");MODULE_DESCRIPTION("GSPCA USB Camera Driver");MODULE_LICENSE("GPL");#define DRIVER_VERSION_NUMBER	KERNEL_VERSION(2, 3, 0)static int video_nr = -1;#ifdef GSPCA_DEBUGint gspca_debug = D_ERR | D_PROBE;EXPORT_SYMBOL(gspca_debug);static void PDEBUG_MODE(char *txt, __u32 pixfmt, int w, int h){	if ((pixfmt >> 24) >= '0' && (pixfmt >> 24) <= 'z') {		PDEBUG(D_CONF|D_STREAM, "%s %c%c%c%c %dx%d",			txt,			pixfmt & 0xff,			(pixfmt >> 8) & 0xff,			(pixfmt >> 16) & 0xff,			pixfmt >> 24,			w, h);	} else {		PDEBUG(D_CONF|D_STREAM, "%s 0x%08x %dx%d",			txt,			pixfmt,			w, h);	}}#else#define PDEBUG_MODE(txt, pixfmt, w, h)#endif/* specific memory types - !! should different from V4L2_MEMORY_xxx */#define GSPCA_MEMORY_NO 0	/* V4L2_MEMORY_xxx starts from 1 */#define GSPCA_MEMORY_READ 7#define BUF_ALL_FLAGS (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE)/* * VMA operations. */static void gspca_vm_open(struct vm_area_struct *vma){	struct gspca_frame *frame = vma->vm_private_data;	frame->vma_use_count++;	frame->v4l2_buf.flags |= V4L2_BUF_FLAG_MAPPED;}static void gspca_vm_close(struct vm_area_struct *vma){	struct gspca_frame *frame = vma->vm_private_data;	if (--frame->vma_use_count <= 0)		frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_MAPPED;}static struct vm_operations_struct gspca_vm_ops = {	.open		= gspca_vm_open,	.close		= gspca_vm_close,};/* get the current input frame buffer */struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev){	struct gspca_frame *frame;	int i;	i = gspca_dev->fr_i;	i = gspca_dev->fr_queue[i];	frame = &gspca_dev->frame[i];	if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS)				!= V4L2_BUF_FLAG_QUEUED)		return NULL;	return frame;}EXPORT_SYMBOL(gspca_get_i_frame);/* * fill a video frame from an URB and resubmit */static void fill_frame(struct gspca_dev *gspca_dev,			struct urb *urb){	struct gspca_frame *frame;	__u8 *data;		/* address of data in the iso message */	int i, len, st;	cam_pkt_op pkt_scan;	if (urb->status != 0) {#ifdef CONFIG_PM		if (!gspca_dev->frozen)#endif			PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);		return;		/* disconnection ? */	}	pkt_scan = gspca_dev->sd_desc->pkt_scan;	for (i = 0; i < urb->number_of_packets; i++) {		/* check the availability of the frame buffer */		frame = gspca_get_i_frame(gspca_dev);		if (!frame) {			gspca_dev->last_packet_type = DISCARD_PACKET;			break;		}		/* check the packet status and length */		len = urb->iso_frame_desc[i].actual_length;		if (len == 0)			continue;		st = urb->iso_frame_desc[i].status;		if (st) {			PDEBUG(D_ERR,				"ISOC data error: [%d] len=%d, status=%d",				i, len, st);			gspca_dev->last_packet_type = DISCARD_PACKET;			continue;		}		/* let the packet be analyzed by the subdriver */		PDEBUG(D_PACK, "packet [%d] o:%d l:%d",			i, urb->iso_frame_desc[i].offset, len);		data = (__u8 *) urb->transfer_buffer					+ urb->iso_frame_desc[i].offset;		pkt_scan(gspca_dev, frame, data, len);	}	/* resubmit the URB */	urb->status = 0;	st = usb_submit_urb(urb, GFP_ATOMIC);	if (st < 0)		PDEBUG(D_ERR|D_PACK, "usb_submit_urb() ret %d", st);}/* * ISOC message interrupt from the USB device * * Analyse each packet and call the subdriver for copy to the frame buffer. */static void isoc_irq(struct urb *urb#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)			, struct pt_regs *regs#endif){	struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;	PDEBUG(D_PACK, "isoc irq");	if (!gspca_dev->streaming)		return;	fill_frame(gspca_dev, urb);}/* * bulk message interrupt from the USB device */static void bulk_irq(struct urb *urb#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)			, struct pt_regs *regs#endif){	struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;	struct gspca_frame *frame;	PDEBUG(D_PACK, "bulk irq");	if (!gspca_dev->streaming)		return;	if (urb->status != 0 && urb->status != -ECONNRESET) {#ifdef CONFIG_PM		if (!gspca_dev->frozen)#endif			PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);		return;		/* disconnection ? */	}	/* check the availability of the frame buffer */	frame = gspca_get_i_frame(gspca_dev);	if (!frame) {		gspca_dev->last_packet_type = DISCARD_PACKET;	} else {		PDEBUG(D_PACK, "packet l:%d", urb->actual_length);		gspca_dev->sd_desc->pkt_scan(gspca_dev,					frame,					urb->transfer_buffer,					urb->actual_length);	}}/* * add data to the current frame * * This function is called by the subdrivers at interrupt level. * * To build a frame, these ones must add *	- one FIRST_PACKET *	- 0 or many INTER_PACKETs *	- one LAST_PACKET * DISCARD_PACKET invalidates the whole frame. * On LAST_PACKET, a new frame is returned. */struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,				    enum gspca_packet_type packet_type,				    struct gspca_frame *frame,				    const __u8 *data,				    int len){	int i, j;	PDEBUG(D_PACK, "add t:%d l:%d",	packet_type, len);	/* when start of a new frame, if the current frame buffer	 * is not queued, discard the whole frame */	if (packet_type == FIRST_PACKET) {		if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS)						!= V4L2_BUF_FLAG_QUEUED) {			gspca_dev->last_packet_type = DISCARD_PACKET;			return frame;		}		frame->data_end = frame->data;		jiffies_to_timeval(get_jiffies_64(),				   &frame->v4l2_buf.timestamp);		frame->v4l2_buf.sequence = ++gspca_dev->sequence;	} else if (gspca_dev->last_packet_type == DISCARD_PACKET) {		if (packet_type == LAST_PACKET)			gspca_dev->last_packet_type = packet_type;		return frame;	}	/* append the packet to the frame buffer */	if (len > 0) {		if (frame->data_end - frame->data + len						 > frame->v4l2_buf.length) {			PDEBUG(D_ERR|D_PACK, "frame overflow %zd > %d",				frame->data_end - frame->data + len,				frame->v4l2_buf.length);			packet_type = DISCARD_PACKET;		} else {			memcpy(frame->data_end, data, len);			frame->data_end += len;		}	}	gspca_dev->last_packet_type = packet_type;	/* if last packet, wake up the application and advance in the queue */	if (packet_type == LAST_PACKET) {		frame->v4l2_buf.bytesused = frame->data_end - frame->data;		frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED;		frame->v4l2_buf.flags |= V4L2_BUF_FLAG_DONE;		atomic_inc(&gspca_dev->nevent);		wake_up_interruptible(&gspca_dev->wq);	/* event = new frame */		i = (gspca_dev->fr_i + 1) % gspca_dev->nframes;		gspca_dev->fr_i = i;		PDEBUG(D_FRAM, "frame complete len:%d q:%d i:%d o:%d",			frame->v4l2_buf.bytesused,			gspca_dev->fr_q,			i,			gspca_dev->fr_o);		j = gspca_dev->fr_queue[i];		frame = &gspca_dev->frame[j];	}	return frame;}EXPORT_SYMBOL(gspca_frame_add);static int gspca_is_compressed(__u32 format){	switch (format) {	case V4L2_PIX_FMT_MJPEG:	case V4L2_PIX_FMT_JPEG:	case V4L2_PIX_FMT_SPCA561:	case V4L2_PIX_FMT_PAC207:		return 1;	}	return 0;}static void *rvmalloc(unsigned long size){	void *mem;	unsigned long adr;	mem = vmalloc_32(size);	if (mem != NULL) {		adr = (unsigned long) mem;		while ((long) size > 0) {			SetPageReserved(vmalloc_to_page((void *) adr));			adr += PAGE_SIZE;			size -= PAGE_SIZE;		}	}	return mem;}static void rvfree(void *mem, long size){	unsigned long adr;	adr = (unsigned long) mem;	while (size > 0) {		ClearPageReserved(vmalloc_to_page((void *) adr));		adr += PAGE_SIZE;		size -= PAGE_SIZE;	}	vfree(mem);}static int frame_alloc(struct gspca_dev *gspca_dev,			unsigned int count){	struct gspca_frame *frame;	unsigned int frsz;	int i;	i = gspca_dev->curr_mode;	frsz = gspca_dev->cam.cam_mode[i].sizeimage;	PDEBUG(D_STREAM, "frame alloc frsz: %d", frsz);	frsz = PAGE_ALIGN(frsz);	gspca_dev->frsz = frsz;	if (count > GSPCA_MAX_FRAMES)		count = GSPCA_MAX_FRAMES;	gspca_dev->frbuf = rvmalloc(frsz * count);	if (!gspca_dev->frbuf) {		err("frame alloc failed");		return -ENOMEM;	}	gspca_dev->nframes = count;	for (i = 0; i < count; i++) {		frame = &gspca_dev->frame[i];		frame->v4l2_buf.index = i;		frame->v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;		frame->v4l2_buf.flags = 0;		frame->v4l2_buf.field = V4L2_FIELD_NONE;		frame->v4l2_buf.length = frsz;		frame->v4l2_buf.memory = gspca_dev->memory;		frame->v4l2_buf.sequence = 0;		frame->data = frame->data_end =					gspca_dev->frbuf + i * frsz;		frame->v4l2_buf.m.offset = i * frsz;	}	gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0;	gspca_dev->last_packet_type = DISCARD_PACKET;	gspca_dev->sequence = 0;	atomic_set(&gspca_dev->nevent, 0);	return 0;}static void frame_free(struct gspca_dev *gspca_dev){	int i;	PDEBUG(D_STREAM, "frame free");	if (gspca_dev->frbuf != NULL) {		rvfree(gspca_dev->frbuf,			gspca_dev->nframes * gspca_dev->frsz);		gspca_dev->frbuf = NULL;		for (i = 0; i < gspca_dev->nframes; i++)			gspca_dev->frame[i].data = NULL;	}	gspca_dev->nframes = 0;}static void destroy_urbs(struct gspca_dev *gspca_dev){	struct urb *urb;	unsigned int i;	PDEBUG(D_STREAM, "kill transfer");	for (i = 0; i < MAX_NURBS; i++) {		urb = gspca_dev->urb[i];		if (urb == NULL)			break;		gspca_dev->urb[i] = NULL;		usb_kill_urb(urb);		if (urb->transfer_buffer != NULL)			usb_buffer_free(gspca_dev->dev,					urb->transfer_buffer_length,					urb->transfer_buffer,					urb->transfer_dma);		usb_free_urb(urb);	}}/* * look for an input transfer endpoint in an alternate setting */static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt,					  __u8 epaddr,					  __u8 xfer){	struct usb_host_endpoint *ep;	int i, attr;	epaddr |= USB_DIR_IN;	for (i = 0; i < alt->desc.bNumEndpoints; i++) {		ep = &alt->endpoint[i];		if (ep->desc.bEndpointAddress == epaddr) {			attr = ep->desc.bmAttributes						& USB_ENDPOINT_XFERTYPE_MASK;			if (attr == xfer)				return ep;			break;		}	}	return NULL;}/* * look for an input (isoc or bulk) endpoint * * The endpoint is defined by the subdriver. * Use only the first isoc (some Zoran - 0x0572:0x0001 - have two such ep). * This routine may be called many times when the bandwidth is too small * (the bandwidth is checked on urb submit). */static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev){	struct usb_interface *intf;	struct usb_host_endpoint *ep;	int i, ret;	intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);	ep = NULL;	i = gspca_dev->alt;			/* previous alt setting */	/* try isoc */	while (--i > 0) {			/* alt 0 is unusable */		ep = alt_xfer(&intf->altsetting[i],				gspca_dev->cam.epaddr,				USB_ENDPOINT_XFER_ISOC);		if (ep)			break;	}	/* if no isoc, try bulk */	if (ep == NULL) {		ep = alt_xfer(&intf->altsetting[0],				gspca_dev->cam.epaddr,				USB_ENDPOINT_XFER_BULK);		if (ep == NULL) {			err("no transfer endpoint found");			return NULL;		}	}	PDEBUG(D_STREAM, "use alt %d ep 0x%02x",			i, ep->desc.bEndpointAddress);	if (i > 0) {		ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i);		if (ret < 0) {			err("set interface err %d", ret);			return NULL;		}	}	gspca_dev->alt = i;		/* memorize the current alt setting */	return ep;}/* * create the URBs for image transfer */static int create_urbs(struct gspca_dev *gspca_dev,			struct usb_host_endpoint *ep){	struct urb *urb;	int n, nurbs, i, psize, npkt, bsize;	/* calculate the packet size and the number of packets */	psize = le16_to_cpu(ep->desc.wMaxPacketSize);

⌨️ 快捷键说明

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