patch-2.3.99-pre1 linux/drivers/usb/ov511.c
Next file: linux/drivers/usb/ov511.h
Previous file: linux/drivers/usb/inode.c
Back to the patch index
Back to the overall index
- Lines: 951
- Date:
Mon Mar 13 13:55:09 2000
- Orig file:
v2.3.51/linux/drivers/usb/ov511.c
- Orig date:
Fri Mar 10 16:40:45 2000
diff -u --recursive --new-file v2.3.51/linux/drivers/usb/ov511.c linux/drivers/usb/ov511.c
@@ -2,20 +2,17 @@
* OmniVision OV511 Camera-to-USB Bridge Driver
* Copyright (c) 1999/2000 Mark W. McClelland
* Many improvements by Bret Wallach
- *
+ * Color fixes by by Orion Sky Lawlor, olawlor@acm.org, 2/26/2000
+ * Snapshot code by Kevin Moore
+ *
* Based on the Linux CPiA driver.
*
* Released under GPL v.2 license.
*
- * Important keywords in comments:
- * CAMERA SPECIFIC - Camera specific code; may not work with other cameras.
- * DEBUG - Debugging code.
- * FIXME - Something that is broken or needs improvement.
- *
- * Version: 1.07
+ * Version: 1.09
*
* Please see the file: linux/Documentation/usb/ov511.txt
- * and the website at: http://people.delphi.com/mmcclelland/linux/
+ * and the website at: http://alpha.dyndns.org/ov511
* for more info.
*/
@@ -39,15 +36,6 @@
/* Handle mangled (versioned) external symbols */
-#include <linux/config.h> /* retrieve the CONFIG_* macros */
-#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)
-# define MODVERSIONS /* force it on */
-#endif
-
-#ifdef MODVERSIONS
-#include <linux/modversions.h>
-#endif
-
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/list.h>
@@ -58,6 +46,7 @@
#include <linux/vmalloc.h>
#include <linux/wrapper.h>
#include <linux/module.h>
+#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/time.h>
#include <linux/usb.h>
@@ -67,8 +56,6 @@
#define OV511_I2C_RETRIES 3
-#define OV7610_AUTO_ADJUST 1
-
/* Video Size 640 x 480 x 3 bytes for RGB */
#define MAX_FRAME_SIZE (640 * 480 * 3)
#define MAX_DATA_SIZE (MAX_FRAME_SIZE + sizeof(struct timeval))
@@ -77,6 +64,33 @@
#define DEFAULT_WIDTH 640
#define DEFAULT_HEIGHT 480
+// PARAMETER VARIABLES:
+static int autoadjust = 1; /* CCD dynamically changes exposure, etc... */
+
+/* 0=no debug messages
+ * 1=init/detection/unload and other significant messages,
+ * 2=some warning messages
+ * 3=config/control function calls
+ * 4=most function calls and data parsing messages
+ * 5=highly repetitive mesgs
+ * NOTE: This should be changed to 0, 1, or 2 for production kernels
+ */
+static int debug = 3;
+
+/* Fix vertical misalignment of red and blue at 640x480 */
+static int fix_rgb_offset = 0;
+
+/* Snapshot mode enabled flag */
+static int snapshot = 0;
+
+MODULE_PARM(autoadjust, "i");
+MODULE_PARM(debug, "i");
+MODULE_PARM(fix_rgb_offset, "i");
+MODULE_PARM(snapshot, "i");
+
+MODULE_AUTHOR("Mark McClelland (and others)");
+MODULE_DESCRIPTION("OV511 USB Camera Driver");
+
char kernel_version[] = UTS_RELEASE;
/*******************************/
@@ -206,10 +220,8 @@
USB_TYPE_CLASS | USB_RECIP_DEVICE,
0, (__u16)reg, &value, 1, HZ);
-#if 0
- PDEBUG("reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc);
-#endif
-
+ PDEBUG(5, "reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc);
+
return rc;
}
@@ -225,9 +237,7 @@
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE,
0, (__u16)reg, buffer, 1, HZ);
-#if 0
- PDEBUG("reg read: 0x%02X:0x%02X", reg, buffer[0]);
-#endif
+ PDEBUG(5, "reg read: 0x%02X:0x%02X", reg, buffer[0]);
if(rc < 0)
return rc;
@@ -239,9 +249,8 @@
{
int rc, retries;
-#if 0
- PDEBUG("i2c write: 0x%02X:0x%02X", reg, value);
-#endif
+ PDEBUG(5, "i2c write: 0x%02X:0x%02X", reg, value);
+
/* Three byte write cycle */
for(retries = OV511_I2C_RETRIES;;) {
/* Select camera register */
@@ -321,9 +330,8 @@
}
value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT);
-#if 0
- PDEBUG("i2c read: 0x%02X:0x%02X", reg, value);
-#endif
+
+ PDEBUG(5, "i2c read: 0x%02X:0x%02X", reg, value);
/* This is needed to make ov511_i2c_write() work */
rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05);
@@ -355,9 +363,8 @@
if (rc < 0) return rc;
value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT);
- #if 0
- PDEBUG("i2c read: 0x%02X:0x%02X", reg, value);
- #endif
+
+ PDEBUG(5, "i2c read: 0x%02X:0x%02X", reg, value);
return (value);
}
@@ -391,15 +398,14 @@
int rc;
for(i=reg1; i<=regn; i++) {
rc = ov511_i2c_read(dev, i);
-#if 0
- PDEBUG("OV7610[0x%X] = 0x%X", i, rc);
-#endif
+
+ PDEBUG(1, "OV7610[0x%X] = 0x%X", i, rc);
}
}
static void ov511_dump_i2c_regs( struct usb_device *dev)
{
- PDEBUG("I2C REGS");
+ PDEBUG(3, "I2C REGS");
ov511_dump_i2c_range(dev, 0x00, 0x38);
}
@@ -409,27 +415,27 @@
int rc;
for(i=reg1; i<=regn; i++) {
rc = ov511_reg_read(dev, i);
- PDEBUG("OV511[0x%X] = 0x%X", i, rc);
+ PDEBUG(1, "OV511[0x%X] = 0x%X", i, rc);
}
}
static void ov511_dump_regs( struct usb_device *dev)
{
- PDEBUG("CAMERA INTERFACE REGS");
+ PDEBUG(1, "CAMERA INTERFACE REGS");
ov511_dump_reg_range(dev, 0x10, 0x1f);
- PDEBUG("DRAM INTERFACE REGS");
+ PDEBUG(1, "DRAM INTERFACE REGS");
ov511_dump_reg_range(dev, 0x20, 0x23);
- PDEBUG("ISO FIFO REGS");
+ PDEBUG(1, "ISO FIFO REGS");
ov511_dump_reg_range(dev, 0x30, 0x31);
- PDEBUG("PIO REGS");
+ PDEBUG(1, "PIO REGS");
ov511_dump_reg_range(dev, 0x38, 0x39);
ov511_dump_reg_range(dev, 0x3e, 0x3e);
- PDEBUG("I2C REGS");
+ PDEBUG(1, "I2C REGS");
ov511_dump_reg_range(dev, 0x40, 0x49);
- PDEBUG("SYSTEM CONTROL REGS");
+ PDEBUG(1, "SYSTEM CONTROL REGS");
ov511_dump_reg_range(dev, 0x50, 0x53);
ov511_dump_reg_range(dev, 0x5e, 0x5f);
- PDEBUG("OmniCE REGS");
+ PDEBUG(1, "OmniCE REGS");
ov511_dump_reg_range(dev, 0x70, 0x79);
ov511_dump_reg_range(dev, 0x80, 0x9f);
ov511_dump_reg_range(dev, 0xa0, 0xbf);
@@ -441,7 +447,7 @@
{
int rc;
- PDEBUG("Reset: type=0x%X", reset_type);
+ PDEBUG(3, "Reset: type=0x%X", reset_type);
rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, reset_type);
if (rc < 0)
err("reset: command failed");
@@ -457,9 +463,7 @@
{
int alt, multiplier, rc;
-#if 0
- PDEBUG("set packet size: %d", size);
-#endif
+ PDEBUG(3, "set packet size: %d", size);
switch (size) {
case 992:
@@ -576,7 +580,7 @@
p->hue = 0x8000;
p->whiteness = 105 << 8;
- p->depth = 24;
+ p->depth = 3; /* Don't know if this is right */
p->palette = VIDEO_PALETTE_RGB24;
/* Restart the camera */
@@ -594,10 +598,8 @@
int rc = 0;
struct usb_device *dev = ov511->dev;
-#if 0
- PDEBUG("ov511_mode_init_regs(ov511, %d, %d, %d, %d)",
+ PDEBUG(3, "ov511_mode_init_regs(ov511, w:%d, h:%d, mode:%d, sub:%d)",
width, height, mode, sub_flag);
-#endif
// ov511_set_packet_size(ov511, 0);
if (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x3d) < 0) {
@@ -606,13 +608,19 @@
}
if (mode == VIDEO_PALETTE_GREY) {
- ov511_reg_write(dev, 0x16, 0);
- ov511_i2c_write(dev, 0xe, 0x44);
+ ov511_reg_write(dev, 0x16, 0x00);
+ ov511_i2c_write(dev, 0x0e, 0x44);
ov511_i2c_write(dev, 0x13, 0x21);
+ /* For snapshot */
+ ov511_reg_write(dev, 0x1e, 0x00);
+ ov511_reg_write(dev, 0x1f, 0x01);
} else {
- ov511_reg_write(dev, 0x16, 1);
- ov511_i2c_write(dev, 0xe, 0x4);
- ov511_i2c_write(dev, 0x13, 0x1);
+ ov511_reg_write(dev, 0x16, 0x01);
+ ov511_i2c_write(dev, 0x0e, 0x04);
+ ov511_i2c_write(dev, 0x13, 0x01);
+ /* For snapshot */
+ ov511_reg_write(dev, 0x1e, 0x01);
+ ov511_reg_write(dev, 0x1f, 0x03);
}
if (width == 640 && height == 480) {
@@ -626,13 +634,26 @@
ov511_reg_write(ov511->dev, 0x12, (ov511->subw>>3)-1);
ov511_reg_write(ov511->dev, 0x13, (ov511->subh>>3)-1);
ov511_i2c_write(dev, 0x11, 0x01);
+
+ /* Snapshot additions */
+ ov511_reg_write(ov511->dev, 0x1a, (ov511->subw>>3)-1);
+ ov511_reg_write(ov511->dev, 0x1b, (ov511->subh>>3)-1);
+ ov511_reg_write(ov511->dev, 0x1c, 0x00);
+ ov511_reg_write(ov511->dev, 0x1d, 0x00);
} else {
ov511_i2c_write(ov511->dev, 0x17, 0x38);
ov511_i2c_write(ov511->dev, 0x18, 0x3a + (640>>2));
ov511_i2c_write(ov511->dev, 0x19, 0x5);
- ov511_i2c_write(ov511->dev, 0x1c, + (480>>1));
+ ov511_i2c_write(ov511->dev, 0x1a, 5 + (480>>1));
ov511_reg_write(dev, 0x12, 0x4f);
ov511_reg_write(dev, 0x13, 0x3d);
+
+ /* Snapshot additions */
+ ov511_reg_write(ov511->dev, 0x1a, 0x4f);
+ ov511_reg_write(ov511->dev, 0x1b, 0x3d);
+ ov511_reg_write(ov511->dev, 0x1c, 0x00);
+ ov511_reg_write(ov511->dev, 0x1d, 0x00);
+
if (mode == VIDEO_PALETTE_GREY) {
ov511_i2c_write(dev, 0x11, 4); /* check */
} else {
@@ -642,6 +663,8 @@
ov511_reg_write(dev, 0x14, 0x00);
ov511_reg_write(dev, 0x15, 0x00);
+
+ /* FIXME?? Shouldn't below be true only for YUV420? */
ov511_reg_write(dev, 0x18, 0x03);
ov511_i2c_write(dev, 0x12, 0x24);
@@ -654,6 +677,12 @@
ov511_reg_write(dev, 0x15, 0x00);
ov511_reg_write(dev, 0x18, 0x03);
+ /* Snapshot additions */
+ ov511_reg_write(dev, 0x1a, 0x27);
+ ov511_reg_write(dev, 0x1b, 0x1f);
+ ov511_reg_write(dev, 0x1c, 0x00);
+ ov511_reg_write(dev, 0x1d, 0x00);
+
if (mode == VIDEO_PALETTE_GREY) {
ov511_i2c_write(dev, 0x11, 1); /* check */
} else {
@@ -671,7 +700,7 @@
// ov511_set_packet_size(ov511, 993);
if (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x00) < 0) {
- PDEBUG("reset: command failed");
+ err("reset: command failed");
return -EIO;
}
@@ -683,52 +712,74 @@
Turn a YUV4:2:0 block into an RGB block
+Video4Linux seems to use the blue, green, red channel
+order convention-- rgb[0] is blue, rgb[1] is green, rgb[2] is red.
+
+Color space conversion coefficients taken from the excellent
+http://www.inforamp.net/~poynton/ColorFAQ.html
+In his terminology, this is a CCIR 601.1 YCbCr -> RGB.
+Y values are given for all 4 pixels, but the U (Pb)
+and V (Pr) are assumed constant over the 2x2 block.
+
+To avoid floating point arithmetic, the color conversion
+coefficients are scaled into 16.16 fixed-point integers.
+
*************************************************************/
-#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
-static inline void ov511_move_420_block(int y00, int y01, int y10, int y11,
- int u, int v, int w,
- unsigned char * pOut)
-{
- int r = 68911 * v;
- int g = -16915 * u + -35101 * v;
- int b = 87097 * u;
- y00 *= 49152;
- y01 *= 49152;
- y10 *= 49152;
- y11 *= 49152;
- *(pOut+w*3) = LIMIT(r + y10);
- *pOut++ = LIMIT(r + y00);
- *(pOut+w*3) = LIMIT(g + y10);
- *pOut++ = LIMIT(g + y00);
- *(pOut+w*3) = LIMIT(b + y10);
- *pOut++ = LIMIT(b + y00);
- *(pOut+w*3) = LIMIT(r + y11);
- *pOut++ = LIMIT(r + y01);
- *(pOut+w*3) = LIMIT(g + y11);
- *pOut++ = LIMIT(g + y01);
- *(pOut+w*3) = LIMIT(b + y11);
- *pOut++ = LIMIT(b + y01);
+// LIMIT: convert a 16.16 fixed-point value to a byte, with clipping.
+#define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16)))
+static inline void ov511_move_420_block(
+ int yTL, int yTR, int yBL, int yBR,
+ int u, int v,
+ int rowPixels, unsigned char * rgb)
+{
+ const double brightness=1.0;//0->black; 1->full scale
+ const double saturation=1.0;//0->greyscale; 1->full color
+ const double fixScale=brightness*256*256;
+ const int rvScale=(int)(1.402*saturation*fixScale);
+ const int guScale=(int)(-0.344136*saturation*fixScale);
+ const int gvScale=(int)(-0.714136*saturation*fixScale);
+ const int buScale=(int)(1.772*saturation*fixScale);
+ const int yScale=(int)(fixScale);
+
+ int r = rvScale * v;
+ int g = guScale * u + gvScale * v;
+ int b = buScale * u;
+ yTL *= yScale; yTR *= yScale;
+ yBL *= yScale; yBR *= yScale;
+
+ //Write out top two pixels
+ rgb[0]=LIMIT(b+yTL); rgb[1]=LIMIT(g+yTL); rgb[2]=LIMIT(r+yTL);
+ rgb[3]=LIMIT(b+yTR); rgb[4]=LIMIT(g+yTR); rgb[5]=LIMIT(r+yTR);
+ rgb+=3*rowPixels;//Skip down to next line to write out bottom two pixels
+ rgb[0]=LIMIT(b+yBL); rgb[1]=LIMIT(g+yBL); rgb[2]=LIMIT(r+yBL);
+ rgb[3]=LIMIT(b+yBR); rgb[4]=LIMIT(g+yBR); rgb[5]=LIMIT(r+yBR);
}
+
/***************************************************************
For a 640x480 YUV4:2:0 images, data shows up in 1200 384 byte segments. The
-first 64 bytes of each segment are V, the next 64 are U. The V and
-U are arranged as follows:
+first 64 bytes of each segment are U, the next 64 are V. The U and
+V are arranged as follows:
0 1 ... 7
8 9 ... 15
...
56 57 ... 63
-The next 256 bytes are Y data and represent 4 squares of 8x8 pixels as
-follows:
+U and V are shipped at half resolution (1 U,V sample -> one 2x2 block).
+
+The next 256 bytes are full resolution Y data and represent 4
+squares of 8x8 pixels as follows:
0 1 ... 7 64 65 ... 71 ... 192 193 ... 199
8 9 ... 15 72 73 ... 79 200 201 ... 207
... ... ...
56 57 ... 63 120 121 127 248 249 ... 255
+Note that the U and V data in one segment represents a 16 x 16 pixel
+area, but the Y data represents a 32 x 8 pixel area.
+
If OV511_DUMPPIX is defined, _parse_data just dumps the
incoming segments, verbatim, in order, into the frame.
When used with vidcat -f ppm -s 640x480 this puts the data
@@ -780,8 +831,8 @@
int y01 = *(pOut+3);
int y10 = *(pOut+iWidth*3);
int y11 = *(pOut+iWidth*3+3);
- int u = *(pIn+64) - 128;
- int v = *pIn++ - 128;
+ int v = *(pIn+64) - 128;
+ int u = *pIn++ - 128;
ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth, pOut);
pOut += 6;
}
@@ -810,8 +861,8 @@
int y00 = *pIn++;
int y11 = *(pIn+8);
int y01 = *pIn++;
- int u = *pOut1 - 128;
- int v = *(pOut1+1) - 128;
+ int v = *pOut1 - 128;
+ int u = *(pOut1+1) - 128;
ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth, pOut1);
pOut1 += 6;
}
@@ -868,6 +919,42 @@
}
}
+
+/**************************************************************
+ * fixFrameRGBoffset--
+ * My camera seems to return the red channel about 1 pixel
+ * low, and the blue channel about 1 pixel high. After YUV->RGB
+ * conversion, we can correct this easily. OSL 2/24/2000.
+ *************************************************************/
+static void fixFrameRGBoffset(struct ov511_frame *frame)
+{
+ int x,y;
+ int rowBytes=frame->width*3,w=frame->width;
+ unsigned char *rgb=frame->data;
+ const int shift=1;//Distance to shift pixels by, vertically
+
+ if (frame->width<400)
+ return;//Don't bother with little images
+
+ //Shift red channel up
+ for (y=shift;y<frame->height;y++)
+ {
+ int lp=(y-shift)*rowBytes;//Previous line offset
+ int lc=y*rowBytes;//Current line offset
+ for (x=0;x<w;x++)
+ rgb[lp+x*3+2]=rgb[lc+x*3+2];//Shift red up
+ }
+ //Shift blue channel down
+ for (y=frame->height-shift-1;y>=0;y--)
+ {
+ int ln=(y+shift)*rowBytes;//Next line offset
+ int lc=y*rowBytes;//Current line offset
+ for (x=0;x<w;x++)
+ rgb[ln+x*3+0]=rgb[lc+x*3+0];//Shift blue down
+ }
+}
+
+
static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
{
unsigned char *cdata;
@@ -887,7 +974,7 @@
if (!n || ov511->curframe == -1) continue;
if (st)
- PDEBUG("data error: [%d] len=%d, status=%d", i, n, st);
+ PDEBUG(2, "data error: [%d] len=%d, status=%d", i, n, st);
frame = &ov511->frame[ov511->curframe];
@@ -899,14 +986,15 @@
struct timeval *ts;
ts = (struct timeval *)(frame->data + MAX_FRAME_SIZE);
do_gettimeofday(ts);
-#if 0
- PDEBUG("Frame End, curframe = %d, packnum=%d, hw=%d, vw=%d",
+
+ PDEBUG(4, "Frame End, curframe = %d, packnum=%d, hw=%d, vw=%d",
ov511->curframe, (int)(cdata[992]),
(int)(cdata[9]), (int)(cdata[10]));
-#endif
if (frame->scanstate == STATE_LINES) {
int iFrameNext;
+ if (fix_rgb_offset)
+ fixFrameRGBoffset(frame);
frame->grabstate = FRAME_DONE;
if (waitqueue_active(&frame->wq)) {
frame->grabstate = FRAME_DONE;
@@ -919,10 +1007,10 @@
ov511->curframe = iFrameNext;
ov511->frame[iFrameNext].scanstate = STATE_SCANNING;
} else {
-#if 0
- PDEBUG("Frame not ready? state = %d",
+
+ PDEBUG(4, "Frame not ready? state = %d",
ov511->frame[iFrameNext].grabstate);
-#endif
+
ov511->curframe = -1;
}
}
@@ -932,11 +1020,18 @@
else if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] |
cdata[4] | cdata[5] | cdata[6] | cdata[7]) == 0 &&
(cdata[8] & 8)) {
-#if 0
- PDEBUG("ov511: Found Frame Start!, framenum = %d",
+
+ PDEBUG(4, "ov511: Found Frame Start!, framenum = %d",
ov511->curframe);
-#endif
- frame->scanstate = STATE_LINES;
+
+ /* Check to see if it's a snapshot frame */
+ /* FIXME?? Should the snapshot reset go here? Performance? */
+ if (cdata[8] & 0x02) {
+ frame->snapshot = 1;
+ PDEBUG(3, "ov511_move_data: snapshot detected");
+ }
+
+ frame->scanstate = STATE_LINES;
frame->segment = 0;
}
@@ -1014,11 +1109,11 @@
}
}
-#if 0
- PDEBUG("pn: %d %d %d %d %d %d %d %d %d %d\n",
+
+ PDEBUG(5, "pn: %d %d %d %d %d %d %d %d %d %d\n",
aPackNum[0], aPackNum[1], aPackNum[2], aPackNum[3], aPackNum[4],
aPackNum[5],aPackNum[6], aPackNum[7], aPackNum[8], aPackNum[9]);
-#endif
+
return totlen;
}
@@ -1032,7 +1127,7 @@
return;
if (!ov511->streaming) {
- PDEBUG("hmmm... not streaming, but got interrupt\n");
+ PDEBUG(2, "hmmm... not streaming, but got interrupt");
return;
}
@@ -1166,6 +1261,7 @@
frame->grabstate = FRAME_GRABBING;
frame->scanstate = STATE_SCANNING;
frame->scanlength = 0; /* accumulated in ov511_parse_data() */
+ frame->snapshot = 0;
ov511->curframe = framenum;
@@ -1192,7 +1288,7 @@
int err = -EBUSY;
struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
- PDEBUG("ov511_open");
+ PDEBUG(4, "ov511_open");
down(&ov511->lock);
if (ov511->user)
@@ -1212,8 +1308,8 @@
ov511->frame[1].data = ov511->fbuf + MAX_DATA_SIZE;
ov511->sub_flag = 0;
- PDEBUG("frame [0] @ %p", ov511->frame[0].data);
- PDEBUG("frame [1] @ %p", ov511->frame[1].data);
+ PDEBUG(4, "frame [0] @ %p", ov511->frame[0].data);
+ PDEBUG(4, "frame [1] @ %p", ov511->frame[1].data);
ov511->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
if (!ov511->sbuf[0].data)
@@ -1222,8 +1318,8 @@
if (!ov511->sbuf[1].data)
goto open_err_on1;
- PDEBUG("sbuf[0] @ %p", ov511->sbuf[0].data);
- PDEBUG("sbuf[1] @ %p", ov511->sbuf[1].data);
+ PDEBUG(4, "sbuf[0] @ %p", ov511->sbuf[0].data);
+ PDEBUG(4, "sbuf[1] @ %p", ov511->sbuf[1].data);
err = ov511_init_isoc(ov511);
if (err)
@@ -1254,7 +1350,7 @@
{
struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
- PDEBUG("ov511_close");
+ PDEBUG(4, "ov511_close");
down(&ov511->lock);
ov511->user--;
@@ -1289,9 +1385,8 @@
static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
{
struct usb_ov511 *ov511 = (struct usb_ov511 *)vdev;
-#if 0
- PDEBUG("IOCtl: 0x%X", cmd);
-#endif
+
+ PDEBUG(4, "IOCtl: 0x%X", cmd);
if (!ov511->dev)
return -EIO;
@@ -1464,11 +1559,9 @@
if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm)))
return -EFAULT;
-#if 0
- PDEBUG("MCAPTURE");
- PDEBUG("frame: %d, size: %dx%d, format: %d",
+ PDEBUG(4, "MCAPTURE");
+ PDEBUG(4, "frame: %d, size: %dx%d, format: %d",
vm.frame, vm.width, vm.height, vm.format);
-#endif
if (vm.format != VIDEO_PALETTE_RGB24 &&
vm.format != VIDEO_PALETTE_GREY)
@@ -1516,10 +1609,9 @@
if (copy_from_user((void *)&frame, arg, sizeof(int)))
return -EFAULT;
-#if 0
- PDEBUG("syncing to frame %d, grabstate = %d", frame,
+ PDEBUG(4, "syncing to frame %d, grabstate = %d", frame,
ov511->frame[frame].grabstate);
-#endif
+
switch (ov511->frame[frame].grabstate) {
case FRAME_UNUSED:
return -EINVAL;
@@ -1552,7 +1644,17 @@
}
ov511->frame[frame].grabstate = FRAME_UNUSED;
-
+
+ /* Reset the hardware snapshot button */
+ /* FIXME - Is this the best place for this? */
+ if ((ov511->snap_enabled) &&
+ (ov511->frame[frame].snapshot)) {
+ ov511->frame[frame].snapshot = 0;
+ ov511_reg_write(ov511->dev, 0x52, 0x01);
+ ov511_reg_write(ov511->dev, 0x52, 0x03);
+ ov511_reg_write(ov511->dev, 0x52, 0x01);
+ }
+
return 0;
}
case VIDIOCGFBUF:
@@ -1594,7 +1696,7 @@
int frmx = -1;
volatile struct ov511_frame *frame;
- PDEBUG("ov511_read: %ld bytes, noblock=%d", count, noblock);
+ PDEBUG(4, "ov511_read: %ld bytes, noblock=%d", count, noblock);
if (!dev || !buf)
return -EFAULT;
@@ -1644,18 +1746,37 @@
goto restart;
}
- PDEBUG("ov511_read: frmx=%d, bytes_read=%ld, scanlength=%ld", frmx,
+
+ /* Repeat until we get a snapshot frame */
+ if (ov511->snap_enabled && !frame->snapshot) {
+ frame->bytes_read = 0;
+ if (ov511_new_frame(ov511, frmx))
+ err("ov511_read: ov511_new_frame error");
+ goto restart;
+ }
+
+ /* Clear the snapshot */
+ if (ov511->snap_enabled && frame->snapshot) {
+ frame->snapshot = 0;
+ ov511_reg_write(ov511->dev, 0x52, 0x01);
+ ov511_reg_write(ov511->dev, 0x52, 0x03);
+ ov511_reg_write(ov511->dev, 0x52, 0x01);
+ }
+
+ PDEBUG(4, "ov511_read: frmx=%d, bytes_read=%ld, scanlength=%ld", frmx,
frame->bytes_read, frame->scanlength);
/* copy bytes to user space; we allow for partials reads */
- if ((count + frame->bytes_read) > frame->scanlength)
- count = frame->scanlength - frame->bytes_read;
+// if ((count + frame->bytes_read) > frame->scanlength)
+// count = frame->scanlength - frame->bytes_read;
+ /* FIXME - count hardwired to be one frame... */
+ count = frame->width * frame->height * frame->depth;
if (copy_to_user(buf, frame->data + frame->bytes_read, count))
return -EFAULT;
frame->bytes_read += count;
- PDEBUG("ov511_read: {copy} count used=%ld, new bytes_read=%ld",
+ PDEBUG(4, "ov511_read: {copy} count used=%ld, new bytes_read=%ld",
count, frame->bytes_read);
if (frame->bytes_read >= frame->scanlength) { /* All data has been read */
@@ -1679,7 +1800,7 @@
if (!ov511->dev)
return -EIO;
- PDEBUG("mmap: %ld (%lX) bytes", size, size);
+ PDEBUG(4, "mmap: %ld (%lX) bytes", size, size);
if (size > (((2 * MAX_DATA_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
return -EINVAL;
@@ -1702,24 +1823,22 @@
}
static struct video_device ov511_template = {
- "OV511 USB Camera",
- VID_TYPE_CAPTURE,
- VID_HARDWARE_OV511,
- ov511_open,
- ov511_close,
- ov511_read,
- ov511_write,
- NULL,
- ov511_ioctl,
- ov511_mmap,
- ov511_init_done,
- NULL,
- 0,
- 0
+ name: "OV511 USB Camera",
+ type: VID_TYPE_CAPTURE,
+ hardware: VID_HARDWARE_OV511,
+ open: ov511_open,
+ close: ov511_close,
+ read: ov511_read,
+ write: ov511_write,
+ ioctl: ov511_ioctl,
+ mmap: ov511_mmap,
+ initialize: ov511_init_done,
};
static int ov7610_configure(struct usb_device *dev)
{
+ int tries;
+
if(ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE,
OV7610_I2C_WRITE_ID) < 0)
return -1;
@@ -1731,6 +1850,7 @@
if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
return -1;
+ /* Reset the 7610 and wait a bit for it to initialize */
if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
schedule_timeout (1 + 150 * HZ / 1000);
@@ -1738,8 +1858,14 @@
if(ov511_i2c_read(dev, 0x00) < 0)
return -1;
- if((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) != 0x7F) ||
- (ov511_i2c_read(dev, OV7610_REG_ID_LOW) != 0xA2)) {
+ tries = 5;
+ while((tries > 0) &&
+ ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) != 0x7F) ||
+ (ov511_i2c_read(dev, OV7610_REG_ID_LOW) != 0xA2))) {
+ --tries;
+ }
+
+ if (tries == 0) {
err("Failed to read OV7610 ID. You might not have an OV7610,");
err("or it may be not responding. Report this to");
err("mmcclelland@delphi.com");
@@ -1786,12 +1912,11 @@
{OV511_I2C_BUS, 0x16, 0x06},
{OV511_I2C_BUS, 0x28, 0x24}, /* 24 */
{OV511_I2C_BUS, 0x2b, 0xac},
- {OV511_I2C_BUS, 0x5, 0x00},
- {OV511_I2C_BUS, 0x6, 0x00},
-#if 0
-#endif
+ {OV511_I2C_BUS, 0x05, 0x00},
+ {OV511_I2C_BUS, 0x06, 0x00},
+
{OV511_I2C_BUS, 0x12, 0x00},
- {OV511_I2C_BUS, 0x13, 0x00},
+// {OV511_I2C_BUS, 0x13, 0x00},
{OV511_I2C_BUS, 0x38, 0x81},
{OV511_I2C_BUS, 0x28, 0x24}, /* 0c */
{OV511_I2C_BUS, 0x05, 0x00},
@@ -1813,7 +1938,7 @@
{OV511_I2C_BUS, 0x33, 0x20},
{OV511_I2C_BUS, 0x34, 0x48},
{OV511_I2C_BUS, 0x12, 0x24},
- {OV511_I2C_BUS, 0x13, 0x01},
+// {OV511_I2C_BUS, 0x13, 0x01},
{OV511_I2C_BUS, 0x11, 0x01},
{OV511_I2C_BUS, 0x0c, 0x24},
{OV511_I2C_BUS, 0x0d, 0x24},
@@ -1853,7 +1978,8 @@
}
ov511->compress = 0;
-
+ ov511->snap_enabled = snapshot;
+
/* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
* (using read() instead). */
ov511->frame[0].width = DEFAULT_WIDTH;
@@ -1864,10 +1990,16 @@
ov511->frame[1].bytes_read = 0;
/* Initialize to DEFAULT_WIDTH, DEFAULT_HEIGHT, YUV4:2:0 */
- if ((rc = ov511_write_regvals(dev, aRegvalsNorm))) return rc;
+ if ((rc = ov511_write_regvals(dev, aRegvalsNorm))) goto error;
if ((rc = ov511_mode_init_regs(ov511, DEFAULT_WIDTH, DEFAULT_HEIGHT,
- VIDEO_PALETTE_RGB24, 0)) < 0) return rc;
+ VIDEO_PALETTE_RGB24, 0)) < 0) goto error;
+ if (autoadjust) {
+ if (ov511_i2c_write(dev, 0x13, 0x01) < 0) goto error;
+ }
+ else {
+ if (ov511_i2c_write(dev, 0x13, 0x00) < 0 ) goto error;
+ }
return 0;
@@ -1887,7 +2019,7 @@
struct usb_ov511 *ov511;
int rc;
- PDEBUG("probing for device...");
+ PDEBUG(1, "probing for device...");
/* We don't handle multi-config cameras */
if (dev->descriptor.bNumConfigurations != 1)
@@ -1933,19 +2065,25 @@
case 3:
printk("ov511: Camera is a D-Link DSB-C300\n");
break;
+ case 4:
+ printk("ov511: Camera is a generic OV511/OV7610\n");
+ break;
case 5:
printk("ov511: Camera is a Puretek PT-6007\n");
break;
case 21:
printk("ov511: Camera is a Creative Labs WebCam 3\n");
break;
+ case 36:
+ printk("ov511: Camera is a Koala-Cam\n");
+ break;
case 100:
printk("ov511: Camera is a Lifeview RoboCam\n");
break;
case 102:
printk("ov511: Camera is a AverMedia InterCam Elite\n");
break;
- case 112:
+ case 112: /* The OmniVision OV7110 evaluation kit uses this too */
printk("ov511: Camera is a MediaForte MV300\n");
break;
default:
@@ -2025,31 +2163,21 @@
{ NULL, NULL }
};
-int usb_ov511_init(void)
+static int __init usb_ov511_init(void)
{
- PDEBUG("usb_ov511_init()");
-
- EXPORT_NO_SYMBOLS;
-
- return usb_register(&ov511_driver);
-}
+ if (usb_register(&ov511_driver) < 0)
+ return -1;
-void usb_ov511_cleanup(void)
-{
- usb_deregister(&ov511_driver);
-}
+ info("ov511 driver registered");
-#ifdef MODULE
-int init_module(void)
-{
- return usb_ov511_init();
+ return 0;
}
-void cleanup_module(void)
+static void __exit usb_ov511_exit(void)
{
- usb_ov511_cleanup();
-
- PDEBUG("Module unloaded");
+ usb_deregister(&ov511_driver);
+ info("ov511 driver deregistered");
}
-#endif
+module_init(usb_ov511_init);
+module_exit(usb_ov511_exit);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)