📄 qcamvc.c
字号:
/* * * Connectix USB QuickCam VC Video Camera driver * * Copyright 2001 De Marchi Daniele * Copyright 2004 Terry Mohan * Copyright 2005 Troy Rollo * * 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. * */#include <linux/config.h>#include <linux/version.h>#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0))#include <linux/wrapper.h>#endif#ifdef CONFIG_KMOD#include <linux/kmod.h>#endif#include <linux/proc_fs.h>#include <linux/vmalloc.h>#include <linux/fs.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/sched.h>#include <linux/ctype.h>#include <linux/parport.h>#include <linux/delay.h>#include "qcamvc.h"//*#define _QCAMVC_DEBUG_//#define CONFIG_VIDEO_QCAMVC_PP_MODULE//#undef CONFIG_PROC_FSstatic int video_nr = -1;#ifdef MODULEMODULE_PARM(video_nr,"i");MODULE_AUTHOR("De Marchi Daniele <demarchidaniele@libero.it>");MODULE_DESCRIPTION("V4L-driver for QuickCam VC cameras");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("video");#endif#ifdef CONFIG_VIDEO_QCAMVC_PPextern int qcamvc_pp_init(void);#endif#ifdef CONFIG_VIDEO_QCAMVC_USBextern int qcamvc_usb_init(void);#endifstatic int qcamvc_vopen(struct inode *inode, struct file *file);static int qcamvc_vrelease(struct inode *inode, struct file *file);static int qcamvc_vioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);static ssize_t qcamvc_vread(struct file *file, char *data, size_t count, loff_t *ppos);static int qcamvc_vmmap(struct file *file, struct vm_area_struct *vma);static int qcamvc_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg);static void qcamvc_parse(struct qcamvc_data *qcamvc, unsigned char *outbuf);static int qcamvc_camera_init(struct qcamvc_data *qcamvc);static struct file_operations qcamvc_fops ={ .owner = THIS_MODULE, .open = qcamvc_vopen, .release = qcamvc_vrelease, .ioctl = qcamvc_vioctl, .llseek = no_llseek, .read = qcamvc_vread, .mmap = qcamvc_vmmap,};static struct video_device qcamvc_template ={ .name = "UNSET", .type = VID_TYPE_CAPTURE, .hardware = VID_HARDWARE_QCAM_C, .fops = &qcamvc_fops, .minor = -1,};#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,10) /* For kernel 2.6.10 and above -- Vikram */ int remap_pfn_range(struct vm_area_struct *vma, unsigned long uvaddr, unsigned long pfn, unsigned long size, pgprot_t prot); static inline int remap_page_range(struct vm_area_struct *vma, unsigned long uvaddr, unsigned long paddr, unsigned long size, pgprot_t prot) { return remap_pfn_range(vma, uvaddr, paddr >> PAGE_SHIFT, size, prot); } #endif /********************************************************************** * * Memory management * **********************************************************************//* Here we want the physical address of the memory. * This is used when initializing the contents of the area. */static inline unsigned long kvirt_to_pa(unsigned long adr){ unsigned long kva, ret; kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); kva |= adr & (PAGE_SIZE-1); ret = __pa(kva); return ret;}static void *rvmalloc(unsigned long size){ void *mem; unsigned long adr; size = PAGE_ALIGN(size); mem = vmalloc_32(size); if (!mem) return NULL; memset(mem, 0, size); adr = (unsigned long) mem; while (size > 0) { SetPageReserved(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; size -= PAGE_SIZE; } return mem;}static void rvfree(void *mem, unsigned long size){ unsigned long adr; if (!mem) return; adr = (unsigned long) mem; while ((long) size > 0) { ClearPageReserved(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; size -= PAGE_SIZE; } vfree(mem);}static inline int allocate_raw_frame(struct qcamvc_data *qcamvc){ if (!qcamvc) return -EFAULT; if (!qcamvc->raw_frame) { qcamvc->raw_frame = kmalloc(MAX_RAW_IMAGE_SIZE, GFP_KERNEL); if (!qcamvc->raw_frame) return -ENOBUFS; } /*if ( (qcamvc->bpc == 6) && !qcamvc->raw_frame_temp ) { qcamvc->raw_frame_temp = kmalloc(MAX_RAW_IMAGE_SIZE, GFP_KERNEL); if (!qcamvc->raw_frame_temp) return -ENOBUFS; }*/ return 0;}static void free_raw_frame(struct qcamvc_data *qcamvc){ if (!qcamvc) return; if (qcamvc->raw_frame) { kfree(qcamvc->raw_frame); qcamvc->raw_frame = NULL; } /*if (qcamvc->raw_frame_temp) { kfree(qcamvc->raw_frame_temp); qcamvc->raw_frame_temp = NULL; }*/}static inline int allocate_frame_buf(struct qcamvc_data *qcamvc){ if (!qcamvc) return -EFAULT; if (qcamvc->frame_buf) return 0; //already allocated /* tries to allocate all the memory needed for frame buffers */ qcamvc->frame_buf = rvmalloc(QCAMVC_NUMFRAMES * MAX_FRAME_SIZE); if (!qcamvc->frame_buf) return -ENOBUFS; int i; for (i = 0; i < QCAMVC_NUMFRAMES; i++) { /* stores the starting addresses of each frame buffer in the qcamvc->frame_buf */ qcamvc->frame[i].data = qcamvc->frame_buf + i * MAX_FRAME_SIZE; qcamvc->frame[i].state = FRAME_READY; } return 0;}static void free_frame_buf(struct qcamvc_data *qcamvc){ if (!qcamvc) return; if (qcamvc->frame_buf) { rvfree(qcamvc->frame_buf, QCAMVC_NUMFRAMES * MAX_FRAME_SIZE); qcamvc->frame_buf = NULL; } int i; for (i=0; i < QCAMVC_NUMFRAMES; i++) { qcamvc->frame[i].state = FRAME_UNUSED; qcamvc->frame[i].data = NULL; }}/********************************************************************** * * Camera Address Read/Write helpers * **********************************************************************/static int get_register(struct qcamvc_data *qcamvc, unsigned char reg, unsigned char *buf){ if (!qcamvc || !qcamvc->ops || !buf) return -1; if (qcamvc->ops->qcamvc_get_reg(qcamvc->lowlevel_data, reg, buf, 1) != 1) return -1; return 0;}static int set_register(struct qcamvc_data *qcamvc, unsigned char reg, unsigned char buf){ if (!qcamvc || !qcamvc->ops) return -1; if (qcamvc->ops->qcamvc_set_reg(qcamvc->lowlevel_data, reg, &buf, 1) != 1) return -1; return 0;}static int set_registers(struct qcamvc_data *qcamvc, unsigned char reg, unsigned char *buf, size_t size){ if (!qcamvc || !qcamvc->ops || !buf) return -1; if (qcamvc->ops->qcamvc_set_reg(qcamvc->lowlevel_data, reg, buf, size) != size) return -1; return 0;}/********************************************************************** * * Image Grabbing * **********************************************************************/static int send_grab_image(struct qcamvc_data *qcamvc, int new_frame){ if (new_frame) qcamvc->frame_count++; /*fresh frame */ if (set_register(qcamvc, QCAM_VC_GET_FRAME, qcamvc->frame_count)) return -ENODEV; return 0;} /* interrogate the camera every 10ms while waiting for an image to be ready. *//* return 0 not ready, or >0 ready */static int frame_is_ready(struct qcamvc_data *qcamvc){#if 0 if (!qcamvc) return 0; struct qcamvc_misc misc = {0}; int attempt = 200; /* 2 second timeout should be plenty */ /* send grab frame command with current frame count */ if (send_grab_image(qcamvc, 0)) return 0; do { if (!get_register(qcamvc, QCAM_VC_SET_MISC, (unsigned char*)&misc)) { /* bit 8 of MISC register is set if the camera has finished capturing a frame */ if (misc.frame_ready) { break; } } else { /* failed reading from the camera. can't do much else here. */ return 0; } /* sleep 10ms. ie. about 1/3 of the time it takes to snap an image at 30fps */ __set_current_state (TASK_INTERRUPTIBLE); schedule_timeout ((HZ + 99) / 100); if (signal_pending (current)) { return 0; } }while (--attempt); return attempt;#else return 1;#endif}/* gets a single raw frame from the camera - waits for it if necessary */static int get_raw_frame(struct qcamvc_data *qcamvc){ if (!qcamvc || !qcamvc->ops) return -EINVAL; unsigned long oldjif, rate, diff; int err; size_t size; if ( (err=allocate_raw_frame(qcamvc)) ) return err; oldjif = jiffies; /* wait for it to become ready */ if (!frame_is_ready(qcamvc)) { /* about 2 secounds has elapsed without the camera telling us a frame is ready... */ printk("Camera timed-out while grabbing frame #%d.\n", qcamvc->frame_count); return -ENODEV; } /* now suck the image data from the camera */ if (set_register(qcamvc, QCAM_VC_GET_FRAME, qcamvc->frame_count)) return -ENODEV; size = qcamvc->ops->qcamvc_stream_read(qcamvc->lowlevel_data, qcamvc->raw_frame, qcamvc->packet_len); if (size == 0) { printk("Failed to read frame #%d from the camera.\n", qcamvc->frame_count); return -1; } /* calc frame rate */ rate = qcamvc->packet_len * HZ / 1024; diff = jiffies-oldjif; qcamvc->transfer_rate = diff==0 ? rate : rate/diff; return size;}/* V4L capture frame using mmap double buffering */static int capture_frame(struct qcamvc_data *qcamvc, int frame){ if (frame < 0 || frame >= QCAMVC_NUMFRAMES) return -EINVAL; qcamvc->curframe = frame; if (qcamvc->frame[qcamvc->curframe].state == FRAME_READY) { /* nothing queued... send grab frame to camera now */ if (!qcamvc->frame_waiting) { if (send_grab_image(qcamvc, 1)) return -ENODEV; } else { /* queue this so that it can be captured immediately next */ qcamvc->frame_waiting++; } qcamvc->frame[qcamvc->curframe].state = FRAME_GRABBING; } else if (qcamvc->frame[qcamvc->curframe].state == FRAME_GRABBING) { int count = get_raw_frame(qcamvc); /* send grab frame now for next frame */ if (qcamvc->frame_waiting) { qcamvc->frame_waiting--; send_grab_image(qcamvc, 1); } if (count < 0) { qcamvc->frame_waiting = 0; /* clear queue on errors */ qcamvc->frame[qcamvc->curframe].state = FRAME_READY; return count; } qcamvc_parse(qcamvc, qcamvc->frame[qcamvc->curframe].data); qcamvc->frame[qcamvc->curframe].state = FRAME_DONE; } return 0;}/********************************************************************** * * Camera Settings * **********************************************************************/static inline void make_valid_res(int *width, int *height){ if (*width > MAX_WIDTH) *width = MAX_WIDTH; else if (*width < MIN_WIDTH) *width = MIN_WIDTH; if (*height > MAX_HEIGHT) *height = MAX_HEIGHT; else if (*height < MIN_HEIGHT) *height = MIN_HEIGHT;} static inline int res_changed(struct qcamvc_data *qcamvc, int width, int height){ if (!qcamvc) return 0; if (qcamvc->width != width) return 1; if (qcamvc->height != height) return 1; return 0;}static inline int valid_res(int width, int height){ if (width > MAX_WIDTH) return 0; else if (width < MIN_WIDTH) return 0; if (height > MAX_HEIGHT) return 0; else if (height < MIN_HEIGHT) return 0; return 1;}static int mode_depth(__u32 mode){ switch (mode) { case V4L2_PIX_FMT_RGB24: case V4L2_PIX_FMT_BGR24: return 24; case V4L2_PIX_FMT_RGB32: case V4L2_PIX_FMT_BGR32: return 32; case V4L2_PIX_FMT_RGB555: case V4L2_PIX_FMT_RGB565: return 16; case V4L2_PIX_FMT_GREY: return 8; case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: return 16; } return 0;}static int mode_bpc(__u32 mode){ switch (mode) { case V4L2_PIX_FMT_RGB555: case V4L2_PIX_FMT_RGB565: return 6; } return 8;}static enum v4l2_colorspace mode_colorspace(__u32 mode){ switch (mode) { case V4L2_PIX_FMT_RGB24: case V4L2_PIX_FMT_BGR24: case V4L2_PIX_FMT_RGB32: case V4L2_PIX_FMT_BGR32: case V4L2_PIX_FMT_RGB555: case V4L2_PIX_FMT_RGB565: return V4L2_COLORSPACE_SRGB; case V4L2_PIX_FMT_GREY: case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: return V4L2_COLORSPACE_SMPTE170M; } return 0;}/* calculate the CCD area rows & cols, return final width and height */static unsigned int calc_CCD_area(int width, int height, struct qcamvc_ccd_area *ccd_area){ if (!ccd_area) return 0; /* allow only even number of row/columns */ width &= ~1; height &= ~1; ccd_area->multiplier = 0; /* sane values for width & height */ make_valid_res(&width, &height); /* anything over 176x144 will be doubled by the camera */ if ( (width > 176) || (height > 144) ) { ccd_area->multiplier = 1; width = width >> 1; height = height >> 1; make_valid_res(&width, &height); } /* width center point is 180/2, +2 because of the left two optically black CCD cells */ ccd_area->first_col = 92 - (width >> 1); ccd_area->last_col = ccd_area->first_col + width; /* height center point is 144/2, +1 because of 1's based rows */ /* camera divides the start row when the height is multiplied, so double it here */ ccd_area->first_row = (73 - (height >> 1)) << ccd_area->multiplier; ccd_area->last_row = ccd_area->first_row + height; /* save the new width and height for no particular reason */ ccd_area->width = width << ccd_area->multiplier; ccd_area->height = height << ccd_area->multiplier; return ((width << 8)|(height));}/* calculate the packet size for the current image size & bpc */static inline int calc_packetlength(struct qcamvc_data *qcamvc, int bpc){ if (!qcamvc) return 0; /* let's not panic */ /* plus 2 is for the two packet length bytes at the start of the image data */ int pl = ((qcamvc->width * qcamvc->height * bpc) / 8) + 64; if (pl > MAX_PACKET_LENGTH) { // this should never happen pl = MAX_PACKET_LENGTH; } return pl;}/* raw is a pointer to the start of raw image data */static inline int header_packetlength(unsigned char *raw){ if (!raw) return 0; /* the pl in raw data header is half the actual packet length. so x2. */ int pl = (int)(((int)raw[1] << 8) | raw[0]) * 2; if (pl > MAX_PACKET_LENGTH) { // bad raw packet received? return 0; } return pl;}/* raw is a pointer to the start of raw image data. packetsize to verify. */static inline int valid_packet(unsigned char *raw, int packetSize){ if (!raw) return 0; return ( header_packetlength(raw) == packetSize );}static int qcamvc_set_ccd_area(struct qcamvc_data *qcamvc){ if (!qcamvc) return -1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -