📄 ovfx2.c
字号:
/* OmniVision/Cypress FX2 Camera-to-USB Bridge Driver * * Copyright (c) 1999-2006 Mark McClelland <mark@ovcam.org>, David Brownell * Many improvements by Bret Wallach <bwallac1@san.rr.com> * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000) * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org> * Changes by Claudio Matsuoka <claudio@conectiva.com> * Kernel I2C interface improvements by Ky鰏ti M鋖kki * URB error messages from pwc driver by Nemosoft * Memory management (rvmalloc) code from bttv driver, by Gerd Knorr and others * * Based on the Linux OV511 driver by Mark W. McClelland, which is * based on the Linux CPiA driver written by Peter Pregler, * Scott J. Bertin and Johannes Erdfelt. * * Please see the website at: http://ovcam.org/ov511 for more info. * * 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. NO WARRANTY OF ANY KIND is expressed or implied. */#include <linux/config.h>#include <linux/version.h>/* 2.6 Doesn't support /proc/video, but this is still defined somewhere */#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)# undef CONFIG_VIDEO_PROC_FS#endif#if defined(CONFIG_VIDEO_PROC_FS)# include <asm/io.h>#endif#include <asm/semaphore.h>#include <asm/processor.h>#include <linux/module.h>#include <linux/init.h>#include <linux/vmalloc.h>#include <linux/slab.h>#if defined(CONFIG_VIDEO_PROC_FS)# include <linux/fs.h># include <linux/proc_fs.h>#endif#include <linux/ctype.h>#include <linux/pagemap.h>#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 68)# include <linux/wrapper.h>#endif#include <linux/mm.h>#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)# include <linux/device.h>#endif#include "ovfx2.h"#include "id.h"#include "driver_version.h"/* Driver will compile with older kernels, but will oops on open() */#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 19)# error "Kernel 2.4.19 is the minimum for this driver"#endif/* * Version Information */#if 0 /* Version is in driver_version.h */#define DRIVER_VERSION "vX.XX"#define DRIVER_VERSION_CODE KERNEL_VERSION(X,XX,0)#endif#define EMAIL "mark@ovcam.org"#define DRIVER_AUTHOR "Mark McClelland <mark@ovcam.org> & Bret Wallach \ & Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha \ <cpbotha@ieee.org> & Claudio Matsuoka <claudio@conectiva.com>"#define DRIVER_DESC "ovfx2 USB Camera Driver"#define OVFX2_MAX_UNIT_VIDEO 16/* Pixel count * 3 bytes per pixel (for BGR24) */#define MAX_FRAME_SIZE(w, h) ((w) * (h) * 3)#define MAX_DATA_SIZE(w, h) (MAX_FRAME_SIZE(w, h) + sizeof(struct timeval))/* Max size * 2 bytes per pixel average + safety margin */#define MAX_RAW_DATA_SIZE(w, h) ((w) * (h) * 2)#define FATAL_ERROR(rc) ((rc) < 0 && (rc) != -EPERM)#if defined (HAVE_V4L2)/* No private controls defined yet */#define OVFX2_CID_LASTP1 (V4L2_CID_PRIVATE_BASE + 0)/* CID offsets for various components */#define OVFX2_CID_OFFSET 0#define SENSOR_CID_OFFSET (OVFX2_CID_OFFSET + \ (OVCAMCHIP_V4L2_CID_LASTP1 - \ V4L2_CID_PRIVATE_BASE))#endif/********************************************************************** * Module Parameters * (See ov511.txt for detailed descriptions of these) **********************************************************************//* These variables (and all static globals) default to zero */static int autobright = 1;static int autoexp = 1;#ifdef OVFX2_DEBUGstatic int debug;#endifstatic int dumppix;static int dump_bridge;static int lightfreq;static int bandingfilter;static int clockdiv = -1;static int framedrop = -1;static int fastset;static int force_palette;static int backlight;static int unit_video[OVFX2_MAX_UNIT_VIDEO];static int mirror;#if defined (HAVE_V4L2)static int v4l2;#endifmodule_param(autobright, int, 0);MODULE_PARM_DESC(autobright, "Sensor automatically changes brightness");module_param(autoexp, int, 0);MODULE_PARM_DESC(autoexp, "Sensor automatically changes exposure");#ifdef OVFX2_DEBUGmodule_param(debug, int, 0);MODULE_PARM_DESC(debug, "Debug level: 0=none, 1=inits, 2=warning, 3=config, 4=functions, 5=max");#endifmodule_param(dumppix, int, 0);MODULE_PARM_DESC(dumppix, "Dump raw pixel data");module_param(dump_bridge, int, 0);MODULE_PARM_DESC(dump_bridge, "Dump the bridge registers");module_param(lightfreq, int, 0);MODULE_PARM_DESC(lightfreq, "Light frequency. Set to 50 or 60 Hz, or zero for default settings");module_param(bandingfilter, int, 0);MODULE_PARM_DESC(bandingfilter, "Enable banding filter (to reduce effects of fluorescent lighting)");module_param(clockdiv, int, 0);MODULE_PARM_DESC(clockdiv, "Force pixel clock divisor to a specific value");module_param(framedrop, int, 0);MODULE_PARM_DESC(framedrop, "Force a specific frame drop register setting");module_param(fastset, int, 0);MODULE_PARM_DESC(fastset, "Allows picture settings to take effect immediately");module_param(force_palette, int, 0);MODULE_PARM_DESC(force_palette, "Force the palette to a specific value");module_param(backlight, int, 0);MODULE_PARM_DESC(backlight, "For objects that are lit from behind");#if defined(module_param_array)static int num_uv;module_param_array(unit_video, int, &num_uv, 0);#elseMODULE_PARM(unit_video, "1-" __MODULE_STRING(OVFX2_MAX_UNIT_VIDEO) "i");#endifMODULE_PARM_DESC(unit_video, "Force use of specific minor number(s). 0 is not allowed.");module_param(mirror, int, 0);MODULE_PARM_DESC(mirror, "Reverse image horizontally");#if defined HAVE_V4L2module_param(v4l2, int, 0);MODULE_PARM_DESC(v4l2, "Enable Video4Linux 2 support");#endifMODULE_AUTHOR(DRIVER_AUTHOR);MODULE_DESCRIPTION(DRIVER_DESC);#if defined(MODULE_LICENSE) /* Introduced in ~2.4.10 */MODULE_LICENSE("GPL");#endif/********************************************************************** * Miscellaneous Globals **********************************************************************/static struct usb_driver ovfx2_driver;/* Prevents double-free of ov struct */static struct semaphore ov_free_lock;/* * Omnivision provided a reference design and firmware connecting an FX2 * to many of their I2C-aware sensor arrays. Different products using * this design could have different sensors. */static struct usb_device_id device_table [] = { /* * Orange Micro "iBOT2" uses an ov7620. It does not have a snapshot * button or L.E.D. * * HIGH SPEED: * * T: Bus=04 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=480 MxCh= 0 * D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 * P: Vendor=0b62 ProdID=0059 Rev= 1.00 * S: Manufacturer=Orange Micro * S: Product=iBOT2 Camera * C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=400mA * I: If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none) * E: Ad=81(I) Atr=03(Int.) MxPS= 4 Ivl= 4ms * E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl= 1ms * * FULL SPEED: * * T: Bus=02 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 3 Spd=12 MxCh= 0 * D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 * P: Vendor=0b62 ProdID=0059 Rev= 1.00 * S: Manufacturer=Orange Micro * S: Product=iBOT2 Camera * C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=400mA * I: If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none) * E: Ad=81(I) Atr=03(Int.) MxPS= 4 Ivl= 1ms * E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl= 1ms * */ { USB_DEVICE (VEND_ORANGE_MICRO, PROD_IBOT2), .driver_info = (unsigned long) "Orange Micro iBOT2", }, /* OmniVision "2800" reference board, which supports almost all * of their camera chips. The following boards are known to work: * - OV7620-ECX */ { USB_DEVICE (VEND_OMNIVISION, PROD_2800), .driver_info = (unsigned long) "OmniVision \"2800\" ref. board", }, /* Trust Sp@cec@m 380 (OV7620), * Aplux MU2-35 (OV7620), * Aplux MU2-48 (OV86108), or * Aplux MU2-130 (OV9620). * * These are all based on the "2800" reference design. They have a * snapshot button and an L.E.D. */ { USB_DEVICE (VEND_APLUX, PROD_MU2CAM), .driver_info = (unsigned long) "Aplux MU2-35/48/130 or Trust 380", }, { }};MODULE_DEVICE_TABLE (usb, device_table);/********************************************************************** * Symbolic Names **********************************************************************//* Video4Linux1 Palettes */static struct symbolic_list v4l1_plist[] = { { VIDEO_PALETTE_GREY, "GREY" }, { VIDEO_PALETTE_HI240, "HI240" }, { VIDEO_PALETTE_RGB565, "RGB565" }, { VIDEO_PALETTE_RGB24, "RGB24" }, { VIDEO_PALETTE_RGB32, "RGB32" }, { VIDEO_PALETTE_RGB555, "RGB555" }, { VIDEO_PALETTE_YUV422, "YUV422" }, { VIDEO_PALETTE_YUYV, "YUYV" }, { VIDEO_PALETTE_UYVY, "UYVY" }, { VIDEO_PALETTE_YUV420, "YUV420" }, { VIDEO_PALETTE_YUV411, "YUV411" }, { VIDEO_PALETTE_RAW, "RAW" }, { VIDEO_PALETTE_YUV422P,"YUV422P" }, { VIDEO_PALETTE_YUV411P,"YUV411P" }, { VIDEO_PALETTE_YUV420P,"YUV420P" }, { VIDEO_PALETTE_YUV410P,"YUV410P" }, { -1, NULL }};#if defined(CONFIG_VIDEO_PROC_FS) \ || LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) || defined(HAVE_V4L2)static struct symbolic_list senlist[] = { { CC_OV76BE, "OV76BE" }, { CC_OV7610, "OV7610" }, { CC_OV7620, "OV7620" }, { CC_OV7620AE, "OV7620AE" }, { CC_OV6620, "OV6620" }, { CC_OV6630, "OV6630" }, { CC_OV6630AE, "OV6630AE" }, { CC_OV6630AF, "OV6630AF" }, { -1, NULL }};#endif/* URB error codes: */static struct symbolic_list urb_errlist[] = { { -ENOSR, "Buffer error (overrun)" }, { -EPIPE, "Stalled (device not responding)" }, { -EOVERFLOW, "Babble (bad cable?)" }, { -EPROTO, "Bit-stuff error (bad cable?)" }, { -EILSEQ, "CRC/Timeout" }, { -ETIMEDOUT, "NAK (device does not respond)" }, { -1, NULL }};#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16)static const char *v4l1_ioctls[] = { "?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT", "CCAPTURE", "GWIN", "SWIN", "GFBUF", "SFBUF", "KEY", "GFREQ", "SFREQ", "GAUDIO", "SAUDIO", "SYNC", "MCAPTURE", "GMBUF", "GUNIT", "GCAPTURE", "SCAPTURE", "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", "SMICROCODE", "GVBIFMT", "SVBIFMT" };#define NUM_V4L1_IOCTLS (sizeof(v4l1_ioctls)/sizeof(char*))#endif/********************************************************************** * Prototypes **********************************************************************/static int ov7xx0_configure(struct usb_ovfx2 *);/********************************************************************** * Memory management **********************************************************************/#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10)/* Here we want the physical address of the memory. * This is used when initializing the contents of the area. */static inline unsigned longkvirt_to_pa(unsigned long adr){ unsigned long kva, ret; kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); kva |= adr & (PAGE_SIZE-1); /* restore the offset */ ret = __pa(kva); return ret;}#endifstatic 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); /* Clear the ram out, no junk to the user */ adr = (unsigned long) mem; while (size > 0) {#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 68) SetPageReserved(vmalloc_to_page((void *)adr));#else mem_map_reserve(vmalloc_to_page((void *)adr));#endif adr += PAGE_SIZE; size -= PAGE_SIZE; } return mem;}static voidrvfree(void *mem, unsigned long size){ unsigned long adr; if (!mem) return; adr = (unsigned long) mem; while ((long) size > 0) {#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 68) ClearPageReserved(vmalloc_to_page((void *)adr));#else mem_map_unreserve(vmalloc_to_page((void *)adr));#endif adr += PAGE_SIZE; size -= PAGE_SIZE; } vfree(mem);}/********************************************************************** * /proc interface * Based on the CPiA driver version 0.7.4 -claudio **********************************************************************/#if defined(CONFIG_VIDEO_PROC_FS)static struct proc_dir_entry *ovfx2_proc_entry = NULL;extern struct proc_dir_entry *video_proc_entry;/* Prototypes */static int sensor_get_picture(struct usb_ovfx2 *, struct video_picture *);static int sensor_get_control(struct usb_ovfx2 *, int, int *);#define YES_NO(x) ((x) ? "yes" : "no")/* /proc/video/ovfx2/<minor#>/info */static intovfx2_read_proc_info(char *page, char **start, off_t off, int count, int *eof, void *data){ char *out = page; int i, len; struct usb_ovfx2 *ov = data; struct video_picture p; int exp = 0; if (!ov || !ov->dev) return -ENODEV; sensor_get_picture(ov, &p); sensor_get_control(ov, OVCAMCHIP_CID_EXP, &exp); /* IMPORTANT: This output MUST be kept under PAGE_SIZE * or we need to get more sophisticated. */ out += sprintf(out, "driver_version : %s\n", DRIVER_VERSION); out += sprintf(out, "model : %s\n", ov->desc); out += sprintf(out, "streaming : %s\n", YES_NO(ov->streaming)); out += sprintf(out, "grabbing : %s\n", YES_NO(ov->grabbing)); out += sprintf(out, "subcapture : %s\n", YES_NO(ov->sub_flag)); out += sprintf(out, "sub_size : %d %d %d %d\n", ov->subx, ov->suby, ov->subw, ov->subh); out += sprintf(out, "brightness : %d\n", p.brightness >> 8); out += sprintf(out, "colour : %d\n", p.colour >> 8); out += sprintf(out, "contrast : %d\n", p.contrast >> 8); out += sprintf(out, "hue : %d\n", p.hue >> 8); out += sprintf(out, "exposure : %d\n", exp); out += sprintf(out, "num_frames : %d\n", OVFX2_NUMFRAMES); for (i = 0; i < OVFX2_NUMFRAMES; i++) { out += sprintf(out, "frame : %d\n", i); out += sprintf(out, " size : %d %d\n", ov->frame[i].width, ov->frame[i].height); out += sprintf(out, " format : %s\n", symbolic(v4l1_plist, ov->frame[i].format)); } out += sprintf(out, "sensor : %s\n", symbolic(senlist, ov->sensor));#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 20) out += sprintf(out, "topology : %s\n", ov->usb_path);#else out += sprintf(out, "usb_bus : %d\n", ov->dev->bus->busnum); out += sprintf(out, "usb_device : %d\n", ov->dev->devnum);#endif out += sprintf(out, "i2c_adap_id : %d\n", i2c_adapter_id(&ov->i2c_adap)); len = out - page; len -= off; if (len < count) { *eof = 1; if (len <= 0) return 0; } else len = count; *start = page + off; return len;}static voidcreate_proc_ovfx2_cam(struct usb_ovfx2 *ov){ char dirname[10]; if (!ovfx2_proc_entry || !ov) return; /* Create per-device directory */ snprintf(dirname, 10, "%d", ov->vdev->minor); ov->proc_devdir = create_proc_entry(dirname, S_IFDIR, ovfx2_proc_entry); if (!ov->proc_devdir) return; ov->proc_devdir->owner = THIS_MODULE; /* Create "info" entry (human readable device information) */ ov->proc_info = create_proc_read_entry("info", S_IFREG|S_IRUGO|S_IWUSR, ov->proc_devdir, ovfx2_read_proc_info, ov); if (!ov->proc_info) return; ov->proc_info->owner = THIS_MODULE;}static voiddestroy_proc_ovfx2_cam(struct usb_ovfx2 *ov){ char dirname[10]; if (!ov || !ov->proc_devdir) return; snprintf(dirname, 10, "%d", ov->vdev->minor); /* Destroy "info" entry */ if (ov->proc_info) { remove_proc_entry("info", ov->proc_devdir); ov->proc_info = NULL; } /* Destroy per-device directory */ remove_proc_entry(dirname, ovfx2_proc_entry); ov->proc_devdir = NULL;}static voidproc_ovfx2_create(void){ if (video_proc_entry == NULL) { err("Error: /proc/video/ does not exist"); return; } ovfx2_proc_entry = create_proc_entry("ovfx2", S_IFDIR, video_proc_entry); if (ovfx2_proc_entry) ovfx2_proc_entry->owner = THIS_MODULE; else err("Unable to create /proc/video/ovfx2");}static voidproc_ovfx2_destroy(void){ if (ovfx2_proc_entry == NULL) return; remove_proc_entry("ovfx2", video_proc_entry);}#elsestatic inline void create_proc_ovfx2_cam(struct usb_ovfx2 *ov) { }static inline void destroy_proc_ovfx2_cam(struct usb_ovfx2 *ov) { }static inline void proc_ovfx2_create(void) { }static inline void proc_ovfx2_destroy(void) { }#endif /* #ifdef CONFIG_VIDEO_PROC_FS *//********************************************************************** * * Register I/O * **********************************************************************//* Write an OVFX2 register */static intreg_w(struct usb_ovfx2 *ov, unsigned char reg, unsigned char value){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -