patch-2.2.18 linux/drivers/char/cpia.c

Next file: linux/drivers/char/cpia.h
Previous file: linux/drivers/char/console.c
Back to the patch index
Back to the overall index

diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.17/drivers/char/cpia.c linux/drivers/char/cpia.c
@@ -3,9 +3,9 @@
  *
  * Supports CPiA based Video Camera's.
  *
- * (C) 1999 Peter Pregler,
- *          Scott J. Bertin,
- *          Johannes Erdfelt
+ * (C) Copyright 1999-2000 Peter Pregler,
+ * (C) Copyright 1999-2000 Scott J. Bertin,
+ * (C) Copyright 1999-2000 Johannes Erdfelt, jerdfelt@valinux.com
  *
  * 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
@@ -22,28 +22,28 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+/* #define _CPIA_DEBUG_		define for verbose debug output */
 #include <linux/config.h>
 
 #include <linux/module.h>
+#include <linux/version.h>
 #include <linux/init.h>
-#include <linux/wrapper.h>
 #include <linux/fs.h>
 #include <linux/vmalloc.h>
-#include <linux/slab.h>
 #include <linux/delay.h>
-#include <linux/smp_lock.h>
+#include <linux/slab.h>
 #include <linux/proc_fs.h>
 #include <linux/ctype.h>
-#include <linux/videodev.h>
+#include <linux/pagemap.h>
 #include <asm/io.h>
 #include <asm/semaphore.h>
+#include <linux/wrapper.h>
 
 #ifdef CONFIG_KMOD
 #include <linux/kmod.h>
 #endif
 
-#undef _CPIA_DEBUG_		/* define for verbose debug output */
-#include <linux/cpia.h>
+#include "cpia.h"
 
 #ifdef CONFIG_VIDEO_CPIA_PP
 extern int cpia_pp_init(void);
@@ -53,7 +53,7 @@
 #endif
 
 #ifdef MODULE
-MODULE_AUTHOR("Scott J. Bertin <sbertin@mindspring.com> & Peter Pregler <Peter_Pregler@email.com>");
+MODULE_AUTHOR("Scott J. Bertin <sbertin@mindspring.com> & Peter Pregler <Peter_Pregler@email.com> & Johannes Erdfelt <jerdfelt@valinux.com>");
 MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras");
 MODULE_SUPPORTED_DEVICE("video");
 #endif
@@ -97,6 +97,7 @@
 #define CPIA_COMMAND_I2CRead		(INPUT | CPIA_MODULE_SYSTEM | 13)
 
 #define CPIA_COMMAND_GetVPVersion	(INPUT | CPIA_MODULE_VP_CTRL | 1)
+#define CPIA_COMMAND_ResetFrameCounter	(INPUT | CPIA_MODULE_VP_CTRL | 2)
 #define CPIA_COMMAND_SetColourParams	(OUTPUT | CPIA_MODULE_VP_CTRL | 3)
 #define CPIA_COMMAND_SetExposure	(OUTPUT | CPIA_MODULE_VP_CTRL | 4)
 #define CPIA_COMMAND_SetColourBalance	(OUTPUT | CPIA_MODULE_VP_CTRL | 6)
@@ -127,6 +128,7 @@
 #define CPIA_COMMAND_SetYUVThresh	(OUTPUT | CPIA_MODULE_CAPTURE | 12)
 #define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
 #define CPIA_COMMAND_DiscardFrame	(OUTPUT | CPIA_MODULE_CAPTURE | 14)
+#define CPIA_COMMAND_GrabReset		(OUTPUT | CPIA_MODULE_CAPTURE | 15)
 
 #define CPIA_COMMAND_OutputRS232	(OUTPUT | CPIA_MODULE_DEBUG | 1)
 #define CPIA_COMMAND_AbortProcess	(OUTPUT | CPIA_MODULE_DEBUG | 4)
@@ -135,117 +137,7 @@
 #define CPIA_COMMAND_StartDummyDtream	(OUTPUT | CPIA_MODULE_DEBUG | 8)
 #define CPIA_COMMAND_AbortStream	(OUTPUT | CPIA_MODULE_DEBUG | 9)
 #define CPIA_COMMAND_DownloadDRAM	(OUTPUT | CPIA_MODULE_DEBUG | 10)
-
-struct cam_params {
-	struct {
-		u8 firmwareVersion;
-		u8 firmwareRevision;
-		u8 vcVersion;
-		u8 vcRevision;
-	} version;
-	struct {
-		u16 vendor;
-		u16 product;
-		u16 deviceRevision;
-	} pnpID;
-	struct {
-		u8 vpVersion;
-		u8 vpRevision;
-		u16 cameraHeadID;
-	} vpVersion;
-	struct {
-		u8 systemState;
-		u8 grabState;
-		u8 streamState;
-		u8 fatalError;
-		u8 cmdError;
-		u8 debugFlags;
-		u8 vpStatus;
-		u8 errorCode;
-	} status;
-	struct {
-		u8 brightness;
-		u8 contrast;
-		u8 saturation;
-	} colourParams;
-	struct {
-		u8 gainMode;
-		u8 expMode;
-		u8 compMode;
-		u8 centreWeight;
-		u8 gain;
-		u8 fineExp;
-		u8 coarseExpLo;
-		u8 coarseExpHi;
-		u8 redComp;
-		u8 green1Comp;
-		u8 green2Comp;
-		u8 blueComp;
-	} exposure;
-	struct {
-		u8 balanceMode;
-		u8 redGain;
-		u8 greenGain;
-		u8 blueGain;
-	} colourBalance;
-	struct {
-		u8 divisor;
-		u8 baserate;
-	} sensorFps;
-	struct {
-		u8 gain1;
-		u8 gain2;
-		u8 gain4;
-		u8 gain8;
-	} apcor;
-	struct {
-		u8 flickerMode;
-		u8 coarseJump;
-		u8 allowableOverExposure;
-	} flickerControl;
-	struct {
-		u8 gain1;
-		u8 gain2;
-		u8 gain4;
-		u8 gain8;
-	} vlOffset;
-	struct {
-		u8 mode;
-		u8 decimation;
-	} compression;
-	struct {
-		u8 frTargeting;
-		u8 targetFR;
-		u8 targetQ;
-	} compressionTarget;
-	struct {
-		u8 yThreshold;
-		u8 uvThreshold;
-	} yuvThreshold;
-	struct {
-		u8 hysteresis;
-		u8 threshMax;
-		u8 smallStep;
-		u8 largeStep;
-		u8 decimationHysteresis;
-		u8 frDiffStepThresh;
-		u8 qDiffStepThresh;
-		u8 decimationThreshMod;
-	} compressionParams;
-	struct {
-		u8 videoSize;		/* CIF/QCIF */
-		u8 subSample;
-		u8 yuvOrder;
-	} format;
-	struct {
-		u8 colStart;		/* skip first 8*colStart pixels */
-		u8 colEnd;		/* finish at 8*colEnd pixels */
-		u8 rowStart;		/* skip first 4*rowStart lines */
-		u8 rowEnd;		/* finish at 4*rowEnd lines */
-	} roi;
-	u8 ecpTiming;
-	u8 streamStartLine;
-};
+#define CPIA_COMMAND_Null		(OUTPUT | CPIA_MODULE_DEBUG | 11)
 
 enum {
 	FRAME_READY,		/* Ready to grab into */
@@ -254,24 +146,6 @@
 	FRAME_UNUSED,		/* Unused (no MCAPTURE) */
 };
 
-#define FRAME_NUM	2	/* double buffering for now */
-struct cpia_frame {
-	u8 *data;
-	int count;
-	int width;
-	int height;
-	volatile int state;
-};
-
-enum v4l_camstates {
-	CPIA_V4L_IDLE = 0,
-	CPIA_V4L_ERROR,
-	CPIA_V4L_COMMAND,
-	CPIA_V4L_GRABBING,
-	CPIA_V4L_STREAMING,
-	CPIA_V4L_STREAMING_PAUSED,
-};
-
 #define COMMAND_NONE			0x0000
 #define COMMAND_SETCOMPRESSION		0x0001
 #define COMMAND_SETCOMPRESSIONTARGET	0x0002
@@ -288,45 +162,7 @@
 #define COMMAND_SETAPCOR		0x1000
 #define COMMAND_SETFLICKERCTRL		0x2000
 #define COMMAND_SETVLOFFSET		0x4000
-
-struct cam_data {
-	int index;			/* which camera is this */
-        struct semaphore busy_lock;     /* guard against SMP multithreading */
-	struct cpia_camera_ops *ops;	/* lowlevel driver operations */
-	void *lowlevel_data;		/* private data for lowlevel driver */
-	u8 *raw_image;			/* buffer for raw image data */
-	struct cpia_frame decompressed_frame;
-                                        /* buffer to hold decompressed frame */
-	int image_size;		        /* sizeof last decompressed image */
-	int open_count;			/* # of process that have camera open */
-				/* camera status */
-	int fps;			/* actual fps reported by the camera */
-	int transfer_rate;		/* transfer rate from camera in kB/s */
-	u8 mainsFreq;			/* for flicker control */
-
-				/* proc interface */
-	struct semaphore param_lock;	/* params lock for this camera */
-	struct cam_params params;	/* camera settings */
-	struct proc_dir_entry *proc_entry;	/* /proc/cpia/videoX */
-	
-					/* v4l */
-	int video_size;			/* VIDEO_SIZE_ */
-	volatile enum v4l_camstates camstate;	/* v4l layer status */
-	struct video_device vdev;	/* v4l videodev */
-	struct video_picture vp;	/* v4l camera settings */
-	struct video_window vw;		/* v4l capture area */
-
-				/* mmap interface */
-	int curframe;			/* the current frame to grab into */
-	u8 *frame_buf;			/* frame buffer data */
-        struct cpia_frame frame[FRAME_NUM];
-				/* FRAME_NUM-buffering, so we need a array */
-
-        int first_frame;
-	volatile u32 cmd_queue;		/* queued commands */
-};
-
-static struct cam_data *camera[CPIA_MAXCAMS] ={ [0 ... CPIA_MAXCAMS-1] = NULL };
+#define COMMAND_SETLIGHTS               0x8000 /* GA 04/14/00 */
 
 /* Developer's Guide Table 5 p 3-34
  * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
@@ -338,6 +174,7 @@
 /* forward declaration of local function */
 static void reset_camera_struct(struct cam_data *cam);
 
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
 /**********************************************************************
  *
  * Memory management
@@ -357,64 +194,134 @@
 	pte_t *ptep, pte;
 
 	pgd = pgd_offset(current->mm, adr);
-	if (pgd_none(*pgd)) return 0;
+	if (pgd_none(*pgd))
+		return 0;
 	pmd = pmd_offset(pgd, adr);
-	if (pmd_none(*pmd)) return 0;
+	if (pmd_none(*pmd))
+		return 0;
 	ptep = pte_offset(pmd, adr/*&(~PGDIR_MASK)*/);
 	pte = *ptep;
-	if(pte_present(pte))
-		return 
-		  virt_to_phys((void *)(pte_page(pte)|(adr&(PAGE_SIZE-1))));
+	if (pte_present(pte))
+		return virt_to_phys((void *)(pte_page(pte)|(adr&(PAGE_SIZE-1))));
 	return 0;
 }
 
-static inline unsigned long kvirt_to_phys(unsigned long adr) 
+static inline unsigned long kvirt_to_pa(unsigned long adr) 
 {
 	return uvirt_to_phys(VMALLOC_VMADDR(adr));
 }
 
-static void * rvmalloc(unsigned long size)
+#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) */
+
+/**********************************************************************
+ *
+ * Memory management
+ *
+ * This is a shameless copy from the USB-cpia driver (linux kernel
+ * version 2.3.29 or so, I have no idea what this code actually does ;).
+ * Actually it seems to be a copy of a shameless copy of the bttv-driver.
+ * Or that is a copy of a shameless copy of ... (To the powers: is there
+ * no generic kernel-function to do this sort of stuff?)
+ *
+ * Yes, it was a shameless copy from the bttv-driver. IIRC, Alan says
+ * there will be one, but apparentely not yet - jerdfelt
+ *
+ **********************************************************************/
+
+/* 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 = page_address(pte_page(pte)) |
+				      (adr & (PAGE_SIZE-1));
+		}
+	}
+	return ret;
+}
+
+/* Here we want the physical address of the memory.
+ * This is used when initializing the contents of the
+ * area and marking the pages as reserved.
+ */
+static inline unsigned long kvirt_to_pa(unsigned long adr)
+{
+	unsigned long va, kva, ret;
+
+	va = VMALLOC_VMADDR(adr);
+	kva = uvirt_to_kva(pgd_offset_k(va), va);
+	ret = __pa(kva);
+	return ret;
+}
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) */
+
+static void *rvmalloc(unsigned long size)
 {
-	void * mem;
+	void *mem;
 	unsigned long adr, page;
 
+	/* Round it off to PAGE_SIZE */
 	size += (PAGE_SIZE - 1);
 	size &= ~(PAGE_SIZE - 1);
 
-	mem=vmalloc(size);
-	if (mem) {
-		/* Clear the ram out, no junk to the user */
-		memset(mem, 0, size);
-		adr=(unsigned long) mem;
-		while (size > 0) {
-			page = kvirt_to_phys(adr);
-			mem_map_reserve(MAP_NR(phys_to_virt(page)));
-			adr+=PAGE_SIZE;
-			if (size > PAGE_SIZE) size-=PAGE_SIZE;
-			else  size=0;
-		}
+	mem = vmalloc(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) {
+		page = kvirt_to_pa(adr);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
+		mem_map_reserve(MAP_NR(phys_to_virt(page)));
+#else
+		mem_map_reserve(MAP_NR(__va(page)));
+#endif
+		adr += PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
 	}
+
 	return mem;
 }
 
-static void rvfree(void * mem, unsigned long size)
+static void rvfree(void *mem, unsigned long size)
 {
 	unsigned long adr, page;
-        
+
+	if (!mem)
+		return;
+
 	size += (PAGE_SIZE - 1);
 	size &= ~(PAGE_SIZE - 1);
 
-	if (mem) {
-		adr=(unsigned long) mem;
-		while (size > 0) {
-			page = kvirt_to_phys(adr);
-			mem_map_unreserve(MAP_NR(phys_to_virt(page)));
-			adr+=PAGE_SIZE;
-			if (size > PAGE_SIZE) size-=PAGE_SIZE;
-			else size=0;
-		}
-		vfree(mem);
+	adr = (unsigned long) mem;
+	while (size > 0) {
+		page = kvirt_to_pa(adr);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
+		mem_map_unreserve(MAP_NR(phys_to_virt(page)));
+#else
+		mem_map_unreserve(MAP_NR(__va(page)));
+#endif
+		adr += PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
 	}
+	vfree(mem);
 }
 
 /**********************************************************************
@@ -422,6 +329,7 @@
  * /proc interface
  *
  **********************************************************************/
+#ifdef CONFIG_PROC_FS
 static struct proc_dir_entry *cpia_proc_root=NULL;
 
 static int cpia_read_proc(char *page, char **start, off_t off,
@@ -432,7 +340,7 @@
 	struct cam_data *cam = data;
 	char tmpstr[20];
 
-	/* IMPORTANT: This output MUST be kept under 4k (FIXME: PAGE_SIZE?)
+	/* IMPORTANT: This output MUST be kept under PAGE_SIZE
 	 *            or we need to get more sophisticated. */
 
 	out += sprintf(out, "read-only\n-----------------------\n");
@@ -467,22 +375,23 @@
 	               cam->params.status.vpStatus);
 	out += sprintf(out, "error_code:               %#04x\n",
 	               cam->params.status.errorCode);
+	/* GA 04/14/00 - QX3 specific entries */
+	if (cam->params.qx3.qx3_detected) {
+		out += sprintf(out, "button:                   %4d\n",
+		               cam->params.qx3.button);
+		out += sprintf(out, "cradled:                  %4d\n",
+		               cam->params.qx3.cradled);
+	}
 	out += sprintf(out, "video_size:               %s\n",
 	               cam->params.format.videoSize == VIDEOSIZE_CIF ?
 		       "CIF " : "QCIF");
-	out += sprintf(out, "sub_sample:               %s\n",
-	               cam->params.format.subSample == SUBSAMPLE_420 ?
-		       "420" : "422");
-	out += sprintf(out, "yuv_order:                %s\n",
-	               cam->params.format.yuvOrder == YUVORDER_YUYV ?
-		       "YUYV" : "UYVY");
 	out += sprintf(out, "roi:                      (%3d, %3d) to (%3d, %3d)\n",
 	               cam->params.roi.colStart*8,
 	               cam->params.roi.rowStart*4,
 	               cam->params.roi.colEnd*8,
 	               cam->params.roi.rowEnd*4);
-	out += sprintf(out, "actual_fps:               %d\n", cam->fps);
-	out += sprintf(out, "transfer_rate:            %dkB/s\n",
+	out += sprintf(out, "actual_fps:               %3d\n", cam->fps);
+	out += sprintf(out, "transfer_rate:            %4dkB/s\n",
 	               cam->transfer_rate);
 	
 	out += sprintf(out, "\nread-write\n");
@@ -490,13 +399,13 @@
 	               "       max   default  comment\n");
 	out += sprintf(out, "brightness:             %8d  %8d  %8d  %8d\n",
 	               cam->params.colourParams.brightness, 0, 100, 50);
-	if(cam->params.version.firmwareVersion == 1 &&
-	   cam->params.version.firmwareRevision == 2) {
+	if (cam->params.version.firmwareVersion == 1 &&
+	   cam->params.version.firmwareRevision == 2)
 		/* 1-02 firmware limits contrast to 80 */
 		tmp = 80;
-	} else {
+	else
 		tmp = 96;
-	}
+
 	out += sprintf(out, "contrast:               %8d  %8d  %8d  %8d"
 	               "  steps of 8\n",
 	               cam->params.colourParams.contrast, 0, tmp, 48);
@@ -510,21 +419,20 @@
 	               2*cam->params.streamStartLine, 0,
 		       cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144,
 		       cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120);
+	out += sprintf(out, "sub_sample:             %8s  %8s  %8s  %8s\n",
+	               cam->params.format.subSample == SUBSAMPLE_420 ?
+		       "420" : "422", "420", "422", "422");
+	out += sprintf(out, "yuv_order:              %8s  %8s  %8s  %8s\n",
+	               cam->params.format.yuvOrder == YUVORDER_YUYV ?
+		       "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV");
 	out += sprintf(out, "ecp_timing:             %8s  %8s  %8s  %8s\n",
 	               cam->params.ecpTiming ? "slow" : "normal", "slow",
 		       "normal", "normal");
 
-	switch(cam->params.colourBalance.balanceMode) {
-	case 1:
-	  // FIXME case 3:
-		sprintf(tmpstr, "manual");
-		break;
-	case 2:
+	if (cam->params.colourBalance.balanceModeIsAuto) {
 		sprintf(tmpstr, "auto");
-		break;
-	default:
-		sprintf(tmpstr, "unknown");
-		break;
+	} else {
+		sprintf(tmpstr, "manual");
 	}
 	out += sprintf(out, "color_balance_mode:     %8s  %8s  %8s"
 		       "  %8s\n",  tmpstr, "manual", "auto", "auto");
@@ -535,20 +443,20 @@
 	out += sprintf(out, "blue_gain:              %8d  %8d  %8d  %8d\n",
 	               cam->params.colourBalance.blueGain, 0, 212, 92);
 
-	if(cam->params.version.firmwareVersion == 1 &&
-	   cam->params.version.firmwareRevision == 2) {
+	if (cam->params.version.firmwareVersion == 1 &&
+	   cam->params.version.firmwareRevision == 2)
 		/* 1-02 firmware limits gain to 2 */
 		sprintf(tmpstr, "%8d  %8d", 1, 2);
-	} else {
+	else
 		sprintf(tmpstr, "%8d  %8d", 1, 8);
-	}
-	if(cam->params.exposure.gainMode == 0) {
+
+	if (cam->params.exposure.gainMode == 0)
 		out += sprintf(out, "max_gain:                unknown  %18s"
 		               "  %8d  powers of 2\n", tmpstr, 2); 
-	} else {
+	else
 		out += sprintf(out, "max_gain:               %8d  %18s  %8d  powers of 2\n", 
 		               1<<(cam->params.exposure.gainMode-1), tmpstr, 2);
-	}
+
 	switch(cam->params.exposure.expMode) {
 	case 1:
 	case 3:
@@ -568,22 +476,22 @@
 	               "off", "on", "on");
 	out += sprintf(out, "gain:                   %8d  %8d  max_gain  %8d  1,2,4,8 possible\n",
 	               1<<cam->params.exposure.gain, 1, 1);
-	if(cam->params.version.firmwareVersion == 1 &&
-	   cam->params.version.firmwareRevision == 2) {
+	if (cam->params.version.firmwareVersion == 1 &&
+	   cam->params.version.firmwareRevision == 2)
 		/* 1-02 firmware limits fineExp to 127 */
 		tmp = 255;
-	} else {
+	else
 		tmp = 511;
-	}
+
 	out += sprintf(out, "fine_exp:               %8d  %8d  %8d  %8d\n",
 	               cam->params.exposure.fineExp*2, 0, tmp, 0);
-	if(cam->params.version.firmwareVersion == 1 &&
-	   cam->params.version.firmwareRevision == 2) {
+	if (cam->params.version.firmwareVersion == 1 &&
+	   cam->params.version.firmwareRevision == 2)
 		/* 1-02 firmware limits coarseExpHi to 0 */
 		tmp = 255;
-	} else {
+	else
 		tmp = 65535;
-	}
+
 	out += sprintf(out, "coarse_exp:             %8d  %8d  %8d"
 		       "  %8d\n", cam->params.exposure.coarseExpLo+
 		       256*cam->params.exposure.coarseExpHi, 0, tmp, 185);
@@ -637,9 +545,9 @@
 		break;
 	}
 	out += sprintf(out, "    none,auto,manual      auto\n");
-	out += sprintf(out, "decimation:             %8s  %8s  %8s  %8s\n",
+	out += sprintf(out, "decimation_enable:      %8s  %8s  %8s  %8s\n",
         	       cam->params.compression.decimation == 
-		       DECIMATION_ENAB ? "on":"off", "off", "off",
+		       DECIMATION_ENAB ? "on":"off", "off", "on",
 		       "off");
 	out += sprintf(out, "compression_target:    %9s %9s %9s %9s\n",
 	               cam->params.compressionTarget.frTargeting  == 
@@ -647,13 +555,13 @@
 		       "framerate":"quality",
 		       "framerate", "quality", "quality");
 	out += sprintf(out, "target_framerate:       %8d  %8d  %8d  %8d\n",
-	               cam->params.compressionTarget.targetFR, 0, 30, 7);
+	               cam->params.compressionTarget.targetFR, 1, 30, 15);
 	out += sprintf(out, "target_quality:         %8d  %8d  %8d  %8d\n",
-	               cam->params.compressionTarget.targetQ, 0, 255, 10);
+	               cam->params.compressionTarget.targetQ, 1, 64, 5);
 	out += sprintf(out, "y_threshold:            %8d  %8d  %8d  %8d\n",
-	               cam->params.yuvThreshold.yThreshold, 0, 31, 15);
+	               cam->params.yuvThreshold.yThreshold, 0, 31, 6);
 	out += sprintf(out, "uv_threshold:           %8d  %8d  %8d  %8d\n",
-	               cam->params.yuvThreshold.uvThreshold, 0, 31, 15);
+	               cam->params.yuvThreshold.uvThreshold, 0, 31, 6);
 	out += sprintf(out, "hysteresis:             %8d  %8d  %8d  %8d\n",
 	               cam->params.compressionParams.hysteresis, 0, 255, 3);
 	out += sprintf(out, "threshold_max:          %8d  %8d  %8d  %8d\n",
@@ -674,15 +582,25 @@
 	out += sprintf(out, "decimation_thresh_mod:  %8d  %8d  %8d  %8d\n",
 	               cam->params.compressionParams.decimationThreshMod,
 		       0, 255, 2);
-	
+
+	/* GA 04/14/00 - QX3 specific entries */
+	if (cam->params.qx3.qx3_detected) {
+		out += sprintf(out, "toplight:               %8s  %8s  %8s  %8s\n", 
+		               cam->params.qx3.toplight ? "on" : "off",
+			       "off", "on", "off");
+		out += sprintf(out, "bottomlight:            %8s  %8s  %8s  %8s\n", 
+		               cam->params.qx3.bottomlight ? "on" : "off",
+			       "off", "on", "off");
+	}
+
 	len = out - page;
 	len -= off;
 	if (len < count) {
 		*eof = 1;
 		if (len <= 0) return 0;
-	} else {
+	} else
 		len = count;
-	}
+
 	*start = page + off;
 	return len;
 }
@@ -692,16 +610,14 @@
 {
 	struct cam_data *cam = data;
 	struct cam_params new_params;
-	int retval, len, colon_found;
+	int retval, find_colon;
 	int size = count;
-	char *p;
 	unsigned long val;
 	u32 command_flags = 0;
 	u8 new_mains;
 	
-	if(down_interruptible(&cam->param_lock)) {
+	if (down_interruptible(&cam->param_lock))
 		return -ERESTARTSYS;
-	}
 	
 	/*
 	 * Skip over leading whitespace
@@ -714,106 +630,112 @@
 	memcpy(&new_params, &cam->params, sizeof(struct cam_params));
 	new_mains = cam->mainsFreq;
 	
-#define MATCH(x) (len=strlen(x), len <= count && strncmp(buffer, x, len) == 0)
+#define MATCH(x) \
+	({ \
+		int _len = strlen(x), _ret, _colon_found; \
+		_ret = (_len <= count && strncmp(buffer, x, _len) == 0); \
+		if (_ret) { \
+			buffer += _len; \
+			count -= _len; \
+			if (find_colon) { \
+				_colon_found = 0; \
+				while (count && (*buffer == ' ' || *buffer == '\t' || \
+				       (!_colon_found && *buffer == ':'))) { \
+					if (*buffer == ':')  \
+						_colon_found = 1; \
+					--count; \
+					++buffer; \
+				} \
+				if (!count || !_colon_found) \
+					retval = -EINVAL; \
+				find_colon = 0; \
+			} \
+		} \
+		_ret; \
+	})
 #define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \
                                new_params.version.firmwareRevision == (y))
 #define VALUE \
-	simple_strtoul(buffer, &p, 0); \
-	if(p == buffer) { \
-		retval = -EINVAL; \
-	} else { \
-		count -= p-buffer; \
-		buffer = p; \
-	}
-#define FIND_VALUE \
-	buffer += len;\
-	count -= len; \
-	colon_found=0; \
-	while(count && (*buffer == ' ' || *buffer == '\t' || \
-	                (!colon_found && *buffer == ':'))) { \
-		if(*buffer == ':') colon_found = 1; \
-		--count; \
-		++buffer; \
-	} \
-	if(!count || !colon_found) { \
-		retval = -EINVAL; \
-	}
-#define FIND_END \
-	if(retval == 0) { \
-		while(count && isspace(*buffer) && *buffer != '\n'){ \
-			--count; \
-			++buffer; \
+	({ \
+		char *_p; \
+		unsigned long int _ret; \
+		_ret = simple_strtoul(buffer, &_p, 0); \
+		if (_p == buffer) \
+			retval = -EINVAL; \
+		else { \
+			count -= _p - buffer; \
+			buffer = _p; \
 		} \
-		if(count) { \
-			if(*buffer != '\n' && *buffer != ';') { \
-				retval = -EINVAL; \
-			} else { \
-				--count; \
-				++buffer; \
-			} \
-		} \
-	}
+		_ret; \
+	})
+
 	retval = 0;
-	while(count && !retval) {
-		if(MATCH("brightness")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val <= 100)
-					new_params.colourParams.brightness=val;
-				else retval = -EINVAL;
+	while (count && !retval) {
+		find_colon = 1;
+		if (MATCH("brightness")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 100)
+					new_params.colourParams.brightness = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOLOURPARAMS;
-			FIND_END
-		} else if(MATCH("contrast")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val <= 100) {
+		} else if (MATCH("contrast")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 100) {
 					/* contrast is in steps of 8, so round*/
 					val = ((val + 3) / 8) * 8;
 					/* 1-02 firmware limits contrast to 80*/
-					if(FIRMWARE_VERSION(1,2) && val > 80)
+					if (FIRMWARE_VERSION(1,2) && val > 80)
 						val = 80;
+
 					new_params.colourParams.contrast = val;
-				} else retval = -EINVAL;
+				} else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOLOURPARAMS;
-			FIND_END
-		} else if(MATCH("saturation")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val <= 100)
-					new_params.colourParams.saturation=val;
-				else retval = -EINVAL;
+		} else if (MATCH("saturation")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 100)
+					new_params.colourParams.saturation = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOLOURPARAMS;
-			FIND_END
-		} else if(MATCH("sensor_fps")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
+		} else if (MATCH("sensor_fps")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
 				/* find values so that sensorFPS is minimized,
 				 * but >= val */
-				if(val > 30) {
+				if (val > 30)
 					retval = -EINVAL;
-				} else if(val > 25) {
+				else if (val > 25) {
 					new_params.sensorFps.divisor = 0;
 					new_params.sensorFps.baserate = 1;
-				} else if(val > 15) {
+				} else if (val > 15) {
 					new_params.sensorFps.divisor = 0;
 					new_params.sensorFps.baserate = 0;
-				} else if(val > 12) {
+				} else if (val > 12) {
 					new_params.sensorFps.divisor = 1;
 					new_params.sensorFps.baserate = 1;
-				} else if(val > 7) {
+				} else if (val > 7) {
 					new_params.sensorFps.divisor = 1;
 					new_params.sensorFps.baserate = 0;
-				} else if(val > 6) {
+				} else if (val > 6) {
 					new_params.sensorFps.divisor = 2;
 					new_params.sensorFps.baserate = 1;
-				} else if(val > 3) {
+				} else if (val > 3) {
 					new_params.sensorFps.divisor = 2;
 					new_params.sensorFps.baserate = 0;
 				} else {
@@ -825,89 +747,100 @@
 					flicker_jumps[new_mains]
 					[new_params.sensorFps.baserate]
 					[new_params.sensorFps.divisor];
-				if(new_params.flickerControl.flickerMode)
+				if (new_params.flickerControl.flickerMode)
 					command_flags |= COMMAND_SETFLICKERCTRL;
 			}
 			command_flags |= COMMAND_SETSENSORFPS;
-			FIND_END
-		} else if(MATCH("stream_start_line")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
+		} else if (MATCH("stream_start_line")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
 				int max_line = 288;
-				if(new_params.format.videoSize==VIDEOSIZE_QCIF)
+
+				if (new_params.format.videoSize == VIDEOSIZE_QCIF)
 					max_line = 144;
-				if(val <= max_line)
+				if (val <= max_line)
 					new_params.streamStartLine = val/2;
-				else retval = -EINVAL;
+				else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("ecp_timing")) {
-			FIND_VALUE
-			if(!retval && MATCH("normal")) {
-				buffer += len;
-				count -= len;
+		} else if (MATCH("sub_sample")) {
+			if (!retval && MATCH("420"))
+				new_params.format.subSample = SUBSAMPLE_420;
+			else if (!retval && MATCH("422"))
+				new_params.format.subSample = SUBSAMPLE_422;
+			else
+				retval = -EINVAL;
+
+			command_flags |= COMMAND_SETFORMAT;
+		} else if (MATCH("yuv_order")) {
+			if (!retval && MATCH("YUYV"))
+				new_params.format.yuvOrder = YUVORDER_YUYV;
+			else if (!retval && MATCH("UYVY"))
+				new_params.format.yuvOrder = YUVORDER_UYVY;
+			else
+				retval = -EINVAL;
+
+			command_flags |= COMMAND_SETFORMAT;
+		} else if (MATCH("ecp_timing")) {
+			if (!retval && MATCH("normal"))
 				new_params.ecpTiming = 0;
-			} else if(!retval && MATCH("slow")) {
-				buffer += len;
-				count -= len;
+			else if (!retval && MATCH("slow"))
 				new_params.ecpTiming = 1;
-			} else {
+			else
 				retval = -EINVAL;
-			}
+
 			command_flags |= COMMAND_SETECPTIMING;
-			FIND_END
-		} else if(MATCH("color_balance_mode")) {
-			FIND_VALUE
-			if(!retval && MATCH("manual")) {
-				buffer += len;
-				count -= len;
-				new_params.colourBalance.balanceMode=1;
-			} else if(!retval && MATCH("auto")) {
-				buffer += len;
-				count -= len;
-				new_params.colourBalance.balanceMode=2;
-			} else {
+		} else if (MATCH("color_balance_mode")) {
+			if (!retval && MATCH("manual"))
+				new_params.colourBalance.balanceModeIsAuto = 0;
+			else if (!retval && MATCH("auto"))
+				new_params.colourBalance.balanceModeIsAuto = 1;
+			else
 				retval = -EINVAL;
-			}
+
 			command_flags |= COMMAND_SETCOLOURBALANCE;
-			FIND_END
-		} else if(MATCH("red_gain")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val <= 212)
+		} else if (MATCH("red_gain")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 212)
 					new_params.colourBalance.redGain = val;
-				else retval = -EINVAL;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOLOURBALANCE;
-			FIND_END
-		} else if(MATCH("green_gain")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val <= 212)
-					new_params.colourBalance.greenGain=val;
-				else retval = -EINVAL;
+		} else if (MATCH("green_gain")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 212)
+					new_params.colourBalance.greenGain = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOLOURBALANCE;
-			FIND_END
-		} else if(MATCH("blue_gain")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val <= 212)
+		} else if (MATCH("blue_gain")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 212)
 					new_params.colourBalance.blueGain = val;
-				else retval = -EINVAL;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOLOURBALANCE;
-			FIND_END
-		} else if(MATCH("max_gain")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
+		} else if (MATCH("max_gain")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
 				/* 1-02 firmware limits gain to 2 */
-				if(FIRMWARE_VERSION(1,2) && val > 2)
+				if (FIRMWARE_VERSION(1,2) && val > 2)
 					val = 2;
 				switch(val) {
 				case 1:
@@ -928,44 +861,32 @@
 				}
 			}
 			command_flags |= COMMAND_SETEXPOSURE;
-			FIND_END
-		} else if(MATCH("exposure_mode")) {
-			FIND_VALUE
-			if(!retval && MATCH("auto")) {
-				buffer += len;
-				count -= len;
+		} else if (MATCH("exposure_mode")) {
+			if (!retval && MATCH("auto"))
 				new_params.exposure.expMode = 2;
-			} else if(!retval && MATCH("manual")) {
-				buffer += len;
-				count -= len;
-				if(new_params.exposure.expMode == 2)
+			else if (!retval && MATCH("manual")) {
+				if (new_params.exposure.expMode == 2)
 					new_params.exposure.expMode = 3;
 				new_params.flickerControl.flickerMode = 0;
 				command_flags |= COMMAND_SETFLICKERCTRL;
-			} else {
+			} else
 				retval = -EINVAL;
-			}
+
 			command_flags |= COMMAND_SETEXPOSURE;
-			FIND_END
-		} else if(MATCH("centre_weight")) {
-			FIND_VALUE
-			if(!retval && MATCH("on")) {
-				buffer += len;
-				count -= len;
+		} else if (MATCH("centre_weight")) {
+			if (!retval && MATCH("on"))
 				new_params.exposure.centreWeight = 1;
-			} else if(!retval && MATCH("off")) {
-				buffer += len;
-				count -= len;
+			else if (!retval && MATCH("off"))
 				new_params.exposure.centreWeight = 2;
-			} else {
+			else
 				retval = -EINVAL;
-			}
+
 			command_flags |= COMMAND_SETEXPOSURE;
-			FIND_END
-		} else if(MATCH("gain")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
+		} else if (MATCH("gain")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
 				switch(val) {
 				case 1:
 					new_params.exposure.gain = 0;
@@ -996,35 +917,36 @@
 					break;
 				}
 				command_flags |= COMMAND_SETEXPOSURE;
-				if(new_params.exposure.gain >
-				   new_params.exposure.gainMode-1)
+				if (new_params.exposure.gain >
+				    new_params.exposure.gainMode-1)
 					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("fine_exp")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val < 256) {
+		} else if (MATCH("fine_exp")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val < 256) {
 					/* 1-02 firmware limits fineExp to 127*/
-					if(FIRMWARE_VERSION(1,2) && val > 127)
+					if (FIRMWARE_VERSION(1,2) && val > 127)
 						val = 127;
 					new_params.exposure.fineExp = val;
 					new_params.exposure.expMode = 1;
 					command_flags |= COMMAND_SETEXPOSURE;
 					new_params.flickerControl.flickerMode = 0;
 					command_flags |= COMMAND_SETFLICKERCTRL;
-				} else retval = -EINVAL;
+				} else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("coarse_exp")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val < 65536) {
+		} else if (MATCH("coarse_exp")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val < 65536) {
 					/* 1-02 firmware limits
 					 * coarseExp to 255 */
-					if(FIRMWARE_VERSION(1,2) && val > 255)
+					if (FIRMWARE_VERSION(1,2) && val > 255)
 						val = 255;
 					new_params.exposure.coarseExpLo =
 						val & 0xff;
@@ -1034,353 +956,398 @@
 					command_flags |= COMMAND_SETEXPOSURE;
 					new_params.flickerControl.flickerMode = 0;
 					command_flags |= COMMAND_SETFLICKERCTRL;
-				} else retval = -EINVAL;
+				} else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("red_comp")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val >= 220 && val <= 255) {
+		} else if (MATCH("red_comp")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val >= 220 && val <= 255) {
 					new_params.exposure.redComp = val;
 					command_flags |= COMMAND_SETEXPOSURE;
-				} else retval = -EINVAL;
+				} else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("green1_comp")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val >= 214 && val <= 255) {
+		} else if (MATCH("green1_comp")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val >= 214 && val <= 255) {
 					new_params.exposure.green1Comp = val;
 					command_flags |= COMMAND_SETEXPOSURE;
-				} else retval = -EINVAL;
+				} else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("green2_comp")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val >= 214 && val <= 255) {
+		} else if (MATCH("green2_comp")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val >= 214 && val <= 255) {
 					new_params.exposure.green2Comp = val;
 					command_flags |= COMMAND_SETEXPOSURE;
-				} else retval = -EINVAL;
+				} else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("blue_comp")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val >= 230 && val <= 255) {
+		} else if (MATCH("blue_comp")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val >= 230 && val <= 255) {
 					new_params.exposure.blueComp = val;
 					command_flags |= COMMAND_SETEXPOSURE;
-				} else retval = -EINVAL;
+				} else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("apcor_gain1")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
+		} else if (MATCH("apcor_gain1")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
 				command_flags |= COMMAND_SETAPCOR;
-				if(val <= 0xff) new_params.apcor.gain1 = val;
-				else retval = -EINVAL;
+				if (val <= 0xff)
+					new_params.apcor.gain1 = val;
+				else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("apcor_gain2")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
+		} else if (MATCH("apcor_gain2")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
 				command_flags |= COMMAND_SETAPCOR;
-				if(val <= 0xff) new_params.apcor.gain2 = val;
-				else retval = -EINVAL;
+				if (val <= 0xff)
+					new_params.apcor.gain2 = val;
+				else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("apcor_gain4")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
+		} else if (MATCH("apcor_gain4")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
 				command_flags |= COMMAND_SETAPCOR;
-				if(val <= 0xff) new_params.apcor.gain4 = val;
-				else retval = -EINVAL;
+				if (val <= 0xff)
+					new_params.apcor.gain4 = val;
+				else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("apcor_gain8")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
+		} else if (MATCH("apcor_gain8")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
 				command_flags |= COMMAND_SETAPCOR;
-				if(val <= 0xff) new_params.apcor.gain8 = val;
-				else retval = -EINVAL;
+				if (val <= 0xff)
+					new_params.apcor.gain8 = val;
+				else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("vl_offset_gain1")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val <= 0xff) new_params.vlOffset.gain1 = val;
-				else retval = -EINVAL;
+		} else if (MATCH("vl_offset_gain1")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.vlOffset.gain1 = val;
+				else
+					retval = -EINVAL;
 			}
-			FIND_END
 			command_flags |= COMMAND_SETVLOFFSET;
-		} else if(MATCH("vl_offset_gain2")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val <= 0xff) new_params.vlOffset.gain2 = val;
-				else retval = -EINVAL;
+		} else if (MATCH("vl_offset_gain2")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.vlOffset.gain2 = val;
+				else
+					retval = -EINVAL;
 			}
-			FIND_END
 			command_flags |= COMMAND_SETVLOFFSET;
-		} else if(MATCH("vl_offset_gain4")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val <= 0xff) new_params.vlOffset.gain4 = val;
-				else retval = -EINVAL;
+		} else if (MATCH("vl_offset_gain4")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.vlOffset.gain4 = val;
+				else
+					retval = -EINVAL;
 			}
-			FIND_END
 			command_flags |= COMMAND_SETVLOFFSET;
-		} else if(MATCH("vl_offset_gain8")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val <= 0xff) new_params.vlOffset.gain8 = val;
-				else retval = -EINVAL;
+		} else if (MATCH("vl_offset_gain8")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.vlOffset.gain8 = val;
+				else
+					retval = -EINVAL;
 			}
-			FIND_END
 			command_flags |= COMMAND_SETVLOFFSET;
-		} else if(MATCH("flicker_control")) {
-			FIND_VALUE
-			if(!retval && MATCH("on")) {
-				buffer += len;
-				count -= len;
+		} else if (MATCH("flicker_control")) {
+			if (!retval && MATCH("on")) {
 				new_params.flickerControl.flickerMode = 1;
 				new_params.exposure.expMode = 2;
 				command_flags |= COMMAND_SETEXPOSURE;
-			} else if(!retval && MATCH("off")) {
-				buffer += len;
-				count -= len;
+			} else if (!retval && MATCH("off"))
 				new_params.flickerControl.flickerMode = 0;
-			} else {
+			else
 				retval = -EINVAL;
-			}
+
 			command_flags |= COMMAND_SETFLICKERCTRL;
-			FIND_END
-		} else if(MATCH("mains_frequency")) {
-			FIND_VALUE
-			if(!retval && MATCH("50")) {
-				buffer += len;
-				count -= len;
+		} else if (MATCH("mains_frequency")) {
+			if (!retval && MATCH("50")) {
 				new_mains = 0;
 				new_params.flickerControl.coarseJump = 
 					flicker_jumps[new_mains]
 					[new_params.sensorFps.baserate]
 					[new_params.sensorFps.divisor];
-				if(new_params.flickerControl.flickerMode)
+				if (new_params.flickerControl.flickerMode)
 					command_flags |= COMMAND_SETFLICKERCTRL;
-			} else if(!retval && MATCH("60")) {
-				buffer += len;
-				count -= len;
+			} else if (!retval && MATCH("60")) {
 				new_mains = 1;
 				new_params.flickerControl.coarseJump = 
 					flicker_jumps[new_mains]
 					[new_params.sensorFps.baserate]
 					[new_params.sensorFps.divisor];
-				if(new_params.flickerControl.flickerMode)
+				if (new_params.flickerControl.flickerMode)
 					command_flags |= COMMAND_SETFLICKERCTRL;
-			} else {
+			} else
 				retval = -EINVAL;
-			}
-			FIND_END
-		} else if(MATCH("allowable_overexposure")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val <= 0xff) {
+		} else if (MATCH("allowable_overexposure")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff) {
 					new_params.flickerControl.
 						allowableOverExposure = val;
 					command_flags |= COMMAND_SETFLICKERCTRL;
-				} else retval = -EINVAL;
+				} else
+					retval = -EINVAL;
 			}
-			FIND_END
-		} else if(MATCH("compression_mode")) {
-			FIND_VALUE
-			if(!retval && MATCH("none")) {
-				buffer += len;
-				count -= len;
+		} else if (MATCH("compression_mode")) {
+			if (!retval && MATCH("none"))
 				new_params.compression.mode =
 					CPIA_COMPRESSION_NONE;
-			} else if(!retval && MATCH("auto")) {
-				buffer += len;
-				count -= len;
+			else if (!retval && MATCH("auto"))
 				new_params.compression.mode =
 					CPIA_COMPRESSION_AUTO;
-			} else if(!retval && MATCH("manual")) {
-				buffer += len;
-				count -= len;
+			else if (!retval && MATCH("manual"))
 				new_params.compression.mode =
 					CPIA_COMPRESSION_MANUAL;
-			} else {
+			else
 				retval = -EINVAL;
-			}
+
 			command_flags |= COMMAND_SETCOMPRESSION;
-			FIND_END
-		} else if(MATCH("decimation")) {
-			FIND_VALUE
-			if(!retval && MATCH("off")) {
-				buffer += len;
-				count -= len;
+		} else if (MATCH("decimation_enable")) {
+			if (!retval && MATCH("off"))
 				new_params.compression.decimation = 0;
-			} else {
+			else if (!retval && MATCH("on"))
+				new_params.compression.decimation = 1;
+			else
 				retval = -EINVAL;
-			}
+
 			command_flags |= COMMAND_SETCOMPRESSION;
-			FIND_END
-		} else if(MATCH("compression_target")) {
-			FIND_VALUE
-			if(!retval && MATCH("quality")) {
-				buffer += len;
-				count -= len;
+		} else if (MATCH("compression_target")) {
+			if (!retval && MATCH("quality"))
 				new_params.compressionTarget.frTargeting = 
 					CPIA_COMPRESSION_TARGET_QUALITY;
-			} else if(!retval && MATCH("framerate")) {
-				buffer += len;
-				count -= len;
+			else if (!retval && MATCH("framerate"))
 				new_params.compressionTarget.frTargeting = 
 					CPIA_COMPRESSION_TARGET_FRAMERATE;
-			} else {
+			else
 				retval = -EINVAL;
-			}
+
 			command_flags |= COMMAND_SETCOMPRESSIONTARGET;
-			FIND_END
-		} else if(MATCH("target_framerate")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval)
-				new_params.compressionTarget.targetFR = val;
+		} else if (MATCH("target_framerate")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if(val > 0 && val <= 30)
+					new_params.compressionTarget.targetFR = val;
+				else
+					retval = -EINVAL;
+			}
 			command_flags |= COMMAND_SETCOMPRESSIONTARGET;
-			FIND_END
-		} else if(MATCH("target_quality")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) new_params.compressionTarget.targetQ = val;
+		} else if (MATCH("target_quality")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if(val > 0 && val <= 64)
+					new_params.compressionTarget.targetQ = val;
+				else 
+					retval = -EINVAL;
+			}
 			command_flags |= COMMAND_SETCOMPRESSIONTARGET;
-			FIND_END
-		} else if(MATCH("y_threshold")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val < 32)
-					new_params.yuvThreshold.yThreshold=val;
-				else retval = -EINVAL;
+		} else if (MATCH("y_threshold")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val < 32)
+					new_params.yuvThreshold.yThreshold = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETYUVTHRESH;
-			FIND_END
-		} else if(MATCH("uv_threshold")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(val < 32)
-					new_params.yuvThreshold.uvThreshold=val;
-				else retval = -EINVAL;
+		} else if (MATCH("uv_threshold")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val < 32)
+					new_params.yuvThreshold.uvThreshold = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETYUVTHRESH;
-			FIND_END
-		} else if(MATCH("hysteresis")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(retval <= 0xff)
-				    new_params.compressionParams.hysteresis=val;
-				else retval = -EINVAL;
+		} else if (MATCH("hysteresis")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.compressionParams.hysteresis = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
-			FIND_END
-		} else if(MATCH("threshold_max")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(retval <= 0xff)
-				    new_params.compressionParams.threshMax=val;
-				else retval = -EINVAL;
+		} else if (MATCH("threshold_max")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.compressionParams.threshMax = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
-			FIND_END
-		} else if(MATCH("small_step")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(retval <= 0xff)
-				    new_params.compressionParams.smallStep=val;
-				else retval = -EINVAL;
+		} else if (MATCH("small_step")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.compressionParams.smallStep = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
-			FIND_END
-		} else if(MATCH("large_step")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(retval <= 0xff)
-				    new_params.compressionParams.largeStep=val;
-				else retval = -EINVAL;
+		} else if (MATCH("large_step")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.compressionParams.largeStep = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
-			FIND_END
-		} else if(MATCH("decimation_hysteresis")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(retval <= 0xff)
-				    new_params.compressionParams.
-				        decimationHysteresis = val;
-				else retval = -EINVAL;
+		} else if (MATCH("decimation_hysteresis")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.compressionParams.decimationHysteresis = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
-			FIND_END
-		} else if(MATCH("fr_diff_step_thresh")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(retval <= 0xff)
-				    new_params.compressionParams.
-				        frDiffStepThresh = val;
-				else retval = -EINVAL;
+		} else if (MATCH("fr_diff_step_thresh")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.compressionParams.frDiffStepThresh = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
-			FIND_END
-		} else if(MATCH("q_diff_step_thresh")) {
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(retval <= 0xff)
-				    new_params.compressionParams.
-				        qDiffStepThresh = val;
-				else retval = -EINVAL;
-			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
-			FIND_END
-		} else if(MATCH("decimation_thresh_mod")) {
+		} else if (MATCH("q_diff_step_thresh")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.compressionParams.qDiffStepThresh = val;
+				else
+					retval = -EINVAL;
 			}
-			FIND_VALUE
-			if(!retval) { val = VALUE }
-			if(!retval) {
-				if(retval <= 0xff)
-				    new_params.compressionParams.
-				        decimationThreshMod = val;
-				else retval = -EINVAL;
+			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+		} else if (MATCH("decimation_thresh_mod")) {
+			if (!retval)
+				val = VALUE;
+
+			if (!retval) {
+				if (val <= 0xff)
+					new_params.compressionParams.decimationThreshMod = val;
+				else
+					retval = -EINVAL;
 			}
 			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
-			FIND_END
+		} else if (MATCH("toplight")) { /* GA 4/14/00 */
+		        if (!retval && MATCH("on"))
+			  new_params.qx3.toplight = 1;
+
+			else if (!retval && MATCH("off")) 
+			  new_params.qx3.toplight = 0;
+
+			else 
+			  retval = -EINVAL;
+                        command_flags |= COMMAND_SETLIGHTS;
+                        
+		} else if (MATCH("bottomlight")) { /* GA 4/14/00 */
+		        if (!retval && MATCH("on"))
+			  new_params.qx3.bottomlight = 1;
+
+			else if (!retval && MATCH("off")) 
+			  new_params.qx3.bottomlight = 0;
+
+			else 
+			  retval = -EINVAL;
+                        command_flags |= COMMAND_SETLIGHTS;
+                        
 		} else {
+			DBG("No match found\n");
 			retval = -EINVAL;
 		}
+
+		if (!retval) {
+			while (count && isspace(*buffer) && *buffer != '\n') {
+				--count;
+				++buffer;
+			}
+			if (count) {
+				if (*buffer != '\n' && *buffer != ';')
+					retval = -EINVAL;
+				else {
+					--count;
+					++buffer;
+				}
+			}
+		}
 	}
 #undef MATCH	
 #undef FIRMWARE_VERSION
 #undef VALUE
 #undef FIND_VALUE
 #undef FIND_END
-	if(retval == 0) {
-		if(command_flags & COMMAND_SETCOLOURPARAMS) {
+	if (!retval) {
+		if (command_flags & COMMAND_SETCOLOURPARAMS) {
 			/* Adjust cam->vp to reflect these changes */
 			cam->vp.brightness =
 				new_params.colourParams.brightness*65535/100;
@@ -1394,9 +1361,8 @@
 		cam->mainsFreq = new_mains;
 		cam->cmd_queue |= command_flags;
 		retval = size;
-	} else {
+	} else
 		DBG("error: %d\n", retval);
-	}
 	
 	up(&cam->param_lock);
 	
@@ -1408,17 +1374,21 @@
 	char name[7];
 	struct proc_dir_entry *ent;
 	
-	if(cpia_proc_root == NULL || cam == NULL) {
+	if (!cpia_proc_root || !cam)
 		return;
-	}
+
 	sprintf(name, "video%d", cam->vdev.minor);
 	
 	ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root);
-	if (!ent) return;
+	if (!ent)
+		return;
+
 	ent->data = cam;
 	ent->read_proc = cpia_read_proc;
 	ent->write_proc = cpia_write_proc;
-	ent->size = 3623;
+	ent->size = 3736;
+	if (cam->params.qx3.qx3_detected)
+		ent->size += 188;
 	cam->proc_entry = ent;
 }
 
@@ -1426,16 +1396,43 @@
 {
 	char name[7];
 	
-	if(cam == NULL || cam->proc_entry == NULL) return;
+	if (!cam || !cam->proc_entry)
+		return;
 	
 	sprintf(name, "video%d", cam->vdev.minor);
 	remove_proc_entry(name, cpia_proc_root);
 	cam->proc_entry = NULL;
 }
 
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
+/*
+ * This is called as the fill_inode function when an inode
+ * is going into (fill = 1) or out of service (fill = 0).
+ * We use it here to manage the module use counts.
+ */
+static void proc_cpia_modcount(struct inode *inode, int fill)
+{
+#ifdef MODULE
+	if (fill)
+		MOD_INC_USE_COUNT;
+	else
+		MOD_DEC_USE_COUNT;
+#endif
+}
+#endif
+
 static void proc_cpia_create(void)
 {
 	cpia_proc_root = create_proc_entry("cpia", S_IFDIR, 0);
+
+	if (cpia_proc_root) {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
+		cpia_proc_root->fill_inode = &proc_cpia_modcount;
+#else
+		cpia_proc_root->owner = THIS_MODULE;
+#endif
+	} else
+		LOG("Unable to initialise /proc/cpia\n");
 }
 
 #ifdef MODULE
@@ -1444,6 +1441,7 @@
 	remove_proc_entry("cpia", 0);
 }
 #endif
+#endif /* CONFIG_PROC_FS */
 
 /* ----------------------- debug functions ---------------------- */
 
@@ -1473,39 +1471,41 @@
 {
 	/* return the best match, where 'best' is as always
 	 * the largest that is not bigger than what is requested. */
-	if(width>=352 && height>=288) {
+	if (width>=352 && height>=288)
 		return VIDEOSIZE_352_288; /* CIF */
-	}
-	if(width>=320 && height>=240) {
+
+	if (width>=320 && height>=240)
 		return VIDEOSIZE_320_240; /* SIF */
-	}
-	if(width>=288 && height>=216) {
+
+	if (width>=288 && height>=216)
 		return VIDEOSIZE_288_216;
-	}
-	if(width>=256 && height>=192) {
+
+	if (width>=256 && height>=192)
 		return VIDEOSIZE_256_192;
-	}
-	if(width>=224 && height>=168) {
+
+	if (width>=224 && height>=168)
 		return VIDEOSIZE_224_168;
-	}
-	if(width>=192 && height>=144) {
+
+	if (width>=192 && height>=144)
 		return VIDEOSIZE_192_144;
-	}
-	if(width>=176 && height>=144) {
+
+	if (width>=176 && height>=144)
 		return VIDEOSIZE_176_144; /* QCIF */
-	}
-	if(width>=160 && height>=120) {
+
+	if (width>=160 && height>=120)
 		return VIDEOSIZE_160_120; /* QSIF */
-	}
-	if(width>=128 && height>=96) {
+
+	if (width>=128 && height>=96)
 		return VIDEOSIZE_128_96;
-	}
-	if(width>=64 && height>=48) {
+
+	if (width>=88 && height>=72)
+		return VIDEOSIZE_88_72;
+
+	if (width>=64 && height>=48)
 		return VIDEOSIZE_64_48;
-	}
-	if(width>=48 && height>=48) {
+
+	if (width>=48 && height>=48)
 		return VIDEOSIZE_48_48;
-	}
 
 	return -1;
 }
@@ -1608,6 +1608,16 @@
 		cam->params.roi.rowEnd=30;
 		cam->params.streamStartLine = 60;
 		break;
+	case VIDEOSIZE_88_72:
+		cam->vw.width = 88;
+		cam->vw.height = 72;
+		cam->params.format.videoSize=VIDEOSIZE_QCIF;
+		cam->params.roi.colStart=5;
+		cam->params.roi.colEnd=16;
+		cam->params.roi.rowStart=9;
+		cam->params.roi.rowEnd=27;
+		cam->params.streamStartLine = 60;
+		break;
 	case VIDEOSIZE_64_48:
 		cam->vw.width = 64;
 		cam->vw.height = 48;
@@ -1635,27 +1645,29 @@
 	return;
 }
 
-static int allocate_frame_buf(struct cam_data *cam) {
+static int allocate_frame_buf(struct cam_data *cam)
+{
 	int i;
 
-	cam->frame_buf = rvmalloc(FRAME_NUM*CPIA_MAX_FRAME_SIZE);
-	if (!cam->frame_buf) {
+	cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE);
+	if (!cam->frame_buf)
 		return -ENOBUFS;
-	}
-	for( i=0; i<FRAME_NUM; i++ ) {
-		cam->frame[i].data = cam->frame_buf + i*CPIA_MAX_FRAME_SIZE;
-	}
+
+	for (i = 0; i < FRAME_NUM; i++)
+		cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE;
+
 	return 0;
 }
 
-static int free_frame_buf(struct cam_data *cam) {
+static int free_frame_buf(struct cam_data *cam)
+{
 	int i;
 	
 	rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE);
-	cam->frame_buf=0;
-	for( i=0; i<FRAME_NUM; i++ ) {
+	cam->frame_buf = 0;
+	for (i=0; i < FRAME_NUM; i++)
 		cam->frame[i].data = NULL;
-	}
+
 	return 0;
 }
 
@@ -1663,9 +1675,9 @@
 static void inline free_frames(struct cpia_frame frame[FRAME_NUM])
 {
 	int i;
-	for( i=0; i<FRAME_NUM; i++ ) {
-		frame[i].state=FRAME_UNUSED;
-	}
+
+	for (i=0; i < FRAME_NUM; i++)
+		frame[i].state = FRAME_UNUSED;
 	return;
 }
 
@@ -1693,6 +1705,10 @@
 		down(&cam->param_lock);
 		datasize=8;
 		break;
+        case CPIA_COMMAND_ReadMCPorts: /* GA 4/14/00 */
+        case CPIA_COMMAND_ReadVCRegs:
+                datasize = 4;
+                break;
 	default:
 		datasize=0;
 		break;
@@ -1708,12 +1724,12 @@
 	cmd[7] = 0;
 
 	retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
-	if(retval) {
-		LOG("%x - failed\n", command);
+	if (retval) {
+		DBG("%x - failed, retval=%d\n", command, retval);
 		if (command == CPIA_COMMAND_GetColourParams ||
 		    command == CPIA_COMMAND_GetColourBalance ||
 		    command == CPIA_COMMAND_GetExposure)
-                	up(&cam->param_lock);
+			up(&cam->param_lock);
 	} else {
 		switch(command) {
 		case CPIA_COMMAND_GetCPIAVersion:
@@ -1765,9 +1781,48 @@
 			cam->params.exposure.green1Comp = data[5];
 			cam->params.exposure.green2Comp = data[6];
 			cam->params.exposure.blueComp = data[7];
+			/* If the *Comp parameters are wacko, generate
+			 * a warning, and reset them back to default
+			 * values.             - rich@annexia.org
+			 */
+			if (cam->params.exposure.redComp < 220 ||
+			    cam->params.exposure.redComp > 255 ||
+			    cam->params.exposure.green1Comp < 214 ||
+			    cam->params.exposure.green1Comp > 255 ||
+			    cam->params.exposure.green2Comp < 214 ||
+			    cam->params.exposure.green2Comp > 255 ||
+			    cam->params.exposure.blueComp < 230 ||
+			    cam->params.exposure.blueComp > 255)
+			  {
+			    printk (KERN_WARNING "*_comp parameters have gone AWOL (%d/%d/%d/%d) - reseting them\n",
+				    cam->params.exposure.redComp,
+				    cam->params.exposure.green1Comp,
+				    cam->params.exposure.green2Comp,
+				    cam->params.exposure.blueComp);
+			    cam->params.exposure.redComp = 220;
+			    cam->params.exposure.green1Comp = 214;
+			    cam->params.exposure.green2Comp = 214;
+			    cam->params.exposure.blueComp = 230;
+			  }
 			up(&cam->param_lock);
 			break;
-		default:
+
+                case CPIA_COMMAND_ReadMCPorts: /* GA 04/14/00 */
+                  if (!cam->params.qx3.qx3_detected) break;
+
+		  /* test button press */ 
+		  cam->params.qx3.button = ((data[1] & 0x02) == 0);
+		  if (cam->params.qx3.button) {
+		    /* button pressed - unlock the latch */
+		    do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0);
+		    do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0);
+		  }
+
+		  /* test whether microscope is cradled */
+		  cam->params.qx3.cradled = ((data[2] & 0x40) == 0);
+		  break;
+
+ 		default:
 			break;
 		}
 	}
@@ -1782,6 +1837,7 @@
 {
 	int retval;
 	u8 cmd[8], data[8];
+
 	cmd[0] = command>>8;
 	cmd[1] = command&0xff;
 	cmd[2] = a;
@@ -1800,29 +1856,186 @@
 	data[7] = l;
 
 	retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
-	if(retval) {
+	if (retval)
 		LOG("%x - failed\n", command);
+
+	return retval;
+}
+
+/**********************************************************************
+ *
+ * Colorspace conversion
+ *
+ **********************************************************************/
+#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
+
+static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt,
+                      int linesize, int mmap_kludge)
+{
+	int y, u, v, r, g, b, y1;
+
+	switch(out_fmt) {
+	case VIDEO_PALETTE_RGB555:
+		y = (*yuv++ - 16) * 76310;
+		y1 = (*yuv - 16) * 76310;
+		r = ((*(rgb+1-linesize)) & 0x7c) << 1;
+		g = ((*(rgb-linesize)) & 0xe0) >> 4 |
+		    ((*(rgb+1-linesize)) & 0x03) << 6;
+		b = ((*(rgb-linesize)) & 0x1f) << 3;
+		u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
+		v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
+		r = 104635 * v;
+		g = -25690 * u - 53294 * v;
+		b = 132278 * u;
+		break;
+	case VIDEO_PALETTE_RGB565:
+		y = (*yuv++ - 16) * 76310;
+		y1 = (*yuv - 16) * 76310;
+		r = (*(rgb+1-linesize)) & 0xf8;
+		g = ((*(rgb-linesize)) & 0xe0) >> 3 |
+		    ((*(rgb+1-linesize)) & 0x07) << 5;
+		b = ((*(rgb-linesize)) & 0x1f) << 3;
+		u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
+		v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
+		r = 104635 * v;
+		g = -25690 * u - 53294 * v;
+		b = 132278 * u;
+		break;
+	case VIDEO_PALETTE_RGB24:
+	case VIDEO_PALETTE_RGB32:
+		y = (*yuv++ - 16) * 76310;
+		y1 = (*yuv - 16) * 76310;
+		if (mmap_kludge) {
+			r = *(rgb+2-linesize);
+			g = *(rgb+1-linesize);
+			b = *(rgb-linesize);
+		} else {
+			r = *(rgb-linesize);
+			g = *(rgb+1-linesize);
+			b = *(rgb+2-linesize);
+		}
+		u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
+		v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
+		r = 104635 * v;
+		g = -25690 * u + -53294 * v;
+		b = 132278 * u;
+		break;
+	case VIDEO_PALETTE_YUV422:
+	case VIDEO_PALETTE_YUYV:
+		y = *yuv++;
+		u = *(rgb+1-linesize);
+		y1 = *yuv;
+		v = *(rgb+3-linesize);
+		/* Just to avoid compiler warnings */
+		r = 0;
+		g = 0;
+		b = 0;
+		break;
+	case VIDEO_PALETTE_UYVY:
+		u = *(rgb-linesize);
+		y = *yuv++;
+		v = *(rgb+2-linesize);
+		y1 = *yuv;
+		/* Just to avoid compiler warnings */
+		r = 0;
+		g = 0;
+		b = 0;
+		break;
+	case VIDEO_PALETTE_GREY:
+	default:
+		y = *yuv++;
+		y1 = *yuv;
+		/* Just to avoid compiler warnings */
+		u = 0;
+		v = 0;
+		r = 0;
+		g = 0;
+		b = 0;
+		break;
+	}
+	switch(out_fmt) {
+	case VIDEO_PALETTE_RGB555:
+		*rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
+		*rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
+		*rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
+		*rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
+		return 4;
+	case VIDEO_PALETTE_RGB565:
+		*rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
+		*rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
+		*rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
+		*rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
+		return 4;
+	case VIDEO_PALETTE_RGB24:
+		if (mmap_kludge) {
+			*rgb++ = LIMIT(b+y);
+			*rgb++ = LIMIT(g+y);
+			*rgb++ = LIMIT(r+y);
+			*rgb++ = LIMIT(b+y1);
+			*rgb++ = LIMIT(g+y1);
+			*rgb = LIMIT(r+y1);
+		} else {
+			*rgb++ = LIMIT(r+y);
+			*rgb++ = LIMIT(g+y);
+			*rgb++ = LIMIT(b+y);
+			*rgb++ = LIMIT(r+y1);
+			*rgb++ = LIMIT(g+y1);
+			*rgb = LIMIT(b+y1);
+		}
+		return 6;
+	case VIDEO_PALETTE_RGB32:
+		if (mmap_kludge) {
+			*rgb++ = LIMIT(b+y);
+			*rgb++ = LIMIT(g+y);
+			*rgb++ = LIMIT(r+y);
+			rgb++;
+			*rgb++ = LIMIT(b+y1);
+			*rgb++ = LIMIT(g+y1);
+			*rgb = LIMIT(r+y1);
+		} else {
+			*rgb++ = LIMIT(r+y);
+			*rgb++ = LIMIT(g+y);
+			*rgb++ = LIMIT(b+y);
+			rgb++;
+			*rgb++ = LIMIT(r+y1);
+			*rgb++ = LIMIT(g+y1);
+			*rgb = LIMIT(b+y1);
+		}
+		return 8;
+	case VIDEO_PALETTE_GREY:
+		*rgb++ = y;
+		*rgb = y1;
+		return 2;
+	case VIDEO_PALETTE_YUV422:
+	case VIDEO_PALETTE_YUYV:
+		*rgb++ = y;
+		*rgb++ = u;
+		*rgb++ = y1;
+		*rgb = v;
+		return 4;
+	case VIDEO_PALETTE_UYVY:
+		*rgb++ = u;
+		*rgb++ = y;
+		*rgb++ = v;
+		*rgb = y1;
+		return 4;
+	default:
+		DBG("Empty: %d\n", out_fmt);
+		return 0;
 	}
-	return retval;
 }
 
-/**********************************************************************
- *
- * Colorspace conversion
- *
- **********************************************************************/
-#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
-
 static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt,
-                      int in_uyvy)
+                      int in_uyvy, int mmap_kludge)
 {
 	int y, u, v, r, g, b, y1;
+
 	switch(out_fmt) {
 	case VIDEO_PALETTE_RGB555:
 	case VIDEO_PALETTE_RGB565:
 	case VIDEO_PALETTE_RGB24:
 	case VIDEO_PALETTE_RGB32:
-		if(in_uyvy) {
+		if (in_uyvy) {
 			u = *yuv++ - 128;
 			y = (*yuv++ - 16) * 76310;
 			v = *yuv++ - 128;
@@ -1862,21 +2075,40 @@
 		*rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
 		return 4;
 	case VIDEO_PALETTE_RGB24:
-		*rgb++ = LIMIT(b+y);
-		*rgb++ = LIMIT(g+y);
-		*rgb++ = LIMIT(r+y);
-		*rgb++ = LIMIT(b+y1);
-		*rgb++ = LIMIT(g+y1);
-		*rgb = LIMIT(r+y1);
+		if (mmap_kludge) {
+			*rgb++ = LIMIT(b+y);
+			*rgb++ = LIMIT(g+y);
+			*rgb++ = LIMIT(r+y);
+			*rgb++ = LIMIT(b+y1);
+			*rgb++ = LIMIT(g+y1);
+			*rgb = LIMIT(r+y1);
+		} else {
+			*rgb++ = LIMIT(r+y);
+			*rgb++ = LIMIT(g+y);
+			*rgb++ = LIMIT(b+y);
+			*rgb++ = LIMIT(r+y1);
+			*rgb++ = LIMIT(g+y1);
+			*rgb = LIMIT(b+y1);
+		}
 		return 6;
 	case VIDEO_PALETTE_RGB32:
-		*rgb++ = LIMIT(b+y);
-		*rgb++ = LIMIT(g+y);
-		*rgb++ = LIMIT(r+y);
-		rgb++;
-		*rgb++ = LIMIT(b+y1);
-		*rgb++ = LIMIT(g+y1);
-		*rgb = LIMIT(r+y1);
+		if (mmap_kludge) {
+			*rgb++ = LIMIT(b+y);
+			*rgb++ = LIMIT(g+y);
+			*rgb++ = LIMIT(r+y);
+			rgb++;
+			*rgb++ = LIMIT(b+y1);
+			*rgb++ = LIMIT(g+y1);
+			*rgb = LIMIT(r+y1);
+		} else {
+			*rgb++ = LIMIT(r+y);
+			*rgb++ = LIMIT(g+y);
+			*rgb++ = LIMIT(b+y);
+			rgb++;
+			*rgb++ = LIMIT(r+y1);
+			*rgb++ = LIMIT(g+y1);
+			*rgb = LIMIT(b+y1);
+		}
 		return 8;
 	case VIDEO_PALETTE_GREY:
 		*rgb++ = y;
@@ -1896,11 +2128,13 @@
 		*rgb = y1;
 		return 4;
 	default:
+		DBG("Empty: %d\n", out_fmt);
 		return 0;
 	}
 }
 
-static int skipcount(int count, int fmt) {
+static int skipcount(int count, int fmt)
+{
 	switch(fmt) {
 	case VIDEO_PALETTE_GREY:
 		return count;
@@ -1922,66 +2156,68 @@
 static int parse_picture(struct cam_data *cam, int size)
 {
 	u8 *obuf, *ibuf, *end_obuf;
-	int ll, in_uyvy, compressed, origsize, out_fmt;
+	int ll, in_uyvy, compressed, decimation, even_line, origsize, out_fmt;
+	int rows, cols, linesize, subsample_422;
 
 	/* make sure params don't change while we are decoding */
 	down(&cam->param_lock);
-	
+
 	obuf = cam->decompressed_frame.data;
 	end_obuf = obuf+CPIA_MAX_FRAME_SIZE;
 	ibuf = cam->raw_image;
 	origsize = size;
 	out_fmt = cam->vp.palette;
-	
-	if((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) {
+
+	if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) {
 		LOG("header not found\n");
 		up(&cam->param_lock);
 		return -1;
 	}
 
-	if((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) {
+	if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) {
 		LOG("wrong video size\n");
 		up(&cam->param_lock);
 		return -1;
 	}
 	
-	if(ibuf[17] != SUBSAMPLE_422) {
+	if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) {
 		LOG("illegal subtype %d\n",ibuf[17]);
 		up(&cam->param_lock);
 		return -1;
 	}
+	subsample_422 = ibuf[17] == SUBSAMPLE_422;
 	
-	if(ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) {
+	if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) {
 		LOG("illegal yuvorder %d\n",ibuf[18]);
 		up(&cam->param_lock);
 		return -1;
 	}
 	in_uyvy = ibuf[18] == YUVORDER_UYVY;
 	
-#if 0
-	/* FIXME: ROI mismatch occurs when switching capture sizes */
-	if((ibuf[24] != cam->params.roi.colStart) ||
-	   (ibuf[25] != cam->params.roi.colEnd) ||
-	   (ibuf[26] != cam->params.roi.rowStart) ||
-	   (ibuf[27] != cam->params.roi.rowEnd)) {
+	if ((ibuf[24] != cam->params.roi.colStart) ||
+	    (ibuf[25] != cam->params.roi.colEnd) ||
+	    (ibuf[26] != cam->params.roi.rowStart) ||
+	    (ibuf[27] != cam->params.roi.rowEnd)) {
 		LOG("ROI mismatch\n");
 		up(&cam->param_lock);
 		return -1;
 	}
-#endif
+	cols = 8*(ibuf[25] - ibuf[24]);
+	rows = 4*(ibuf[27] - ibuf[26]);
 	
-	if((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) {
+	if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) {
 		LOG("illegal compression %d\n",ibuf[28]);
 		up(&cam->param_lock);
 		return -1;
 	}
 	compressed = (ibuf[28] == COMPRESSED);
 	
-	if(ibuf[29] != NO_DECIMATION) {
-		LOG("decimation not supported\n");
+	if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) {
+		LOG("illegal decimation %d\n",ibuf[29]);
 		up(&cam->param_lock);
 		return -1;
 	}
+	decimation = (ibuf[29] == DECIMATION_ENAB);
 	
 	cam->params.yuvThreshold.yThreshold = ibuf[30];
 	cam->params.yuvThreshold.uvThreshold = ibuf[31];
@@ -1994,98 +2230,135 @@
 	cam->params.status.vpStatus = ibuf[38];
 	cam->params.status.errorCode = ibuf[39];
 	cam->fps = ibuf[41];
+	up(&cam->param_lock);
 	
-	ibuf += 64;
-	size -= 64;
+	linesize = skipcount(cols, out_fmt);
+	ibuf += FRAME_HEADER_SIZE;
+	size -= FRAME_HEADER_SIZE;
 	ll = ibuf[0] | (ibuf[1] << 8);
 	ibuf += 2;
-	while(size > 0) {
+	even_line = 1;
+
+	while (size > 0) {
 		size -= (ll+2);
-		if(size < 0) {
+		if (size < 0) {
 			LOG("Insufficient data in buffer\n");
-			up(&cam->param_lock);
 			return -1;
 		}
-		while(ll > 1) {
-			if(!compressed || (compressed && !(*ibuf & 1))) {
-				obuf += yuvconvert(ibuf,obuf,out_fmt,in_uyvy);
-				ibuf += 4;
-				ll -= 4;
+
+		while (ll > 1) {
+			if (!compressed || (compressed && !(*ibuf & 1))) {
+				if(subsample_422 || even_line) {
+					obuf += yuvconvert(ibuf, obuf, out_fmt,
+			                	   in_uyvy, cam->mmap_kludge);
+					ibuf += 4;
+					ll -= 4;
+				} else {
+					/* SUBSAMPLE_420 on an odd line */
+					obuf += convert420(ibuf, obuf,
+					                   out_fmt, linesize,
+			                		   cam->mmap_kludge);
+					ibuf += 2;
+					ll -= 2;
+				}
 			} else {
 				/*skip compressed interval from previous frame*/
-				int skipsize = skipcount(*ibuf >> 1, out_fmt);
-				obuf += skipsize;
-				if( obuf > end_obuf ) {
-					LOG("Insufficient data in buffer\n");
-					up(&cam->param_lock);
+				obuf += skipcount(*ibuf >> 1, out_fmt);
+				if (obuf > end_obuf) {
+					LOG("Insufficient buffer size\n");
 					return -1;
 				}
 				++ibuf;
 				ll--;
 			}
 		}
-		if(ll == 1) {
-			if(*ibuf != EOL) {
+		if (ll == 1) {
+			if (*ibuf != EOL) {
 				LOG("EOL not found giving up after %d/%d"
 				    " bytes\n", origsize-size, origsize);
-				up(&cam->param_lock);
 				return -1;
 			}
 
-			ibuf++; /* skip over EOL */
+			++ibuf; /* skip over EOL */
 
-			if((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) &&
+			if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) &&
 			   (ibuf[2] == EOI) && (ibuf[3] == EOI)) {
 			 	size -= 4;
 				break;
 			}
 
-			if(size > 1) {
+			if(decimation) {
+				/* skip the odd lines for now */
+				obuf += linesize;
+			}
+			
+			if (size > 1) {
 				ll = ibuf[0] | (ibuf[1] << 8);
 				ibuf += 2; /* skip over line length */
 			}
+			
+			if(!decimation)
+				even_line = !even_line;
 		} else {
 			LOG("line length was not 1 but %d after %d/%d bytes\n",
 			    ll, origsize-size, origsize);
-			up(&cam->param_lock);
 			return -1;
 		}
 	}
 	
+	if(decimation) {
+		/* interpolate odd rows */
+		int i, j;
+		u8 *prev, *next;
+		prev = cam->decompressed_frame.data;
+		obuf = prev+linesize;
+		next = obuf+linesize;
+		for(i=1; i<rows-1; i+=2) {
+			for(j=0; j<linesize; ++j) {
+				*obuf++ = ((int)*prev++ + *next++) / 2;
+			}
+			prev += linesize;
+			obuf += linesize;
+			next += linesize;
+		}
+		/* last row is odd, just copy previous row */
+		memcpy(obuf, prev, linesize);
+	}
+	
 	cam->decompressed_frame.count = obuf-cam->decompressed_frame.data;
 
-	up(&cam->param_lock);
-	
 	return cam->decompressed_frame.count;
 }
 
 /* InitStreamCap wrapper to select correct start line */
-static inline int init_stream_cap(struct cam_data *cam) {
+static inline int init_stream_cap(struct cam_data *cam)
+{
 	return do_command(cam, CPIA_COMMAND_InitStreamCap,
 	                  0, cam->params.streamStartLine, 0, 0);
 }
 
 /* update various camera modes and settings */
-static void dispatch_commands(struct cam_data *cam ) {
+static void dispatch_commands(struct cam_data *cam)
+{
 	down(&cam->param_lock);
-	if( cam->cmd_queue==COMMAND_NONE ) {
+	if (cam->cmd_queue==COMMAND_NONE) {
 		up(&cam->param_lock);
 		return;
 	}
 	DEB_BYTE(cam->cmd_queue);
 	DEB_BYTE(cam->cmd_queue>>8);
-	if( cam->cmd_queue & COMMAND_SETCOLOURPARAMS ) {
+	if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS)
 		do_command(cam, CPIA_COMMAND_SetColourParams,
 		           cam->params.colourParams.brightness,
 		           cam->params.colourParams.contrast,
 		           cam->params.colourParams.saturation, 0);
-	}
-	if( cam->cmd_queue & COMMAND_SETCOMPRESSION ) {
+
+	if (cam->cmd_queue & COMMAND_SETCOMPRESSION)
 		do_command(cam, CPIA_COMMAND_SetCompression,
 		           cam->params.compression.mode,
 			   cam->params.compression.decimation, 0, 0);
-	}
-	if( cam->cmd_queue & COMMAND_SETFORMAT ) {
+
+	if (cam->cmd_queue & COMMAND_SETFORMAT) {
 		do_command(cam, CPIA_COMMAND_SetFormat,
 	        	   cam->params.format.videoSize,
 	        	   cam->params.format.subSample,
@@ -2095,22 +2368,23 @@
 	        	   cam->params.roi.rowStart, cam->params.roi.rowEnd);
 		cam->first_frame = 1;
 	}
-	if( cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET ) {
+
+	if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET)
 		do_command(cam, CPIA_COMMAND_SetCompressionTarget,
 		           cam->params.compressionTarget.frTargeting,
 		           cam->params.compressionTarget.targetFR,
 		           cam->params.compressionTarget.targetQ, 0);
-	}
-	if( cam->cmd_queue & COMMAND_SETYUVTHRESH ) {
+
+	if (cam->cmd_queue & COMMAND_SETYUVTHRESH)
 		do_command(cam, CPIA_COMMAND_SetYUVThresh,
 		           cam->params.yuvThreshold.yThreshold,
 		           cam->params.yuvThreshold.uvThreshold, 0, 0);
-	}
-	if( cam->cmd_queue & COMMAND_SETECPTIMING ) {
+
+	if (cam->cmd_queue & COMMAND_SETECPTIMING)
 		do_command(cam, CPIA_COMMAND_SetECPTiming,
 		           cam->params.ecpTiming, 0, 0, 0);
-	}
-	if( cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS ) {
+
+	if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS)
 		do_command_extended(cam, CPIA_COMMAND_SetCompressionParams,
 		            0, 0, 0, 0,
 		            cam->params.compressionParams.hysteresis,
@@ -2121,8 +2395,8 @@
 		            cam->params.compressionParams.frDiffStepThresh,
 		            cam->params.compressionParams.qDiffStepThresh,
 		            cam->params.compressionParams.decimationThreshMod);
-	}
-	if( cam->cmd_queue & COMMAND_SETEXPOSURE ) {
+
+	if (cam->cmd_queue & COMMAND_SETEXPOSURE)
 		do_command_extended(cam, CPIA_COMMAND_SetExposure,
 		                    cam->params.exposure.gainMode,
 		                    cam->params.exposure.expMode,
@@ -2136,150 +2410,191 @@
 		                    cam->params.exposure.green1Comp,
 		                    cam->params.exposure.green2Comp,
 		                    cam->params.exposure.blueComp);
+
+	if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) {
+		if (cam->params.colourBalance.balanceModeIsAuto) {
+			do_command(cam, CPIA_COMMAND_SetColourBalance,
+				   2, 0, 0, 0);
+		} else {
+			do_command(cam, CPIA_COMMAND_SetColourBalance,
+				   1,
+				   cam->params.colourBalance.redGain,
+				   cam->params.colourBalance.greenGain,
+				   cam->params.colourBalance.blueGain);
+			do_command(cam, CPIA_COMMAND_SetColourBalance,
+				   3, 0, 0, 0);
+		}
 	}
-	if( cam->cmd_queue & COMMAND_SETCOLOURBALANCE ) {
-		do_command(cam, CPIA_COMMAND_SetColourBalance,
-		           cam->params.colourBalance.balanceMode,
-		           cam->params.colourBalance.redGain,
-		           cam->params.colourBalance.greenGain,
-		           cam->params.colourBalance.blueGain);
-	}
-	if( cam->cmd_queue & COMMAND_SETSENSORFPS ) {
+
+	if (cam->cmd_queue & COMMAND_SETSENSORFPS)
 		do_command(cam, CPIA_COMMAND_SetSensorFPS,
 		           cam->params.sensorFps.divisor,
 		           cam->params.sensorFps.baserate, 0, 0);
-	}
-	if( cam->cmd_queue & COMMAND_SETAPCOR ) {
+
+	if (cam->cmd_queue & COMMAND_SETAPCOR)
 		do_command(cam, CPIA_COMMAND_SetApcor,
 		           cam->params.apcor.gain1,
 		           cam->params.apcor.gain2,
 		           cam->params.apcor.gain4,
 		           cam->params.apcor.gain8);
-	}
-	if( cam->cmd_queue & COMMAND_SETFLICKERCTRL ) {
+
+	if (cam->cmd_queue & COMMAND_SETFLICKERCTRL)
 		do_command(cam, CPIA_COMMAND_SetFlickerCtrl,
 		           cam->params.flickerControl.flickerMode,
 		           cam->params.flickerControl.coarseJump,
 		           cam->params.flickerControl.allowableOverExposure, 0);
-	}
-	if( cam->cmd_queue & COMMAND_SETVLOFFSET ) {
+
+	if (cam->cmd_queue & COMMAND_SETVLOFFSET)
 		do_command(cam, CPIA_COMMAND_SetVLOffset,
 		           cam->params.vlOffset.gain1,
 		           cam->params.vlOffset.gain2,
 		           cam->params.vlOffset.gain4,
 		           cam->params.vlOffset.gain8);
-	}
-	if( cam->cmd_queue & COMMAND_PAUSE ) {
+
+	if (cam->cmd_queue & COMMAND_PAUSE)
 		do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
-	}
-	if( cam->cmd_queue & COMMAND_RESUME ) {
-		init_stream_cap( cam );
-	}
+
+	if (cam->cmd_queue & COMMAND_RESUME)
+		init_stream_cap(cam);
+
+	/* GA 04/14/00 */
+	if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected)
+	  {
+	    int p1 = (cam->params.qx3.bottomlight == 0) << 1;
+            int p2 = (cam->params.qx3.toplight == 0) << 3;
+            do_command(cam, CPIA_COMMAND_WriteVCReg,  0x90, 0x8F, 0x50, 0);
+            do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0);
+	  }
+
 	up(&cam->param_lock);
-	cam->cmd_queue=COMMAND_NONE;
+	cam->cmd_queue = COMMAND_NONE;
 	return;
 }
 
 /* kernel thread function to read image from camera */
 static void fetch_frame(void *data)
 {
-	int image_size;
+	int image_size, retry;
 	struct cam_data *cam = (struct cam_data *)data;
 	unsigned long oldjif, rate, diff;
 
-        /* load first frame always uncompressed */
-        if( cam->first_frame &&
-            cam->params.compression.mode != CPIA_COMPRESSION_NONE ) {
-                do_command(cam, CPIA_COMMAND_SetCompression,
-                           CPIA_COMPRESSION_NONE,
-                           NO_DECIMATION, 0, 0);
-        }
-
-        /* init camera upload */
-        if(do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_CONTINUOUS,
-                      0, 0, 0)) goto end;
-        if(do_command(cam, CPIA_COMMAND_GrabFrame, 0,
-                      cam->params.streamStartLine, 0, 0)) goto end;
-
-	/* loop until image ready */
-	do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
-	while(cam->params.status.streamState != STREAM_READY) {
-		if(current->need_resched) {schedule();}
-		current->state=TASK_INTERRUPTIBLE;
-		schedule_timeout(10*HZ/1000); /* 10 ms, hopefully ;) */
-		if(signal_pending(current)) {
-			goto end;
-		}
-		do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
-	}
-
-	/* grab image from camera */
-	if(current->need_resched) {schedule();}
-	oldjif = jiffies;
-	image_size=cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0);
-	if(image_size<=0) {
-		DBG("read frame failed\n");
-		goto end;
-	}
-	rate = image_size * HZ / 1024;
-	diff = jiffies-oldjif;
-	rate = diff==0 ? rate : rate/diff; /* unlikely but possible */
-	/* Keep track of transfer_rate as a runnung average over 3 frames
-	 * to smooth out any inconsistencies */
-	cam->transfer_rate = (2*cam->transfer_rate + rate) / 3;
-
-	/* camera idle now so dispatch queued commands */
-	dispatch_commands( cam );
-
-	/* Update our knowledge of the camera state - FIXME: necessary? */
-	do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
-	do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
-	
-	/* decompress and convert image to by copying it from
-	*  raw_image to decompressed_frame
-	*/
-	if(current->need_resched) {schedule();}
-	cam->image_size = parse_picture(cam, image_size);
-	if( cam->image_size <= 0 ) {
-		DBG("parse frame failed\n");
-		goto end;
-	}
-
-	/* FIXME: this only works for double buffering */
-	if( cam->frame[cam->curframe].state == FRAME_READY ) {
-		memcpy(cam->frame[cam->curframe].data,
-		       cam->decompressed_frame.data,
-		       cam->decompressed_frame.count);
-		cam->frame[cam->curframe].state = FRAME_DONE;
-	} else {
-		cam->decompressed_frame.state = FRAME_DONE;
-	}
+	/* Allow up to two bad images in a row to be read and
+	 * ignored before an error is reported */
+	for (retry = 0; retry < 3; ++retry) {
+		if (retry)
+			DBG("retry=%d\n", retry);
+
+		if (!cam->ops)
+			continue;
+
+		/* load first frame always uncompressed */
+		if (cam->first_frame &&
+		    cam->params.compression.mode != CPIA_COMPRESSION_NONE)
+			do_command(cam, CPIA_COMMAND_SetCompression,
+				   CPIA_COMPRESSION_NONE,
+				   NO_DECIMATION, 0, 0);
+
+		/* init camera upload */
+		if (do_command(cam, CPIA_COMMAND_SetGrabMode,
+			       CPIA_GRAB_CONTINUOUS, 0, 0, 0))
+			continue;
+
+		if (do_command(cam, CPIA_COMMAND_GrabFrame, 0,
+			       cam->params.streamStartLine, 0, 0))
+			continue;
+
+		if (cam->ops->wait_for_stream_ready) {
+			/* loop until image ready */
+			do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
+			while (cam->params.status.streamState != STREAM_READY) {
+				if (current->need_resched)
+					schedule();
+
+				current->state = TASK_INTERRUPTIBLE;
+
+				/* sleep for 10 ms, hopefully ;) */
+				schedule_timeout(10*HZ/1000);
+				if (signal_pending(current))
+					return;
+
+				do_command(cam, CPIA_COMMAND_GetCameraStatus,
+				           0, 0, 0, 0);
+			}
+		}
+
+		/* grab image from camera */
+		if (current->need_resched)
+			schedule();
+
+		oldjif = jiffies;
+		image_size = cam->ops->streamRead(cam->lowlevel_data,
+						  cam->raw_image, 0);
+		if (image_size <= 0) {
+			DBG("streamRead failed: %d\n", image_size);
+			continue;
+		}
+
+		rate = image_size * HZ / 1024;
+		diff = jiffies-oldjif;
+		cam->transfer_rate = diff==0 ? rate : rate/diff;
+			/* diff==0 ? unlikely but possible */
+
+		/* camera idle now so dispatch queued commands */
+		dispatch_commands(cam);
+
+		/* Update our knowledge of the camera state - FIXME: necessary? */
+		do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
+		do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
+		do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0); /* GA */
+
+
+		/* decompress and convert image to by copying it from
+		 * raw_image to decompressed_frame
+		 */
+		if (current->need_resched)
+			schedule();
+
+		cam->image_size = parse_picture(cam, image_size);
+		if (cam->image_size <= 0)
+			DBG("parse_picture failed %d\n", cam->image_size);
+		else
+			break;
+	}
+
+	if (retry < 3) {
+		/* FIXME: this only works for double buffering */
+		if (cam->frame[cam->curframe].state == FRAME_READY) {
+			memcpy(cam->frame[cam->curframe].data,
+			       cam->decompressed_frame.data,
+			       cam->decompressed_frame.count);
+			cam->frame[cam->curframe].state = FRAME_DONE;
+		} else
+			cam->decompressed_frame.state = FRAME_DONE;
 
 #if 0
-	if( cam->first_frame &&
-	    cam->params.compression.mode != CPIA_COMPRESSION_NONE ) {
-		cam->first_frame = 0;
-		cam->cmd_queue |= COMMAND_SETCOMPRESSION;
-	}
+		if (cam->first_frame &&
+		    cam->params.compression.mode != CPIA_COMPRESSION_NONE) {
+			cam->first_frame = 0;
+			cam->cmd_queue |= COMMAND_SETCOMPRESSION;
+		}
 #else
-	if( cam->first_frame ) {
-		cam->first_frame = 0;
-		cam->cmd_queue |= COMMAND_SETCOMPRESSION;
-		cam->cmd_queue |= COMMAND_SETEXPOSURE;
-	}
+		if (cam->first_frame) {
+			cam->first_frame = 0;
+			cam->cmd_queue |= COMMAND_SETCOMPRESSION;
+			cam->cmd_queue |= COMMAND_SETEXPOSURE;
+		}
 #endif
-end:
-	return;
+	}
 }
 
 static int capture_frame(struct cam_data *cam, struct video_mmap *vm)
 {
 	int retval = 0;
 
-	if( cam->frame_buf == NULL ) { /* we do lazy allocation */
-		if( (retval = allocate_frame_buf(cam)) ) {
+	if (!cam->frame_buf) {
+		/* we do lazy allocation */
+		if ((retval = allocate_frame_buf(cam)))
 			return retval;
-		}
 	}
 	
 	/* FIXME: the first frame seems to be captured by the camera
@@ -2287,25 +2602,30 @@
 	   that one, the next one is generated with our settings
 	   (exposure, color balance, ...)
 	*/
-	if(cam->first_frame) {
-	  cam->curframe = vm->frame;
-	  cam->frame[cam->curframe].state = FRAME_READY;
-	  fetch_frame(cam);
-	  if(cam->frame[cam->curframe].state != FRAME_DONE) retval=-EIO;
+	if (cam->first_frame) {
+		cam->curframe = vm->frame;
+		cam->frame[cam->curframe].state = FRAME_READY;
+		fetch_frame(cam);
+		if (cam->frame[cam->curframe].state != FRAME_DONE)
+			retval = -EIO;
 	}
 	cam->curframe = vm->frame;
-        cam->frame[cam->curframe].state = FRAME_READY;
-        fetch_frame(cam);
-        if(cam->frame[cam->curframe].state != FRAME_DONE) retval=-EIO;
+	cam->frame[cam->curframe].state = FRAME_READY;
+	fetch_frame(cam);
+	if (cam->frame[cam->curframe].state != FRAME_DONE)
+		retval=-EIO;
 
 	return retval;
 }
   
 static int goto_high_power(struct cam_data *cam)
 {
-	if(do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0)) return -1;
-	if(do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) return -1;
-	if(cam->params.status.systemState == HI_POWER_STATE) {
+	if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0))
+		return -1;
+	mdelay(100);		/* windows driver does it too */
+	if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
+		return -1;
+	if (cam->params.status.systemState == HI_POWER_STATE) {
 		DBG("camera now in HIGH power state\n");
 		return 0;
 	}
@@ -2315,14 +2635,15 @@
 
 static int goto_low_power(struct cam_data *cam)
 {
-	if(do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0)) return -1;
-	if(do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) return -1;
-	if(cam->params.status.systemState == LO_POWER_STATE) {
+	if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0))
+		return -1;
+	if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
+		return -1;
+	if (cam->params.status.systemState == LO_POWER_STATE) {
 		DBG("camera now in LOW power state\n");
 		return 0;
 	}
 	printstatus(cam);
-	mdelay(100);		/* windows driver does it too */
 	return -1;
 }
 
@@ -2344,21 +2665,23 @@
 	     cam->params.colourBalance.redGain,
 	     cam->params.colourBalance.greenGain,
 	     cam->params.colourBalance.blueGain);
-
-	return;
 }
 
 static void set_camera_state(struct cam_data *cam)
 {
-	if(cam->params.colourBalance.balanceMode != 1) {
+	if(cam->params.colourBalance.balanceModeIsAuto) {
+		do_command(cam, CPIA_COMMAND_SetColourBalance, 
+	        	   2, 0, 0, 0);
+	} else {
 		do_command(cam, CPIA_COMMAND_SetColourBalance, 
 	        	   1,
 	        	   cam->params.colourBalance.redGain,
 	        	   cam->params.colourBalance.greenGain,
 	        	   cam->params.colourBalance.blueGain);
+		do_command(cam, CPIA_COMMAND_SetColourBalance, 
+	        	   3, 0, 0, 0);
 	}
-	if(cam->params.colourBalance.balanceMode == 0)
-		cam->params.colourBalance.balanceMode = 2;
+
 
 	do_command_extended(cam, CPIA_COMMAND_SetExposure,
 			    cam->params.exposure.gainMode, 1, 1,
@@ -2367,19 +2690,19 @@
 	                    cam->params.exposure.fineExp,
 	                    cam->params.exposure.coarseExpLo,
 	                    cam->params.exposure.coarseExpHi,
-	                    cam->params.exposure.redComp,
-	                    cam->params.exposure.green1Comp,
-	                    cam->params.exposure.green2Comp,
-	                    cam->params.exposure.blueComp);
+			    cam->params.exposure.redComp,
+			    cam->params.exposure.green1Comp,
+			    cam->params.exposure.green2Comp,
+			    cam->params.exposure.blueComp);
 	do_command_extended(cam, CPIA_COMMAND_SetExposure,
 			    0, 3, 0, 0,
 			    0, 0, 0, 0, 0, 0, 0, 0);
 
-	if(cam->params.exposure.gainMode == 0)
+	if (!cam->params.exposure.gainMode)
 		cam->params.exposure.gainMode = 2;
-	if(cam->params.exposure.expMode == 0)
+	if (!cam->params.exposure.expMode)
 		cam->params.exposure.expMode = 2;
-	if(cam->params.exposure.centreWeight == 0)
+	if (!cam->params.exposure.centreWeight)
 		cam->params.exposure.centreWeight = 1;
 	
 	cam->cmd_queue = COMMAND_SETCOMPRESSION |
@@ -2407,32 +2730,25 @@
 {
 	/* GetCPIAVersion */
 	do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
-	printk(KERN_INFO "  CPIA Version: %d.%02d (%d.%d)\n",
-	       cam->params.version.firmwareVersion,
-	       cam->params.version.firmwareRevision,
-	       cam->params.version.vcVersion,
-	       cam->params.version.vcRevision);
 
 	/* GetPnPID */
 	do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
-	printk(KERN_INFO "  CPIA PnP-ID: %04x:%04x:%04x\n",
-	       cam->params.pnpID.vendor, cam->params.pnpID.product,
-	       cam->params.pnpID.deviceRevision);
 }
 
 /* initialize camera */
 static int reset_camera(struct cam_data *cam)
 {
 	/* Start the camera in low power mode */
-	if(goto_low_power(cam)) {
-		if( cam->params.status.systemState != 0x4 ) {
+	if (goto_low_power(cam)) {
+		if (cam->params.status.systemState != WARM_BOOT_STATE)
 			return -ENODEV;
-		}
+
 		/* FIXME: this is just dirty trial and error */
 		reset_camera_struct(cam);
 		goto_high_power(cam);
 		do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
-		if(goto_low_power(cam)) return -NODEV;
+		if (goto_low_power(cam))
+			return -NODEV;
 	}
 	
 	/* procedure described in developer's guide p3-28 */
@@ -2440,11 +2756,15 @@
 	/* Check the firmware version FIXME: should we check PNPID? */
 	cam->params.version.firmwareVersion = 0;
 	get_version_information(cam);
-	if(cam->params.version.firmwareVersion != 1) {
+	if (cam->params.version.firmwareVersion != 1)
 		return -ENODEV;
-	}
+	
+	/* GA 04/14/00 - set QX3 detected flag */
+	cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 &&
+					cam->params.pnpID.product == 0x0001);
 
-	/* The fatal error checking should be done after the camera powers up (developer's guide p 3-38) */
+	/* The fatal error checking should be done after
+	 * the camera powers up (developer's guide p 3-38) */
 
 	/* Set streamState before transition to high power to avoid bug
 	 * in firmware 1-02 */
@@ -2452,40 +2772,38 @@
 	           STREAM_NOT_READY, 0);
 	
 	/* GotoHiPower */
-	if(goto_high_power(cam))
+	if (goto_high_power(cam))
 		return -ENODEV;
 
-	
 	/* Check the camera status */
-	if(do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
+	if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
 		return -EIO;
-	if(cam->params.status.fatalError) {
-		DBG("fatal_error:              %#04x\n",cam->params.status.fatalError);
-		DBG("vp_status:                %#04x\n",cam->params.status.vpStatus);
-		if(cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) {
+
+	if (cam->params.status.fatalError) {
+		DBG("fatal_error:              %#04x\n",
+		    cam->params.status.fatalError);
+		DBG("vp_status:                %#04x\n",
+		    cam->params.status.vpStatus);
+		if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) {
 			/* Fatal error in camera */
 			return -EIO;
-		} else if(cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)){
+		} else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) {
 			/* Firmware 1-02 may do this for parallel port cameras,
 			 * just clear the flags (developer's guide p 3-38) */
 			do_command(cam, CPIA_COMMAND_ModifyCameraStatus,
 			           FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0);
 		}
 	}
-		
+	
 	/* Check the camera status again */
-	if(cam->params.status.fatalError) {
-		if(cam->params.status.fatalError)
+	if (cam->params.status.fatalError) {
+		if (cam->params.status.fatalError)
 			return -EIO;
 	}
 	
 	/* VPVersion can't be retrieved before the camera is in HiPower,
 	 * so get it here instead of in get_version_information. */
 	do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
-	printk(KERN_INFO "  VP-Version: %d.%d %04x\n",
-	       cam->params.vpVersion.vpVersion,
-	       cam->params.vpVersion.vpRevision,
-	       cam->params.vpVersion.cameraHeadID);
 
 	/* set camera to a known state */
 	set_camera_state(cam);
@@ -2499,54 +2817,60 @@
 	int i;
 	struct cam_data *cam = dev->priv;
 
-	DBG("cpia_open\n");
-
-	if(cam->open_count > 0) {
-		DBG("Camera[%d] already open\n",cam->index);
+	if (!cam) {
+		DBG("Internal error, cam_data not found!\n");
+		return -EBUSY;
+	}
+	    
+	if (cam->open_count > 0) {
+		DBG("Camera already open\n");
 		return -EBUSY;
 	}
 	
-	if(cam->raw_image == NULL) {
-		if((cam->raw_image=rvmalloc(CPIA_MAX_IMAGE_SIZE)) == NULL){
+	if (!cam->raw_image) {
+		cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE);
+		if (!cam->raw_image)
 			return -ENOMEM;
-		}
 	}
-	if(cam->decompressed_frame.data == NULL) {
-		cam->decompressed_frame.data=rvmalloc(CPIA_MAX_FRAME_SIZE);
-		if(cam->decompressed_frame.data == NULL){
+
+	if (!cam->decompressed_frame.data) {
+		cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE);
+		if (!cam->decompressed_frame.data) {
 			rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
-			cam->raw_image=NULL;
+			cam->raw_image = NULL;
 			return -ENOMEM;
 		}
 	}
 	
 	/* open cpia */
-	if (cam->ops->open(cam->index, &cam->lowlevel_data)) {
+	if (cam->ops->open(cam->lowlevel_data)) {
 		rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
-		cam->decompressed_frame.data=NULL;
+		cam->decompressed_frame.data = NULL;
 		rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
-		cam->raw_image=NULL;
+		cam->raw_image = NULL;
 		return -ENODEV;
 	}
 	
 	/* reset the camera */
-	if((i = reset_camera(cam)) != 0) {
+	if ((i = reset_camera(cam)) != 0) {
 		cam->ops->close(cam->lowlevel_data);
 		rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
-		cam->decompressed_frame.data=NULL;
+		cam->decompressed_frame.data = NULL;
 		rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
-		cam->raw_image=NULL;
+		cam->raw_image = NULL;
 		return i;
 	}
 	
 	/* Set ownership of /proc/cpia/videoX to current user */
-	if(cam->proc_entry != NULL)
+	if(cam->proc_entry)
 		cam->proc_entry->uid = current->uid;
-	
+
 	/* set mark for loading first frame uncompressed */
 	cam->first_frame = 1;
-	cam->busy_lock = MUTEX;
-        
+
+	/* init it to something */
+	cam->mmap_kludge = 0;
+	
 	++cam->open_count;
 #ifdef MODULE
 	MOD_INC_USE_COUNT;
@@ -2554,78 +2878,51 @@
 	return 0;
 }
 
-/* FIXME */
 static void cpia_close(struct video_device *dev)
 {
 	struct cam_data *cam;
 
-	DBG("cpia_close\n");
 	cam = dev->priv;
 
-	if(cam->ops == NULL) {
-		if(--cam->open_count == 0) {
-			int i;
-			video_unregister_device(dev);
-			/* Need a lock when adding/removing cameras */
-			lock_kernel();
-			if(cam->raw_image) {
-				rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
-				cam->raw_image=0;
-			}
-			if(cam->decompressed_frame.data) {
-				rvfree(cam->decompressed_frame.data,
-				       CPIA_MAX_FRAME_SIZE);
-				cam->decompressed_frame.data=0;
-			}
-			if(cam->frame_buf) {
-				free_frame_buf(cam);
-			}
-			for(i=0; i<CPIA_MAXCAMS; ++i) {
-				if(camera[i] == cam) break;
-			}
-			kfree(cam);
-			if(i != CPIA_MAXCAMS) { /* should always be true */
-				camera[i] = NULL;
-			}
-			unlock_kernel();
-		}
-#ifdef MODULE
-		MOD_DEC_USE_COUNT;
-#endif
-		return;
-	}
+	if (cam->ops) {
+	        /* Return ownership of /proc/cpia/videoX to root */
+		if(cam->proc_entry)
+			cam->proc_entry->uid = 0;
 	
-	/* Return ownership of /proc/cpia/videoX to root */
-	if(cam->proc_entry != NULL)
-		cam->proc_entry->uid = 0;
-	
-	/* save camera state for later open (developers guide ch 3.5.3) */
-	save_camera_state(cam);
+		/* save camera state for later open (developers guide ch 3.5.3) */
+		save_camera_state(cam);
 
-	/* GotoLoPower */
-	goto_low_power(cam);
+		/* GotoLoPower */
+		goto_low_power(cam);
 
-	/* cleanup internal state stuff */
-	free_frames(cam->frame);
+		/* Update the camera ststus */
+		do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
 
-	/* Update the camera ststus */
-	do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+		/* cleanup internal state stuff */
+		free_frames(cam->frame);
 
-	/* close cpia */
-	cam->ops->close(cam->lowlevel_data);
+		/* close cpia */
+		cam->ops->close(cam->lowlevel_data);
+	}
 
-	if(--cam->open_count == 0) {
+	if (--cam->open_count == 0) {
 		/* clean up capture-buffers */
-		if(cam->raw_image) {
+		if (cam->raw_image) {
 			rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
-			cam->raw_image = 0;
+			cam->raw_image = NULL;
 		}
-		if(cam->decompressed_frame.data) {
+
+		if (cam->decompressed_frame.data) {
 			rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
-			cam->decompressed_frame.data = 0;
+			cam->decompressed_frame.data = NULL;
 		}
-		if(cam->frame_buf) {
+
+		if (cam->frame_buf)
 			free_frame_buf(cam);
+
+		if (!cam->ops) {
+			video_unregister_device(dev);
+			kfree(cam);
 		}
 	}
 	
@@ -2637,26 +2934,27 @@
 }
 
 static long cpia_read(struct video_device *dev, char *buf,
-                     unsigned long count, int noblock)
+                      unsigned long count, int noblock)
 {
 	struct cam_data *cam = dev->priv;
 
 	/* make this _really_ smp and multithredi-safe */
-	if( down_interruptible(&cam->busy_lock) ) {
+	if (down_interruptible(&cam->busy_lock))
 		return -EINTR;
-	}
 
 	if (!buf) {
 		DBG("buf NULL\n");
 		up(&cam->busy_lock);
 		return -EINVAL;
 	}
-	if(count == 0) {
+
+	if (!count) {
 		DBG("count 0\n");
 		up(&cam->busy_lock);
 		return 0;
 	}
-	if(!cam->ops) {
+
+	if (!cam->ops) {
 		DBG("ops NULL\n");
 		up(&cam->busy_lock);
 		return -ENODEV;
@@ -2664,8 +2962,9 @@
 
 	/* upload frame */
 	cam->decompressed_frame.state = FRAME_READY;
+	cam->mmap_kludge=0;
 	fetch_frame(cam);
-	if( cam->decompressed_frame.state != FRAME_DONE ) {
+	if (cam->decompressed_frame.state != FRAME_DONE) {
 		DBG("upload failed %d/%d\n", cam->decompressed_frame.count,
 		    cam->decompressed_frame.state);
 		up(&cam->busy_lock);
@@ -2674,12 +2973,13 @@
 	cam->decompressed_frame.state = FRAME_UNUSED;
 
 	/* copy data to user space */
-	if(cam->decompressed_frame.count > count) {
-		DBG("count wrong\n");
+	if (cam->decompressed_frame.count > count) {
+		DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count,
+		    count);
 		up(&cam->busy_lock);
 		return -EFAULT;
 	}
-	if(copy_to_user(buf, cam->decompressed_frame.data,
+	if (copy_to_user(buf, cam->decompressed_frame.data,
 	                cam->decompressed_frame.count)) {
 		DBG("copy_to_user failed\n");
 		up(&cam->busy_lock);
@@ -2695,31 +2995,34 @@
 	struct cam_data *cam = dev->priv;
 	int retval = 0;
 
+	if (!cam || !cam->ops)
+		return -ENODEV;
+	
 	/* make this _really_ smp-safe */
-	if( down_interruptible(&cam->busy_lock) ) {
+	if (down_interruptible(&cam->busy_lock))
 		return -EINTR;
-	}
+
 	//DBG("cpia_ioctl: %u\n", ioctlnr);
 
 	switch (ioctlnr) {
-
 	/* query capabilites */
 	case VIDIOCGCAP:
 	{
 		struct video_capability b;
 
 		DBG("VIDIOCGCAP\n");
-		strcpy(b.name, "CPiA Parport Camera");
+		strcpy(b.name, "CPiA Camera");
 		b.type = VID_TYPE_CAPTURE;
 		b.channels = 1;
 		b.audios = 0;
-		b.maxwidth = 352;         /* VIDEOSIZE_CIF */
+		b.maxwidth = 352;	/* VIDEOSIZE_CIF */
 		b.maxheight = 288;
-		b.minwidth = 48;         /* VIDEOSIZE_48_48 */
+		b.minwidth = 48;	/* VIDEOSIZE_48_48 */
 		b.minheight = 48;
 
 		if (copy_to_user(arg, &b, sizeof(b)))
 			retval = -EFAULT;
+
 		break;
 	}
 
@@ -2782,13 +3085,15 @@
 			retval = -EFAULT;
 			break;
 		}
+
 		/* check validity */
 		DBG("palette: %d\n", vp.palette);
 		DBG("depth: %d\n", vp.depth);
-		if ( !valid_mode(vp.palette, vp.depth) ) {
+		if (!valid_mode(vp.palette, vp.depth)) {
 			retval = -EINVAL;
 			break;
 		}
+
 		down(&cam->param_lock);
 		/* brightness, colour, contrast need no check 0-65535 */
 		memcpy( &cam->vp, &vp, sizeof(vp) );
@@ -2799,12 +3104,13 @@
 		/* contrast is in steps of 8, so round */
 		cam->params.colourParams.contrast =
 			((cam->params.colourParams.contrast + 3) / 8) * 8;
-		if(cam->params.version.firmwareVersion == 1 &&
-		   cam->params.version.firmwareRevision == 2 &&
-		   cam->params.colourParams.contrast > 80) {
+		if (cam->params.version.firmwareVersion == 1 &&
+		    cam->params.version.firmwareRevision == 2 &&
+		    cam->params.colourParams.contrast > 80) {
 			/* 1-02 firmware limits contrast to 80 */
 			cam->params.colourParams.contrast = 80;
 		}
+
 		/* queue command to update camera */
 		cam->cmd_queue |= COMMAND_SETCOLOURPARAMS;
 		up(&cam->param_lock);
@@ -2845,9 +3151,10 @@
 		* is requested by the user???
 		*/
 		down(&cam->param_lock);
-		if(vw.width!=cam->vw.width || vw.height!=cam->vw.height) {
+		if (vw.width != cam->vw.width || vw.height != cam->vw.height) {
 			int video_size = match_videosize(vw.width, vw.height);
-			if(video_size < 0) {
+
+			if (video_size < 0) {
 				retval = -EINVAL;
 				up(&cam->param_lock);
 				break;
@@ -2863,7 +3170,7 @@
 
 		/* setformat ignored by camera during streaming,
 		 * so stop/dispatch/start */
-		if(cam->cmd_queue & COMMAND_SETFORMAT) {
+		if (cam->cmd_queue & COMMAND_SETFORMAT) {
 			DBG("\n");
 			dispatch_commands(cam);
 		}
@@ -2882,10 +3189,10 @@
 		memset(&vm, 0, sizeof(vm));
 		vm.size = CPIA_MAX_FRAME_SIZE*FRAME_NUM;
 		vm.frames = FRAME_NUM;
-		for( i=0; i<FRAME_NUM; i++ ) {
-			vm.offsets[i] = CPIA_MAX_FRAME_SIZE*i;
-		}
-		if(copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
+		for (i = 0; i < FRAME_NUM; i++)
+			vm.offsets[i] = CPIA_MAX_FRAME_SIZE * i;
+
+		if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
 			retval = -EFAULT;
 
 		break;
@@ -2900,7 +3207,7 @@
 			retval = -EFAULT;
 			break;
 		}
-#if 0
+#if 1
 		DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm.format, vm.frame,
 		    vm.width, vm.height);
 #endif
@@ -2913,6 +3220,8 @@
 		cam->vp.palette = vm.format;
 		switch(vm.format) {
 		case VIDEO_PALETTE_GREY:
+			cam->vp.depth = 8;
+			break;
 		case VIDEO_PALETTE_RGB555:
 		case VIDEO_PALETTE_RGB565:
 		case VIDEO_PALETTE_YUV422:
@@ -2930,15 +3239,16 @@
 			retval = -EINVAL;
 			break;
 		}
-		if(retval != 0) break;
+		if (retval)
+			break;
 
 		/* set video size */
 		video_size = match_videosize(vm.width, vm.height);
-		if(cam->video_size<0) {
+		if (cam->video_size < 0) {
 			retval = -EINVAL;
 			break;
 		}
-		if( video_size != cam->video_size ) {
+		if (video_size != cam->video_size) {
 			cam->video_size = video_size;
 			set_vw_size(cam);
 			cam->cmd_queue |= COMMAND_SETFORMAT;
@@ -2949,6 +3259,7 @@
 		    cam->vw.width, cam->vw.height);
 #endif
 		/* according to v4l-spec we must start streaming here */
+		cam->mmap_kludge = 1;
 		retval = capture_frame(cam, &vm);
 
 		break;
@@ -2964,7 +3275,7 @@
 		}
 		//DBG("VIDIOCSYNC: %d\n", frame);
 
-		if (frame<0||frame>=FRAME_NUM) {
+		if (frame<0 || frame >= FRAME_NUM) {
 			retval = -EINVAL;
 			break;
 		}
@@ -2982,7 +3293,7 @@
 			//DBG("VIDIOCSYNC: %d synced\n", frame);
 			break;
 		}
-		if(retval == -EINTR) {
+		if (retval == -EINTR) {
 			/* FIXME - xawtv does not handle this nice */
 			retval = 0;
 		}
@@ -3040,49 +3351,57 @@
 {
 	unsigned long start = (unsigned long)adr;
 	unsigned long page, pos;
-	struct cam_data *cam;
+	struct cam_data *cam = dev->priv;
 	int retval;
 
+	if (!cam || !cam->ops)
+		return -ENODEV;
+	
 	DBG("cpia_mmap: %ld\n", size);
 
-	if(size > FRAME_NUM*CPIA_MAX_FRAME_SIZE){
+	if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE)
 		return -EINVAL;
-	}
-
-	cam = dev->priv;
 
+	if (!cam || !cam->ops)
+		return -ENODEV;
+	
 	/* make this _really_ smp-safe */
-	if( down_interruptible(&cam->busy_lock) ) {
+	if (down_interruptible(&cam->busy_lock))
 		return -EINTR;
-	}
 
-	if( cam->frame_buf == NULL ) { /* we do lazy allocation */
-		if( (retval = allocate_frame_buf(cam)) ) {
+	if (!cam->frame_buf) {	/* we do lazy allocation */
+		if ((retval = allocate_frame_buf(cam))) {
 			up(&cam->busy_lock);
 			return retval;
 		}
 	}
+
 	pos = (unsigned long)(cam->frame_buf);
 	while (size > 0) {
-		page = kvirt_to_phys(pos);
+		page = kvirt_to_pa(pos);
 		if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) {
 			up(&cam->busy_lock);
 			return -EAGAIN;
 		}
-		start+=PAGE_SIZE;
-		pos+=PAGE_SIZE;
-		if (size > PAGE_SIZE) size-=PAGE_SIZE;
-		else size=0;
+		start += PAGE_SIZE;
+		pos += PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
 	}
 
 	DBG("cpia_mmap: %ld\n", size);
 	up(&cam->busy_lock);
+
 	return 0;
 }
 
 int cpia_video_init(struct video_device *vdev)
 {
+#ifdef CONFIG_PROC_FS
 	create_proc_cpia_cam(vdev->priv);
+#endif
 	return 0;
 }
 
@@ -3124,7 +3443,7 @@
 	cam->params.exposure.green1Comp = 214;
 	cam->params.exposure.green2Comp = 214;
 	cam->params.exposure.blueComp = 230;
-	cam->params.colourBalance.balanceMode = 2;	/* auto color balance */
+	cam->params.colourBalance.balanceModeIsAuto = 1;
 	cam->params.colourBalance.redGain = 32;
 	cam->params.colourBalance.greenGain = 6;
 	cam->params.colourBalance.blueGain = 92;
@@ -3151,16 +3470,15 @@
 	cam->params.compressionParams.decimationThreshMod = 2;
 	/* End of default values from Software Developer's Guide */
 	
-	/* Start with a reasonable transfer rate */
-	cam->transfer_rate = 500;
+	cam->transfer_rate = 0;
 	
 	/* Set Sensor FPS to 15fps. This seems better than 30fps
 	 * for indoor lighting. */
 	cam->params.sensorFps.divisor = 1;
 	cam->params.sensorFps.baserate = 1;
 	
-	cam->params.yuvThreshold.yThreshold = 15; /* FIXME? */
-	cam->params.yuvThreshold.uvThreshold = 15; /* FIXME? */
+	cam->params.yuvThreshold.yThreshold = 6; /* From windows driver */
+	cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */
 	
 	cam->params.format.subSample = SUBSAMPLE_422;
 	cam->params.format.yuvOrder = YUVORDER_YUYV;
@@ -3168,8 +3486,14 @@
 	cam->params.compression.mode = CPIA_COMPRESSION_AUTO;
 	cam->params.compressionTarget.frTargeting =
 		CPIA_COMPRESSION_TARGET_QUALITY;
-	cam->params.compressionTarget.targetFR = 7; /* FIXME? */
-	cam->params.compressionTarget.targetQ = 10; /* FIXME? */
+	cam->params.compressionTarget.targetFR = 15; /* From windows driver */
+	cam->params.compressionTarget.targetQ = 5; /* From windows driver */
+	
+	cam->params.qx3.qx3_detected = 0;
+	cam->params.qx3.toplight = 0;
+	cam->params.qx3.bottomlight = 0;
+	cam->params.qx3.button = 0;
+	cam->params.qx3.cradled = 0;
 
 	cam->video_size = VIDEOSIZE_CIF;
 	
@@ -3179,7 +3503,7 @@
 	cam->vp.contrast = 32768;    /* 50% */
 	cam->vp.whiteness = 0;       /* not used -> grayscale only */
 	cam->vp.depth = 0;           /* FIXME: to be set by user? */
-	cam->vp.palette = 0;         /* FIXME: to be set by user? */
+	cam->vp.palette = VIDEO_PALETTE_RGB24;         /* FIXME: to be set by user? */
 
 	cam->vw.x = 0;
 	cam->vw.y = 0;
@@ -3201,11 +3525,13 @@
                                struct cpia_camera_ops *ops )
 {
 	int i;
+
 	/* Default everything to 0 */
 	memset(cam, 0, sizeof(struct cam_data));
 
 	cam->ops = ops;
-	cam->param_lock = MUTEX;
+	init_MUTEX(&cam->param_lock);
+	init_MUTEX(&cam->busy_lock);
 
 	reset_camera_struct(cam);
 
@@ -3215,7 +3541,7 @@
 	cam->vdev.priv = cam;
 	
 	cam->curframe = 0;
-	for( i=0; i<FRAME_NUM; i++) {
+	for (i = 0; i < FRAME_NUM; i++) {
 		cam->frame[i].width = 0;
 		cam->frame[i].height = 0;
 		cam->frame[i].state = FRAME_UNUSED;
@@ -3225,95 +3551,84 @@
 	cam->decompressed_frame.height = 0;
 	cam->decompressed_frame.state = FRAME_UNUSED;
 	cam->decompressed_frame.data = NULL;
-
-	return;
 }
 
-int cpia_register_camera(struct cpia_camera_ops *ops)
+struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel)
 {
-	int i;
-	struct cam_data *cam;
+        struct cam_data *camera;
+	
 	/* Need a lock when adding/removing cameras.  This doesn't happen
 	 * often and doesn't take very long, so grabbing the kernel lock
 	 * should be OK. */
-	lock_kernel();
-	for(i=0; i < CPIA_MAXCAMS && camera[i] != NULL; ++i); /* no loop body */
-	if(i == CPIA_MAXCAMS) {
-		unlock_kernel();
-		return -ENODEV;
-	}
 	
-	if((camera[i] = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL) {
+	if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL) {
 		unlock_kernel();
-		return -ENOMEM;
+		return NULL;
 	}
 	
-	init_camera_struct( camera[i], ops );
-	camera[i]->index = i;
-
+	init_camera_struct( camera, ops );
+	camera->lowlevel_data = lowlevel;
+	
 	/* register v4l device */
-	if (video_register_device(&camera[i]->vdev, VFL_TYPE_GRABBER) == -1) {
-		kfree(camera[i]);
-		camera[i] = NULL;
+	if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER) == -1) {
+		kfree(camera);
 		unlock_kernel();
 		printk(KERN_DEBUG "video_register_device failed\n");
-		return -ENODEV;
+		return NULL;
 	}
 
-	unlock_kernel();
-	
-	/* FIXME */
-#if 0
-	cam = camera[i];
+	/* get version information from camera: open/reset/close */
+
 	/* open cpia */
-	if (cam->ops->open(cam->index, &cam->lowlevel_data)) {
-		rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
-		cam->decompressed_frame.data=NULL;
-		rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
-		cam->raw_image=NULL;
-		return -ENODEV;
-	}
+	if (camera->ops->open(camera->lowlevel_data))
+		return camera;
 	
 	/* reset the camera */
-	if((i = reset_camera(cam)) != 0) {
-		cam->ops->close(cam->lowlevel_data);
-		rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
-		cam->decompressed_frame.data=NULL;
-		rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
-		cam->raw_image=NULL;
-		return i;
+	if (reset_camera(camera) != 0) {
+		camera->ops->close(camera->lowlevel_data);
+		return camera;
 	}
-#endif	
-	return i;
+
+	/* close cpia */
+	camera->ops->close(camera->lowlevel_data);
+
+	printk(KERN_INFO "  CPiA Version: %d.%02d (%d.%d)\n",
+	       camera->params.version.firmwareVersion,
+	       camera->params.version.firmwareRevision,
+	       camera->params.version.vcVersion,
+	       camera->params.version.vcRevision);
+	printk(KERN_INFO "  CPiA PnP-ID: %04x:%04x:%04x\n",
+	       camera->params.pnpID.vendor,
+	       camera->params.pnpID.product,
+	       camera->params.pnpID.deviceRevision);
+	printk(KERN_INFO "  VP-Version: %d.%d %04x\n",
+	       camera->params.vpVersion.vpVersion,
+	       camera->params.vpVersion.vpRevision,
+	       camera->params.vpVersion.cameraHeadID);
+
+	return camera;
 }
 
-void cpia_unregister_camera(int camnr)
+void cpia_unregister_camera(struct cam_data *cam)
 {
-	struct cam_data *cam;
-	if(camnr >= CPIA_MAXCAMS || camera[camnr] == NULL) return;
-	cam = camera[camnr];
-	
-	DBG("unregistering video\n");
-	/* FIXME: Is this safe if the device is open? */
-	video_unregister_device(&cam->vdev);
-	
-	/* Need a lock when adding/removing cameras.  This doesn't happen
-	 * often and doesn't take very long, so grabbing the kernel lock
-	 * should be OK. */
-	lock_kernel();
+	if (!cam->open_count) {
+		DBG("unregistering video\n");
+		video_unregister_device(&cam->vdev);
+	} else {
+		LOG("/dev/video%d removed while open, "
+		    "deferring video_unregister_device\n", cam->vdev.minor);
+		DBG("camera open -- setting ops to NULL\n");
+		cam->ops = NULL;
+	}
 	
+#ifdef CONFIG_PROC_FS
 	DBG("destroying /proc/cpia/video%d\n", cam->vdev.minor);
 	destroy_proc_cpia_cam(cam);
-	
-	if(cam->open_count == 0) {
+#endif	
+	if (!cam->open_count) {
 		DBG("freeing camera\n");
-		kfree(camera[camnr]);
-		camera[camnr] = NULL;
-	} else {
-		DBG("camera open -- setting ops to NULL\n");
-		cam->ops = NULL;
+		kfree(cam);
 	}
-	unlock_kernel();
 }
 
 /****************************************************************************
@@ -3327,7 +3642,9 @@
 {
 	printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
 	       CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
+#ifdef CONFIG_PROC_FS
 	proc_cpia_create();
+#endif
 #ifdef CONFIG_KMOD
 #ifdef CONFIG_VIDEO_CPIA_PP_MODULE
 	request_module("cpia_pp");
@@ -3341,14 +3658,9 @@
 
 void cleanup_module(void)
 {
-	int i;
-	for(i=0; i<CPIA_MAXCAMS; ++i) {
-		if(camera[i]) {
-			video_unregister_device(&camera[i]->vdev);
-		}
-	}
-	
+#ifdef CONFIG_PROC_FS
 	proc_cpia_destroy();
+#endif
 }
 
 #else
@@ -3357,25 +3669,25 @@
 {
 	printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
 	       CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
+#ifdef CONFIG_PROC_FS
 	proc_cpia_create();
+#endif
 
-#ifdef MODULE
+#ifdef CONFIG_VIDEO_CPIA_PP
+	cpia_pp_init();
+#endif
 #ifdef CONFIG_KMOD
 #ifdef CONFIG_VIDEO_CPIA_PP_MODULE
 	request_module("cpia_pp");
 #endif
+
 #ifdef CONFIG_VIDEO_CPIA_USB_MODULE
 	request_module("cpia_usb");
 #endif
 #endif	/* CONFIG_KMOD */
-#else	/* !MODULE */
-#ifdef CONFIG_VIDEO_CPIA_PP
-	cpia_pp_init();
-#endif
 #ifdef CONFIG_VIDEO_CPIA_USB
 	cpia_usb_init();
 #endif
-#endif	/* !MODULE */
 	return 0;
 }
 

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