patch-2.3.43 linux/drivers/usb/ov511.c

Next file: linux/drivers/usb/ov511.h
Previous file: linux/drivers/usb/keybdev.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.42/linux/drivers/usb/ov511.c linux/drivers/usb/ov511.c
@@ -1,6 +1,7 @@
 /*
  * OmniVision OV511 Camera-to-USB Bridge Driver
- * Copyright 1999/2000 Mark W. McClelland
+ * Copyright (c) 1999/2000 Mark W. McClelland
+ * Many improvements by Bret Wallach
  *
  * Based on the Linux CPiA driver.
  * 
@@ -11,7 +12,7 @@
  *    DEBUG - Debugging code.
  *    FIXME - Something that is broken or needs improvement.
  *
- * Version: 1.06
+ * Version: 1.07
  *
  * Please see the file: linux/Documentation/usb/ov511.txt 
  * and the website at:  http://people.delphi.com/mmcclelland/linux/ 
@@ -58,7 +59,7 @@
 #include <linux/wrapper.h>
 #include <linux/module.h>
 #include <linux/spinlock.h>
-
+#include <linux/time.h>
 #include <asm/io.h>
 
 #include "usb.h"
@@ -70,9 +71,9 @@
 
 /* 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))
 
-// FIXME - Force CIF to make some apps happy for the moment. Should find a 
-//         better way to do this.
+// FIXME - Should find a better way to do this.
 #define DEFAULT_WIDTH 640
 #define DEFAULT_HEIGHT 480
 
@@ -206,7 +207,7 @@
 		0, (__u16)reg, &value, 1, HZ);	
 			
 #if 0
-	PDEBUG("reg write: 0x%02X:0x%02X", reg, value);
+	PDEBUG("reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc);
 #endif
 			
 	return rc;
@@ -261,10 +262,10 @@
 
 		if((rc&2) == 0) /* Ack? */
 			break;
-
+#if 0
 		/* I2C abort */	
 		ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x10);
-
+#endif
 		if (--retries < 0) return -1;
 	}
 
@@ -331,6 +332,58 @@
 	return (value);
 }
 
+
+// This version doesn't always work
+#if 0
+ /* returns: negative is error, pos or zero is data */
+ int ov511_i2c_read(struct usb_device *dev, unsigned char reg)
+ {
+	int rc, value;
+
+	/* Select camera register */
+	rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_2_BYTE, reg);
+	if (rc < 0) return rc;
+ 
+
+	/* Initiate 2-byte write cycle */
+	rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x03);
+	if (rc < 0) return rc;
+ 
+
+	/* Initiate 2-byte read cycle */
+	rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05);
+	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
+ 		
+ 	return (value);
+ }
+#endif
+
+static int ov511_write_regvals(struct usb_device *dev,
+			       struct ov511_regvals * pRegvals)
+{
+	int ret;
+	while(pRegvals->bus != OV511_DONE_BUS) {
+		if (pRegvals->bus == OV511_REG_BUS) {
+			if ((ret = ov511_reg_write(dev, pRegvals->reg,
+			                           pRegvals->val)) < 0)
+				return ret;
+		} else if (pRegvals->bus == OV511_I2C_BUS) {
+			if ((ret = ov511_i2c_write(dev, pRegvals->reg, 
+			                           pRegvals->val)) < 0)
+				return ret;
+		} else {
+		  err("Bad regval array");
+		}
+		pRegvals++;
+	}
+	return 0;
+}
+
 #if 0
 static void ov511_dump_i2c_range( struct usb_device *dev, int reg1, int regn)
 {
@@ -384,18 +437,6 @@
 }
 #endif
 
-int ov511_i2c_reset(struct usb_device *dev)
-{
-	int rc;
-
-	PDEBUG("Reset 7610");
-	rc = ov511_i2c_write(dev, 0x12, 0x80);
-	if (rc < 0)
-		err("i2c reset: command failed");
-
-	return rc;
-}
-
 int ov511_reset(struct usb_device *dev, unsigned char reset_type)
 {
 	int rc;
@@ -480,6 +521,21 @@
 static inline int ov7610_set_picture(struct usb_ov511 *ov511,
                                      struct video_picture *p)
 {
+	int ret;
+
+	/* Stop the camera */
+	if (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, 0x3d) < 0) {
+		err("reset: command failed");
+		return -EIO;
+	}
+
+	if((ret = ov511_i2c_read(ov511->dev, OV7610_REG_COM_B)) < 0)
+		return -EIO;
+#if 0
+	if(ov511_i2c_write(ov511->dev, OV7610_REG_COM_B, ret & 0xfe) < 0)
+		return -EIO;
+#endif
+
 	if(ov511_i2c_write(ov511->dev, OV7610_REG_SAT, p->colour >> 8) < 0)
 		return -EIO;
 
@@ -489,6 +545,12 @@
 	if(ov511_i2c_write(ov511->dev, OV7610_REG_BRT, p->brightness >> 8) < 0)
 		return -EIO;
 
+	/* Restart the camera */
+	if (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, 0x0) < 0) {
+		err("reset: command failed");
+		return -EIO;
+	}
+
 	return 0;
 }
 
@@ -497,6 +559,12 @@
 {
 	int ret;
 
+	/* Stop the camera */
+	if (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, 0x3d) < 0) {
+		err("reset: command failed");
+		return -EIO;
+	}
+
 	if((ret = ov511_i2c_read(ov511->dev, OV7610_REG_SAT)) < 0) return -EIO;
 	p->colour = ret << 8;
 
@@ -511,43 +579,71 @@
 	p->depth = 24;
 	p->palette = VIDEO_PALETTE_RGB24;
 
+	/* Restart the camera */
+	if (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, 0x0) < 0) {
+		err("reset: command failed");
+		return -EIO;
+	}
+
 	return 0;
 }
 
 static int ov511_mode_init_regs(struct usb_ov511 *ov511,
-				int width, int height, int mode)
+				int width, int height, int mode, int sub_flag)
 {
 	int rc = 0;
 	struct usb_device *dev = ov511->dev;
 
 #if 0
-	PDEBUG("ov511_mode_init_regs(ov511, %d, %d, %d)", width, height, mode);
+	PDEBUG("ov511_mode_init_regs(ov511, %d, %d, %d, %d)",
+	       width, height, mode, sub_flag);
 #endif
-	ov511_set_packet_size(ov511, 0);
 
-	/* Set mode consistent registers */
-	ov511_i2c_write(dev, 0x0f, 0x03);
-	ov511_i2c_write(dev, 0x10, 0xff);
-	ov511_i2c_write(dev, 0x13, 0x01);
-	ov511_i2c_write(dev, 0x16, 0x06);
-	ov511_i2c_write(dev, 0x20, 0x1c);
-	ov511_i2c_write(dev, 0x24, 0x2e); /* 10 */
-	ov511_i2c_write(dev, 0x25, 0x7c); /* 8a */
-	ov511_i2c_write(dev, 0x26, 0x00); /* was 0x70 */
-	ov511_i2c_write(dev, 0x28, 0x24); /* 24 */
-	ov511_i2c_write(dev, 0x2b, 0xac);
-	ov511_i2c_write(dev, 0x2c, 0xfe);
-	ov511_i2c_write(dev, 0x2d, 0x93);
-	ov511_i2c_write(dev, 0x34, 0x8b);
+//	ov511_set_packet_size(ov511, 0);
+	if (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x3d) < 0) {
+		err("reset: command failed");
+		return -EIO;
+	}
+
+	if (mode == VIDEO_PALETTE_GREY) {
+		ov511_reg_write(dev, 0x16, 0);
+		ov511_i2c_write(dev, 0xe, 0x44);
+		ov511_i2c_write(dev, 0x13, 0x21);
+	} else {
+		ov511_reg_write(dev, 0x16, 1);
+		ov511_i2c_write(dev, 0xe, 0x4);
+		ov511_i2c_write(dev, 0x13, 0x1);
+	}
 
 	if (width == 640 && height == 480) {
-		ov511_reg_write(dev, 0x12, 0x4f);
-		ov511_reg_write(dev, 0x13, 0x3d);
+		if (sub_flag) {
+			ov511_i2c_write(ov511->dev, 0x17, 0x38+(ov511->subx>>2));
+			ov511_i2c_write(ov511->dev, 0x18,
+					0x3a+((ov511->subx+ov511->subw)>>2));
+			ov511_i2c_write(ov511->dev, 0x19, 0x5+(ov511->suby>>1));
+			ov511_i2c_write(ov511->dev, 0x1a,
+					0x5+((ov511->suby+ov511->subh)>>1));
+			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);
+		} 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_reg_write(dev, 0x12, 0x4f);
+			ov511_reg_write(dev, 0x13, 0x3d);
+			if (mode == VIDEO_PALETTE_GREY) {
+			  ov511_i2c_write(dev, 0x11, 4); /* check */
+			} else {
+			  ov511_i2c_write(dev, 0x11, 6); /* check */
+			}
+		}
+
 		ov511_reg_write(dev, 0x14, 0x00);
 		ov511_reg_write(dev, 0x15, 0x00);
 		ov511_reg_write(dev, 0x18, 0x03);
 
-		ov511_i2c_write(dev, 0x11, 0x01);
 		ov511_i2c_write(dev, 0x12, 0x24);
 		ov511_i2c_write(dev, 0x14, 0x04);
 		ov511_i2c_write(dev, 0x35, 0x9e);
@@ -558,7 +654,12 @@
 		ov511_reg_write(dev, 0x15, 0x00);
 		ov511_reg_write(dev, 0x18, 0x03);
 
-		ov511_i2c_write(dev, 0x11, 0x00);
+		if (mode == VIDEO_PALETTE_GREY) {
+		  ov511_i2c_write(dev, 0x11, 1); /* check */
+		} else {
+		  ov511_i2c_write(dev, 0x11, 1); /* check */
+		}
+
 		ov511_i2c_write(dev, 0x12, 0x04);
 		ov511_i2c_write(dev, 0x14, 0x24);
 		ov511_i2c_write(dev, 0x35, 0x1e);
@@ -566,7 +667,13 @@
 		err("Unknown mode (%d, %d): %d", width, height, mode);
 		rc = -EINVAL;
 	}
-	ov511_set_packet_size(ov511, 993);
+
+//	ov511_set_packet_size(ov511, 993);
+
+	if (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x00) < 0) {
+		PDEBUG("reset: command failed");
+		return -EIO;
+	}
 
 	return rc;
 }
@@ -633,25 +740,20 @@
 #define HDIV 8
 #define WDIV (256/HDIV)
 
-static void ov511_parse_data(unsigned char * pIn0,
-			     unsigned char * pOut0,
-			     int iWidth,
-			     int iSegment)
-			    
+
+static void ov511_parse_data_rgb24(unsigned char * pIn0,
+				   unsigned char * pOut0,
+				   int iOutY,
+				   int iOutUV,
+				   int iHalf,
+				   int iWidth)
+ 			    			    
 {
 #ifndef OV511_DUMPPIX
     int k, l, m;
     unsigned char * pIn;
     unsigned char * pOut, * pOut1;
 
-    int iHalf = (iSegment / (iWidth / 32)) & 1;
-    int iY     = iSegment / (iWidth / WDIV);
-    int jY     = iSegment - iY * (iWidth / WDIV);
-    int iOutY  = (iY*HDIV*iWidth + jY*WDIV) * 3;
-    int iUV    = iSegment / (iWidth / WDIV * 2);
-    int jUV    = iSegment - iUV * (iWidth / WDIV * 2);
-    int iOutUV = (iUV*HDIV*2*iWidth + jUV*WDIV/2) * 3;
-
     /* Just copy the Y's if in the first stripe */
     if (!iHalf) {
 	pIn = pIn0 + 128;
@@ -723,13 +825,49 @@
 #else
 	/* Just dump pix data straight out for debug */
 	int i;
-	pOut0 += iSegment * 384;
+	pOut0 += iSegmentY * 384;
 	for(i=0; i<384; i++) {
 	  *pOut0++ = *pIn0++;
 	}
 #endif
 }
 
+/***************************************************************
+
+For 640x480 RAW BW images, data shows up in 1200 256 byte segments.
+The segments 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
+
+****************************************************************/ 
+static void ov511_parse_data_grey(unsigned char * pIn0,
+				  unsigned char * pOut0,
+				  int iOutY,
+				  int iWidth)
+			    
+{
+    int k, l, m;
+    unsigned char * pIn;
+    unsigned char * pOut, * pOut1;
+
+    pIn = pIn0;
+    pOut = pOut0 + iOutY;
+    for(k=0; k<4; k++) {
+      pOut1 = pOut;
+      for(l=0; l<8; l++) {
+	for(m=0; m<8; m++) {
+	  *pOut1++ = *pIn++;
+	}
+	pOut1 += iWidth - 8;
+      }
+      pOut += 8;
+    }
+}
+
 static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
 {
 	unsigned char *cdata;
@@ -741,12 +879,13 @@
 		int n = urb->iso_frame_desc[i].actual_length;
 		int st = urb->iso_frame_desc[i].status;
 		urb->iso_frame_desc[i].actual_length = 0;
-		urb->iso_frame_desc[i].status = 0;		
+		urb->iso_frame_desc[i].status = 0;
 		cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
 
 		aPackNum[i] = n ? cdata[992] : -1;
 
 		if (!n || ov511->curframe == -1) continue;
+
 		if (st)
 			PDEBUG("data error: [%d] len=%d, status=%d", i, n, st);
 
@@ -757,33 +896,35 @@
 		     cdata[4] | cdata[5] | cdata[6] | cdata[7]) == 0 &&
 		    (cdata[8] & 8) && (cdata[8] & 0x80)) {
 
+		    struct timeval *ts;
+		    ts = (struct timeval *)(frame->data + MAX_FRAME_SIZE);
+		    do_gettimeofday(ts);
 #if 0
-		    PDEBUG("Found Frame End!, packnum = %d", (int)(cdata[992]));
-		    PDEBUG("Current frame = %d", ov511->curframe);
+		    PDEBUG("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;
+				frame->grabstate = FRAME_DONE;
 		        if (waitqueue_active(&frame->wq)) {
+			  		frame->grabstate = FRAME_DONE;
+			 	 	wake_up_interruptible(&frame->wq);
+				}
+				/* If next frame is ready or grabbing, point to it */
+				iFrameNext = (ov511->curframe + 1) % OV511_NUMFRAMES;
+				if (ov511->frame[iFrameNext].grabstate== FRAME_READY ||
+				    ov511->frame[iFrameNext].grabstate== FRAME_GRABBING) {
+				  ov511->curframe = iFrameNext;
+				  ov511->frame[iFrameNext].scanstate = STATE_SCANNING;
+				} else {
 #if 0
-			  PDEBUG("About to wake up waiting processes");
-#endif
-			  frame->grabstate = FRAME_DONE;
-			  wake_up_interruptible(&frame->wq);
-			}
-			/* If next frame is ready or grabbing, point to it */
-			iFrameNext = (ov511->curframe + 1) % OV511_NUMFRAMES;
-			if (ov511->frame[iFrameNext].grabstate== FRAME_READY ||
-			    ov511->frame[iFrameNext].grabstate== FRAME_GRABBING) {
-			  ov511->curframe = iFrameNext;
-			  frame->scanstate = STATE_SCANNING;
-			} else {
-#if 0
-			  PDEBUG("Frame not ready? state = %d",
-				 ov511->frame[iFrameNext].grabstate);
+				  PDEBUG("Frame not ready? state = %d",
+					 ov511->frame[iFrameNext].grabstate);
 #endif
-			  ov511->curframe = -1;
-			}
+				  ov511->curframe = -1;
+				}
 		    }
 		}
 
@@ -792,8 +933,8 @@
 			  cdata[4] | cdata[5] | cdata[6] | cdata[7]) == 0 &&
 			 (cdata[8] & 8)) {
 #if 0
-			PDEBUG("ov511: Found Frame Start!, packnum = %d", (int)(cdata[992]));
-			PDEBUG("ov511: Frame Header Byte = 0x%x", (int)(cdata[8]));
+			PDEBUG("ov511: Found Frame Start!, framenum = %d",
+			       ov511->curframe);
 #endif
 		    frame->scanstate = STATE_LINES;
 			frame->segment = 0;
@@ -808,26 +949,67 @@
 			if (frame->segment) {
 			  pData = ov511->scratch;
 			  iPix = - ov511->scratchlen;
-			  memmove(pData + ov511->scratchlen, cdata, iPix+384);
-		  } else {
+			  memmove(pData + ov511->scratchlen, cdata,
+				  iPix+frame->segsize);
+			} else {
 			  pData = &cdata[iPix = 9];
-		  }
+		 	}
 
 			/* Parse the segments */
-			while(iPix <= 992 - 384 &&
+			while(iPix <= 992 - frame->segsize &&
 			      frame->segment < frame->width * frame->height / 256) {
-			  ov511_parse_data(pData, frame->data,
-					   frame->width,
-					   frame->segment);
+			  int iSegY;
+			  int iSegUV;
+			  int iY, jY, iUV, jUV;
+			  int iOutY, iOutUV;
+			  unsigned char * pOut;
+
+			  iSegY = iSegUV = frame->segment;
+			  pOut = frame->data;
+			  
 			  frame->segment++;
-			  iPix += 384;
+			  iPix += frame->segsize;
+
+			  if (frame->sub_flag) {
+			    int iSeg1;
+			    iSeg1 = iSegY / (ov511->subw / 32);
+			    iSeg1 *= frame->width / 32;
+			    iSegY = iSeg1 + (iSegY % (ov511->subw / 32));
+			    if (iSegY >= frame->width * ov511->subh / 256)
+			      break;
+
+			    iSeg1 = iSegUV / (ov511->subw / 16);
+			    iSeg1 *= frame->width / 16;
+			    iSegUV = iSeg1 + (iSegUV % (ov511->subw / 16));
+
+			    pOut += (ov511->subx +
+				     ov511->suby * frame->width) * frame->depth;
+			  }
+
+			  iY     = iSegY / (frame->width / WDIV);
+			  jY     = iSegY - iY * (frame->width / WDIV);
+			  iOutY  = (iY*HDIV*frame->width + jY*WDIV) * frame->depth;
+			  iUV    = iSegUV / (frame->width / WDIV * 2);
+			  jUV    = iSegUV - iUV * (frame->width / WDIV * 2);
+			  iOutUV = (iUV*HDIV*2*frame->width + jUV*WDIV/2) * frame->depth;
+
+			  if (frame->format == VIDEO_PALETTE_GREY) {
+			    ov511_parse_data_grey(pData, pOut, iOutY, frame->width);
+			  } else if (frame->format == VIDEO_PALETTE_RGB24) {
+			    ov511_parse_data_rgb24(pData, pOut, iOutY, iOutUV, iY & 1,
+						   frame->width);
+			  }
 			  pData = &cdata[iPix];
-		}
+			}
 
 			/* Save extra data for next time */
 			if (frame->segment < frame->width * frame->height / 256) {
-			  memmove(ov511->scratch, pData, 992 - iPix);
 			  ov511->scratchlen = 992 - iPix;
+			  if (ov511->scratchlen < frame->segsize) {
+			    memmove(ov511->scratch, pData, ov511->scratchlen);
+			  } else {
+			    ov511->scratchlen = 0;
+			  }
 			}
 		}
 	}
@@ -845,7 +1027,9 @@
 	int len;
 	struct usb_ov511 *ov511 = urb->context;
 	struct ov511_sbuf *sbuf;
-	int i;
+
+	if (!ov511->dev)
+		return;
 
 	if (!ov511->streaming) {
 		PDEBUG("hmmm... not streaming, but got interrupt\n");
@@ -855,14 +1039,10 @@
 	sbuf = &ov511->sbuf[ov511->cursbuf];
 
 	/* Copy the data received into our scratch buffer */
-	if (ov511->curframe >= 0) {
+	if (ov511->curframe >= 0)
 	  len = ov511_move_data(ov511, urb);
-	}
-
-	for (i = 0; i < FRAMES_PER_DESC; i++) {
-		sbuf->urb->iso_frame_desc[i].status = 0;
-		sbuf->urb->iso_frame_desc[i].actual_length = 0;
-	}
+	else if (waitqueue_active(&ov511->wq))
+	  wake_up_interruptible(&ov511->wq);
 	
 	/* Move to the next sbuf */
 	ov511->cursbuf = (ov511->cursbuf + 1) % OV511_NUMSBUF;
@@ -872,7 +1052,6 @@
 
 static int ov511_init_isoc(struct usb_ov511 *ov511)
 {
-	struct usb_device *dev = ov511->dev;
 	urb_t *urb;
 	int fx, err;
 	
@@ -891,9 +1070,9 @@
 		return -ENOMEM;
 	}
 	ov511->sbuf[0].urb = urb;
-	urb->dev = dev;
+	urb->dev = ov511->dev;
 	urb->context = ov511;
-	urb->pipe = usb_rcvisocpipe(dev, OV511_ENDPOINT_ADDRESS);
+	urb->pipe = usb_rcvisocpipe(ov511->dev, OV511_ENDPOINT_ADDRESS);
 	urb->transfer_flags = USB_ISO_ASAP;
 	urb->transfer_buffer = ov511->sbuf[0].data;
  	urb->complete = ov511_isoc_irq;
@@ -910,9 +1089,9 @@
 		return -ENOMEM;
 	}
 	ov511->sbuf[1].urb = urb;
-	urb->dev = dev;
+	urb->dev = ov511->dev;
 	urb->context = ov511;
-	urb->pipe = usb_rcvisocpipe(dev, OV511_ENDPOINT_ADDRESS);
+	urb->pipe = usb_rcvisocpipe(ov511->dev, OV511_ENDPOINT_ADDRESS);
 	urb->transfer_flags = USB_ISO_ASAP;
 	urb->transfer_buffer = ov511->sbuf[1].data;
  	urb->complete = ov511_isoc_irq;
@@ -928,10 +1107,10 @@
 
 	err = usb_submit_urb(ov511->sbuf[0].urb);
 	if (err)
-		err("ov511_init_isoc: usb_run_isoc(0) ret %d", err);
+		err("ov511_init_isoc: usb_submit_urb(0) ret %d", err);
 	err = usb_submit_urb(ov511->sbuf[1].urb);
 	if (err)
-		err("ov511_init_isoc: usb_run_isoc(1) ret %d", err);
+		err("ov511_init_isoc: usb_submit_urb(1) ret %d", err);
 
 	ov511->streaming = 1;
 
@@ -941,27 +1120,37 @@
 
 static void ov511_stop_isoc(struct usb_ov511 *ov511)
 {
-	if (!ov511->streaming)
+	if (!ov511->streaming || !ov511->dev)
 		return;
 
 	ov511_set_packet_size(ov511, 0);
-	
-	/* Unschedule all of the iso td's */
-	usb_unlink_urb(ov511->sbuf[1].urb);
-	usb_unlink_urb(ov511->sbuf[0].urb);
 
 	ov511->streaming = 0;
 
-	/* Delete them all */
-	usb_free_urb(ov511->sbuf[1].urb);
-	usb_free_urb(ov511->sbuf[0].urb);
+	/* Unschedule all of the iso td's */
+	if (ov511->sbuf[1].urb) {
+		ov511->sbuf[1].urb->next = NULL;
+		usb_unlink_urb(ov511->sbuf[1].urb);
+		usb_free_urb(ov511->sbuf[1].urb);
+		ov511->sbuf[1].urb = NULL;
+	}
+	if (ov511->sbuf[0].urb) {
+		ov511->sbuf[0].urb->next = NULL;
+		usb_unlink_urb(ov511->sbuf[0].urb);
+		usb_free_urb(ov511->sbuf[0].urb);
+		ov511->sbuf[0].urb = NULL;
+	}
 }
 
 static int ov511_new_frame(struct usb_ov511 *ov511, int framenum)
 {
+#if 1
 	struct ov511_frame *frame;
 	int width, height;
 
+	if (!ov511->dev)
+		return -1;
+
 	/* If we're not grabbing a frame right now and the other frame is */
 	/*  ready to be grabbed into, then use it instead */
 	if (ov511->curframe == -1) {
@@ -988,15 +1177,15 @@
 	if (height > DEFAULT_HEIGHT)
 		height = DEFAULT_HEIGHT;
 	height = (height / 4) * 4;	/* Multiple of 4 */
-	
+
 //	/* We want a fresh frame every 30 we get */
 //	ov511->compress = (ov511->compress + 1) % 30;
 
+#endif
 	return 0;
 }
 
 
-
 /* Video 4 Linux API */
 static int ov511_open(struct video_device *dev, int flags)
 {
@@ -1004,23 +1193,24 @@
 	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
 
 	PDEBUG("ov511_open");
-	
+
 	down(&ov511->lock);
 	if (ov511->user)
 		goto out_unlock;
-		
+
 	ov511->frame[0].grabstate = FRAME_UNUSED;
 	ov511->frame[1].grabstate = FRAME_UNUSED;
 
 	err = -ENOMEM;
-	
-	/* Allocate memory for the frame buffers */				
-	ov511->fbuf = rvmalloc(2 * MAX_FRAME_SIZE);
+
+	/* Allocate memory for the frame buffers */
+	ov511->fbuf = rvmalloc(2 * MAX_DATA_SIZE);
 	if (!ov511->fbuf)
 		goto open_err_ret;
 
 	ov511->frame[0].data = ov511->fbuf;
-	ov511->frame[1].data = ov511->fbuf + MAX_FRAME_SIZE;
+	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);
@@ -1051,7 +1241,7 @@
 open_err_on1:
 	kfree (ov511->sbuf[0].data);
 open_err_on0:
-	rvfree(ov511->fbuf, 2 * MAX_FRAME_SIZE);
+	rvfree(ov511->fbuf, 2 * MAX_DATA_SIZE);
 open_err_ret:
 	return err;
 out_unlock:
@@ -1073,12 +1263,17 @@
 
 	ov511_stop_isoc(ov511);
 
-	rvfree(ov511->fbuf, 2 * MAX_FRAME_SIZE);
+	rvfree(ov511->fbuf, 2 * MAX_DATA_SIZE);
 
 	kfree(ov511->sbuf[1].data);
 	kfree(ov511->sbuf[0].data);
 
 	up(&ov511->lock);
+
+	if (!ov511->dev) {
+		video_unregister_device(&ov511->vdev);
+		kfree(ov511);
+	}
 }
 
 static int ov511_init_done(struct video_device *dev)
@@ -1091,13 +1286,16 @@
 	return -EINVAL;
 }
 
-// FIXME - Needs much work!!!
 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	
+
+	if (!ov511->dev)
+		return -EIO;	
+
 	switch (cmd) {
 		case VIDIOCGCAP:
 		{
@@ -1109,8 +1307,8 @@
 			b.audios = 0;
 			b.maxwidth = DEFAULT_WIDTH;
 			b.maxheight = DEFAULT_HEIGHT;
-			b.minwidth = 8;
-			b.minheight = 4;
+			b.minwidth = 32;
+			b.minheight = 16;
 
 			if (copy_to_user(arg, &b, sizeof(b)))
 				return -EFAULT;
@@ -1173,6 +1371,42 @@
 
 			return 0;
 		}
+		case VIDIOCGCAPTURE:
+		{
+			int vf;
+			if (copy_from_user(&vf, arg, sizeof(vf)))
+				return -EFAULT;
+			ov511->sub_flag = vf;
+			return 0;
+		}
+		case VIDIOCSCAPTURE:
+		{
+			struct video_capture vc;
+
+			if (copy_from_user(&vc, arg, sizeof(vc)))
+				return -EFAULT;
+			if (vc.flags)
+				return -EINVAL;
+			if (vc.decimation)
+				return -EINVAL;
+			vc.x /= 4;
+			vc.x *= 4;
+			vc.y /= 2;
+			vc.y *= 2;
+			vc.width /= 32;
+			vc.width *= 32;
+			if (vc.width == 0) vc.width = 32;
+			vc.height /= 16;
+			vc.height *= 16;
+			if (vc.height == 0) vc.height = 16;
+
+			ov511->subx = vc.x;
+			ov511->suby = vc.y;
+			ov511->subw = vc.width;
+			ov511->subh = vc.height;
+
+			return 0;
+		}
 		case VIDIOCSWIN:
 		{
 			struct video_window vw;
@@ -1213,10 +1447,10 @@
 			struct video_mbuf vm;
 
 			memset(&vm, 0, sizeof(vm));
-			vm.size = MAX_FRAME_SIZE * 2;
+			vm.size = 2 * MAX_DATA_SIZE;
 			vm.frames = 2;
 			vm.offsets[0] = 0;
-			vm.offsets[1] = MAX_FRAME_SIZE;
+			vm.offsets[1] = MAX_FRAME_SIZE + sizeof (struct timeval);
 
 			if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
 				return -EFAULT;
@@ -1236,7 +1470,8 @@
 				vm.frame, vm.width, vm.height, vm.format);
 #endif
 
-			if (vm.format != VIDEO_PALETTE_RGB24)
+			if (vm.format != VIDEO_PALETTE_RGB24 &&
+			    vm.format != VIDEO_PALETTE_GREY)
 				return -EINVAL;
 
 			if ((vm.frame != 0) && (vm.frame != 1))
@@ -1247,18 +1482,27 @@
 
 			/* Don't compress if the size changed */
 			if ((ov511->frame[vm.frame].width != vm.width) ||
-			    (ov511->frame[vm.frame].height != vm.height)) {
-				ov511->compress = 0;
+			    (ov511->frame[vm.frame].height != vm.height) ||
+			    (ov511->frame[vm.frame].format != vm.format) ||
+			    (ov511->frame[vm.frame].sub_flag !=
+			     ov511->sub_flag)) {
+				/* If we're collecting previous frame wait
+				   before changing modes */
+				interruptible_sleep_on(&ov511->wq);
+				if (signal_pending(current)) return -EINTR;
 				ov511_mode_init_regs(ov511,
-						     vm.width, vm.height, 0);
-#if 0
-				PDEBUG("ov511: Setting frame %d to (%d, %d) : %d",
-				       vm.frame, vm.width, vm.height, 0);
-#endif
+						     vm.width, vm.height,
+						     vm.format, ov511->sub_flag);
 			}
 
 			ov511->frame[vm.frame].width = vm.width;
 			ov511->frame[vm.frame].height = vm.height;
+			ov511->frame[vm.frame].format = vm.format;
+			ov511->frame[vm.frame].sub_flag = ov511->sub_flag;
+			ov511->frame[vm.frame].segsize =
+			  vm.format == VIDEO_PALETTE_RGB24 ? 384 : 256;
+			ov511->frame[vm.frame].depth =
+			  vm.format == VIDEO_PALETTE_RGB24 ? 3 : 1;
 
 			/* Mark it as ready */
 			ov511->frame[vm.frame].grabstate = FRAME_READY;
@@ -1273,7 +1517,8 @@
 				return -EFAULT;
 
 #if 0
-			PDEBUG("syncing to frame %d", frame);
+			PDEBUG("syncing to frame %d, grabstate = %d", frame,
+			       ov511->frame[frame].grabstate);
 #endif
 			switch (ov511->frame[frame].grabstate) {
 				case FRAME_UNUSED:
@@ -1282,6 +1527,9 @@
 				case FRAME_GRABBING:
 				case FRAME_ERROR:
 redo:
+				if (!ov511->dev)
+					return -EIO;
+
 				do {
 #if 0
 					init_waitqueue_head(&ov511->frame[frame].wq);
@@ -1351,6 +1599,9 @@
 	if (!dev || !buf)
 		return -EFAULT;
 
+	if (!ov511->dev)
+		return -EIO;
+
 	/* See if a frame is completed, then use it. */
 	if (ov511->frame[0].grabstate >= FRAME_DONE)	/* _DONE or _ERROR */
 		frmx = 0;
@@ -1376,6 +1627,9 @@
 	frame = &ov511->frame[frmx];
 
 restart:
+	if (!ov511->dev)
+		return -EIO;
+
 	while (frame->grabstate == FRAME_GRABBING) {
 		interruptible_sleep_on(&ov511->frame[frmx].wq);
 		if (signal_pending(current))
@@ -1422,9 +1676,12 @@
 	unsigned long start = (unsigned long)adr;
 	unsigned long page, pos;
 
+	if (!ov511->dev)
+		return -EIO;
+
 	PDEBUG("mmap: %ld (%lX) bytes", size, size);
 
-	if (size > (((2 * MAX_FRAME_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
+	if (size > (((2 * MAX_DATA_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
 		return -EINVAL;
 
 	pos = (unsigned long)ov511->fbuf;
@@ -1444,11 +1701,10 @@
 	return 0;
 }
 
-// FIXME - needs V4L ID to be assigned
 static struct video_device ov511_template = {
 	"OV511 USB Camera",
 	VID_TYPE_CAPTURE,
-	VID_HARDWARE_CPIA,  /* FIXME */
+	VID_HARDWARE_OV511,
 	ov511_open,
 	ov511_close,
 	ov511_read,
@@ -1471,25 +1727,17 @@
 	if(ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ,
 	                        OV7610_I2C_READ_ID) < 0)
 		return -1;
-	
-	/* Reset the camera chip */
-	if (ov511_i2c_reset(dev) < 0)
-		return -1;
 
-#if 0
-	if(usb_ov511_reg_write(dev, OV511_REG_I2C_CLOCK_PRESCALER,
-	                            OV511_I2C_CLOCK_PRESCALER))
-		return -1;
-#endif
-	
 	if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
 		return -1;
 	
+	if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
+	schedule_timeout (1 + 150 * HZ / 1000);
+
 	/* Dummy read to sync I2C */
 	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)) {
 		err("Failed to read OV7610 ID. You might not have an OV7610,");
@@ -1504,6 +1752,74 @@
 static int ov511_configure(struct usb_ov511 *ov511)
 {
 	struct usb_device *dev = ov511->dev;
+	int rc;
+
+	static struct ov511_regvals aRegvalsInit[] =
+	{{OV511_REG_BUS,  OV511_REG_SYSTEM_RESET, 0x7f},
+	 {OV511_REG_BUS,  OV511_REG_SYSTEM_INIT, 0x01},
+	 {OV511_REG_BUS,  OV511_REG_SYSTEM_RESET, 0x7f},
+	 {OV511_REG_BUS,  OV511_REG_SYSTEM_INIT, 0x01},
+	 {OV511_REG_BUS,  OV511_REG_SYSTEM_RESET, 0x3f},
+	 {OV511_REG_BUS,  OV511_REG_SYSTEM_INIT, 0x01},
+	 {OV511_REG_BUS,  OV511_REG_SYSTEM_RESET, 0x3d},
+	 {OV511_DONE_BUS, 0x0, 0x00},
+	};
+	static struct ov511_regvals aRegvalsNorm[] =
+	{{OV511_REG_BUS, 0x20, 1},
+#if 1
+	 {OV511_REG_BUS, 0x52, 0x02},
+	 {OV511_REG_BUS, 0x52, 0x00},
+	 {OV511_REG_BUS, 0x31, 0x1f}, /* 0f */
+	 {OV511_REG_BUS, 0x70, 0x3f},
+	 {OV511_REG_BUS, 0x71, 0x3f},
+	 {OV511_REG_BUS, 0x72, 0x01},
+	 {OV511_REG_BUS, 0x73, 0x01},
+	 {OV511_REG_BUS, 0x74, 0x01},
+	 {OV511_REG_BUS, 0x75, 0x01},
+	 {OV511_REG_BUS, 0x76, 0x01},
+	 {OV511_REG_BUS, 0x77, 0x01},
+	 {OV511_REG_BUS, 0x78, 0x06},
+	 {OV511_REG_BUS, 0x79, 0x03},
+
+
+	 {OV511_I2C_BUS, 0x10, 0xff},
+	 {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, 0x12, 0x00},
+	 {OV511_I2C_BUS, 0x13, 0x00},
+	 {OV511_I2C_BUS, 0x38, 0x81},
+	 {OV511_I2C_BUS, 0x28, 0x24}, /* 0c */
+	 {OV511_I2C_BUS, 0x05, 0x00},
+	 {OV511_I2C_BUS, 0x0f, 0x05},
+	 {OV511_I2C_BUS, 0x15, 0x01},
+	 {OV511_I2C_BUS, 0x20, 0x1c},
+	 {OV511_I2C_BUS, 0x23, 0x2a},
+	 {OV511_I2C_BUS, 0x24, 0x10},
+	 {OV511_I2C_BUS, 0x25, 0x8a},
+	 {OV511_I2C_BUS, 0x26, 0x90},
+	 {OV511_I2C_BUS, 0x27, 0xc2},
+	 {OV511_I2C_BUS, 0x29, 0x03}, /* 91 */
+	 {OV511_I2C_BUS, 0x2a, 0x04},
+	 {OV511_I2C_BUS, 0x2c, 0xfe},
+	 {OV511_I2C_BUS, 0x2d, 0x93}, /* d7 */
+	 {OV511_I2C_BUS, 0x30, 0x71},
+	 {OV511_I2C_BUS, 0x31, 0x60},
+	 {OV511_I2C_BUS, 0x32, 0x26},
+	 {OV511_I2C_BUS, 0x33, 0x20},
+	 {OV511_I2C_BUS, 0x34, 0x48},
+	 {OV511_I2C_BUS, 0x12, 0x24},
+	 {OV511_I2C_BUS, 0x13, 0x01},
+	 {OV511_I2C_BUS, 0x11, 0x01},
+	 {OV511_I2C_BUS, 0x0c, 0x24},
+	 {OV511_I2C_BUS, 0x0d, 0x24},
+#endif
+	 {OV511_DONE_BUS, 0x0, 0x00},
+	};
 
 	/* Set altsetting 0 */
 	if (usb_set_interface(dev, ov511->iface, 0) < 0) {
@@ -1515,25 +1831,15 @@
 
 	init_waitqueue_head(&ov511->frame[0].wq);
 	init_waitqueue_head(&ov511->frame[1].wq);
+	init_waitqueue_head(&ov511->wq);
 
 	if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER) == -1) {
 		err("video_register_device failed");
 		return -EBUSY;
 	}
 
-	/* Reset in case driver was unloaded and reloaded without unplug */
-	if (ov511_reset(dev, OV511_RESET_ALL) < 0)
-		goto error;
-
-	/* Initialize system */
-	if (ov511_reg_write(dev, OV511_REG_SYSTEM_INIT, 0x01) < 0) {
-		err("enable system: command failed");
-		goto error;
-	}
-
-	/* This seems to be necessary */
-	if (ov511_reset(dev, OV511_RESET_ALL) < 0)
-		goto error;
+	if ((rc = ov511_write_regvals(dev, aRegvalsInit)))
+		return rc;
 
 	if(ov7610_configure(dev) < 0) {
 		err("failed to configure OV7610");
@@ -1558,7 +1864,10 @@
 	ov511->frame[1].bytes_read = 0;
 
 	/* Initialize to DEFAULT_WIDTH, DEFAULT_HEIGHT, YUV4:2:0 */
-	ov511_mode_init_regs(ov511, DEFAULT_WIDTH, DEFAULT_HEIGHT, 0);
+	if ((rc = ov511_write_regvals(dev, aRegvalsNorm))) return rc;
+	if ((rc = ov511_mode_init_regs(ov511, DEFAULT_WIDTH, DEFAULT_HEIGHT,
+				       VIDEO_PALETTE_RGB24, 0)) < 0) return rc;
+
 
 	return 0;
 	
@@ -1624,6 +1933,9 @@
 	case 3:
 		printk("ov511: Camera is a D-Link DSB-C300\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;
@@ -1661,13 +1973,49 @@
 
 	struct usb_ov511 *ov511 = (struct usb_ov511 *) ptr;
 
-	video_unregister_device(&ov511->vdev);
+//	video_unregister_device(&ov511->vdev);
+
+	/* We don't want people trying to open up the device */
+	if (!ov511->user)
+		video_unregister_device(&ov511->vdev);
 
 	usb_driver_release_interface(&ov511_driver,
 		&ov511->dev->actconfig->interface[ov511->iface]);
 
+	ov511->dev = NULL;
+	ov511->frame[0].grabstate = FRAME_ERROR;
+	ov511->frame[1].grabstate = FRAME_ERROR;
+	ov511->curframe = -1;
+
+	/* This will cause the process to request another frame */
+	if (waitqueue_active(&ov511->frame[0].wq))
+		wake_up_interruptible(&ov511->frame[0].wq);
+	if (waitqueue_active(&ov511->frame[1].wq))
+		wake_up_interruptible(&ov511->frame[1].wq);
+	if (waitqueue_active(&ov511->wq))
+		wake_up_interruptible(&ov511->wq);
+
+	ov511->streaming = 0;
+
+	/* Unschedule all of the iso td's */
+	if (ov511->sbuf[1].urb) {
+		ov511->sbuf[1].urb->next = NULL;
+		usb_unlink_urb(ov511->sbuf[1].urb);
+		usb_free_urb(ov511->sbuf[1].urb);
+		ov511->sbuf[1].urb = NULL;
+	}
+	if (ov511->sbuf[0].urb) {
+		ov511->sbuf[0].urb->next = NULL;
+		usb_unlink_urb(ov511->sbuf[0].urb);
+		usb_free_urb(ov511->sbuf[0].urb);
+		ov511->sbuf[0].urb = NULL;
+	}	
+
 	/* Free the memory */
-	kfree(ov511); ov511 = NULL;
+	if (!ov511->user) {
+		kfree(ov511);
+		ov511 = NULL;
+	}
 }
 
 static struct usb_driver ov511_driver = {

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)