diff -Naur linux-2.3.99-pre9-2-orig/CREDITS linux/CREDITS
--- linux-2.3.99-pre9-2-orig/CREDITS	Sat May 20 11:58:30 2000
+++ linux/CREDITS	Sat May 20 11:02:25 2000
@@ -1617,7 +1617,7 @@
 E: claudio@conectiva.com
 E: claudio@helllabs.org
 W: http://helllabs.org/~claudio
-D: OV511 driver hacks
+D: V4L, OV511 driver hacks
 S: Conectiva S.A.
 S: R. Tocantins 89
 S: 80050-430  Curitiba PR
diff -Naur linux-2.3.99-pre9-2-orig/Documentation/Configure.help linux/Documentation/Configure.help
--- linux-2.3.99-pre9-2-orig/Documentation/Configure.help	Thu May 18 09:03:03 2000
+++ linux/Documentation/Configure.help	Sat May 20 11:02:26 2000
@@ -14832,6 +14832,14 @@
   whenever you want). If you want to compile it as a module, say M
   here and read Documentation/modules.txt.
 
+Video For Linux /proc file system information
+CONFIG_VIDEO_PROC_FS
+  If you say Y here, you are able to access video device information
+  in /proc/video.
+
+  To use this option, you have to check, that the "/proc file system
+  support" (CONFIG_PROC_FS) is enabled too.
+
 AIMSlab RadioTrack (aka RadioReveal) support
 CONFIG_RADIO_RTRACK
   Choose Y here if you have one of these FM radio cards, and then fill
diff -Naur linux-2.3.99-pre9-2-orig/Documentation/usb/ov511.txt linux/Documentation/usb/ov511.txt
--- linux-2.3.99-pre9-2-orig/Documentation/usb/ov511.txt	Sat May 20 11:58:30 2000
+++ linux/Documentation/usb/ov511.txt	Sat May 20 11:48:04 2000
@@ -6,8 +6,8 @@
 Homepage: http://alpha.dyndns.org/ov511
 
 NEW IN THIS VERSION:
- o 352x288 mode
- o force_rgb parameter for apps that expect RGB instead of BGR
+ o 384x288 and 448x336 modes
+ o better /proc/video support
 
 INTRODUCTION:
 
@@ -179,7 +179,7 @@
         your colors look VERY wrong, you may want to change this.
 
 WORKING FEATURES:
- o Color streaming/capture at 640x480, 352x288, and 320x240
+ o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, and 320x240
  o YUV420 color
  o Monochrome
  o Setting/getting of saturation, contrast and brightness (no hue yet; only
diff -Naur linux-2.3.99-pre9-2-orig/drivers/char/Config.in linux/drivers/char/Config.in
--- linux-2.3.99-pre9-2-orig/drivers/char/Config.in	Wed May 10 16:56:41 2000
+++ linux/drivers/char/Config.in	Sat May 20 11:02:26 2000
@@ -153,6 +153,7 @@
 
 tristate 'Video For Linux' CONFIG_VIDEO_DEV
 if [ "$CONFIG_VIDEO_DEV" != "n" ]; then
+   bool '  V4L information in proc filesystem' CONFIG_VIDEO_PROC_FS Y
    dep_tristate '  I2C on parallel port' CONFIG_I2C_PARPORT $CONFIG_PARPORT $CONFIG_I2C
    comment 'Radio Adapters'
    dep_tristate '  ADS Cadet AM/FM Tuner' CONFIG_RADIO_CADET $CONFIG_VIDEO_DEV
diff -Naur linux-2.3.99-pre9-2-orig/drivers/char/videodev.c linux/drivers/char/videodev.c
--- linux-2.3.99-pre9-2-orig/drivers/char/videodev.c	Thu Apr 13 07:54:32 2000
+++ linux/drivers/char/videodev.c	Sat May 20 11:02:26 2000
@@ -11,7 +11,8 @@
  *
  * Author:	Alan Cox, <alan@redhat.com>
  *
- * Fixes:
+ * Fixes:	20000516  Claudio Matsuoka <claudio@conectiva.com>
+ *		- Added procfs support
  */
 
 #include <linux/config.h>
@@ -40,6 +41,27 @@
  
 static struct video_device *video_device[VIDEO_NUM_DEVICES];
 
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+
+#include <linux/proc_fs.h>
+
+struct videodev_proc_data {
+	struct list_head proc_list;
+	char name[16];
+	struct video_device *vdev;
+	struct proc_dir_entry *proc_entry;
+	struct video_capability vcap;
+};
+
+static struct proc_dir_entry *video_dev_proc_entry = NULL;
+struct proc_dir_entry *video_proc_entry = NULL;
+EXPORT_SYMBOL(video_proc_entry);
+LIST_HEAD(videodev_proc_list);
+
+#endif /* CONFIG_PROC_FS && CONFIG_VIDEO_PROC_FS */
+
+
 #ifdef CONFIG_VIDEO_BT848
 extern int init_bttv_cards(struct video_init *);
 extern int i2c_tuner_init(struct video_init *);
@@ -217,6 +239,152 @@
 	return -EINVAL;
 }
 
+/*
+ *	/proc support
+ */
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+
+/* Hmm... i'd like to see video_capability information here, but
+ * how can I access it (without changing the other drivers? -claudio
+ */
+static int videodev_proc_read(char *page, char **start, off_t off,
+			       int count, int *eof, void *data)
+{
+	char *out = page;
+	struct video_device *vfd = data;
+	struct videodev_proc_data *d;
+	struct list_head *tmp;
+	int len;
+	char c = ' ';
+
+	list_for_each (tmp, &videodev_proc_list) {
+		d = list_entry(tmp, struct videodev_proc_data, proc_list);
+		if (vfd == d->vdev)
+			break;
+	}
+
+	/* Sanity check */
+	if (tmp == &videodev_proc_list)
+		goto skip;
+		
+#define PRINT_VID_TYPE(x) do { if (vfd->type & x) \
+	out += sprintf (out, "%c%s", c, #x); c='|';} while (0)
+
+	out += sprintf (out, "name            : %s\n", vfd->name);
+	out += sprintf (out, "type            :");
+		PRINT_VID_TYPE(VID_TYPE_CAPTURE);
+		PRINT_VID_TYPE(VID_TYPE_TUNER);
+		PRINT_VID_TYPE(VID_TYPE_TELETEXT);
+		PRINT_VID_TYPE(VID_TYPE_OVERLAY);
+		PRINT_VID_TYPE(VID_TYPE_CHROMAKEY);
+		PRINT_VID_TYPE(VID_TYPE_CLIPPING);
+		PRINT_VID_TYPE(VID_TYPE_FRAMERAM);
+		PRINT_VID_TYPE(VID_TYPE_SCALES);
+		PRINT_VID_TYPE(VID_TYPE_MONOCHROME);
+		PRINT_VID_TYPE(VID_TYPE_SUBCAPTURE);
+		PRINT_VID_TYPE(VID_TYPE_MPEG_DECODER);
+		PRINT_VID_TYPE(VID_TYPE_MPEG_ENCODER);
+		PRINT_VID_TYPE(VID_TYPE_MJPEG_DECODER);
+		PRINT_VID_TYPE(VID_TYPE_MJPEG_ENCODER);
+	out += sprintf (out, "\n");
+	out += sprintf (out, "hardware        : 0x%x\n", vfd->hardware);
+#if 0
+	out += sprintf (out, "channels        : %d\n", d->vcap.channels);
+	out += sprintf (out, "audios          : %d\n", d->vcap.audios);
+	out += sprintf (out, "maxwidth        : %d\n", d->vcap.maxwidth);
+	out += sprintf (out, "maxheight       : %d\n", d->vcap.maxheight);
+	out += sprintf (out, "minwidth        : %d\n", d->vcap.minwidth);
+	out += sprintf (out, "minheight       : %d\n", d->vcap.minheight);
+#endif
+
+skip:
+	len = out - page;
+	len -= off;
+	if (len < count) {
+		*eof = 1;
+		if (len <= 0)
+			return 0;
+	} else
+		len = count;
+
+	*start = page + off;
+
+	return len;
+}
+
+static void videodev_proc_create(void)
+{
+	video_proc_entry = create_proc_entry("video", S_IFDIR, &proc_root);
+
+	if (video_proc_entry == NULL) {
+		printk("video_dev: unable to initialise /proc/video\n");
+		return;
+	}
+
+	video_proc_entry->owner = THIS_MODULE;
+	video_dev_proc_entry = create_proc_entry("dev", S_IFDIR, video_proc_entry);
+
+	if (video_dev_proc_entry == NULL) {
+		printk("video_dev: unable to initialise /proc/video/dev\n");
+		return;
+	}
+
+	video_dev_proc_entry->owner = THIS_MODULE;
+}
+
+static void videodev_proc_destroy(void)
+{
+	if (video_dev_proc_entry != NULL)
+		remove_proc_entry("dev", video_proc_entry);
+
+	if (video_proc_entry != NULL)
+		remove_proc_entry("video", &proc_root);
+}
+
+static void videodev_proc_create_dev (struct video_device *vfd, char *name)
+{
+	struct videodev_proc_data *d;
+	struct proc_dir_entry *p;
+
+	if (video_dev_proc_entry == NULL)
+		return;
+
+	d = kmalloc (sizeof (struct videodev_proc_data), GFP_KERNEL);
+	if (!d)
+		return;
+
+	p = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, video_dev_proc_entry);
+	p->data = vfd;
+	p->read_proc = videodev_proc_read;
+
+	d->proc_entry = p;
+	d->vdev = vfd;
+	strcpy (d->name, name);
+
+	/* How can I get capability information ? */
+
+	list_add (&d->proc_list, &videodev_proc_list);
+}
+
+static void videodev_proc_destroy_dev (struct video_device *vfd)
+{
+	struct list_head *tmp;
+	struct videodev_proc_data *d;
+
+	list_for_each (tmp, &videodev_proc_list) {
+		d = list_entry(tmp, struct videodev_proc_data, proc_list);
+		if (vfd == d->vdev) {
+			remove_proc_entry(d->name, video_dev_proc_entry);
+			list_del (&d->proc_list);
+			kfree (d);
+			break;
+		}
+	}
+}
+
+#endif /* CONFIG_VIDEO_PROC_FS */
+
 extern struct file_operations video_fops;
 
 /**
@@ -308,6 +476,13 @@
 					    VIDEO_MAJOR, vfd->minor,
 					    S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
 					    &video_fops, NULL);
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+			sprintf (name, "%s%d", name_base, i - base);
+			videodev_proc_create_dev (vfd, name);
+#endif
+			
+
 			return 0;
 		}
 	}
@@ -326,6 +501,11 @@
 {
 	if(video_device[vfd->minor]!=vfd)
 		panic("vfd: bad unregister");
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+	videodev_proc_destroy_dev (vfd);
+#endif
+
 	devfs_unregister (vfd->devfs_handle);
 	video_device[vfd->minor]=NULL;
 	MOD_DEC_USE_COUNT;
@@ -363,6 +543,10 @@
 	 *	Init kernel installed video drivers
 	 */
 	 	
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+	videodev_proc_create ();
+#endif
+	
 	while(vfli->init!=NULL)
 	{
 		vfli->init(vfli);
@@ -379,6 +563,10 @@
 
 void cleanup_module(void)
 {
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+	videodev_proc_destroy ();
+#endif
+	
 	devfs_unregister_chrdev(VIDEO_MAJOR, "video_capture");
 }
 
diff -Naur linux-2.3.99-pre9-2-orig/drivers/usb/ov511.c linux/drivers/usb/ov511.c
--- linux-2.3.99-pre9-2-orig/drivers/usb/ov511.c	Sat May 20 11:58:30 2000
+++ linux/drivers/usb/ov511.c	Sat May 20 11:47:38 2000
@@ -30,7 +30,7 @@
  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-static const char version[] = "1.14";
+static const char version[] = "1.15";
 
 #define __NO_VERSION__
 
@@ -263,9 +263,10 @@
  * Based on the CPiA driver version 0.7.4 -claudio
  **********************************************************************/
 
-#ifdef CONFIG_PROC_FS
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+
 static struct proc_dir_entry *ov511_proc_entry = NULL;
-static struct proc_dir_entry *video_proc_entry = NULL;
+extern struct proc_dir_entry *video_proc_entry;
 
 #define YES_NO(x) ((x) ? "yes" : "no")
 
@@ -343,6 +344,7 @@
 		len = count;
 
 	*start = page + off;
+
 	return len;
 }
 
@@ -357,12 +359,11 @@
 	char name[7];
 	struct proc_dir_entry *ent;
 	
-	PDEBUG (4, "creating /proc/video/ov511/videoX entry");
 	if (!ov511_proc_entry || !ov511)
 		return;
 
 	sprintf(name, "video%d", ov511->vdev.minor);
-	PDEBUG (4, "creating %s", name);
+	PDEBUG (4, "creating /proc/video/ov511/%s", name);
 	
 	ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, ov511_proc_entry);
 
@@ -372,7 +373,6 @@
 	ent->data = ov511;
 	ent->read_proc = ov511_read_proc;
 	ent->write_proc = ov511_write_proc;
-	ent->size = 3626;	/* FIXME */
 	ov511->proc_entry = ent;
 }
 
@@ -391,22 +391,13 @@
 
 static void proc_ov511_create(void)
 {
-	struct proc_dir_entry *p = NULL;
-
 	/* No current standard here. Alan prefers /proc/video/ as it keeps
 	 * /proc "less cluttered than /proc/randomcardifoundintheshed/"
 	 * -claudio
 	 */
-	PDEBUG (3, "creating /proc/video");
-	video_proc_entry = proc_mkdir("video", p);
-	if (!video_proc_entry) {
-		if (!p) {
-			err("Unable to initialise /proc/video\n");
-			return;
-		} else {	/* FIXME - this doesn't work */
-			PDEBUG (3, "/proc/video already exists");
-			video_proc_entry = p;
-		}
+	if (video_proc_entry == NULL) {
+		err("Unable to initialise /proc/video/ov511");
+		return;
 	}
 
 	ov511_proc_entry = create_proc_entry("ov511", S_IFDIR, video_proc_entry);
@@ -414,16 +405,19 @@
 	if (ov511_proc_entry)
 		ov511_proc_entry->owner = THIS_MODULE;
 	else
-		err("Unable to initialise /proc/video/ov511\n");
+		err("Unable to initialise /proc/ov511");
 }
 
 static void proc_ov511_destroy(void)
 {
 	PDEBUG (3, "removing /proc/video/ov511");
+
+	if (ov511_proc_entry == NULL)
+		return;
+
 	remove_proc_entry("ov511", video_proc_entry);
-	remove_proc_entry("video", NULL);
 }
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_PROC_FS && CONFIG_VIDEO_PROC_FS */
 
 
 /**********************************************************************
@@ -852,7 +846,7 @@
 	return 0;
 }
 
-/* FIXME: add 176x144, 160x140 */
+/* FIXME: add 400x300, 176x144, 160x140 */
 static struct mode_list mlist[] = {
 	{ 640, 480, VIDEO_PALETTE_GREY, 0x4f, 0x3d, 0x00, 0x00,
 	  0x4f, 0x3d, 0x00, 0x00, 0x04, 0x03, 0x24, 0x04, 0x9e },
@@ -866,6 +860,14 @@
 	  0x2b, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e },
 	{ 352, 288, VIDEO_PALETTE_RGB24,0x2b, 0x25, 0x00, 0x00,
 	  0x2b, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e },
+	{ 384, 288, VIDEO_PALETTE_GREY, 0x2f, 0x25, 0x00, 0x00,
+	  0x2f, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e },
+	{ 384, 288, VIDEO_PALETTE_RGB24,0x2f, 0x25, 0x00, 0x00,
+	  0x2f, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e },
+	{ 448, 336, VIDEO_PALETTE_GREY, 0x37, 0x29, 0x00, 0x00,
+	  0x37, 0x29, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e },
+	{ 448, 336, VIDEO_PALETTE_RGB24,0x37, 0x29, 0x00, 0x00,
+	  0x37, 0x29, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e },
 	{ 0, 0 }
 };
 
@@ -1302,158 +1304,190 @@
 	int i, totlen = 0;
 	int aPackNum[10];
 	struct ov511_frame *frame;
+	unsigned char *pData;
+	int iPix;
 
-	PDEBUG(4, "ov511_move_data");
+	PDEBUG (4, "Moving %d packets", urb->number_of_packets);
 
 	for (i = 0; i < urb->number_of_packets; i++) {
 		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;
+
 		cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
 
 		aPackNum[i] = n ? cdata[ov511->packet_size - 1] : -1;
 
-		if (!n || ov511->curframe == -1) continue;
+		if (!n || ov511->curframe == -1)
+			continue;
 
 		if (st)
 			PDEBUG(2, "data error: [%d] len=%d, status=%d", i, n, st);
 
 		frame = &ov511->frame[ov511->curframe];
 		
-		/* Can we find a frame end */
+		/* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th
+		 * byte non-zero. The EOF packet has image width/height in the
+		 * 10th and 11th packets. The 9th bit is given as follows:
+		 *
+		 * bit 7: EOF
+		 *     6: compression enabled
+		 *     5: 422/420/400 modes
+		 *     4: 422/420/400 modes
+		 *     3: 1
+		 *     2: snapshot bottom on
+		 *     1: snapshot frame
+		 *     0: even/odd field
+		 */
+
+		/* Check for SOF/EOF packet */
 		if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] | 
-		     cdata[4] | cdata[5] | cdata[6] | cdata[7]) == 0 &&
-		    (cdata[8] & 8) && (cdata[8] & 0x80)) {
+		     cdata[4] | cdata[5] | cdata[6] | cdata[7]) ||
+		     (~cdata[8] & 0x08))
+			goto check_middle;
+
+		/* Frame end */
+		if (cdata[8] & 0x80) {
+#if 0
+			struct timeval *ts;
+
+			ts = (struct timeval *)(frame->data + MAX_FRAME_SIZE);
+		 	do_gettimeofday (ts);
+#endif
+
+		 	PDEBUG(4, "Frame end, curframe = %d, packnum=%d, hw=%d, vw=%d",
+				ov511->curframe, (int)(cdata[ov511->packet_size - 1]),
+				(int)(cdata[9]), (int)(cdata[10]));
 
-		    struct timeval *ts;
-		    ts = (struct timeval *)(frame->data + MAX_FRAME_SIZE);
-		    do_gettimeofday(ts);
-
-		    PDEBUG(4, "Frame End, curframe = %d, packnum=%d, hw=%d, vw=%d",
-			   ov511->curframe, (int)(cdata[ov511->packet_size - 1]),
-			   (int)(cdata[9]), (int)(cdata[10]));
+			if (frame->scanstate == STATE_LINES) {
+		    		int iFrameNext;
 
-		    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;
-			 	 	wake_up_interruptible(&frame->wq);
+
+				if (waitqueue_active(&frame->wq)) {
+					frame->grabstate = FRAME_DONE;
+					wake_up_interruptible(&frame->wq);
 				}
-				/* If next frame is ready or grabbing, point to it */
+
+				/* 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;
+				if (ov511->frame[iFrameNext].grabstate == FRAME_READY
+				    || ov511->frame[iFrameNext].grabstate == FRAME_GRABBING) {
+					ov511->curframe = iFrameNext;
+					ov511->frame[iFrameNext].scanstate = STATE_SCANNING;
 				} else {
+					if (frame->grabstate == FRAME_DONE) {
+						PDEBUG(4, "Frame done! congratulations");
+					} else {
+						PDEBUG(4, "Frame not ready? state = %d",
+							ov511->frame[iFrameNext].grabstate);
+					}
 
-				  PDEBUG(4, "Frame not ready? state = %d",
-					 ov511->frame[iFrameNext].grabstate);
-
-				  ov511->curframe = -1;
+					ov511->curframe = -1;
 				}
-		    }
-		}
-
-		/* Can we find a frame start */
-		else if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] | 
-			  cdata[4] | cdata[5] | cdata[6] | cdata[7]) == 0 &&
-			 (cdata[8] & 8)) {
-
-			PDEBUG(4, "ov511: Found Frame Start!, framenum = %d",
-			       ov511->curframe);
+			}
+			/* Image corruption caused by misplaced frame->segment = 0
+			 * fixed by carlosf@conectiva.com.br
+			 */
+		} else {
+			/* Frame start */
+			PDEBUG(4, "Frame start, framenum = %d", ov511->curframe);
 
 			/* 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");
+				PDEBUG(3, "snapshot detected");
 			}
 
 			frame->scanstate = STATE_LINES;
 			frame->segment = 0;
 		}
 
+check_middle:
 		/* Are we in a frame? */
-		if (frame->scanstate == STATE_LINES) {
-			unsigned char * pData;
-			int iPix;
-
-			/* Deal with leftover from last segment, if any */
-			if (frame->segment) {
-			  pData = ov511->scratch;
-			  iPix = - ov511->scratchlen;
-			  memmove(pData + ov511->scratchlen, cdata,
-				  iPix+frame->segsize);
-			} else {
-			  pData = &cdata[iPix = 9];
-		 	}
+		if (frame->scanstate != STATE_LINES)
+			continue;
 
-			/* Parse the segments */
-			while(iPix <= (ov511->packet_size - 1) - frame->segsize &&
-			      frame->segment < frame->width * frame->height / 256) {
-			  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 += 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];
-			}
+		/* Deal with leftover from last segment, if any */
+		if (frame->segment) {
+			pData = ov511->scratch;
+			iPix = -ov511->scratchlen;
+			memmove(pData + ov511->scratchlen, cdata,
+				iPix+frame->segsize);
+		} else {
+			pData = &cdata[iPix = 9];
+	 	}
+
+		/* Parse the segments */
+		while (iPix <= (ov511->packet_size - 1) - frame->segsize &&
+		    frame->segment < frame->width * frame->height / 256) {
+			int iSegY, iSegUV;
+			int iY, jY, iUV, jUV;
+			int iOutY, iOutUV;
+			unsigned char *pOut;
+
+			iSegY = iSegUV = frame->segment;
+			pOut = frame->data;
+			frame->segment++;
+			iPix += frame->segsize;
+
+			/* Handle subwindow */
+			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));
 
-			/* Save extra data for next time */
-			if (frame->segment < frame->width * frame->height / 256) {
-			  ov511->scratchlen = (ov511->packet_size - 1) - iPix;
-			  if (ov511->scratchlen < frame->segsize) {
-			    memmove(ov511->scratch, pData, ov511->scratchlen);
-			  } else {
-			    ov511->scratchlen = 0;
-			  }
+				pOut += (ov511->subx + ov511->suby * frame->width) *
+					(frame->depth >> 3);
 			}
+
+			/* 
+			 * iY counts segment lines
+			 * jY counts segment columns
+			 * iOutY is the offset (in bytes) of the segment upper left corner
+			 */
+			iY = iSegY / (frame->width / WDIV);
+			jY = iSegY - iY * (frame->width / WDIV);
+			iOutY = (iY*HDIV*frame->width + jY*WDIV) * (frame->depth >> 3);
+			iUV = iSegUV / (frame->width / WDIV * 2);
+			jUV = iSegUV - iUV * (frame->width / WDIV * 2);
+			iOutUV = (iUV*HDIV*2*frame->width + jUV*WDIV/2) * (frame->depth >> 3);
+
+			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) {
+			ov511->scratchlen = (ov511->packet_size - 1) - iPix;
+			if (ov511->scratchlen < frame->segsize)
+				memmove(ov511->scratch, pData,
+					ov511->scratchlen);
+			else
+				ov511->scratchlen = 0;
 		}
 	}
 
-
-	PDEBUG(5, "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",
 	       aPackNum[0], aPackNum[1], aPackNum[2], aPackNum[3], aPackNum[4],
 	       aPackNum[5],aPackNum[6], aPackNum[7], aPackNum[8], aPackNum[9]);
 
@@ -1472,8 +1506,6 @@
 	if (!ov511->streaming) {
 		PDEBUG(2, "hmmm... not streaming, but got interrupt");
 		return;
-	} else {
-		PDEBUG(5, "streaming. got interrupt");
 	}
 	
 	sbuf = &ov511->sbuf[ov511->cursbuf];
@@ -1723,7 +1755,7 @@
 
 static int ov511_init_done(struct video_device *dev)
 {
-#ifdef CONFIG_PROC_FS
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
 	create_proc_ov511_cam((struct usb_ov511 *)dev);
 #endif
 
@@ -1749,6 +1781,8 @@
 	{
 		struct video_capability b;
 
+		PDEBUG (4, "VIDIOCGCAP");
+
 		strcpy(b.name, "OV511 USB Camera");
 		b.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
 		b.channels = 1;
@@ -1798,6 +1832,8 @@
 	{
 		struct video_picture p;
 
+		PDEBUG (4, "VIDIOCGPICT");
+
 		if (ov7610_get_picture(ov511, &p))
 			return -EIO;
 							
@@ -1809,18 +1845,33 @@
 	case VIDIOCSPICT:
 	{
 		struct video_picture p;
+		int i;
+
+		PDEBUG (4, "VIDIOCSPICT");
 
 		if (copy_from_user(&p, arg, sizeof(p)))
 			return -EFAULT;
-			
+
 		if (ov7610_set_picture(ov511, &p))
 			return -EIO;
 
+		/* FIXME: check validity */
+
+		PDEBUG(4, "Setting depth=%d, palette=%d", p.depth, p.palette);
+		for (i = 0; i < OV511_NUMFRAMES; i++) {
+			ov511->frame[i].depth = p.depth;
+			ov511->frame[i].format = p.palette;
+			ov511->frame[i].segsize = GET_SEGSIZE(p.palette);
+		}
+
 		return 0;
 	}
 	case VIDIOCGCAPTURE:
 	{
 		int vf;
+
+		PDEBUG (4, "VIDIOCGCAPTURE");
+
 		if (copy_from_user(&vf, arg, sizeof(vf)))
 			return -EFAULT;
 		ov511->sub_flag = vf;
@@ -1836,16 +1887,25 @@
 			return -EINVAL;
 		if (vc.decimation)
 			return -EINVAL;
+#if 0
 		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;
+#else
+		vc.x &= ~3L;
+		vc.y &= ~1L;
+		vc.y &= ~31L;
+#endif
+		if (vc.width == 0)
+			vc.width = 32;
+
 		vc.height /= 16;
 		vc.height *= 16;
-		if (vc.height == 0) vc.height = 16;
+		if (vc.height == 0)
+			vc.height = 16;
 
 		ov511->subx = vc.x;
 		ov511->suby = vc.y;
@@ -1857,9 +1917,15 @@
 	case VIDIOCSWIN:
 	{
 		struct video_window vw;
+		int i, result;
 
 		if (copy_from_user(&vw, arg, sizeof(vw)))
 			return -EFAULT;
+
+		PDEBUG (4, "VIDIOCSWIN: width=%d, height=%d",
+			vw.width, vw.height);
+
+#if 0
 		if (vw.flags)
 			return -EINVAL;
 		if (vw.clipcount)
@@ -1868,8 +1934,22 @@
 			return -EINVAL;
 		if (vw.width != DEFAULT_WIDTH)
 			return -EINVAL;
+#endif
 
-		ov511->compress = 0;
+		/* If we're collecting previous frame wait
+		   before changing modes */
+		interruptible_sleep_on(&ov511->wq);
+		if (signal_pending(current)) return -EINTR;
+
+		result = ov511_mode_init_regs(ov511, vw.width, vw.height,
+			ov511->frame[0].format, ov511->sub_flag);
+		if (result < 0)
+			return result;
+
+		for (i = 0; i < OV511_NUMFRAMES; i++) {
+			ov511->frame[i].width = vw.width;
+			ov511->frame[i].height = vw.height;
+		}
 
 		return 0;
 	}
@@ -1877,13 +1957,15 @@
 	{
 		struct video_window vw;
 
-		vw.x = 0;
+		vw.x = 0;		/* FIXME */
 		vw.y = 0;
-		vw.width = DEFAULT_WIDTH;
-		vw.height = DEFAULT_HEIGHT;
+		vw.width = ov511->frame[0].width;
+		vw.height = ov511->frame[0].height;
 		vw.chromakey = 0;
 		vw.flags = 30;
 
+		PDEBUG (4, "VIDIOCGWIN: %dx%d", vw.width, vw.height);
+
 		if (copy_to_user(arg, &vw, sizeof(vw)))
 			return -EFAULT;
 
@@ -1935,19 +2017,16 @@
 			   before changing modes */
 			interruptible_sleep_on(&ov511->wq);
 			if (signal_pending(current)) return -EINTR;
-			ov511_mode_init_regs(ov511,
-					     vm.width, vm.height,
-					     vm.format, ov511->sub_flag);
+			ov511_mode_init_regs(ov511, 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;
+		ov511->frame[vm.frame].segsize = GET_SEGSIZE(vm.format);
+		ov511->frame[vm.frame].depth = GET_DEPTH(vm.format);
 
 		/* Mark it as ready */
 		ov511->frame[vm.frame].grabstate = FRAME_READY;
@@ -1965,11 +2044,11 @@
 		       ov511->frame[frame].grabstate);
 
 		switch (ov511->frame[frame].grabstate) {
-			case FRAME_UNUSED:
-				return -EINVAL;
-			case FRAME_READY:
-			case FRAME_GRABBING:
-			case FRAME_ERROR:
+		case FRAME_UNUSED:
+			return -EINVAL;
+		case FRAME_READY:
+		case FRAME_GRABBING:
+		case FRAME_ERROR:
 redo:
 			if (!ov511->dev)
 				return -EIO;
@@ -1989,23 +2068,21 @@
 				if ((ret = ov511_new_frame(ov511, frame)) < 0)
 					return ret;
 				goto redo;
-			}				
-			case FRAME_DONE:
-				ov511->frame[frame].grabstate = FRAME_UNUSED;
-				break;
-		}
-
-		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);
-		}
+			}			
+		case FRAME_DONE:
+			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, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
+				ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03);
+				ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
+			}
+			break;
+		} /* end switch */
 
 		return 0;
 	}
@@ -2038,7 +2115,7 @@
 		return -EINVAL;
 	default:
 		return -ENOIOCTLCMD;
-	}	/* End switch(cmd) */
+	} /* end switch */
 
 	return 0;
 }
@@ -2610,7 +2687,7 @@
 		ov511->sbuf[0].urb = NULL;
 	}
 
-#ifdef CONFIG_PROC_FS
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
         destroy_proc_ov511_cam(ov511);
 #endif
 
@@ -2637,8 +2714,7 @@
 
 static int __init usb_ov511_init(void)
 {
-#ifdef CONFIG_PROC_FS
-	PDEBUG(3, "creating /proc/ov511");
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
         proc_ov511_create();
 #endif
 
@@ -2655,7 +2731,7 @@
 	usb_deregister(&ov511_driver);
 	info("ov511 driver deregistered");
 
-#ifdef CONFIG_PROC_FS
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
         proc_ov511_destroy();
 #endif 
 }
