📄 ov511.c
字号:
/*
* OmniVision OV511 Camera-to-USB Bridge Driver
*
* Copyright (c) 1999-2002 Mark W. McClelland
* Original decompression code Copyright 1998-2000 OmniVision Technologies
* Many improvements by Bret Wallach <bwallac1@san.rr.com>
* Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000)
* Snapshot code by Kevin Moore
* OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
* Changes by Claudio Matsuoka <claudio@conectiva.com>
* Original SAA7111A code by Dave Perks <dperks@ibm.net>
* URB error messages from pwc driver by Nemosoft
* generic_ioctl() code from videodev.c by Gerd Knorr and Alan Cox
* Memory management (rvmalloc) code from bttv driver, by Gerd Knorr and others
*
* Based on the Linux CPiA driver written by Peter Pregler,
* Scott J. Bertin and Johannes Erdfelt.
*
* Please see the file: linux/Documentation/usb/ov511.txt
* and the website at: http://alpha.dyndns.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.
*
* 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/config.h>
#if defined(OUTSIDE_KERNEL)
#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)
#define MODVERSIONS
#endif
#include <linux/version.h>
#ifdef MODVERSIONS
#include <linux/modversions.h>
#endif
#else
#include <linux/version.h>
#endif
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/ctype.h>
#include <linux/pagemap.h>
#include <asm/io.h>
#include <asm/semaphore.h>
#include <asm/processor.h>
#include <linux/wrapper.h>
/* 2.4.18+/2.5.5+ forward-compatibility; for page_address() */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 18)
#include <linux/mm.h>
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
#if defined (__i386__)
#include <asm/cpufeature.h>
#endif
#endif
/* 2.2.x compatibility */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0)
#ifdef CONFIG_KMOD
#include <linux/kmod.h>
#endif
/* For rvmalloc() and friends */
#define virt_to_page(va) MAP_NR(va)
#endif
/************** Special compatibility options **************/
/* A new implementation of the V4L 1 API exists that gives drivers direct
* access to file_operations. The old API is compatible with all 2.2 and 2.4
* kernels, and all 2.5 kernels through 2.5.5 (at least).
*
* Remove this #define to enable the new API
*
* Note: This has nothing to do with the V4L 2 API.
*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 7)
#define OV511_OLD_V4L
#endif
/* Conversion between standard V4L formats should only be done by apps. This
* #define performs conversion in this driver, to maintain backward
* compatibility with apps that can't convert YUV420 to their native format
*
* This should remain defined for 2.2 and 2.4 kernels. It should be removed
* for 2.5 kernels, including the code that it enables.
*/
#define OV511_ALLOW_CONVERSION
/***********************************************************/
#include "ov511.h"
/*
* Version Information
*/
#define DRIVER_VERSION "v1.63"
#define EMAIL "mark@alpha.dyndns.org"
#define DRIVER_AUTHOR "Mark McClelland <mark@alpha.dyndns.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 "ov511 USB Camera Driver"
#define OV511_I2C_RETRIES 3
#define ENABLE_Y_QUANTABLE 1
#define ENABLE_UV_QUANTABLE 1
#define OV511_MAX_UNIT_VIDEO 16
#ifdef OV511_ALLOW_CONVERSION
/* Pixel count * 3 bytes for RGB */
#define MAX_FRAME_SIZE(w, h) ((w) * (h) * 3)
#else
/* Pixel count * bytes per YUV420 pixel (1.5) */
#define MAX_FRAME_SIZE(w, h) ((w) * (h) * 3 / 2)
#endif
#define MAX_DATA_SIZE(w, h) (MAX_FRAME_SIZE(w, h) + sizeof(struct timeval))
/* Max size * bytes per YUV420 pixel (1.5) + one extra isoc frame for safety */
#define MAX_RAW_DATA_SIZE(w, h) ((w) * (h) * 3 / 2 + 1024)
#define FATAL_ERROR(rc) ((rc) < 0 && (rc) != -EPERM)
/**********************************************************************
* 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 autogain = 1;
static int autoexp = 1;
static int debug;
static int snapshot;
#ifdef OV511_ALLOW_CONVERSION
static int fix_rgb_offset;
static int force_rgb;
#endif
static int cams = 1;
static int compress;
static int testpat;
static int dumppix;
static int led = 1;
static int dump_bridge;
static int dump_sensor;
static int printph;
static int phy = 0x1f;
static int phuv = 0x05;
static int pvy = 0x06;
static int pvuv = 0x06;
static int qhy = 0x14;
static int qhuv = 0x03;
static int qvy = 0x04;
static int qvuv = 0x04;
static int lightfreq;
static int bandingfilter;
/* Pixel clock divisor */
static int clockdiv = -1;
/* Isoc packet size */
static int packetsize = -1;
/* Frame drop register (16h) */
static int framedrop = -1;
static int fastset;
static int force_palette;
static int backlight;
static int unit_video[OV511_MAX_UNIT_VIDEO];
static int remove_zeros;
static int mirror;
static int ov518_color;
MODULE_PARM(autobright, "i");
MODULE_PARM_DESC(autobright, "Sensor automatically changes brightness");
MODULE_PARM(autogain, "i");
MODULE_PARM_DESC(autogain, "Sensor automatically changes gain");
MODULE_PARM(autoexp, "i");
MODULE_PARM_DESC(autoexp, "Sensor automatically changes exposure");
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug,
"Debug level: 0=none, 1=inits, 2=warning, 3=config, 4=functions, 5=max");
MODULE_PARM(snapshot, "i");
MODULE_PARM_DESC(snapshot, "Enable snapshot mode");
#ifdef OV511_ALLOW_CONVERSION
MODULE_PARM(fix_rgb_offset, "i");
MODULE_PARM_DESC(fix_rgb_offset,
"Fix vertical misalignment of red and blue at 640x480");
MODULE_PARM(force_rgb, "i");
MODULE_PARM_DESC(force_rgb, "Read RGB instead of BGR");
#endif
MODULE_PARM(cams, "i");
MODULE_PARM_DESC(cams, "Number of simultaneous cameras");
MODULE_PARM(compress, "i");
MODULE_PARM_DESC(compress, "Turn on compression (not reliable yet)");
MODULE_PARM(testpat, "i");
MODULE_PARM_DESC(testpat,
"Replace image with vertical bar testpattern (only partially working)");
MODULE_PARM(dumppix, "i");
MODULE_PARM_DESC(dumppix, "Dump raw pixel data");
MODULE_PARM(led, "i");
MODULE_PARM_DESC(led,
"LED policy (OV511+ or later). 0=off, 1=on (default), 2=auto (on when open)");
MODULE_PARM(dump_bridge, "i");
MODULE_PARM_DESC(dump_bridge, "Dump the bridge registers");
MODULE_PARM(dump_sensor, "i");
MODULE_PARM_DESC(dump_sensor, "Dump the sensor registers");
MODULE_PARM(printph, "i");
MODULE_PARM_DESC(printph, "Print frame start/end headers");
MODULE_PARM(phy, "i");
MODULE_PARM_DESC(phy, "Prediction range (horiz. Y)");
MODULE_PARM(phuv, "i");
MODULE_PARM_DESC(phuv, "Prediction range (horiz. UV)");
MODULE_PARM(pvy, "i");
MODULE_PARM_DESC(pvy, "Prediction range (vert. Y)");
MODULE_PARM(pvuv, "i");
MODULE_PARM_DESC(pvuv, "Prediction range (vert. UV)");
MODULE_PARM(qhy, "i");
MODULE_PARM_DESC(qhy, "Quantization threshold (horiz. Y)");
MODULE_PARM(qhuv, "i");
MODULE_PARM_DESC(qhuv, "Quantization threshold (horiz. UV)");
MODULE_PARM(qvy, "i");
MODULE_PARM_DESC(qvy, "Quantization threshold (vert. Y)");
MODULE_PARM(qvuv, "i");
MODULE_PARM_DESC(qvuv, "Quantization threshold (vert. UV)");
MODULE_PARM(lightfreq, "i");
MODULE_PARM_DESC(lightfreq,
"Light frequency. Set to 50 or 60 Hz, or zero for default settings");
MODULE_PARM(bandingfilter, "i");
MODULE_PARM_DESC(bandingfilter,
"Enable banding filter (to reduce effects of fluorescent lighting)");
MODULE_PARM(clockdiv, "i");
MODULE_PARM_DESC(clockdiv, "Force pixel clock divisor to a specific value");
MODULE_PARM(packetsize, "i");
MODULE_PARM_DESC(packetsize, "Force a specific isoc packet size");
MODULE_PARM(framedrop, "i");
MODULE_PARM_DESC(framedrop, "Force a specific frame drop register setting");
MODULE_PARM(fastset, "i");
MODULE_PARM_DESC(fastset, "Allows picture settings to take effect immediately");
MODULE_PARM(force_palette, "i");
MODULE_PARM_DESC(force_palette, "Force the palette to a specific value");
MODULE_PARM(backlight, "i");
MODULE_PARM_DESC(backlight, "For objects that are lit from behind");
MODULE_PARM(unit_video, "1-" __MODULE_STRING(OV511_MAX_UNIT_VIDEO) "i");
MODULE_PARM_DESC(unit_video,
"Force use of specific minor number(s). 0 is not allowed.");
MODULE_PARM(remove_zeros, "i");
MODULE_PARM_DESC(remove_zeros,
"Remove zero-padding from uncompressed incoming data");
MODULE_PARM(mirror, "i");
MODULE_PARM_DESC(mirror, "Reverse image horizontally");
MODULE_PARM(ov518_color, "i");
MODULE_PARM_DESC(ov518_color, "Enable OV518 color (experimental)");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
#if defined(MODULE_LICENSE) /* Introduced in ~2.4.10 */
MODULE_LICENSE("GPL");
#endif
/**********************************************************************
* Miscellaneous Globals
**********************************************************************/
/* 2.2.x compatibility */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0)
static char kernel_version[] = UTS_RELEASE;
#endif
static struct usb_driver ov511_driver;
static struct ov51x_decomp_ops *ov511_decomp_ops;
static struct ov51x_decomp_ops *ov511_mmx_decomp_ops;
static struct ov51x_decomp_ops *ov518_decomp_ops;
static struct ov51x_decomp_ops *ov518_mmx_decomp_ops;
/* Number of times to retry a failed I2C transaction. Increase this if you
* are getting "Failed to read sensor ID..." */
static int i2c_detect_tries = 5;
/* MMX support is present in kernel and CPU. Checked upon decomp module load. */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
#if defined(__i386__) || defined(__x86_64__)
#define ov51x_mmx_available (cpu_has_mmx)
#else
#define ov51x_mmx_available (0)
#endif
#else
static int ov51x_mmx_available;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
static struct usb_device_id device_table [] = {
{ USB_DEVICE(VEND_OMNIVISION, PROD_OV511) },
{ USB_DEVICE(VEND_OMNIVISION, PROD_OV511PLUS) },
{ USB_DEVICE(VEND_OMNIVISION, PROD_OV518) },
{ USB_DEVICE(VEND_OMNIVISION, PROD_OV518PLUS) },
{ USB_DEVICE(VEND_MATTEL, PROD_ME2CAM) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, device_table);
#endif
static unsigned char yQuanTable511[] = OV511_YQUANTABLE;
static unsigned char uvQuanTable511[] = OV511_UVQUANTABLE;
static unsigned char yQuanTable518[] = OV518_YQUANTABLE;
static unsigned char uvQuanTable518[] = OV518_UVQUANTABLE;
/**********************************************************************
* Symbolic Names
**********************************************************************/
/* Known OV511-based cameras */
static struct symbolic_list camlist[] = {
{ 0, "Generic Camera (no ID)" },
{ 1, "Mustek WCam 3X" },
{ 3, "D-Link DSB-C300" },
{ 4, "Generic OV511/OV7610" },
{ 5, "Puretek PT-6007" },
{ 6, "Lifeview USB Life TV (NTSC)" },
{ 21, "Creative Labs WebCam 3" },
{ 22, "Lifeview USB Life TV (PAL D/K+B/G)" },
{ 36, "Koala-Cam" },
{ 38, "Lifeview USB Life TV (PAL)" },
{ 41, "Samsung Anycam MPC-M10" },
{ 43, "Mtekvision Zeca MV402" },
{ 46, "Suma eON" },
{ 70, "Lifeview USB Life TV (PAL/SECAM)" },
{ 100, "Lifeview RoboCam" },
{ 102, "AverMedia InterCam Elite" },
{ 112, "MediaForte MV300" }, /* or OV7110 evaluation kit */
{ 134, "Ezonics EZCam II" },
{ 192, "Webeye 2000B" },
{ 253, "Alpha Vision Tech. AlphaCam SE" },
{ -1, NULL }
};
/* 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 }
};
static struct symbolic_list brglist[] = {
{ BRG_OV511, "OV511" },
{ BRG_OV511PLUS, "OV511+" },
{ BRG_OV518, "OV518" },
{ BRG_OV518PLUS, "OV518+" },
{ -1, NULL }
};
static struct symbolic_list senlist[] = {
{ SEN_OV76BE, "OV76BE" },
{ SEN_OV7610, "OV7610" },
{ SEN_OV7620, "OV7620" },
{ SEN_OV7620AE, "OV7620AE" },
{ SEN_OV6620, "OV6620" },
{ SEN_OV6630, "OV6630" },
{ SEN_OV6630AE, "OV6630AE" },
{ SEN_OV6630AF, "OV6630AF" },
{ SEN_OV8600, "OV8600" },
{ SEN_KS0127, "KS0127" },
{ SEN_KS0127B, "KS0127B" },
{ SEN_SAA7111A, "SAA7111A" },
{ -1, NULL }
};
/* 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 }
};
/**********************************************************************
* Prototypes
**********************************************************************/
static void ov51x_clear_snapshot(struct usb_ov511 *);
static inline int sensor_get_picture(struct usb_ov511 *,
struct video_picture *);
#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
static int sensor_get_exposure(struct usb_ov511 *, unsigned char *);
static int ov51x_control_ioctl(struct inode *, struct file *, unsigned int,
unsigned long);
static int ov51x_check_snapshot(struct usb_ov511 *);
#endif
/**********************************************************************
* Memory management
**********************************************************************/
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 18)
/* 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); /* restore the offset */
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); /* Clear the ram out, no junk to the user */
adr = (unsigned long) mem;
while (size > 0) {
mem_map_reserve(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) {
mem_map_unreserve(vmalloc_to_page((void *)adr));
adr += PAGE_SIZE;
size -= PAGE_SIZE;
}
vfree(mem);
}
#else /* if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 4, 18) */
/* Given PGD from the address space's page table, return the kernel
* virtual mapping of the physical memory mapped at ADR.
*/
static inline unsigned long
uvirt_to_kva(pgd_t *pgd, unsigned long adr)
{
unsigned long ret = 0UL;
pmd_t *pmd;
pte_t *ptep, pte;
if (!pgd_none(*pgd)) {
pmd = pmd_offset(pgd, adr);
if (!pmd_none(*pmd)) {
ptep = pte_offset(pmd, adr);
pte = *ptep;
if (pte_present(pte)) {
ret = (unsigned long)
page_address(pte_page(pte));
ret |= (adr & (PAGE_SIZE - 1));
}
}
}
return ret;
}
/* Here we want the physical address of the memory.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -