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

📄 qcamvc_pp.c

📁 VC编写的USB QuickCam驱动程序,实现四种视频格式在linux下的编码,信号来自摄像源
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * *    Connectix USB QuickCam VC Video Camera driver * *    (C) Copyright 2001 De Marchi Daniele *    (C) Copyright 2004 Terry Mohan * *    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. * *    Jan 2001 This driver develop started on the linux  *            kernel 2.4.0. * */#undef _QCAMVC_DEBUG_ //#define _QCAMVC_DEBUG_  #undef QCAMVC_PARPORT_DMA#define QCAMVC_PARPORT_DMA 1   /* comment this out to remove ECP DMA support */#define CONFIG_PARPORT_PC #include <linux/config.h>#include <linux/version.h>#include <linux/module.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/spinlock.h>#include <linux/pci.h>#include <asm/dma.h> #include <linux/parport.h>#include <linux/parport_pc.h>#include <linux/delay.h>#include <linux/sched.h>#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,3))#ifdef CONFIG_KMOD#include <linux/kmod.h>#endif#else#include <linux/kmod.h>#endif#include "qcamvc.h"struct pp_cam_entry{	struct parport *port;	struct pardevice *pdev;		int open_count;};/* Magic numbers for defining port-device mappings */#define QCAMVC_PARPORT_UNSPEC -4#define QCAMVC_PARPORT_AUTO -3#define QCAMVC_PARPORT_OFF -2#define QCAMVC_PARPORT_NONE -1#ifdef MODULEstatic int parport_nr[PARPORT_MAX] = {[0 ... PARPORT_MAX - 1] = QCAMVC_PARPORT_UNSPEC};static char *parport[PARPORT_MAX] = {NULL,};MODULE_AUTHOR("De Marchi Daniele <demarchidaniele@libero.it>");MODULE_DESCRIPTION("Connectix QuickCam VC(PP) Driver");MODULE_LICENSE("GPL");MODULE_PARM(parport, "1-" __MODULE_STRING(PARPORT_MAX) "s");MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp.");#elsestatic int parport_nr[PARPORT_MAX] __initdata =	{[0 ... PARPORT_MAX - 1] = QCAMVC_PARPORT_UNSPEC};static int parport_ptr = 0;#endif/*****************************************************************************//*                                                                           *//* Lowlevel driver for Connectix QuickCam VC(PP)                             *//*                                                                           *//*****************************************************************************/static int		qcamvc_open(void *privdata);static int		qcamvc_close(void *privdata);static size_t	qcamvc_get_reg(void *privdata, unsigned char reg, void *buffer, size_t len);static int		qcamvc_set_reg(void *privdata, unsigned char reg, void *buffer, size_t len);static size_t	qcamvc_read(void *privdata, void *buffer, size_t len);static int		qcamvc_write(void *privdata, void *buffer, size_t len);static int		qcamvc_stream_start(void *privdata);static int		qcamvc_stream_stop(void *privdata);static size_t	qcamvc_stream_read(void *privdata, void *buffer, size_t len);static struct qcamvc_camera_ops qcamvc_pp_ops = {	qcamvc_open,	qcamvc_close,	qcamvc_write,	qcamvc_read,	qcamvc_set_reg,	qcamvc_get_reg,	qcamvc_stream_start,	qcamvc_stream_stop,	qcamvc_stream_read,	THIS_MODULE,};static LIST_HEAD(cam_list);static spinlock_t cam_list_lock_pp;static unsigned short ppmode = IEEE1284_MODE_ECP;#ifdef QCAMVC_PARPORT_DMA/* forward declarations for IEEE1284 ECP DMA read support  */static int change_mode(struct parport *, int );static void frob_econtrol (struct parport *, unsigned char, unsigned char);/* ----------------------------------------*//* Parallel Port DMA Code from CPiA driver *//* ----------------------------------------*//* DMA support here thanks to: Blaise Gassend <blaise@gassend.com> *//*ECP parport lowlevel IEEE1284 DMA read support: */#define ECR_PS2 01#define ECR_ECP 03/* this an adaptation of code from parport_pc_ecp_read_block_pio() and * parport_pc_fifo_write_block_dma() */static size_t qcamvc_ecp_read_block_dma (struct parport *port,				       void *bufv, size_t length, int flags){	int r = 0;	unsigned long dmaflag;	size_t left = length;	const struct parport_pc_private *priv = port->physport->private_data;	dma_addr_t dma_addr, dma_handle = 0;	size_t maxlen = 0x10000; /* max 64k per DMA transfer */	char *buf = bufv;	unsigned long start = (unsigned long) buf;	unsigned long end = (unsigned long) buf + length - 1;	size_t count = length;		if (length == 0)	{		DBG ("(length == 0)");		return 0;	}		/* Special case: a timeout of zero means we cannot call schedule(). */	if (!port->cad->timeout)	{		DBG ("cannot call schedule()");		return parport_ieee1284_ecp_read_data (port, buf, length, flags);	}		port = port->physport;		if (port->ieee1284.phase != IEEE1284_PH_REV_IDLE)	{		/* change to reverse-idle phase (must be in forward-idle) */				/* Event 38: Set nAutoFd low (also make sure nStrobe is high) */ 		parport_frob_control (port,				      PARPORT_CONTROL_STROBE |PARPORT_CONTROL_AUTOFD,				      PARPORT_CONTROL_AUTOFD);		parport_pc_data_reverse (port); /* Must be in PS2 mode */		udelay (5);				/* Event 39: Set nInit low to initiate bus reversal */		parport_frob_control (port,				      PARPORT_CONTROL_INIT,				      0);				/* Event 40: Wait for  nAckReverse (PError) to go low */		if ( (r = parport_wait_peripheral (port, 						  PARPORT_STATUS_PAPEROUT, 						  0)) )		{			printk (KERN_DEBUG "%s: PE timeout Event 40 (%d) "				"in ecp_read_block_dma\n", port->name, r);			return 0;		}				port->ieee1284.phase = IEEE1284_PH_REV_DATA;				parport_frob_control (port,				      PARPORT_CONTROL_STROBE |PARPORT_CONTROL_AUTOFD,				      PARPORT_CONTROL_AUTOFD);		if (change_mode (port, ECR_ECP))      /* ECP FIFO */			printk (KERN_DEBUG "%s: Warning change_mode ECR_ECP failed\n", 				port->name);				/* the first byte must be collected manually */		/* Event 43: Wait for nAck to go low */		if (parport_wait_peripheral (port, 					     PARPORT_STATUS_ACK, 					     0))		{			/* timed out while reading -- no data */			printk (KERN_DEBUG "DMA read timed out (initial byte)\n");			goto out_no_data;		}#if 0   /* this is active in parport_pc_ecp_read_block_pio */		/* read byte */		*buf++ = inb (DATA (port));	       	left--;#endif				/* Event 44: nAutoFd (HostAck) goes high to acknowledge */		parport_pc_frob_control (port,					 PARPORT_CONTROL_AUTOFD,					 0);		#if 0  /* this is commented out  in parport_pc_ecp_read_block_pio */		/* Event 45: Wait for nAck to go high */		if (parport_wait_peripheral (port, 					     PARPORT_STATUS_ACK, 					     PARPORT_STATUS_ACK)) {			/* timed out while waiting for peripheral to respond to ack */			printk ("ECP DMA read timed out (waiting for nAck)\n");						/* keep hold of the byte we've got already */			goto out_no_data;		}#endif	}	#if 0   /* active in parport_pc_ecp_read_block_pio */	/* Event 46: nAutoFd (HostAck) goes low to accept more data */	parport_pc_frob_control (port,				 PARPORT_CONTROL_AUTOFD,				 PARPORT_CONTROL_AUTOFD);#endif		if (!left) 	{		DBG ("(!left)\n");		goto out_no_data;	}		/* now do the DMA transfer (adapted from parport_pc_fifo_write_block_dma) */		if (end < MAX_DMA_ADDRESS)	{		/* If it would cross a 64k boundary, cap it at the end. */		if ((start ^ end) & ~0xffffUL)			maxlen = 0x10000 - (start & 0xffff);				dma_addr = dma_handle = pci_map_single(priv->dev, (void *)buf, length,						       PCI_DMA_FROMDEVICE);	}	else	{		/* above 16 MB we use a bounce buffer as ISA-DMA is not possible */		maxlen   = PAGE_SIZE;          /* sizeof(priv->dma_buf) */		dma_addr = priv->dma_handle;	}		DBG ("DMA buffer is ready\n");		/* We don't want to be interrupted every character. */	parport_pc_disable_irq (port);	/* set nErrIntrEn and serviceIntr */	frob_econtrol (port, (1<<4) | (1<<2), (1<<4) | (1<<2));		/* Reverse mode. */	parport_pc_data_reverse (port); /* Must be in PS2 mode */	while (left)	{#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))		long expire = jiffies + port->physport->cad->timeout;#else		unsigned long expire = jiffies + port->physport->cad->timeout;#endif			//DBG ("start of while %i left\n", left);				count = left;				if (count > maxlen)			count = maxlen;				dmaflag = claim_dma_lock();		disable_dma(port->dma);		clear_dma_ff(port->dma);		set_dma_mode(port->dma, DMA_MODE_READ);		set_dma_addr(port->dma, dma_addr);		set_dma_count(port->dma, count);				/* Set DMA mode */		frob_econtrol (port, 1<<3, 1<<3);				/* Clear serviceIntr */		frob_econtrol (port, 1<<2, 0);				enable_dma(port->dma);		release_dma_lock(dmaflag);				/* Wait for interrupt. */		//DBG ("about to wait\n");false_alarm:		if (parport_wait_event (port, HZ) < 0)		{			DBG ("(parport_wait_event (port, HZ) < 0)\n");			break;		}		if (!time_before (jiffies, expire))		{			/* Timed out. */			DBG("DMA read timed out\n");			break;		}		/* Is serviceIntr set? */		if (!(inb (ECONTROL (port)) & (1<<2)))		{#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))			if (current->need_resched)				schedule ();#else			cond_resched();#endif						DBG("false_alarm\n");			goto false_alarm;		}				dmaflag = claim_dma_lock();		disable_dma(port->dma);		clear_dma_ff(port->dma);		count -= get_dma_residue(port->dma);		release_dma_lock(dmaflag);#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))				if (current->need_resched)			/* Can't yield the port. */			schedule ();#else		cond_resched(); /* Can't yield the port. */#endif		/* update for possible DMA residue ! */		DBG ("read %i in loop\n", count);				if (dma_handle) 			dma_addr += count;		else 			memcpy(buf, priv->dma_buf, count);		buf  += count;		left -= count;	}		frob_econtrol (port, 1<<2, 1<<2);	/* Turn off DMA mode */	frob_econtrol (port, 1<<3, 0);		if (dma_handle)		pci_unmap_single(priv->dev, dma_handle, length, PCI_DMA_TODEVICE);	out_no_data:		if (change_mode (port, ECR_PS2)) /* ECP FIFO */ 		printk (KERN_DEBUG "%s: Warning change_mode ECR_PS2 failed\n", 			port->name);		/* Go to forward idle mode to shut the peripheral up (event 47). */	parport_frob_control (port, PARPORT_CONTROL_INIT | PARPORT_CONTROL_AUTOFD, 			      PARPORT_CONTROL_INIT | PARPORT_CONTROL_AUTOFD);		/* event 49: PError goes high */	if ( (r = parport_wait_peripheral (port,					  PARPORT_STATUS_PAPEROUT,					  PARPORT_STATUS_PAPEROUT)) )	{		printk (KERN_DEBUG			"%s: PE timeout FWDIDLE (%d) in ecp_read_block_dma\n",			port->name, r);	}	else	{		parport_data_forward (port);        		port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;	}	//DBG ("returning %i bytes read, endseen = %d\n", length - left, endseen);	return length - left;}#endif /*QCAMVC_PARPORT_DMA */static int qcamvc_open(void *privdata){	struct pp_cam_entry *cam = (struct pp_cam_entry *)privdata;	if (cam == NULL)		return -EINVAL;	if(cam->open_count == 0)	{		if (parport_claim_or_block(cam->pdev))		{			printk(KERN_ERR "failed to claim the port\n");			return -EBUSY;		}		parport_negotiate(cam->port, IEEE1284_MODE_COMPAT);		parport_data_forward(cam->port);		parport_write_control(cam->port, PARPORT_CONTROL_SELECT);		udelay(50);		parport_write_control(cam->port, PARPORT_CONTROL_SELECT|PARPORT_CONTROL_INIT);	}		++cam->open_count;		return 0;}static int qcamvc_close(void *privdata){	struct pp_cam_entry *cam = privdata;	if(!cam || !cam->open_count) return 0;		if (--cam->open_count == 0)	{		parport_release(cam->pdev);	}		return 0;}static int qcamvc_stream_start(void *privdata){	int ret = 0;	struct pp_cam_entry *cam = privdata;	if(!cam || !cam->open_count) return 0;		struct parport *parport = cam->port;	parport_pc_data_reverse (parport); /* Must be in PS2 mode */	return ret;}static int qcamvc_stream_stop(void *privdata){	int ret = 0;	return ret;}static size_t qcamvc_stream_read(void *privdata, void *buffer, size_t len){	struct pp_cam_entry *cam = privdata;	if(!cam || !cam->open_count) return 0;		struct parport *parport = cam->port;	//unsigned char a = 0x0;	/*if(parport_negotiate(parport, IEEE1284_DATA | ppmode) != 0)	{		printk(KERN_ERR "QuickCamVC: qcamvc_stream_read1: ieee1284 failed negotiation\n");		return -1;	}	parport_write(parport, (void *) &a, 1);*/	#ifdef QCAMVC_PARPORT_DMA	if (parport->dma != PARPORT_DMA_NONE)	{		int read_bytes = 0,  chunks = 0;		size_t chunksize = len;		u8 *buf = buffer, *buf_end = buffer + len;		size_t datasize, bufsize;		while (read_bytes < len)		{#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))			if (current->need_resched)				schedule ();#else			cond_resched();#endif			bufsize = chunksize;			if(bufsize > (size_t)(buf_end - buf)) 				bufsize = (size_t)(buf_end - buf);				datasize = qcamvc_ecp_read_block_dma(parport, buf, bufsize, 0);			if (datasize <= 0)			{				//printk("datasize <= 0\n");				break;			}			chunks++;				buf += datasize;			read_bytes += (int) datasize;		}				return read_bytes;	}#endif		unsigned long counter;	int read, read_bytes = 0;	for (counter=0;counter<len;counter++)	{#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))		if (current->need_resched)			schedule ();#else

⌨️ 快捷键说明

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